Skip to content

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:

yaml
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 itself

Each field earns its place:

  • source_ref gives the git referent and yields a changed-file list.
  • source_hash survives force-pushes and covers non-git sources.
  • source_contract catches 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 surface

A 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:

StateMeaning
freshno changed file intersects the extraction surface
driftedchanged source files intersect the extraction surface
contract-driftthe extraction contract moved (a glob or template), even with no source change
hash-driftthe input file set hashes differently (e.g. a force-push moved the ref)
pinneda SHA/tag-pinned source with no tracking branch - drift is not computed
unverifiedthe 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.

E11 Alloy - knowledge as a build artifact.