docs(901): the third count had no in-repo source, so it becomes what both cited artifacts say
Build ErsatzTV Image / CI toolchain image resolves (pull_request) Successful in 9s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 12s
Build ErsatzTV Image / Delimiter ban (release path) (pull_request) Successful in 23s
PR Gates / Docs update reminder (pull_request) Successful in 22s
PR Gates / decisions lifecycle (pull_request) Successful in 22s
PR Gates / Fix proofs (Proves trailers) (pull_request) Successful in 13s
Review verdict / Set review-verdict status (pull_request_target) Successful in 30s
review-verdict/h10 Review-verdict: MERGEABLE @ 022a05a (base: main)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 9m20s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 6m21s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Skipped
PR Gates / Script lint and tests (ruff + pytest) (pull_request) Successful in 19m34s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 6m9s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 7s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 6s
Build ErsatzTV Image / CI toolchain image resolves (pull_request) Successful in 9s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 12s
Build ErsatzTV Image / Delimiter ban (release path) (pull_request) Successful in 23s
PR Gates / Docs update reminder (pull_request) Successful in 22s
PR Gates / decisions lifecycle (pull_request) Successful in 22s
PR Gates / Fix proofs (Proves trailers) (pull_request) Successful in 13s
Review verdict / Set review-verdict status (pull_request_target) Successful in 30s
review-verdict/h10 Review-verdict: MERGEABLE @ 022a05a (base: main)
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 9m20s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 6m21s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Skipped
PR Gates / Script lint and tests (ruff + pytest) (pull_request) Successful in 19m34s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 6m9s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 7s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 6s
The thesis measurement quoted three numbers side by side. Two are verbatim from the corpus — `docs/guard-inventory.md:179` "wrong NINE times", and `scripts/tests/test_image_build_delegates_the_spa_suite.py:772` "defeated seven measured ways". The third, "a lexical rule over a hook preamble **five**", was reachable only from #901's own issue body ("Five spellings, one mechanism"); nothing in the repo re-derives it, and both artifacts the record cites for #891 — `docs/decisions/records/process/hook-resolves-inputs-from-repo-root.md:64-68` and `scripts/tests/test_hook_fire_log.py:144` — count the SAME sequence as three ("Three successive lexical rules over this line each fell"). Counted as spellings it is eight or nine; five is neither basis. A number in prose that no artifact re-derives is this record's own subject matter, and "Measured 2026-08-30" invites trust rather than re-derivation. Answered by subtraction, not by new prose: - body: the third clause states what both cited artifacts state — three successive lexical rules, each defeated by the next shape. - `rule:` drops the copied numeric triple ("nine, seven and five times") for a pointer to the sequences below, so the counts live in one place (`dont-keep-a-copy-of-a-set`). - `signals:` swaps the unsourced token for the sourced one. Same class, found while checking the neighbours: "would have licensed the parser above through most of nine rounds" (`rule:` and body) counted #887's NINE DEFECTS as rounds — guard-inventory records them as nine defects across THREE cold-review rounds. Now "most of those nine defects". Body stays 58 lines; the paragraph is reflowed at the file's existing width. Refs #901 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015QqCpYFsKgnAnx6jVwrKiV
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -5,8 +5,8 @@ status: active
|
||||
since: '2026-09-05'
|
||||
supersedes: none
|
||||
superseded-by: none
|
||||
rule: 'A predicate that decides something by matching a SHAPE inside an artifact whose GRAMMAR it does not implement pins the artifact WHOLE instead — the whole file, or a whole named span, compared for equality under a STATED normalisation. The boundary is the grammar and never the file type: shell source, a workflow `run:` body, TOML, YAML, a jq expression and JSON5 all qualify, and a shape matched over the OUTPUT of a real parser for that grammar does not. Extracting a `run:` value with a real YAML parser and then matching shell inside it IS in the class, because the shell is the unparsed half. THE PIN IS THE DEFAULT FOR A NEW PREDICATE; REJECTED is the alternative of writing the shape-matcher and pinning only once it has been defeated. The defeat a remedy policy waits for is a false GREEN, so it is found by a reviewer or by an incident and never by the guard: "not defeated yet" is a statement about who has looked, not about the input space. A SHAPE-MATCHER IS THE EXCEPTION, and its argument carries FOUR things, written where the predicate is and re-read whenever a clause of it is widened. (1) The GRAMMAR named, and the parser that implements it — if the shape is matched over a real parse there is no exception to write. (2) The INPUT SPACE as a CLOSED enumeration with the reason it is closed: a generator whose output form is fixed, or a canonicaliser the guard itself runs. A list of the spellings the author thought of is not that reason, and is the thing that was defeated nine, seven and five times. (3) The FAILURE DIRECTION measured rather than asserted: each defeat the matcher claims to catch is a DECLARED, executed mutation under `testing.mutation-claims-are-executed`, because a shape-matcher''s miss is a false green and an unrun claim about a fail direction is not evidence. (4) WHAT IT BUYS over the pin, priced in a cost the pin actually charges — the artifact is edited routinely or written by tooling, so a pin would redden as background noise and train its readers to ignore it; or the artifact''s population is DISCOVERED rather than fixed, so there is nothing to pin. Fewer than four is a refusal, and neither elegance nor an unwillingness to update a constant is one of the four. TWO OTHER BARS ARE REJECTED for this exception: a NUMERIC one ("survives N adversarial spellings"), which measures the reviewer''s imagination rather than the input space and would have licensed the parser above through most of nine rounds; and a REVIEWER SIGN-OFF, which makes the exception depend on the very signal the threshold below says arrives late. THE THRESHOLD for withdrawing a shape-matcher already shipped is not a count of spellings: it is the moment the NEXT spelling is found by the REVIEWER rather than by the author, which is evidence that the author''s model of the input space is not the input space. TWO RIDERS, both measured. A pin assumes it is pinning the artifact that still DECIDES — ask what else could decide this and whether the pin would still match: another FILE that outranks it, another OCCURRENCE in the same file, another WORKFLOW, or a HOOK the pinned command invokes. And WIDENING A CLAUSE RETIRES THE PROOFS CALIBRATED AGAINST THE NARROW ONE: a canary asserting that a finer mutation still SURVIVES has as its precondition that the guard is deliberately coarse there, so widening the main clause turns the canary into a tautology, which reads exactly like a passing proof — re-derive every proof calibrated against the narrow clause in the same commit that widens it. THE COST IS STATED RATHER THAN DISCOVERED LATER: any edit to a pinned artifact reddens, and the pin is updated deliberately in the same commit, with a reason. That red IS the review trigger the pin exists to be. A pin also declares its NORMALISATION and what the normalisation cannot see, because "pinned whole" invites a reader to assume byte equality.'
|
||||
signals: 'pin the artifact whole · shape-matcher is the exception that argues for itself · the boundary is the grammar not the file type · predicate does not implement the grammar it parses · defeated by the next spelling · the reviewer found the next spelling · false RED versus false GREEN · nine defects one mechanism · seven respellings of a partial config match · five spellings of a lexical preamble rule · the four-part exception argument: grammar and parser, closed input space, measured fail direction, what it buys · authority moves where the pin is not looking · an outranking config file · a decoy occurrence · an npm lifecycle hook · widening a clause retires its own canary · a survived-clause canary becomes a tautology · a pin declares its normalisation · instances: `ci.image-build-delegates-the-spa-suite`, `process.hook-resolves-inputs-from-repo-root` · paths: `scripts/tests/test_image_build_delegates_the_spa_suite.py`, `scripts/tests/test_hook_fire_log.py`, `docs/guard-inventory.md`, `docs/defect-shapes-773.md` · issues: #901, #887, #891, #774, #790, #881, #773'
|
||||
rule: 'A predicate that decides something by matching a SHAPE inside an artifact whose GRAMMAR it does not implement pins the artifact WHOLE instead — the whole file, or a whole named span, compared for equality under a STATED normalisation. The boundary is the grammar and never the file type: shell source, a workflow `run:` body, TOML, YAML, a jq expression and JSON5 all qualify, and a shape matched over the OUTPUT of a real parser for that grammar does not. Extracting a `run:` value with a real YAML parser and then matching shell inside it IS in the class, because the shell is the unparsed half. THE PIN IS THE DEFAULT FOR A NEW PREDICATE; REJECTED is the alternative of writing the shape-matcher and pinning only once it has been defeated. The defeat a remedy policy waits for is a false GREEN, so it is found by a reviewer or by an incident and never by the guard: "not defeated yet" is a statement about who has looked, not about the input space. A SHAPE-MATCHER IS THE EXCEPTION, and its argument carries FOUR things, written where the predicate is and re-read whenever a clause of it is widened. (1) The GRAMMAR named, and the parser that implements it — if the shape is matched over a real parse there is no exception to write. (2) The INPUT SPACE as a CLOSED enumeration with the reason it is closed: a generator whose output form is fixed, or a canonicaliser the guard itself runs. A list of the spellings the author thought of is not that reason, and is the thing that was defeated in all three sequences below. (3) The FAILURE DIRECTION measured rather than asserted: each defeat the matcher claims to catch is a DECLARED, executed mutation under `testing.mutation-claims-are-executed`, because a shape-matcher''s miss is a false green and an unrun claim about a fail direction is not evidence. (4) WHAT IT BUYS over the pin, priced in a cost the pin actually charges — the artifact is edited routinely or written by tooling, so a pin would redden as background noise and train its readers to ignore it; or the artifact''s population is DISCOVERED rather than fixed, so there is nothing to pin. Fewer than four is a refusal, and neither elegance nor an unwillingness to update a constant is one of the four. TWO OTHER BARS ARE REJECTED for this exception: a NUMERIC one ("survives N adversarial spellings"), which measures the reviewer''s imagination rather than the input space and would have licensed the parser above through most of those nine defects; and a REVIEWER SIGN-OFF, which makes the exception depend on the very signal the threshold below says arrives late. THE THRESHOLD for withdrawing a shape-matcher already shipped is not a count of spellings: it is the moment the NEXT spelling is found by the REVIEWER rather than by the author, which is evidence that the author''s model of the input space is not the input space. TWO RIDERS, both measured. A pin assumes it is pinning the artifact that still DECIDES — ask what else could decide this and whether the pin would still match: another FILE that outranks it, another OCCURRENCE in the same file, another WORKFLOW, or a HOOK the pinned command invokes. And WIDENING A CLAUSE RETIRES THE PROOFS CALIBRATED AGAINST THE NARROW ONE: a canary asserting that a finer mutation still SURVIVES has as its precondition that the guard is deliberately coarse there, so widening the main clause turns the canary into a tautology, which reads exactly like a passing proof — re-derive every proof calibrated against the narrow clause in the same commit that widens it. THE COST IS STATED RATHER THAN DISCOVERED LATER: any edit to a pinned artifact reddens, and the pin is updated deliberately in the same commit, with a reason. That red IS the review trigger the pin exists to be. A pin also declares its NORMALISATION and what the normalisation cannot see, because "pinned whole" invites a reader to assume byte equality.'
|
||||
signals: 'pin the artifact whole · shape-matcher is the exception that argues for itself · the boundary is the grammar not the file type · predicate does not implement the grammar it parses · defeated by the next spelling · the reviewer found the next spelling · false RED versus false GREEN · nine defects one mechanism · seven respellings of a partial config match · three successive lexical rules over a hook preamble · the four-part exception argument: grammar and parser, closed input space, measured fail direction, what it buys · authority moves where the pin is not looking · an outranking config file · a decoy occurrence · an npm lifecycle hook · widening a clause retires its own canary · a survived-clause canary becomes a tautology · a pin declares its normalisation · instances: `ci.image-build-delegates-the-spa-suite`, `process.hook-resolves-inputs-from-repo-root` · paths: `scripts/tests/test_image_build_delegates_the_spa_suite.py`, `scripts/tests/test_hook_fire_log.py`, `docs/guard-inventory.md`, `docs/defect-shapes-773.md` · issues: #901, #887, #891, #774, #790, #881, #773'
|
||||
mechanics: 'Two worked pins. `scripts/tests/test_image_build_delegates_the_spa_suite.py` pins command TEXT (`PINNED_STAGE_COMMANDS`), `web/package.json`''s script map (`PINNED_PACKAGE_SCRIPTS`) and `web/vite.config.ts` entire (`PINNED_VITE_CONFIG`), the last two after a SELECTION and then a partial match were defeated. The three normalise DIFFERENTLY, so a residual read off one of them NEED NOT transfer to the others — and one of them does transfer, because two share a primitive: the stage commands are compared after line continuations are joined and whitespace within each command is collapsed (`_normalise`), so a reflow is free and a line boundary is not; the script map is dict equality over parsed JSON, so the file''s own formatting and key ORDER are free while every script body is exact; and of the three only `PINNED_VITE_CONFIG` runs through `_normalise_lines`, which additionally drops blank lines. `_normalise_lines` IS `_normalise` applied per line, so the whitespace collapse — including inside a QUOTED STRING, where it can change meaning — belongs to both TEXT pins and not to the vite one alone; of the two only the vite test states it, in place of claiming byte equality, which is what the rule above asks of a pin. `scripts/tests/test_hook_fire_log.py` pins the hook preamble''s two lines byte for byte (`CANONICAL_SINK_ASSIGNMENT`, `CANONICAL_SINK_SOURCE`) — but its normalisation is a SELECTION, and the rule above obliges this record to say what that selection cannot see, because the pin itself does not. The compared set is the lines that contain the literal `ETV_HOOK_FIRE_LIB` and are not comment-led, so the pin reaches exactly the two preamble lines and nothing else in the file: a later reassignment of that variable which never spells the literal — one composed at runtime and `eval`ed, say — is outside the selection, and therefore outside the pin, however the checker then behaves. Byte-identity WITHIN a selection is not byte-identity over the file — the pin''s reach ends where its selector does. That is stated here as a structural fact about the selector and NOT as a measured checker outcome, because an outcome claim about a mutation is a declared, harness-executed `CLAIMS` entry under `testing.mutation-claims-are-executed` or it is not written, wherever it is written — this record included. The residual is #891 code and predates this record; stating it is this record''s own rule applied to one of its own worked pins. Each of the three image-build pins faults with a request for the reason in the same commit, and two of them name the CONSTANT to update (`PINNED_STAGE_COMMANDS`, `PINNED_VITE_CONFIG`) while the script-map fault names the FILE and prints both maps. The hook-preamble fault asks for neither: it reports the divergent lines, since the two canonical strings ARE what a deliberate change edits.'
|
||||
---
|
||||
**The class, and what approximating it costs.** A predicate over an artifact with a grammar is a partial
|
||||
@@ -14,11 +14,11 @@ reimplementation of that grammar, defeated not by a clever attacker but by an or
|
||||
looks local, one more case to handle, so the sequence stops only when someone stops it. Measured 2026-08-30 across
|
||||
two unrelated subjects: a guard parsing shell text to decide whether a command runs the SPA suite was wrong **nine**
|
||||
ways from one mechanism (#887), its replacement — a partial match of `web/vite.config.ts` — **seven** further ways,
|
||||
and a lexical rule over a hook preamble **five** (#891). Every fix was locally correct; none of the three sequences
|
||||
converged. All three ended in the same move, and what the move buys is a fail DIRECTION: a pin's failure direction is
|
||||
a false RED, which a human reads in the diff; a shape-matcher's is a false GREEN, which nobody reads. Which edits a
|
||||
pin accepts is its NORMALISATION's business, so every pin — whole-file or selected — declares that normalisation and
|
||||
what it cannot see, which is what `mechanics:` does for both worked pins.
|
||||
and over a hook preamble **three** successive lexical rules each fell to the next shape (#891). Every fix was locally
|
||||
correct; none of the three sequences converged. All three ended in the same move, and what the move buys is a fail
|
||||
DIRECTION: a pin's failure direction is a false RED, which a human reads in the diff; a shape-matcher's is a false
|
||||
GREEN, which nobody reads. Which edits a pin accepts is its NORMALISATION's business, so every pin — whole-file or
|
||||
selected — declares that normalisation and what it cannot see, which is what `mechanics:` does for both worked pins.
|
||||
|
||||
**Why the pin is the DEFAULT and not the remedy — the first thing #901 left open.** The rejected alternative is the
|
||||
tempting one: write the shape-matcher, and pin only once it has been defeated. It is rejected because of what the
|
||||
@@ -32,8 +32,8 @@ narrow clause (rider 2), so "pin it when it breaks" understates its own bill.
|
||||
**What a shape-matcher's exception argument must contain — the second thing #901 left open.** The four requirements
|
||||
are chosen so a reviewer can REFUSE one: each is a thing the author either wrote or did not. Two alternatives are
|
||||
rejected. A NUMERIC bar ("survives N adversarial spellings") measures the reviewer's imagination, not the input
|
||||
space, and rises while the defect stands — it would have licensed #887's parser through most of nine rounds. A
|
||||
REVIEWER SIGN-OFF bar is worse: it makes the exception depend on the very signal the threshold says arrives late.
|
||||
space, and rises while the defect stands — it would have licensed #887's parser through most of those nine defects.
|
||||
A REVIEWER SIGN-OFF bar is worse: it makes the exception depend on the very signal the threshold says arrives late.
|
||||
What survives is requirement 2, the only candidate that is a claim about the ARTIFACT rather than about the effort
|
||||
spent on it, and requirement 3, which forces the fail-direction claim to be executed, not reasoned.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user