Indexing¶
An indexing run has two phases: prepare a complete target snapshot, then apply it. The same planner powers plan, index, and migration previews.
Establish the corpus boundary¶
For repeatable projects, put the full source scope in steadlith.toml:
[sources]
include = ["handbook/**/*.md", "policies/**/*.txt"]
exclude = ["**/drafts/**", "**/.git/**", "**/.steadlith/**"]
Then run commands without positional paths:
steadlith plan
steadlith index
Positional paths replace the configured include set for that invocation. They still define a complete desired corpus:
steadlith plan handbook/ policies/
Use this form for controlled one-off indexes, not to add a single file to an established corpus.
Read the plan¶
Each occurrence receives one operation:
Operation |
Meaning |
New embedding when identity is unchanged? |
|---|---|---|
|
New chunk occurrence. |
Only when neither the active index nor cache holds its chunk hash. |
|
Same hash and position. |
No. |
|
Same chunk content at a different position or with changed source metadata. |
No. |
|
Previously active occurrence absent from the target. |
No; the old record is tombstoned. |
A model or provider-parameter migration can re-embed kept and moved content because embedding identity changed. The chunk operation alone does not describe embedding work, so also inspect embedding_count, cache_hits, tokens, and the price estimate.
Unknown provider prices remain unknown. Steadlith never fetches pricing or assumes that its word-based token count matches a provider billing tokenizer.
Copied, repeated, and renamed chunks reuse the active vector even after its cache entry is pruned. This reuse does not count as a cache hit in the plan or apply result.
Apply approval gates¶
Steadlith requires explicit approval for three classes of effect.
Network or model download:
steadlith index --allow-network
Any logical deletion:
steadlith index --allow-delete
Replacing a non-empty corpus with an empty one:
steadlith index --allow-delete --allow-empty
Combine flags only after reviewing the plan. These approvals are invocation-scoped and are not saved.
Apply behavior¶
An apply performs the following work:
Rebuild the target manifest from sources.
Compare the current generation and corpus root with the prepared state.
Reuse active vectors with the same embedding identity.
Read content-addressed cache entries for remaining chunk hashes.
Call the configured provider in bounded batches for misses.
Write successful batches to the cache.
Build all target index records in memory.
Publish records, document state, manifest, root, embedding identity, and the next generation in one SQLite transaction.
Mirror the latest committed manifest under a short SQLite writer reservation, preventing an older apply from overwriting a newer generation’s mirror.
Queries never observe a partially published generation. If another writer committed after preparation, the apply fails instead of overwriting the newer state.
Plan preparation, status, and database verification each read one consistent SQLite snapshot. An interrupted apply rolls back its uncommitted index changes; the same connection can be reused after the interruption.
Readers remain available during mirror publication. A mirror write failure leaves
the committed index usable and reports a repair instruction; rerunning index
with the same configuration and source scope repairs the mirror.
Provider-side charging cannot be strictly transactional with a local SQLite commit. A process failure after a remote provider accepts a request but before the cache records its response can lead to a repeated charge on retry.
Idempotent routine¶
A practical update routine is:
steadlith plan --json > plan.json
steadlith index
steadlith status --json
steadlith verify --json
If plan reports deletions, review source scope and rerun index --allow-delete. Do not automatically attach --allow-delete to every scheduled job. The gate is most useful when it can stop a bad glob, an empty mount, or an accidental partial path.
JSON automation¶
Use process exit codes and JSON together:
steadlith plan --json
Consumers should:
check the process exit code before parsing success output;
treat paths and source text as untrusted display data;
ignore unknown JSON fields for forward compatibility;
compare operation and embedding counts against policy limits;
require a separate operator approval when deletes are present;
run
verifyafter a successful apply.
See JSON output for envelopes and compatibility rules.
Hard cuts¶
A hard cut occurs when no primary or backup Rabin boundary is usable before max_tokens. High hard-cut rates can indicate parameters that fit the corpus poorly.
Check hard_cut_rate in status --json and compare it across representative documents before changing defaults. A hard cut is not a verification failure, but it weakens the practical edit-locality benefit expected from content-defined boundaries.
Common mistakes¶
- Indexing one changed file by position
The omitted files are planned for deletion. Use configured globs for the full corpus.
- Changing
steadlith.tomland querying immediately The active index still carries the old identity. Run
plan, apply the migration, then query.- Putting cache and index at the same path
Validation rejects the configuration because the schemas and lifecycles differ.
- Indexing generated state
Managed state is excluded unconditionally. Add application build outputs and private working directories to
[sources].exclude.