From dc3f158bcbc28545ad588c40564cf8ba4d88def3 Mon Sep 17 00:00:00 2001 From: Timothy Date: Sat, 5 Sep 2026 16:01:18 +0200 Subject: [PATCH] docs(901): #885 merged, so the sentence the record's structural fact refutes is corrected at its source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `scripts/tests/test_hook_fire_log.py:154` ended "this arm's errors are refusals, never acceptances". That is false for the SELECTION, which is the half this branch's record is about: `mentions` is chosen by the literal `ETV_HOOK_FIRE_LIB`, so a writer of that variable which never spells the literal is outside the compared set altogether, and the checker never sees it. The comment already conceded an acceptance hole of the same kind six lines later, about `split("\n")` versus `splitlines()`, so the universal was contradicted inside its own block. The correction stays STRUCTURAL — it reads the selector one line below and says what the selector reaches — and makes no claim about what a mutated hook would return. An outcome claim would be a `CLAIMS` entry in `scripts/tests/mutation_manifest.py` or nothing, per `testing.mutation-claims-are-executed` as amended by #881, which is the same reason `mechanics:` states the selector rather than a measured result (`4d5bd0dbb`). WHY THIS LANDS NOW: #885 closed while this branch was in review (merged as #919, `c30847204`), which frees `scripts/tests/`. Of the two obligations `47619320e` recorded as owed, that message is superseded here: - DISCHARGED: this one, inline, above. - NOT OWED, and the reason is not the blocker: the `CLAIMS` entry for the hook-preamble selector. `4d5bd0dbb` removed the outcome claim from `mechanics:` rather than binding it, so the record asserts no mutation outcome and the rule it invokes has nothing to bind. Re-adding a claim in order to bind it would reverse a review-mandated change; an executed GREEN entry (target `.claude/hooks/decisions-guard.sh`, clause = the canonical sink assignment, replacement = that line plus the `eval` spelling, plus the mandatory `reach_replacement`/`reach_expect`) remains available as an ENRICHMENT of the structural fact, and belongs to whoever wants the fact executed rather than argued. Rebased onto `366a0f904..c30847204` on the way: the `docs/guard-inventory.md` conflict is two rows, resolved by taking #885's newer `test_workflow_persist_credentials.py` row (it gained a second invariant) and this branch's `test_image_build_delegates_the_spa_suite.py` row (it dates the vite `DEFAULT_CONFIG_FILES` reading to 8.1.3 and to the lockfile). `docs/decisions/README.md` regenerated, not merged. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_015QqCpYFsKgnAnx6jVwrKiV --- scripts/tests/test_hook_fire_log.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/tests/test_hook_fire_log.py b/scripts/tests/test_hook_fire_log.py index 87b976333..8aa8a1c4f 100644 --- a/scripts/tests/test_hook_fire_log.py +++ b/scripts/tests/test_hook_fire_log.py @@ -151,7 +151,13 @@ def instrumentation_faults(text: str, name: str) -> list[str]: # `#` is a comment in every shell. `pretooluse-merge-consent.sh` legitimately discusses this # variable in prose. Every other line mentioning it must be one of the two canonical lines, in # order. A heredoc carrying the token would fault; no hook has one, and refusing is the safe - # direction — this arm's errors are refusals, never acceptances. + # direction for the lines this arm COMPARES — but not for the ones it SELECTS. `mentions` is + # chosen by the literal `ETV_HOOK_FIRE_LIB`, so a writer of that variable which never spells the + # literal — one composed at runtime and `eval`ed, say — is outside the compared set altogether, + # and byte-identity WITHIN a selection is not byte-identity over the file + # (`testing.guard-pins-the-artifact-not-a-shape`, which states this pin's reach because the pin + # itself does not). So this arm's errors are not all refusals, and the `split("\n")` residual + # below is the second acceptance of the same kind. # `split("\n")`, not `splitlines()`: the latter also breaks on \x0b, \x0c, \x1c-\x1e, \x85 and the # Unicode separators, none of which ends a line for the shell. The divergence runs BOTH ways — # a canonical assignment followed by `\x0c` and garbage on one physical line parses clean under