Manifests and Merkle roots¶
A manifest records the desired content state without storing vectors. Its roots provide deterministic identifiers for document and corpus state.
Document manifest¶
A document manifest contains:
schema version;
document ID;
ordered chunk records;
document metadata;
chunker ID;
chunker parameter hash;
normalizer version;
computed document root.
Each chunk record contains:
chunk hash;
start and end source offsets;
deterministic token count;
boundary and source metadata not already represented by reserved fields.
All chunks in one document manifest must carry the same chunker, parameter, and normalizer identity. Mixed identities are rejected.
Document Merkle tree¶
Ordered chunk hashes become domain-separated leaf hashes. Adjacent nodes are combined upward. When a level has an odd final node, the parent construction records an empty right input instead of duplicating the node.
The empty document has one defined empty-root value. Order is significant:
[hash_A, hash_B] != [hash_B, hash_A]
The tree can compare equal-sized states while pruning equal subtrees. A root match with the same leaf count means the ordered chunk-hash sequence matches.
Corpus manifest¶
A corpus manifest maps document IDs to document manifests. For each document, Steadlith hashes the ordered pair (document_id, document_root). These values, sorted by document ID, form the corpus Merkle tree.
The corpus root changes when:
a document is added or removed;
a document ID changes;
an ordered document chunk hash changes;
chunk order changes.
It does not change for offsets or filesystem metadata alone when ordered chunk hashes remain the same. The separate generation counter protects same-root transactions such as metadata moves and model migrations.
Authoritative and mirror copies¶
The serialized manifest stored inside SQLite is authoritative. After an index transaction commits, Steadlith writes a formatted JSON mirror beside the database:
.steadlith/index.sqlite3.manifest.json
The mirror is intended for inspection, backup checks, and diffs. It is excluded from source discovery so indexing cannot ingest its own changing output. steadlith verify compares it with the authoritative SQLite copy and reports a missing, unreadable, or different mirror as a verification failure.
If mirror writing fails after commit, the CLI reports that the index committed. Verification then reports the mirror mismatch. Rerun steadlith index with the same configuration and source scope to repair the mirror without creating a new SQLite generation. After a migration failure, use the finalized configuration and its configured sources; do not repeat the migration command.
Root and generation¶
Use both values as a state token:
State change |
Corpus root |
Generation |
|---|---|---|
No-op plan |
Same |
Same |
Text or chunk-order change |
Usually changes |
Increments on apply |
Metadata-only move |
Can remain same |
Increments |
Embedding-model migration |
Same |
Increments |
Delete then restore same content |
Can return to earlier root |
Continues increasing |
This is why stale-write checks cannot rely on the root alone.
Serialization checks¶
Manifest deserialization validates schema versions, record types, and declared roots. Unknown persisted schema versions fail closed.
The JSON representation is deterministic for the same manifest, but consumers should not derive application authorization from a root alone. A root proves equality under the specified hash domains, not source trust or operator intent.