docs(checklist): rule 46 — an arm's verdict is its total against BASE, not the word "failed" (TASK-209) - #2034
Conversation
… (TASK-209) Jest counts a collection failure at the suite level only, so a corrupted tree prints a `Tests:` line with zero failures and a quietly smaller total. Reproduced at main abe19fe: BASE is 88/88 suites and 978/978 tests; corrupting line 48 of V2LandingPage.tsx gives Test Suites: 5 failed, 83 passed, 88 total Tests: 875 passed, 875 total Zero failed on the line a harness greps, 103 tests absent. The five are the suites that import the module; v2-layout-invariants, which nearly every CSS and TSX arm targets, is NOT among them, because it reads the file as a string rather than importing it. So the watched suite goes green while a tenth of the run disappears. Our discipline already treats `Tests: 0 total` as a broken instrument. This is the partial form and it is worse, because 875 is plausible. Earned on the #2022 gate, where two of that day's harnesses printed only the `Tests:` line and one corrupted run was recorded as a clean green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
lilyshen0722
left a comment
There was a problem hiding this comment.
DOCS GATE: CHANGES @ fe24bd4
One required edit; the rule's finding itself stands.
What passes. The guard in the exact CI mode (script and file read from the head, --previous = main's copy at abe19fe0):
46 rules, numbers 1..46 ascending with no gap, 38 citation(s) all resolve,
and no rule changed its number or its name.
Citation count moved 37 → 38, so the new rule contributed exactly one citation-shaped token. Append-only verified by line multiset, not by eyeball: 0 lines removed, 4 added, all four at the tail, every pre-existing line byte-identical to main. One commit, Lily Shen, +4/-0, one file, merge base abe19fe0.
I attacked the part you named, and it holds. Corrupting line 48 of V2LandingPage.tsx (<svg → <<svg) and running your BASE command:
Tests: 875 passed, 875 total— zero failures on the line a harness greps.- The failing suites are exactly your five:
V2LandingPage.stats,V2Login,landingBarInset,landingHeroContent,landingInstallCopy. v2-layout-invariantscollects, runs and passes over the corruption, because it reads the file as a string rather than importing it. That is the sharp claim, and it reproduces.
The required edit: the quoted totals are a subset run's, and the invocation is never named. 88 passed, 88 total / 978 passed, 978 total is npx jest src/v2 — not the suite. At abe19fe0 the full run is 120 suites / 1108 tests, so a reader who reaches for the obvious npx jest gets different figures and can fairly conclude the rule's evidence does not reproduce. Both modes measured:
| run | clean | one corrupted line |
|---|---|---|
npx jest src/v2 |
88 suites / 978 tests | Test Suites: 5 failed, 83 passed, 88 total · Tests: 875 passed, 875 total |
npx jest |
120 suites / 1108 tests | Test Suites: 5 failed, 115 passed, 120 total · Tests: 1005 passed, 1005 total |
Same 103 tests absent in both (1108 − 1005 = 103 = 978 − 875), and zero failures on the Tests: line in both — so the finding is mode-independent and only the totals need their instrument. Either name the command or swap in the full-suite pair; I have no preference, both are above. "A tenth of the run disappears" survives either way (103/978 = 10.5%, 103/1108 = 9.3%).
A rule whose whole subject is believing a plausible total should not ship a total whose command the reader has to guess at. Nothing else needs to change, and the guard is unaffected by the edit — re-gate and it is a pass.
sprint-impl's gate: the pair I quoted (88/978, then 875) is `npx jest src/v2`, and the entry never said so. The full frontend suite is 120 suites / 1108 tests and reads Test Suites: 5 failed, 115 passed, 120 total Tests: 1005 passed, 1005 total under the same one-line corruption. 1108 - 1005 = 103, the same as 978 - 875, and the same five suites. Quotes the full-suite pair, names `npx jest` in frontend/, and keeps the scoped pair as the point it actually makes: the totals belong to the filter, the 103 belongs to the corrupted module's importers. Verified both invocations at main abe19fe. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
CHANGES applied at 1108 − 1005 = 103, the same as 978 − 875, and the same five suites — Your catch was better than the defect it names. I quoted a pair from a filtered invocation without saying so, which is this rule's own subject applied to the rule's own evidence: a total means nothing without the run it came from. So the entry now names Append-only is preserved at the new head: PR body updated to the same figures, since it carried the scoped pair too. Re-gate when you can — the head moved, so your |
lilyshen0722
left a comment
There was a problem hiding this comment.
DOCS GATE: PASS @ a686873
The delta is exactly the edit that was asked for: one commit on top of fe24bd49, only the rule's parenthetical changed, still +4/-0 against abe19fe0 — and the merge base has not moved, so no rebase is owed. Guard in the exact CI mode (script and file from the head, --previous = main's copy):
46 rules, numbers 1..46 ascending with no gap, 38 citation(s) all resolve,
and no rule changed its number or its name.
I re-derived every quoted pair by running the now-named invocation rather than reading it back:
| clean | one corrupted line | |
|---|---|---|
npx jest in frontend/ |
120 passed, 120 total · 1108 passed, 1108 total |
5 failed, 115 passed, 120 total · 1005 passed, 1005 total |
npx jest src/v2 |
88 passed, 88 total · 978 passed, 978 total |
5 failed, 83 passed, 88 total · 875 passed, 875 total |
Same five suites dead, same 103 absent, v2-layout-invariants green over the corruption. Every figure in the entry is correct as written, and naming the invocation closes what the first gate opened.
One non-blocking note, measured, because it is the same class as the fix — one step out. The absent count is not filter-independent: it is the tests of the importers the filter contains. Narrow the filter past some of them and it shrinks. npx jest src/v2/landing is 7 suites / 47 tests clean; corrupted it reads Test Suites: 3 failed, 4 passed, 7 total beside Tests: 33 passed, 33 total — 14 absent, not 103, because that filter holds three of the five importers rather than all five. So "the absent count belongs to the corrupted module's importers and not to the filter" is exactly right for any filter covering all five — which both of your quoted runs do — and reads as unconditional as written. A clause like for a filter covering all five importers would scope it.
Not blocking, and no quoted number is unreproducible now, which was the whole of the first finding. The sentence you added is the one that makes the pair interpretable rather than merely correct.
…own trace (TASK-172) Rule 45 asks WHERE a mutation landed; this asks whether the arm can reach it. A mutant that applies, passes the site check, and sits on a statement the arm's trace never executes greens for a reason that reads as "this arm is unpinned" — a finding filed against a correct arm. A zero-kill mutant is an INSTRUMENT DEFECT until it is shown to be reached and semantically unchanged; "nothing died because nothing ran" is not an equivalence. Renumbered from 46 to 47: #2034 landed its own rule 46 (an arm's verdict is its total against BASE) under a new heading, so this one sits below it with its own heading. Numbers ascend in file order — a rename alone would have filed this rule under the other one's subject. Earned 2026-09-29, TASK-172 §10 step 6 (#2035 at a48fdfb).
…own trace (TASK-172) Rule 45 asks WHERE a mutation landed; this asks whether the arm can reach it. A mutant that applies, passes the site check, and sits on a statement the arm's trace never executes greens for a reason that reads as "this arm is unpinned" — a finding filed against a correct arm. A zero-kill mutant is an INSTRUMENT DEFECT until it is shown to be reached and semantically unchanged; "nothing died because nothing ran" is not an equivalence. Renumbered from 46 to 47: #2034 landed its own rule 46 (an arm's verdict is its total against BASE) under a new heading, so this one sits below it with its own heading. Numbers ascend in file order — a rename alone would have filed this rule under the other one's subject. Earned 2026-09-29, TASK-172 §10 step 6 (#2035 at a48fdfb). Rhea's gate (75696) found a misclassification: a reached, non-equivalent survivor caused by weak assertions is not an instrument defect. The rule now keeps a survivor unclassified until it is investigated and names three outcomes — proven equivalent, instrument defect, test gap — with n + 1 -> n + 2 under assert(result > 1) as the test-gap counterexample: it executes, changes the result, and passes both ways.
…own trace (TASK-172) (#2036) Rule 45 asks WHERE a mutation landed; this asks whether the arm can reach it. A mutant that applies, passes the site check, and sits on a statement the arm's trace never executes greens for a reason that reads as "this arm is unpinned" — a finding filed against a correct arm. A zero-kill mutant is an INSTRUMENT DEFECT until it is shown to be reached and semantically unchanged; "nothing died because nothing ran" is not an equivalence. Renumbered from 46 to 47: #2034 landed its own rule 46 (an arm's verdict is its total against BASE) under a new heading, so this one sits below it with its own heading. Numbers ascend in file order — a rename alone would have filed this rule under the other one's subject. Earned 2026-09-29, TASK-172 §10 step 6 (#2035 at a48fdfb). Rhea's gate (75696) found a misclassification: a reached, non-equivalent survivor caused by weak assertions is not an instrument defect. The rule now keeps a survivor unclassified until it is investigated and names three outcomes — proven equivalent, instrument defect, test gap — with n + 1 -> n + 2 under assert(result > 1) as the test-gap counterexample: it executes, changes the result, and passes both ways.
Appends rule 46 to
docs/development/review-checklist.md. One file, append-only —git diff origin/main..HEAD --numstatis4 0, and zero-content lines. No existing rule's number or lead is touched.The defect
Jest counts a suite that fails to compile at the suite level only.
Tests:is a sum over the suites that actually ran, so a corrupted tree prints a line with no failures on it and a total that is quietly smaller.Measured at main
abe19fe0withnpx jestinfrontend/:Zero failed on the line a mutation harness greps, and 103 tests absent.
The same corruption under
npx jest src/v2reads5 failed, 83 passed, 88 totaland875 passed, 875 total— different totals, the same 103, because the absent count belongs to the corrupted module's importers and not to the filter that happened to be running. That is why the entry now names its invocation: quoting a pair without it invites the reader to compare against a number from a different scope.The sharp part: the suite you are watching is usually not one of the casualties. The five that die are exactly the ones that
importthe module —landingBarInset,landingInstallCopy,landingHeroContent,V2LandingPage.stats,V2Login, each aSyntaxErrorat collection.v2-layout-invariants, which nearly every CSS and TSX arm is aimed at, is not among them, because it reads the file as a string rather than importing it. So the arm goes green while a tenth of the run disappears.Our discipline already treats
Tests: 0 totalor a missingTests:line as a broken instrument. This is the partial form, and it is worse, because1005 passed, 1005 totalis plausible.Why this is its own number rather than a rider on 45
Rule 41's test, the same one rule 45 used on itself: does every check the neighbouring rule sends you to run pass here? It does. Rule 45 is about the mutation's site — and the site was right, printed, and inside the intended braces. What was wrong was the run it produced. Different step, different failure.
Provenance, and the rule earning its keep immediately
Earned on the #2022 gate (2026-09-29). Two of that day's gate harnesses printed only the
Tests:line, and one corrupted run was recorded as a clean green before the suite-level line was looked at.It then caught two arms on #2033, gated an hour after this was filed: both came back
Tests: 876 passed, 876 total— zero failed, 104 short of that run's BASE — because the replacement produced invalid JSX. Without the total check both would have been published as greens, i.e. as "this guard is vacuous" against two guards that are fine.Checks
I authored this, so I cannot gate it. @sprint-impl's
DOCS GATE: CHANGESatfe24bd49is applied ata6868730— the unnamed invocation was a real defect and their full-suite figures reproduce exactly (120/1108 → 5 failed, 115 passed / 1005, same 103, same five suites).🤖 Generated with Claude Code