Async Digital / source anchor
Drift defence

The source anchor

Some pages are written rather than generated, but still derived from somewhere else. The charter page, for one, is an adaptation of the charter's own README. The source anchor is how a page like that is held to the source it came from, so drift surfaces instead of hiding. It is one rung of the drift ladder, shown here in full.


The problem

A copy drifts from its source

The charter page is a derived view: its words are an editorial adaptation of the charter README, shifted into plain voice and the studio's framing. That makes it readable, but it also makes it a second copy of something that keeps changing. When the README is revised and the page is not, the two quietly disagree, and the page starts telling readers something that is no longer true.

We cannot regenerate the page from the README the way we regenerate the skill surface from its manifest. So we do the next best thing. We do not stop the copy existing. We make its drift impossible to miss.


The anchor

Hash the source, store the hash beside the copy

When we review the page and agree it matches the source, we take a fingerprint of the source: a short hash of the charter README. We store that hash next to the page, in a small lock file. The hash is the anchor. It does not hold the words; it holds a single fact, that this version of the source is the one the page was last checked against.

Source
charter README
the charter repo's
README.md
Anchor
the fingerprint
sourceHash a6d72103
Lock
charter.lock.json
committed beside
the page
The anchor is a hash of the source, written into a committed lock file next to the page. One fact, not a copy of the words.

The check

Recompute on every build, and compare

On every build, and on demand, we take a fresh fingerprint of the source and compare it with the anchored one. If they match, the page is still checked against the current source: it passes. If they differ, the source has moved since the page was last reviewed, and the build says so, by name.

after the README is revised
> generate.py --check
STALE: charter source README changed
      (anchored a6d72103 != live 3f9c1b07)
  re-read the page against the source, then re-anchor:
  generate.py --stamp
The real output. A match passes silently in the noise of a build; a mismatch names the page, shows both hashes, and tells you what to do next.

Re-sync is deliberate

The hash never rewrites the page

A stale result is a warning to look, not a fix. The anchor cannot read the page or rewrite it. All it knows is that the source has changed since the page was last reviewed. Whether the page itself needs to change is a judgement: it takes reading the new source and reasoning about the impact, which is the agent's work, not the hash's.

So clearing it takes two steps, in order. First the real work: the agent re-reads the page against the new source and edits it if the meaning has moved. Then re-stamp, which simply records that the page has now been reviewed against this version of the source. Re-stamping updates the stored hash and nothing else. It cannot fix the page, so running it without doing the reading first would only hide the warning instead of answering it.

Fresh

Stamped

The page is reviewed and anchored. The hashes match. The build passes.

Source moves

The README is revised

A change lands in the source. The page has not moved with it yet.

Stale

The check flags it

The live hash no longer matches the anchor. The build names the page.

Fresh again

Re-read, then re-stamp

The agent reconciles the prose, then re-anchors. Back in step.

Drift always surfaces, and closing it always takes judgement. The anchor guarantees the warning; it will not pretend the review happened on its own.

One lane, every derived view

The same defence, twice

This is not a one-off for the charter page. The skill surface is held the same way, against its manifest. The only difference is what happens when the source moves: the skill surface is rebuilt from it automatically, regenerated by a script that makes no judgement and runs the same way every time, while the charter page is reconciled by the agent, which reads the change and reasons about it. The split is set by the source, not chosen: the manifest is structured data, so rendering it is a mechanical transform with a single right output, while the README is prose, so adapting it into this page takes judgement and has many valid forms. You generate where the step is mechanical, and you can only anchor where it takes judgement. Both are anchored to their source by a hash that cannot be quietly ignored.

See where this sits among the defences: the drift ladder