A fourth cold review attacked the pin itself. BLOCKER empty, the mechanism upheld, and
every finding was prose claiming more than I had measured — plus one one-token gap that
was live.
THE ABSOLUTES, refuted by execution and now corrected in all three places they appeared
(guard docstring, `docs/guard-inventory.md`, the decision record's `rule:`):
* "A pin cannot produce a false green." True only in the trivial reading. A pin is
immune to a different SPELLING of the command — the entire class that defeated the
parser nine times — and is NOT immune to the same text MEANING something else. Two
mutants re-armed ersatztv#887 through `web/package.json` alone: `RUN npm run build`
executes whatever that file says, so `"build": "vitest run && …"` puts the suite back
into the gitless stage with every pin still matching, and `"prepare"` does it via
`npm ci`. Now pinned: exactly one script may mention vitest, and its body is fixed.
* Residual (1), "a stage that does not carry the SPA source is unpinned — correct,
since without `web/` there is no suite there". False. The boundary is what
`copies_spa_source` RECOGNISES, which is narrower than "has the suite available".
Restated, with the case still outside it named: a stage copy that RENAMES the tree.
* The substring sweep's "never a false green". Its reported failures are false reds;
what it fails to REPORT is not. `SUITE_MENTIONS` is a hand-written SELECTOR — a third
category beside population and pin, and the worst-behaved, because a population going
short is caught by an equality and a stale pin reddens loudly, while a selector going
short is silent. It was short by exactly one entry: `npm t`, npm's own alias, which
this guard already names among the spellings that defeated the parser. A stage
running `npm t -- --run` escaped it. Fixed, and the category is now named.
ALSO CLOSED: `run: |` -> `run: >` folded the two-line body into one command whose
whitespace-normalised text was byte-identical to the pin, so the marker script swallowed
the suite as its arguments — the body is now compared LINE BY LINE, since a newline
separates two commands. A SECOND step named `Test SPA` inherited the exemption both the
pin lookup and the sweep key on; exactly one is now required. And `COPY web*/` — a glob
that matches `web/` — was read as not carrying the source, leaving the receiving stage
unpinned.
Battery 45 -> 51, 0 missed. The remaining meaning-change route, an `ENV` rewriting `PATH`
so a pinned `RUN` resolves a different `npm`, is not modelled and is recorded as a
residual rather than implied away.
Refs: #887
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019T79beF1Ufid3dXju4yqkF