From 761a71278d28e04da6fb10b2c722d99b0d387bc9 Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 5 Sep 2026 15:04:28 +0200 Subject: [PATCH] docs(901): pay for the two corrections by tightening the prose around them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two corrections added four prose lines to a record already one line over the 60-line advisory ceiling. Recover what can be recovered without losing substance: reflow the paragraphs, drop the padding ("and it fails silently" → ", silently"; "the count rises" → "and rises"), and cut one restatement. It lands at 65 lines, not 60. That is a deliberate stop: the remaining candidates are the *why* behind non-obvious choices — which the repo's own docs rule says to keep — and `decisions_validate` reports the constant itself as drifted from the distribution it is supposed to mark the tail of (p90=104, p95=142, 50 of 222 records over it). The validator passes. Refs #901 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_015QqCpYFsKgnAnx6jVwrKiV --- .../guard-pins-the-artifact-not-a-shape.md | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/docs/decisions/records/testing/guard-pins-the-artifact-not-a-shape.md b/docs/decisions/records/testing/guard-pins-the-artifact-not-a-shape.md index a0152f1b8..f4157dba1 100644 --- a/docs/decisions/records/testing/guard-pins-the-artifact-not-a-shape.md +++ b/docs/decisions/records/testing/guard-pins-the-artifact-not-a-shape.md @@ -16,11 +16,11 @@ respelling — and each defeat looks local, one more case to handle, so the sequ 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 and none of the three sequences +over a hook preamble **five** (#891). Every fix was locally correct; none of the three sequences converged. All three ended in the same move: a pin cannot be defeated by a respelling of what it COMPARES, since it need not RECOGNISE one in order to reject it. What stays exposed is whatever it -does not compare — nothing for a whole-file pin, and exactly the reach of the SELECTOR for a pin -scoped by one, which is why `mechanics:` states the hook pin's. +does not compare: nothing for a whole-file pin, and the SELECTOR's reach for a pin scoped by one — +which is why `mechanics:` states the hook pin's. **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 @@ -28,50 +28,50 @@ is rejected because of what the trigger event is made of. A shape-matcher's fail GREEN, which the guard by construction does not report; the defeat therefore arrives from a reviewer or from an incident, so its absence measures who has looked rather than what the input can be. Both measurements above are that asymmetry: neither predicate withdrew because a count got high, and both -had the precedent available for every round. The second reason is that switching later is not the -same price as starting there. A withdrawal costs the rounds already spent AND the proofs calibrated -against the narrow clause (rider 2), so "pin it when it breaks" understates its own bill. +had the precedent available for every round. The second reason is that switching later costs more +than starting there: a withdrawal costs the rounds already spent AND the proofs calibrated against +the 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 rather than the input space, and the count 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. What survives is +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. 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. **Rider 1 — a pin assumes it pins the artifact that still DECIDES.** This is where the pinned form -fails, and it fails silently, so it is the question to ask of every new pin. In #887 every hole -found after the first withdrawal was authority moving where the pin was not looking: another FILE -(vitest prefers `vitest.config.*`, and `vite build` prefers `vite.config.js`/`.mjs` to the pinned -`.ts` — an ordering that belongs to a vite VERSION, so the release it was read at is dated in -`docs/guard-inventory.md`'s `test_image_build_delegates_the_spa_suite.py` row rather than copied -here), another OCCURRENCE in the same file, another WORKFLOW (a `needs:` edge naming a different job -called `test`), a HOOK the pinned command invokes (a vite plugin's `buildStart`, an npm -`prebuild`/`preinstall` script). A pin removes the SPELLING problem outright and reduces the MEANING -problem to a list that can be worked through; a pin sold as immune to both stops being re-examined. +fails, silently, so it is the question to ask of every new pin. In #887 every hole found after the +first withdrawal was authority moving where the pin was not looking: another FILE (vitest prefers +`vitest.config.*`; `vite build` prefers `vite.config.js`/`.mjs` to the pinned `.ts` — a +VERSION-bound ordering, so the vite release it was read at is dated in `docs/guard-inventory.md`'s +`test_image_build_delegates_the_spa_suite.py` row rather than copied here), another OCCURRENCE in +the same file, another WORKFLOW (a `needs:` edge naming a different job called `test`), a HOOK the +pinned command invokes (a vite plugin's `buildStart`, an npm `prebuild`/`preinstall` script). A pin +removes the SPELLING problem outright and reduces the MEANING problem to a list that can be worked +through; a pin sold as immune to both stops being re-examined. -**Rider 2 — widening a clause can retire its own evidence.** A canary asserting that a finer mutation -still SURVIVES is calibrated against a deliberately coarse clause. Widen that clause and the canary -does not go red, it goes vacuous — indistinguishable from a passing proof at the point of reading — -so the widening commit re-derives every proof calibrated against the narrow form. Same shape as the -`DETECTOR` grade in `testing.mutation-claims-are-executed`: a surviving mutation is worth something -only while the precondition that made it survive still holds. +**Rider 2 — widening a clause can retire its own evidence.** A canary asserting that a finer +mutation still SURVIVES is calibrated against a deliberately coarse clause. Widen that clause and +the canary does not go red, it goes vacuous — indistinguishable from a passing proof at the point of +reading — so the widening commit re-derives every proof calibrated against the narrow form. Same +shape as the `DETECTOR` grade in `testing.mutation-claims-are-executed`: a surviving mutation is +worth something only while the precondition that made it survive still holds. **Where this sits against its neighbours, so it is not read as a duplicate.** The two incidents keep their own records — `ci.image-build-delegates-the-spa-suite` and `process.hook-resolves-inputs-from-repo-root` — and this is the class they instantiate, while `testing.guard-derives-population-from-source` answers the case where the authoritative source is -MISSING (create one, never approximate it with a predicate over text) and this one the case where -the artifact IS present and authoritative and what is approximated is its GRAMMAR. +MISSING (create one, never approximate it with a predicate over text); this one, the case where the +artifact IS present and authoritative and its GRAMMAR is what gets approximated. `docs/defect-shapes-773.md` does NOT carry this rule: its §3.6 partition lists the nearest class, -string-predicate churn, as `no detector proposed`, and §3.7 argues that class away as a cross-cutting -property. Only §4's closing meta-finding supports the FORM — a class-level rule beats one record per -instance — so the rule itself is new here, and resolvable BY TOPIC, where `docs/guard-inventory.md` +string-predicate churn, as `no detector proposed`, and §3.7 argues that class away as a +cross-cutting property. Only §4's meta-finding supports the FORM — a class-level rule beats one +record per instance — so the rule is new here, resolvable BY TOPIC where `docs/guard-inventory.md` carries its precedent per incident, findable only inside one. **What this does not cover.** Predicates over structured input a real parser produces — an AST, the git index, parsed workflow YAML consumed as data — are not approximations and keep their shape matching. And a pin is not mandatory for an artifact edited continuously by many hands: that is -requirement 4's own escape hatch, and a pin reddening as background noise is a guard nobody reads. +requirement 4's own escape hatch, and a pin reddening as background noise is a guard nobody reads. \ No newline at end of file