ADR-0004: Derive chunk ids from doc_id + ordinal + content hash¶
- Status: Accepted
- Date: 2026-08-31
- Deciders: Maintainers
Context¶
The freshness lesson — keeping an index current without a nightly full rebuild — only works if an incremental update is genuinely cheaper than a rebuild. That depends entirely on the chunk identifier.
Options considered¶
Option A — sequential integers¶
chunk_0, chunk_1, …
Costs: re-chunking a document shifts every subsequent id, so a one-paragraph edit
invalidates the whole document's vectors. It also makes an upsert impossible: you cannot tell
whether chunk_7 is the same chunk it was yesterday.
Option B — a hash of the content alone¶
Costs: loses provenance — you cannot tell which document or which position a chunk came from without a lookup, and two identical boilerplate paragraphs in different documents collide.
Option C — doc_id : ordinal : sha1(text)[:10]¶
Decision¶
Option C.
Consequences¶
Good. An unchanged chunk keeps its id across a re-chunk, so it needs no new vector — which
is what makes the content-hash diff worth doing at all. A changed chunk gets a new id that an
upsert writes over, and the old one is tombstoned rather than deleted, so in-flight queries
stay consistent. Provenance is readable straight off the id, which is why a packed evidence
block can carry doc_id: nw-8842 · chunk: 3/11 and a trace can be diffed between two runs.
Bad. Ids are long and appear in traces, which makes trace output wide. Ordinal is chunking-strategy-dependent, so the same text chunked two ways gets two ids — correct, but it surprises people the first time. And a pure metadata edit that touches the body normalisation will churn ids unnecessarily if the normaliser is not stable, which makes the normaliser a load-bearing component nobody thinks about.
Revisit when: chunks need to be addressable across chunking strategies — for example if a future extension wants to compare two strategies' retrieval of "the same" passage. That would need a separate content-addressed identity alongside this one.
What would change this¶
A re-chunking strategy under which doc_id + ordinal + content_hash is not stable.
The scheme assumes ordinal is meaningful and stable within a document. A semantic or sliding-window chunker that re-segments on every ingest breaks it — the same text lands at a different ordinal, the id changes, and the incremental path degrades into a full rebuild without saying so.
Check it by re-running the same document through a chunker twice with a one-word edit in the middle and counting how many chunk ids change. More than the chunks that actually changed means this decision does not hold for that strategy.