Evidence should belong to the run that used it
How per-run source rows, revision checks and publication-time binding keep a research report tied to the evidence it used, and what that doesn't prove.

On this page
One row per source is a perfectly reasonable database constraint. If two research runs read the same page, why store it twice? You canonicalise the URL, make that key unique and let the second run find the row the first one created. The local version of my research pipeline worked this way, and the trouble came from a column that doesn’t look like evidence at all. Each source row also carried a family label, a classification of what kind of source it was, and when a later run classified a source, it rewrote the label on the shared row. The ledger behind an earlier run could therefore change without anybody touching that run.
A research report makes a claim that is easy to overlook, which is that these particular sources are what it was based on. For that to mean anything, neither a changing source set nor a later run can be allowed to alter the answer after the fact. In the August 2026 versions of my personal knowledge system, a Rust engine and a locally tested Cloudflare port approached this at two boundaries: the source set a writer submitted and the rows a research run could change.
The writer and the set it was given
In the Rust engine, a host agent outside the engine wrote a dossier for a cluster of source cards and submitted it. That leaves a window in which the cluster can change underneath the writer, and the writer can’t be relied on to notice. So submission compared the card IDs in the dossier against the cluster’s current set and refused anything short of an exact match, which meant a dossier could neither rest on a stale set nor leave out a card that had arrived in the meantime.
That check is narrower than it looks. Equal sets say nothing about order or about what the cards contain, so a card edited under the same ID passes. A second, stronger revision covered this for backfills: a hash over each card’s ID, content hash and update time, together with the model and the prompt version, computed so that order doesn’t matter. Change a card, or rerun with a new model or prompt, and you get a different revision instead of something that can be mistaken for the old work. Editorial decisions worked from the ID-set revision. Replaying a decision compared the revision it recorded with the cluster’s current one, and a changed evidence revision minted a new decision while recorded history was never rewritten. Refusals left a trace as well: a failed quality gate recorded a blocked decision and stopped before any wiki write.
A run’s sources belong to that run
The Cloudflare port dealt with the shared row directly. Its migration made sources unique on the pair of run and canonical key. Imagine two invented runs that both read https://example.org/post, one filing it as vendor documentation and the other as commentary. Each now keeps its own row and its own label, and nothing the second run decides can reach the first. That is a narrower guarantee than immutability, since a run’s rows are unreachable from other runs without being append-only within the run. It is the guarantee a later reader of the report needs, though, and the same constraint did a second job. Sources were added with INSERT OR IGNORE, so offering a known key to the same run reported zero new rows, and the loop’s stop rules read that count as novelty. An agent that keeps finding the same pages isn’t learning anything, and now the database could say so.
Citing only what the run saw
When a model cites a card ID the run never minted, it is tempting to assume it meant something close and repair the citation to a neighbouring card. The port dropped those claims instead and counted them as uncited. A repair might be a good guess, but a bad guess would present an unsupported claim as a sourced one, which is worse than having no claim at all. Contradictions got the same treatment: the IDs in a reported contradiction had to belong to the run’s own claims, so an invented one was refused. An error in the audit marked the report incomplete and said so in its first line. And no model wrote the report. It was rendered deterministically from the ledger, which left a dropped claim no way back in through fluent prose.
Separating outcomes mattered as much as the gates. The retained report, its audit quality and its publication to the wiki each had their own result, so a failed wiki write left the frozen report intact and recorded the run as completed without a write, instead of losing the report or pretending it had been published. Interruption followed the same thinking. The workflow ran on Cloudflare Workflows, whose memoized steps let a replay reuse completed results, and because card IDs were deterministic and writes were upserts on natural keys, even a crash that forced a step to run again converged on the same row counts.
What the rewrite changed
The rewrite kept the idea and moved the decisive check to the moment of publication. A material claim was bound to evidence at a specific revision, and before publishing, each binding was resolved again: the card had to exist and be authorised, its current revision had to equal the one the claim was bound to, its class had to be evidence rather than discovery or editorial context, and a supporting source had to be available. The knowledge store then wrote the new version, its ordered manifest of sources and the pointer advance in one conditional batch, so a body was never visible without its complete binding, a version with no source cards was refused, and a publisher that lost a race wrote nothing. Order had become part of the record, which the old set comparison never attempted.
Provenance is a smaller promise than truth
Every one of these mechanisms answers the same question: which evidence, at which revision, did this report use? None of them answers whether the report is right. A dossier can match its cluster exactly and misread every card, and a binding that is current at the instant of publication can support a claim that is simply false. A tidy citation carries an authority it hasn’t earned, and the better the plumbing, the more convincing it looks. The promise is smaller and still worth having. When a report turns out to be wrong, you can go back and argue with the evidence it actually used, instead of with whatever the database happens to contain by then.
Chris Chabot, November 2026