DR-108: The provenance register gains an append-only ingest-event history
DR-108: The provenance register gains an append-only ingest-event history
Section titled “DR-108: The provenance register gains an append-only ingest-event history”Context
Section titled “Context”source_documents.parent_id and .version have zero writers in TypeScript,
Python and SQL. The /documents/[id]/diff version-pair branch is therefore
unreachable in production and every document renders “This is the first version”.
Found as a side-finding while wiring an unrelated column (id-407), and investigated
as id-409.
The columns are not merely unwritten — a chain is unreachable by construction.
The ratified admission writer resolve_or_mint_source_identity
(20260703160100_id138_admission_identity_fn.sql) omits both from its INSERT column
list, and mints id = uuid5(NS, 'sd:' || rel_path) with ON CONFLICT (id) DO NOTHING. A changed file at the same path resolves to the same row, by design —
that stability is what makes the register a register.
Corpus-reframe R5, the only current statement of source_documents semantics, calls
its rows the permanent provenance register: never deleted, meaningful once the bytes
are discarded. Register semantics, not version-chain semantics. Current docs are
otherwise silent on what a document version is.
The read surface is wide but shallow — 11 production sites plus an RPC pair, an api view and an MCP tool, with no hub symbol. Its origin is id-117, ratified roughly two weeks before the corpus reframe that superseded its premise.
Decision
Section titled “Decision”Re-ingest history is recorded as an append-only ingest-event row on a separate
register-history table, generalising the polymorphic verification_history shape
DR-076 already names as the pattern for this class.
source_documents.parent_id/.version are not the ratified mechanism and are
not adopted as one. The version-chain read surface is re-pointed at the new
history, not deleted — it gains a real source of truth for the first time.
Decided on end-user value against the platform’s purpose, at the owner’s direction. The concrete need for a procurement SMB is “this tender pack was reissued with amendments — are my bid answers still valid?” That is a trust question about evidence, and it sits on the platform’s reason for existing: structured data an AI can cite. R1 already commits to citations degrading gracefully to the provenance register; “what changed on re-ingest” is the missing half of a promise already made.
Alternatives Considered
Section titled “Alternatives Considered”- (a) A new
source_documentsrow chained byparent_id. Buys the same user value at much higher cost: it reopens DR-026 clause (i) and the SEED-CONTRACTuuid5-on-path identity formula, which is load-bearing. - (c) Nothing — the register records current state only. Cheapest, and defensible on the documents, which are silent here. Rejected on user value: for a tender-driven product, losing “has my evidence changed” is a real capability loss. The investigation rated deleting the reader LOW confidence precisely because the reframe had reasoned about records and concepts, never the evidence UI.
Consequences
Section titled “Consequences”- id-409 needs a spec chain before implementation: what counts as an ingest event, the row shape, and how the read surfaces compose it are all open.
parent_id’s place in id-399’sR8_PROTECTED_COLUMNSis now decidable but not decided — R8’s own text names origin type, locator, retention class, cadence and auth, andparent_idis not among them, so its protection is a conservative do-not-drop rather than a ratified column.- The
source_documentstable comment (“Each row is a specific version of a document. The parent_id chain links versions together.”) contradicts this ruling and should be corrected when the table is next touched. app/api/cron/review-cadence/route.tsdropped its.is('superseded_by', null)guard, justified in-comment by “the existingparent_idversion chain” — a chain that never forms. The ingest-event history is what a restored guard would read.