On this page
Writing-style store
The writing-style store is Skryba's record of the text its user actually
wrote: the passages and preferences tables of its
fleet database, with the samples the person rewrites and
the harvesters that collect their writing beside them. It is the only
durable state in the product that keeps prose rather than a measurement of
prose.
Why this store keeps the text
The detector cache deliberately forgets what it scored: it holds
(detector, sha256(text)) -> probability and nothing else, because a
probability is all it ever has to answer again, and a hash is enough to know
the question repeated. That design is correct there and useless here. A hash
cannot be trained on. An adapter that writes like a person needs the person's
sentences, so this store keeps them, in rows the person reads and exports with
skryba writing-style.
Everything else in Skryba is about machine prose: the input is AI text, the candidates are model completions, the judge scores a rewrite. This store is the other half, and only that half.
Location
The schema skryba, or the one SKRYBA_DATABASE_SCHEMA names. There is no
store file.
Schema
CREATE TABLE skryba.passages (
text_hash TEXT PRIMARY KEY,
source TEXT NOT NULL,
words BIGINT NOT NULL,
text TEXT NOT NULL,
sequence BIGSERIAL NOT NULL,
kind TEXT NOT NULL DEFAULT 'files'
);
CREATE TABLE skryba.preferences (
pair_hash TEXT PRIMARY KEY,
kept TEXT NOT NULL,
machine TEXT NOT NULL,
strategy TEXT,
sequence BIGSERIAL NOT NULL,
sample TEXT
);
CREATE TABLE skryba.samples (
id TEXT PRIMARY KEY,
seed TEXT NOT NULL,
kind TEXT NOT NULL,
words BIGINT NOT NULL,
machine TEXT NOT NULL,
model TEXT NOT NULL,
created_at BIGINT NOT NULL,
sequence BIGSERIAL NOT NULL
);
CREATE TABLE skryba.harvesters (
name TEXT PRIMARY KEY,
kind TEXT NOT NULL,
path TEXT,
min_words BIGINT NOT NULL,
max_words BIGINT NOT NULL,
cursor TEXT,
last_run_at BIGINT,
last_added BIGINT,
created_at BIGINT NOT NULL,
updated_at BIGINT NOT NULL
);
CREATE TABLE skryba.harvest_schedule (
owner TEXT PRIMARY KEY,
schedule_id TEXT NOT NULL
);
A store an older Skryba wrote gains kind and sample the first time this
one opens it; every passage it already held is of kind files, which is
where add read it from.
Passages are prose the person wrote, ingested from their own files by
writing-style add or by a harvester.
source is the path the passage came from, or <kind>:<harvester>/<item>
for a harvested one; words is its whitespace word count, and kind is
files, messages, notes or transcripts.
Preferences are pairs the person themselves resolved, recorded by
writing-style keep: kept is what they kept or wrote, machine is what the
model produced for the same passage, strategy is the rewrite strategy
that produced it when a rewrite named one, and sample is the sample it
rewrote when it rewrote one.
Samples are machine-written passages the person rewrites, written by
writing-style samples generate: id is the
same zero-terminated digest as text_hash, over the model's text; seed is
the text_hash of the passage that set
its subject and length, kind and words the seed's, and model the Brama
model that wrote it. Harvesters are the places a harvest reads, with the
cursor its last run stopped at, and harvest_schedule holds the one Stado
schedule id that runs them.
Identity is the text itself
text_hash is a lowercase hexadecimal SHA-256 over the passage text followed
by one zero byte; pair_hash is the same digest over the kept text, a zero
byte, the machine text, and a zero byte. Both are primary keys written with
ON CONFLICT DO NOTHING, which makes re-ingestion cheap and honest:
run add again over a directory you edited and it adds what changed and
nothing else, counting the rest in skipped. The same hash is why a
preference cannot be recorded twice.
The zero byte is a terminator, not decoration: without it the pair
("ab", "c") and the pair ("a", "bc") would hash identically and the
second one recorded would be silently dropped as a duplicate. It also means
this digest is not the detector cache's text_hash, which is a plain SHA-256
over the exact UTF-8 input — the two tables answer different questions and
their keys are not interchangeable.
Reads are oldest first
passages and preferences are read ORDER BY sequence, the order rows were
added, so export --limit N takes the same first N rows before and after the
store grows.
Invariants and failures
add,keep,exportandstatuscall neither Brama nor a detector, so no model or detector credential is read.trainreads none either: it spawns the localsterbinary.- Skryba runs no gradient over these rows itself. It writes the file Ster reads and then calls Ster; see the training boundary.
- Both texts of a preference are trimmed before storage, and a pair whose sides are equal is refused rather than stored.
creating the writing-style tables in …,adding a passage from <SOURCE> to …,keeping a preference in …— the database refused; the cause is Postgres's own sentence. A connection failure is described underskryba database check.
Deleting rows from these tables deletes the person's captured writing and nothing else: their original files are untouched, and no rewrite, score, or policy depends on it.
Maintained as part of the website-owned Skryba documentation corpus.