← 1.0.0 scope ISS-40

A green badge that was never a statement about the published page

Status
Half closed
Found while
SAPE-12, measuring criteria 3 and 6
Triage
Defer — the comparison now exists; automating it does not
Component
ci, docs

What was measured

The CI job named worked example replays docs/external-operations-page.md and asserts every literal in docs_worked_example_test.go's agreed table:

docs_worked_example_test.go:56   workedExamplePage = "docs/external-operations-page.md"

SAPE-12's criterion 6 is about learn/external-operations.html, which lives in a different repository this job cannot see. So the badge was a statement about a markdown copy, and never about the page a stranger reads.

It had already gone wrong. Four of the guarded literals had fallen off the published page while the job stayed green — measured at the pre-fix commit and after:

literalbeforeafter
sapedb invoke HOST NAME ARG=VALUE01
"library:books.get" does not take "shelf"01
"joined" is a number, and "yesterday" is not one01
there is no operation called "library:books.burn" here01

And a second divergence the same shape: the markdown's guard-breaking block called python3 -u mutate.py no-projection. That script appears in no commit of the repositorygit log --all -- '*mutate*' is empty — while the page had already been corrected to break the guard inside a throwaway copy. Two things hid it: the job does not replay that block, and no literal from it was on the list.

What closed, and what did not

Closed: the comparison now exists, as tools/page-drift-check.sh in the workspace repository — the only place that can see both repositories at once. It reads the literals out of the Go test rather than keeping a fourth copy of them, because a checker with its own list goes stale silently, which is the defect it exists to catch. Both divergences above are now fixed and the check passes.

Not closed: nothing runs it. It is a command somebody has to remember, which is one category better than nothing and one worse than a guard. Automating it needs something that can read both repositories — the site is not a submodule and the server's CI has no access to it.

Two things the checker itself taught, worth keeping

Its first version read 18 of 24 rows and called itself green. Six rows are Go raw strings in backticks, because their literals contain double quotes — and every row about a refusal naming an argument is that shape. It was blind to exactly the rows most worth checking. The control that matters turned out not to be a sentinel but a count: read floorOnAgreed from the source and compare it against the row count, because under-reading makes a guard greener, not redder, and so never announces itself.

And a pin can be attached to the wrong thing. Adding one of these literals to agreed turned TestTheWorkedExamplePageAndTheCIJobAgree red, saying the job “no longer holds” a line it had never held — because agreed binds three documents including the CI script, and the job has no reason to break that guard. The page-and-markdown pin belongs in the checker, which makes the weaker and correct claim: the two documents must agree, whether or not the job runs them.

Why Defer

It loses no data and breaks no promise on a public surface. What it did was let the published page drift from its guard, and that gap is now measurable on demand. The remaining half — making it run without being remembered — is infrastructure for a repository boundary, not a defect in the product.

It is the same family as ISS-2: copies kept identical by hand, with nothing comparing them.