Status: Accepted Date: 2026-07-11 Related: 0008, 0011, 0012, 0017
Hybrid indexing writes metadata, dense vectors, and sparse/exact structures. Partial success must never become the active search generation. Chunk 04+ needs minimal state and idempotency rules before commit code exists.
| State | Meaning | Searchable as active? |
|---|---|---|
building |
Commit in progress; indexes may be partial | No |
ready |
All required writers succeeded; sealed | Yes (if selected active) |
failed |
Commit aborted; partial data must not be used | No |
superseded |
Was ready; replaced by a newer ready snapshot | No (except explicit pin/replay) |
ADR-0008's building | ready | superseded is extended with failed.
active_snapshot_idfor a project points only at a snapshot inready.- Flip is atomic in the metadata store (single transactional update).
- Search defaults to
active_snapshot_idunless a caller pins asnapshot_idfor replay/eval; pinnedfailed/buildingsnapshots are rejected. - A
readysnapshot becomessupersededwhen a newerreadysnapshot is activated (optional retain for rollback/eval).
1. Create snapshot row: status=building, parent_snapshot_id, versions, roots TBD
2. Write artifacts/chunks/metadata for this snapshot_id only
3. Write dense vectors for this snapshot_id only (VectorStore)
4. Write sparse/exact structures for this snapshot_id only
5. Verify counts/hashes against manifest expectations
6. Set status=ready (seal)
7. Flip active_snapshot_id -> this snapshot_id
If any step before seal fails: set status=failed, record failure_reason,
do not flip active_snapshot_id. Prior active remains searchable.
- All dense/sparse upserts for a commit use the building
snapshot_id. - Readers with
snapshot_id=activenever see building rows. - Failed snapshot data may be deleted by GC later; until GC, queries must still filter it out by status.
- Never reuse a
failedsnapshot_id for a new attempt; allocate a new id.
- Re-running commit for the same building id is allowed only while
building, and writers must be idempotent on(snapshot_id, chunk_id)/ chunk_hash keys. - After
readyorfailed, mutation of index payloads for that id is forbidden (read-only seal). - Retry after failure starts a new
buildingsnapshot with a new id, optionally copying unchanged chunk hashes from parent (incremental). - Manifest fields
source_merkle_rootandchunk_set_hashare written before seal and are immutable thereafter.
Per ADR-0017, seal requires success of:
- metadata manifest + chunk records
- exact index path used by PoC (in-memory/exact structures acceptable in 02–08)
- dense writer when dense is enabled for the snapshot; if dense is disabled in a
sparse-only test profile, snapshot must record
dense_enabled=false - sparse/FTS writer when sparse is enabled; else
sparse_enabled=false
A snapshot cannot be ready if a declared-enabled writer failed.
validation_error, artifact_missing, hash_mismatch, dense_write_failed,
sparse_write_failed, metadata_write_failed, verify_failed, cancelled.
Traces record snapshot_id, transition, and failure code.
- Active search stays consistent under crash mid-commit.
- Tests can assert failed snapshots never appear in default search.
- Failed snapshots need later GC to reclaim vector/sparse space.
- True distributed two-phase commit across services is still best-effort in PoC; metadata status is the gate.
- Chunk 04 implements state machine + golden transition tests.
- Future-layer: retention/GC and cross-service snapshot export verification (ADR-0012).