Skip to main content
Six kinds, and each one is here because the planner needs a specific fact from it.

Five rebuild; one travels

Five of them are deterministic functions of the blocks, so any client rebuilds them and gets identical bytes. The vector index is not — embedding vectors and HNSW structure are not reproducible byte for byte across machines, and no design fixes that (BLAS kernel selection, non-associative float reduction, insertion order). That is exactly why the protocol has a travelling index: the vector layer ships inside the artifact instead of being rebuilt. See 7. Embeddings and the vector index.

Freshness has two levels

index status compares two things, and both are needed:
  • the module root, which catches any change of composition;
  • a leaf fingerprint over the resolvable block ids, which catches what the root cannot — a redaction that tombstones bytes without changing the composition.
A stale index is not consulted with stale data. It makes the planner pessimistic about selectivity, raises the recall floor, bypasses the plan cache, and says so in explain.

An empty index is worse than an absent one

The planner consults an empty index, gets nothing, and reports “nothing found” with confidence. So population is a hard requirement on every vitruvio index, an empty index is treated as absent and warned about loudly, and flush() refuses to write one to disk.

Everything derived lives in .vitruvio/

Sibling of the SDK’s boltzmann/, and never inside the layout. Deleting it costs a rebuild, never knowledge.