Provenance and drift
This is the machinery that answers "why should an agent trust this file, and is it still true?" - in a way you can compute rather than hope.
Provenance is written only by stamp
Provenance is the frontmatter that records what a generated doc was derived from. It is written only after the gate passes, by a dedicated command, never at generation time:
last_verified: 2026-08-27
source: your-repo:service/audit # which unit this doc describes
source_ref: 3f9a1c… # the exact commit it was derived from
source_hash: 9b2e… # sha256 over the matched input file set
source_contract: 71c4… # hash of the extraction contract itselfEach field earns its place:
source_refgives the git referent and yields a changed-file list.source_hashsurvives force-pushes and covers non-git sources.source_contractcatches the drift git cannot see - a widened glob or an edited template makes a doc stale with no source file touched.
alloy stamp is the only writer of these fields. It re-runs the gate first and refuses to write anything if it fails, because provenance asserts a doc was verified against its source, and a doc that fails the gate has not been.
Why stamp is the honest step
stamp asserts "these docs were authored or reviewed against this commit" - and the gate cannot check that assertion for anyone. It validates shape, not derivation. So the guarantee is behavioural: stamp only the docs you actually wrote or re-verified against the bundle's source files. Stamping a doc nobody read is the one way to make alloy status lie. When the source tip has moved since the bundle was written, the CLI warns, and the honest move is to regenerate and re-author rather than stamp stale work.
Drift is a computation
"Is this doc stale?" stops being a judgment and becomes arithmetic:
drift = files changed (source_ref → tracked tip) ∩ the doc's extraction surfaceA change inside the doc's declared input set marks it drifted. A change outside it marks nothing. In a monorepo the ref is a coordinate, not a staleness bit: one repo-wide commit serves as many independent per-unit referents, because each unit's intersection is computed separately.
There are six honest states, so a doc never reads as verified when nothing verified it:
| State | Meaning |
|---|---|
fresh | no changed file intersects the extraction surface |
drifted | changed source files intersect the extraction surface |
contract-drift | the extraction contract moved (a glob or template), even with no source change |
hash-drift | the input file set hashes differently (e.g. a force-push moved the ref) |
pinned | a SHA/tag-pinned source with no tracking branch - drift is not computed |
unverified | the stamp cannot be resolved (a torn stamp, a vanished unit, a rule pack that failed to load) - a human decides |
In a spoke, provenance is nearly free
In the spoke path the source is self: true: the referent is the repo's own HEAD, so a doc and the code it describes share one commit history. Every commit that changes a unit's files marks that unit's docs drifted on the next alloy status, and the drift signal needs no external checkout to compute. This is the property the federated model always promised - documentation and code moving on one timeline.
Curated docs get parallel machinery
A hand-written doc has no extraction surface, so it gets a parallel mechanism rather than an exemption: a required owner, plus either a review_after calendar date (legitimate only where no code referent exists) or referents: naming source files whose change marks the prose drifted. Prefer referents wherever real code governs the doc - a mechanical signal beats a calendar guess.