From 2bed320006e0baca9687124ac6e46b6899b393f7 Mon Sep 17 00:00:00 2001 From: Dan Mordechay Date: Thu, 15 Oct 2026 16:24:40 +0300 Subject: [PATCH 1/3] R7 Q7: a per-leaf question on a whole-repository review R7 Q7 asks for a mutation run, or a hand check, on each leaf. Under a whole-repository hunter's budget that never fits, so every --all run ended with "0 of 50 hits judged" and nothing else. On a whole-repository review the question is now not judged: its receipt reads "per-leaf, not judged", R7's tally takes the partial form, and the not-reached line names the three leaf directories with the most hits as the scoped reviews that judge them. On a scoped review the question is judged in full, as before. The lead skips what the Detection already says to skip: .tsx components, components/, hooks/ and context/ in ts-react; cmd/, main and handlers/ in Go and Python. On the fixtures that moves the count from 50 to 44 (ts-react), 35 to 33 (py) and 36 to 36 (go); every Q7 plant keeps its hits. Co-Authored-By: Claude Opus 5.5 --- core/agents/rule-hunter.md | 5 +++-- core/includes/rules/R7/falsifying-questions.md | 6 ++++++ core/skills/pre-commit-review/SKILL.md | 4 +++- go-linter-driven-development/agents/rule-hunter.md | 5 +++-- go-linter-driven-development/rules/R7-test-placement.md | 8 +++++++- .../skills/pre-commit-review/SKILL.md | 4 +++- lang/go/rules/R7/falsifying-questions.md | 8 +++++++- lang/python/rules/R7/falsifying-questions.md | 8 +++++++- lang/ts-react/rules/R7/falsifying-questions.md | 8 +++++++- linter-driven-development/agents/rule-hunter.md | 5 +++-- linter-driven-development/rules/R7-test-placement.md | 6 ++++++ .../skills/pre-commit-review/SKILL.md | 4 +++- python-linter-driven-development/agents/rule-hunter.md | 5 +++-- .../rules/R7-test-placement.md | 8 +++++++- .../skills/pre-commit-review/SKILL.md | 4 +++- ts-react-linter-driven-development/agents/rule-hunter.md | 5 +++-- .../rules/R7-test-placement.md | 8 +++++++- .../skills/pre-commit-review/SKILL.md | 4 +++- 18 files changed, 84 insertions(+), 21 deletions(-) diff --git a/core/agents/rule-hunter.md b/core/agents/rule-hunter.md index efe912d4..e6782eec 100644 --- a/core/agents/rule-hunter.md +++ b/core/agents/rule-hunter.md @@ -120,8 +120,9 @@ question the table marks `judgment` reads `R Q: judgment → finding(s)`. When your budget ended before every counted hit was read, the receipt says so — `R Q: of hit(s) judged → finding(s)` — and the parent renders the review as partial; a receipt that carries the table's number -over hits you did not read is a false verdict. A question with no receipt was not -run. Then one tally line per rule you were given, always: +over hits you did not read is a false verdict. A question its rule leaves unjudged +on a whole-repository review reads `R Q: hit(s) → per-leaf, not +judged`, as the rule spells it. A question with no receipt was not run. Then one tally line per rule you were given, always: `R: finding(s)`, or when that rule is clean: `R: hunted clean — hits judged, every question receipted over the full scope`. When the budget ended the hunt, each rule's tally is instead `R: finding(s) in diff --git a/core/includes/rules/R7/falsifying-questions.md b/core/includes/rules/R7/falsifying-questions.md index 7fe6c89d..5dbd6c31 100644 --- a/core/includes/rules/R7/falsifying-questions.md +++ b/core/includes/rules/R7/falsifying-questions.md @@ -59,6 +59,12 @@ Test files are the ones the repository's test runner picks up (`*{{.TestGlob}}`, and boolean conditions, check the table for a row at each boundary value and on each side of each condition, and hand-flip any operator whose rows are missing (`<` to `<=`, `>` to `>=`, a condition negated), run the leaf's tests, revert. + On a whole-repository review this question is not judged: a mutation run or a + hand check over every leaf does not fit a hunter's budget. The receipt reads + `R7 Q7: hit(s) → per-leaf, not judged`, R7's tally takes the partial form, + and the `not reached:` line names the three leaf directories with the most hits, + each as the review that judges it: `R7 Q7: /{{.CmdPrefix}}-review `. On a + scoped review the question is judged in full. Violation: any survivor on a leaf type that is not recorded as equivalent — a missing table row or dead logic; name the mutant (file, line, operator) and the row that would kill it. diff --git a/core/skills/pre-commit-review/SKILL.md b/core/skills/pre-commit-review/SKILL.md index 6e1fda90..58e10d71 100644 --- a/core/skills/pre-commit-review/SKILL.md +++ b/core/skills/pre-commit-review/SKILL.md @@ -151,7 +151,9 @@ finding blocks (`rule | file:line | evidence | fix pattern | effort`), one recei falsifying question of each rule it was given (`R Q: hit(s) → finding(s)` with the counts table's number; `R Q: judgment → finding(s)` for a judgment question; `R Q: of hit(s) judged → - finding(s)` when its budget ended before every hit was read), one tally + finding(s)` when its budget ended before every hit was read; `R Q: + hit(s) → per-leaf, not judged` for a question its rule leaves to a scoped +review on a whole-repository one), one tally per rule — or, for a rule file that did not read, `R: rule unreadable at ` in the tally's place — and, when its budget ended the hunt, one `not reached:` line. A question with no receipt was not run. Findings, receipts, tallies (or unreadable diff --git a/go-linter-driven-development/agents/rule-hunter.md b/go-linter-driven-development/agents/rule-hunter.md index fac119f6..263c8ecf 100644 --- a/go-linter-driven-development/agents/rule-hunter.md +++ b/go-linter-driven-development/agents/rule-hunter.md @@ -120,8 +120,9 @@ question the table marks `judgment` reads `R Q: judgment → finding(s)`. When your budget ended before every counted hit was read, the receipt says so — `R Q: of hit(s) judged → finding(s)` — and the parent renders the review as partial; a receipt that carries the table's number -over hits you did not read is a false verdict. A question with no receipt was not -run. Then one tally line per rule you were given, always: +over hits you did not read is a false verdict. A question its rule leaves unjudged +on a whole-repository review reads `R Q: hit(s) → per-leaf, not +judged`, as the rule spells it. A question with no receipt was not run. Then one tally line per rule you were given, always: `R: finding(s)`, or when that rule is clean: `R: hunted clean — hits judged, every question receipted over the full scope`. When the budget ended the hunt, each rule's tally is instead `R: finding(s) in diff --git a/go-linter-driven-development/rules/R7-test-placement.md b/go-linter-driven-development/rules/R7-test-placement.md index 0f5bc1cd..89101b40 100644 --- a/go-linter-driven-development/rules/R7-test-placement.md +++ b/go-linter-driven-development/rules/R7-test-placement.md @@ -211,7 +211,7 @@ Answer each with evidence (`file:line`, command output) — never a bare verdict Violation: any hit — replace with channels/wait groups. 7. **Does a mutant survive a leaf type's tests?** - Detect-grep: `(<=?|>=?) *('.'|-?[0-9]+|[a-zA-Z_.]*[Ll]en\b)|\b[Ll]en\([^)]*\) *(<=?|>=?|==|!=)` + Detect-grep: `(<=?|>=?) *('.'|-?[0-9]+|[a-zA-Z_.]*[Ll]en\b)|\b[Ll]en\([^)]*\) *(<=?|>=?|==|!=)` exclude-path=(^|/)cmd/,(^|/)main\.go$,(^|/)handlers/ Detection: for each new or changed leaf package, `gremlins unleash ./path/to/leaf | grep -E '^\s*LIVED'`; skip orchestrators, the top rung and any package that does I/O. `gremlins` not on `PATH`: propose the @@ -221,6 +221,12 @@ Answer each with evidence (`file:line`, command output) — never a bare verdict leaf's comparisons and boolean conditions, check the table for a row at each boundary value and on each side of each condition, and name the row that is missing. + On a whole-repository review this question is not judged: a mutation run or a + hand check over every leaf does not fit a hunter's budget. The receipt reads + `R7 Q7: hit(s) → per-leaf, not judged`, R7's tally takes the partial form, + and the `not reached:` line names the three leaf directories with the most hits, + each as the review that judges it: `R7 Q7: /go-ldd-review `. On a + scoped review the question is judged in full. Violation: any `LIVED` line on a leaf package that is not recorded as an equivalent mutant — a missing table row or dead logic; name the mutant (file, line, mutation) and the row that would kill it. diff --git a/go-linter-driven-development/skills/pre-commit-review/SKILL.md b/go-linter-driven-development/skills/pre-commit-review/SKILL.md index dc12c2b8..d78cf565 100644 --- a/go-linter-driven-development/skills/pre-commit-review/SKILL.md +++ b/go-linter-driven-development/skills/pre-commit-review/SKILL.md @@ -153,7 +153,9 @@ finding blocks (`rule | file:line | evidence | fix pattern | effort`), one recei falsifying question of each rule it was given (`R Q: hit(s) → finding(s)` with the counts table's number; `R Q: judgment → finding(s)` for a judgment question; `R Q: of hit(s) judged → - finding(s)` when its budget ended before every hit was read), one tally + finding(s)` when its budget ended before every hit was read; `R Q: + hit(s) → per-leaf, not judged` for a question its rule leaves to a scoped +review on a whole-repository one), one tally per rule — or, for a rule file that did not read, `R: rule unreadable at ` in the tally's place — and, when its budget ended the hunt, one `not reached:` line. A question with no receipt was not run. Findings, receipts, tallies (or unreadable diff --git a/lang/go/rules/R7/falsifying-questions.md b/lang/go/rules/R7/falsifying-questions.md index 5daa875f..4cc0db0b 100644 --- a/lang/go/rules/R7/falsifying-questions.md +++ b/lang/go/rules/R7/falsifying-questions.md @@ -39,7 +39,7 @@ Violation: any hit — replace with channels/wait groups. 7. **Does a mutant survive a leaf type's tests?** - Detect-grep: `(<=?|>=?) *('.'|-?[0-9]+|[a-zA-Z_.]*[Ll]en\b)|\b[Ll]en\([^)]*\) *(<=?|>=?|==|!=)` + Detect-grep: `(<=?|>=?) *('.'|-?[0-9]+|[a-zA-Z_.]*[Ll]en\b)|\b[Ll]en\([^)]*\) *(<=?|>=?|==|!=)` exclude-path=(^|/)cmd/,(^|/)main\.go$,(^|/)handlers/ Detection: for each new or changed leaf package, `gremlins unleash ./path/to/leaf | grep -E '^\s*LIVED'`; skip orchestrators, the top rung and any package that does I/O. `gremlins` not on `PATH`: propose the @@ -49,6 +49,12 @@ leaf's comparisons and boolean conditions, check the table for a row at each boundary value and on each side of each condition, and name the row that is missing. + On a whole-repository review this question is not judged: a mutation run or a + hand check over every leaf does not fit a hunter's budget. The receipt reads + `R7 Q7: hit(s) → per-leaf, not judged`, R7's tally takes the partial form, + and the `not reached:` line names the three leaf directories with the most hits, + each as the review that judges it: `R7 Q7: /{{.CmdPrefix}}-review `. On a + scoped review the question is judged in full. Violation: any `LIVED` line on a leaf package that is not recorded as an equivalent mutant — a missing table row or dead logic; name the mutant (file, line, mutation) and the row that would kill it. diff --git a/lang/python/rules/R7/falsifying-questions.md b/lang/python/rules/R7/falsifying-questions.md index bd92b4f2..d494bdb8 100644 --- a/lang/python/rules/R7/falsifying-questions.md +++ b/lang/python/rules/R7/falsifying-questions.md @@ -48,7 +48,7 @@ yields the loop once is scheduling, not synchronization, and stays. 7. **Does a mutant survive a leaf type's tests?** - Detect-grep: `(<=?|>=?) *('.'|-?[0-9]+|[a-zA-Z_.]*len\b)|\blen\([^)]*\) *(<=?|>=?|==|!=)` + Detect-grep: `(<=?|>=?) *('.'|-?[0-9]+|[a-zA-Z_.]*len\b)|\blen\([^)]*\) *(<=?|>=?|==|!=)` exclude-path=(^|/)handlers/,(^|/)__main__\.py$,(^|/)main\.py$,(^|/)cli\.py$ Detection: for each new or changed leaf package, with `paths_to_mutate` under `[tool.mutmut]` naming that package only, `mutmut run` then `mutmut results`; skip orchestrators, the top rung and any module that does I/O. `mutmut` not @@ -57,6 +57,12 @@ where no tests may run, take the hand check instead: list the leaf's comparisons and boolean conditions, check the parametrize table for a row at each boundary value and on each side of each condition, and name the row that is missing. + On a whole-repository review this question is not judged: a mutation run or a + hand check over every leaf does not fit a hunter's budget. The receipt reads + `R7 Q7: hit(s) → per-leaf, not judged`, R7's tally takes the partial form, + and the `not reached:` line names the three leaf directories with the most hits, + each as the review that judges it: `R7 Q7: /{{.CmdPrefix}}-review `. On a + scoped review the question is judged in full. Violation: any surviving mutant on a leaf module that is not recorded as equivalent — a missing parametrize row or dead logic; name the mutant (`mutmut show `) and the row that would kill it. diff --git a/lang/ts-react/rules/R7/falsifying-questions.md b/lang/ts-react/rules/R7/falsifying-questions.md index caaff4f5..be9a261b 100644 --- a/lang/ts-react/rules/R7/falsifying-questions.md +++ b/lang/ts-react/rules/R7/falsifying-questions.md @@ -56,7 +56,7 @@ synchronization, and stays. 7. **Does a mutant survive a leaf type's tests?** - Detect-grep: `(<=?|>=?) *('.'|-?[0-9]+|[a-zA-Z_.]*\.length\b)|\.length *(<=?|>=?|===|!==)` + Detect-grep: `(<=?|>=?) *('.'|-?[0-9]+|[a-zA-Z_.]*\.length\b)|\.length *(<=?|>=?|===|!==)` exclude-path=\.tsx$,(^|/)components/,(^|/)hooks/,(^|/)context/ Detection: for each new or changed leaf module, `npx stryker run` with a `stryker.config.mjs` whose `mutate` lists that module only (`.ts` leaves — parsers, reducers, a pure hook's helpers — never `.tsx` components, whose mutants @@ -71,6 +71,12 @@ may run, take the hand check instead: list the leaf's comparisons and boolean conditions, check the `it.each` table for a row at each boundary value and on each side of each condition, and name the row that is missing. + On a whole-repository review this question is not judged: a mutation run or a + hand check over every leaf does not fit a hunter's budget. The receipt reads + `R7 Q7: hit(s) → per-leaf, not judged`, R7's tally takes the partial form, + and the `not reached:` line names the three leaf directories with the most hits, + each as the review that judges it: `R7 Q7: /{{.CmdPrefix}}-review `. On a + scoped review the question is judged in full. Violation: any surviving mutant on a leaf module that is not recorded as equivalent — a missing `it.each` row or dead logic; name the mutant (file, line, replacement from the `Survived` line) and the row that would kill it. diff --git a/linter-driven-development/agents/rule-hunter.md b/linter-driven-development/agents/rule-hunter.md index c0bb667f..c9c92b87 100644 --- a/linter-driven-development/agents/rule-hunter.md +++ b/linter-driven-development/agents/rule-hunter.md @@ -120,8 +120,9 @@ question the table marks `judgment` reads `R Q: judgment → finding(s)`. When your budget ended before every counted hit was read, the receipt says so — `R Q: of hit(s) judged → finding(s)` — and the parent renders the review as partial; a receipt that carries the table's number -over hits you did not read is a false verdict. A question with no receipt was not -run. Then one tally line per rule you were given, always: +over hits you did not read is a false verdict. A question its rule leaves unjudged +on a whole-repository review reads `R Q: hit(s) → per-leaf, not +judged`, as the rule spells it. A question with no receipt was not run. Then one tally line per rule you were given, always: `R: finding(s)`, or when that rule is clean: `R: hunted clean — hits judged, every question receipted over the full scope`. When the budget ended the hunt, each rule's tally is instead `R: finding(s) in diff --git a/linter-driven-development/rules/R7-test-placement.md b/linter-driven-development/rules/R7-test-placement.md index 86cf2a7b..685dd80b 100644 --- a/linter-driven-development/rules/R7-test-placement.md +++ b/linter-driven-development/rules/R7-test-placement.md @@ -209,6 +209,12 @@ Test files are the ones the repository's test runner picks up (`*` to `>=`, a condition negated), run the leaf's tests, revert. + On a whole-repository review this question is not judged: a mutation run or a + hand check over every leaf does not fit a hunter's budget. The receipt reads + `R7 Q7: hit(s) → per-leaf, not judged`, R7's tally takes the partial form, + and the `not reached:` line names the three leaf directories with the most hits, + each as the review that judges it: `R7 Q7: /ldd-review `. On a + scoped review the question is judged in full. Violation: any survivor on a leaf type that is not recorded as equivalent — a missing table row or dead logic; name the mutant (file, line, operator) and the row that would kill it. diff --git a/linter-driven-development/skills/pre-commit-review/SKILL.md b/linter-driven-development/skills/pre-commit-review/SKILL.md index 430366c6..a2c3a83b 100644 --- a/linter-driven-development/skills/pre-commit-review/SKILL.md +++ b/linter-driven-development/skills/pre-commit-review/SKILL.md @@ -155,7 +155,9 @@ finding blocks (`rule | file:line | evidence | fix pattern | effort`), one recei falsifying question of each rule it was given (`R Q: hit(s) → finding(s)` with the counts table's number; `R Q: judgment → finding(s)` for a judgment question; `R Q: of hit(s) judged → - finding(s)` when its budget ended before every hit was read), one tally + finding(s)` when its budget ended before every hit was read; `R Q: + hit(s) → per-leaf, not judged` for a question its rule leaves to a scoped +review on a whole-repository one), one tally per rule — or, for a rule file that did not read, `R: rule unreadable at ` in the tally's place — and, when its budget ended the hunt, one `not reached:` line. A question with no receipt was not run. Findings, receipts, tallies (or unreadable diff --git a/python-linter-driven-development/agents/rule-hunter.md b/python-linter-driven-development/agents/rule-hunter.md index 58dd3d0b..96744f2b 100644 --- a/python-linter-driven-development/agents/rule-hunter.md +++ b/python-linter-driven-development/agents/rule-hunter.md @@ -120,8 +120,9 @@ question the table marks `judgment` reads `R Q: judgment → finding(s)`. When your budget ended before every counted hit was read, the receipt says so — `R Q: of hit(s) judged → finding(s)` — and the parent renders the review as partial; a receipt that carries the table's number -over hits you did not read is a false verdict. A question with no receipt was not -run. Then one tally line per rule you were given, always: +over hits you did not read is a false verdict. A question its rule leaves unjudged +on a whole-repository review reads `R Q: hit(s) → per-leaf, not +judged`, as the rule spells it. A question with no receipt was not run. Then one tally line per rule you were given, always: `R: finding(s)`, or when that rule is clean: `R: hunted clean — hits judged, every question receipted over the full scope`. When the budget ended the hunt, each rule's tally is instead `R: finding(s) in diff --git a/python-linter-driven-development/rules/R7-test-placement.md b/python-linter-driven-development/rules/R7-test-placement.md index e128b1d2..f398ac65 100644 --- a/python-linter-driven-development/rules/R7-test-placement.md +++ b/python-linter-driven-development/rules/R7-test-placement.md @@ -234,7 +234,7 @@ Answer each with evidence (`file:line`, command output) — never a bare verdict yields the loop once is scheduling, not synchronization, and stays. 7. **Does a mutant survive a leaf type's tests?** - Detect-grep: `(<=?|>=?) *('.'|-?[0-9]+|[a-zA-Z_.]*len\b)|\blen\([^)]*\) *(<=?|>=?|==|!=)` + Detect-grep: `(<=?|>=?) *('.'|-?[0-9]+|[a-zA-Z_.]*len\b)|\blen\([^)]*\) *(<=?|>=?|==|!=)` exclude-path=(^|/)handlers/,(^|/)__main__\.py$,(^|/)main\.py$,(^|/)cli\.py$ Detection: for each new or changed leaf package, with `paths_to_mutate` under `[tool.mutmut]` naming that package only, `mutmut run` then `mutmut results`; skip orchestrators, the top rung and any module that does I/O. `mutmut` not @@ -243,6 +243,12 @@ Answer each with evidence (`file:line`, command output) — never a bare verdict where no tests may run, take the hand check instead: list the leaf's comparisons and boolean conditions, check the parametrize table for a row at each boundary value and on each side of each condition, and name the row that is missing. + On a whole-repository review this question is not judged: a mutation run or a + hand check over every leaf does not fit a hunter's budget. The receipt reads + `R7 Q7: hit(s) → per-leaf, not judged`, R7's tally takes the partial form, + and the `not reached:` line names the three leaf directories with the most hits, + each as the review that judges it: `R7 Q7: /py-ldd-review `. On a + scoped review the question is judged in full. Violation: any surviving mutant on a leaf module that is not recorded as equivalent — a missing parametrize row or dead logic; name the mutant (`mutmut show `) and the row that would kill it. diff --git a/python-linter-driven-development/skills/pre-commit-review/SKILL.md b/python-linter-driven-development/skills/pre-commit-review/SKILL.md index 0fe3c579..6fbaaf48 100644 --- a/python-linter-driven-development/skills/pre-commit-review/SKILL.md +++ b/python-linter-driven-development/skills/pre-commit-review/SKILL.md @@ -157,7 +157,9 @@ finding blocks (`rule | file:line | evidence | fix pattern | effort`), one recei falsifying question of each rule it was given (`R Q: hit(s) → finding(s)` with the counts table's number; `R Q: judgment → finding(s)` for a judgment question; `R Q: of hit(s) judged → - finding(s)` when its budget ended before every hit was read), one tally + finding(s)` when its budget ended before every hit was read; `R Q: + hit(s) → per-leaf, not judged` for a question its rule leaves to a scoped +review on a whole-repository one), one tally per rule — or, for a rule file that did not read, `R: rule unreadable at ` in the tally's place — and, when its budget ended the hunt, one `not reached:` line. A question with no receipt was not run. Findings, receipts, tallies (or unreadable diff --git a/ts-react-linter-driven-development/agents/rule-hunter.md b/ts-react-linter-driven-development/agents/rule-hunter.md index dfe92e3e..e8f83e3c 100644 --- a/ts-react-linter-driven-development/agents/rule-hunter.md +++ b/ts-react-linter-driven-development/agents/rule-hunter.md @@ -120,8 +120,9 @@ question the table marks `judgment` reads `R Q: judgment → finding(s)`. When your budget ended before every counted hit was read, the receipt says so — `R Q: of hit(s) judged → finding(s)` — and the parent renders the review as partial; a receipt that carries the table's number -over hits you did not read is a false verdict. A question with no receipt was not -run. Then one tally line per rule you were given, always: +over hits you did not read is a false verdict. A question its rule leaves unjudged +on a whole-repository review reads `R Q: hit(s) → per-leaf, not +judged`, as the rule spells it. A question with no receipt was not run. Then one tally line per rule you were given, always: `R: finding(s)`, or when that rule is clean: `R: hunted clean — hits judged, every question receipted over the full scope`. When the budget ended the hunt, each rule's tally is instead `R: finding(s) in diff --git a/ts-react-linter-driven-development/rules/R7-test-placement.md b/ts-react-linter-driven-development/rules/R7-test-placement.md index 5d646322..7443a414 100644 --- a/ts-react-linter-driven-development/rules/R7-test-placement.md +++ b/ts-react-linter-driven-development/rules/R7-test-placement.md @@ -263,7 +263,7 @@ Answer each with evidence (`file:line`, command output) — never a bare verdict synchronization, and stays. 7. **Does a mutant survive a leaf type's tests?** - Detect-grep: `(<=?|>=?) *('.'|-?[0-9]+|[a-zA-Z_.]*\.length\b)|\.length *(<=?|>=?|===|!==)` + Detect-grep: `(<=?|>=?) *('.'|-?[0-9]+|[a-zA-Z_.]*\.length\b)|\.length *(<=?|>=?|===|!==)` exclude-path=\.tsx$,(^|/)components/,(^|/)hooks/,(^|/)context/ Detection: for each new or changed leaf module, `npx stryker run` with a `stryker.config.mjs` whose `mutate` lists that module only (`.ts` leaves — parsers, reducers, a pure hook's helpers — never `.tsx` components, whose mutants @@ -278,6 +278,12 @@ Answer each with evidence (`file:line`, command output) — never a bare verdict may run, take the hand check instead: list the leaf's comparisons and boolean conditions, check the `it.each` table for a row at each boundary value and on each side of each condition, and name the row that is missing. + On a whole-repository review this question is not judged: a mutation run or a + hand check over every leaf does not fit a hunter's budget. The receipt reads + `R7 Q7: hit(s) → per-leaf, not judged`, R7's tally takes the partial form, + and the `not reached:` line names the three leaf directories with the most hits, + each as the review that judges it: `R7 Q7: /tsr-ldd-review `. On a + scoped review the question is judged in full. Violation: any surviving mutant on a leaf module that is not recorded as equivalent — a missing `it.each` row or dead logic; name the mutant (file, line, replacement from the `Survived` line) and the row that would kill it. diff --git a/ts-react-linter-driven-development/skills/pre-commit-review/SKILL.md b/ts-react-linter-driven-development/skills/pre-commit-review/SKILL.md index be0075f8..b33fc297 100644 --- a/ts-react-linter-driven-development/skills/pre-commit-review/SKILL.md +++ b/ts-react-linter-driven-development/skills/pre-commit-review/SKILL.md @@ -157,7 +157,9 @@ finding blocks (`rule | file:line | evidence | fix pattern | effort`), one recei falsifying question of each rule it was given (`R Q: hit(s) → finding(s)` with the counts table's number; `R Q: judgment → finding(s)` for a judgment question; `R Q: of hit(s) judged → - finding(s)` when its budget ended before every hit was read), one tally + finding(s)` when its budget ended before every hit was read; `R Q: + hit(s) → per-leaf, not judged` for a question its rule leaves to a scoped +review on a whole-repository one), one tally per rule — or, for a rule file that did not read, `R: rule unreadable at ` in the tally's place — and, when its budget ended the hunt, one `not reached:` line. A question with no receipt was not run. Findings, receipts, tallies (or unreadable From a464860e9517a9ad8709c9381febbf6dee994493 Mon Sep 17 00:00:00 2001 From: Dan Mordechay Date: Sun, 18 Oct 2026 16:58:10 +0300 Subject: [PATCH 2/3] R7 Q7: the hand check reads the error assertions (Python, ts-react) In case G, no Python or ts-react report found the tenant mutant. The tests match only the word "tenant", which every tenant error shares. In ts-react, a removed empty check survives: the empty input reaches the character check, and its error also contains "tenant". In Python, a changed message survives. The hand check asked for rows at comparisons only, so it did not look at the assertions. The hand check now opens the leaf's test file, also when the file is outside the review's scope, and reads each error assertion. An assertion on part of the message is a violation. Go and generic do not change. Co-Authored-By: Claude Opus 5.5 --- lang/python/rules/R7/falsifying-questions.md | 9 +++++++-- lang/ts-react/rules/R7/falsifying-questions.md | 9 +++++++-- .../rules/R7-test-placement.md | 9 +++++++-- .../rules/R7-test-placement.md | 9 +++++++-- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/lang/python/rules/R7/falsifying-questions.md b/lang/python/rules/R7/falsifying-questions.md index d494bdb8..1df827af 100644 --- a/lang/python/rules/R7/falsifying-questions.md +++ b/lang/python/rules/R7/falsifying-questions.md @@ -56,7 +56,11 @@ survivors from a run that did not execute is not a pass. In a read-only review, where no tests may run, take the hand check instead: list the leaf's comparisons and boolean conditions, check the parametrize table for a row at each boundary - value and on each side of each condition, and name the row that is missing. + value and on each side of each condition, and name the row that is missing. Open + the leaf's test file for this, also when the file is outside the review's scope. + Then read each error assertion. An assertion that matches only a word all the + leaf's errors share (`match="tenant"`) cannot tell one error path from another, so + a removed check or a changed message survives. On a whole-repository review this question is not judged: a mutation run or a hand check over every leaf does not fit a hunter's budget. The receipt reads `R7 Q7: hit(s) → per-leaf, not judged`, R7's tally takes the partial form, @@ -64,5 +68,6 @@ each as the review that judges it: `R7 Q7: /{{.CmdPrefix}}-review `. On a scoped review the question is judged in full. Violation: any surviving mutant on a leaf module that is not recorded as - equivalent — a missing parametrize row or dead logic; name the mutant (`mutmut + equivalent — a missing parametrize row, an assertion on part of the message, or + dead logic; name the mutant (`mutmut show `) and the row that would kill it. diff --git a/lang/ts-react/rules/R7/falsifying-questions.md b/lang/ts-react/rules/R7/falsifying-questions.md index be9a261b..9d005c87 100644 --- a/lang/ts-react/rules/R7/falsifying-questions.md +++ b/lang/ts-react/rules/R7/falsifying-questions.md @@ -70,7 +70,11 @@ run that did not execute is not a pass. In a read-only review, where no tests may run, take the hand check instead: list the leaf's comparisons and boolean conditions, check the `it.each` table for a row at each boundary value and on - each side of each condition, and name the row that is missing. + each side of each condition, and name the row that is missing. Open the leaf's + test file for this, also when the file is outside the review's scope. Then read + each error assertion. An assertion that matches only a word all the leaf's errors + share (`toThrow('tenant')`) cannot tell one error path from another, so a removed + check or a changed message survives. On a whole-repository review this question is not judged: a mutation run or a hand check over every leaf does not fit a hunter's budget. The receipt reads `R7 Q7: hit(s) → per-leaf, not judged`, R7's tally takes the partial form, @@ -78,5 +82,6 @@ each as the review that judges it: `R7 Q7: /{{.CmdPrefix}}-review `. On a scoped review the question is judged in full. Violation: any surviving mutant on a leaf module that is not recorded as - equivalent — a missing `it.each` row or dead logic; name the mutant (file, line, + equivalent — a missing `it.each` row, an assertion on part of the message, or dead + logic; name the mutant (file, line, replacement from the `Survived` line) and the row that would kill it. diff --git a/python-linter-driven-development/rules/R7-test-placement.md b/python-linter-driven-development/rules/R7-test-placement.md index f398ac65..8cbc6d75 100644 --- a/python-linter-driven-development/rules/R7-test-placement.md +++ b/python-linter-driven-development/rules/R7-test-placement.md @@ -242,7 +242,11 @@ Answer each with evidence (`file:line`, command output) — never a bare verdict survivors from a run that did not execute is not a pass. In a read-only review, where no tests may run, take the hand check instead: list the leaf's comparisons and boolean conditions, check the parametrize table for a row at each boundary - value and on each side of each condition, and name the row that is missing. + value and on each side of each condition, and name the row that is missing. Open + the leaf's test file for this, also when the file is outside the review's scope. + Then read each error assertion. An assertion that matches only a word all the + leaf's errors share (`match="tenant"`) cannot tell one error path from another, so + a removed check or a changed message survives. On a whole-repository review this question is not judged: a mutation run or a hand check over every leaf does not fit a hunter's budget. The receipt reads `R7 Q7: hit(s) → per-leaf, not judged`, R7's tally takes the partial form, @@ -250,5 +254,6 @@ Answer each with evidence (`file:line`, command output) — never a bare verdict each as the review that judges it: `R7 Q7: /py-ldd-review `. On a scoped review the question is judged in full. Violation: any surviving mutant on a leaf module that is not recorded as - equivalent — a missing parametrize row or dead logic; name the mutant (`mutmut + equivalent — a missing parametrize row, an assertion on part of the message, or + dead logic; name the mutant (`mutmut show `) and the row that would kill it. diff --git a/ts-react-linter-driven-development/rules/R7-test-placement.md b/ts-react-linter-driven-development/rules/R7-test-placement.md index 7443a414..9aff4288 100644 --- a/ts-react-linter-driven-development/rules/R7-test-placement.md +++ b/ts-react-linter-driven-development/rules/R7-test-placement.md @@ -277,7 +277,11 @@ Answer each with evidence (`file:line`, command output) — never a bare verdict run that did not execute is not a pass. In a read-only review, where no tests may run, take the hand check instead: list the leaf's comparisons and boolean conditions, check the `it.each` table for a row at each boundary value and on - each side of each condition, and name the row that is missing. + each side of each condition, and name the row that is missing. Open the leaf's + test file for this, also when the file is outside the review's scope. Then read + each error assertion. An assertion that matches only a word all the leaf's errors + share (`toThrow('tenant')`) cannot tell one error path from another, so a removed + check or a changed message survives. On a whole-repository review this question is not judged: a mutation run or a hand check over every leaf does not fit a hunter's budget. The receipt reads `R7 Q7: hit(s) → per-leaf, not judged`, R7's tally takes the partial form, @@ -285,5 +289,6 @@ Answer each with evidence (`file:line`, command output) — never a bare verdict each as the review that judges it: `R7 Q7: /tsr-ldd-review `. On a scoped review the question is judged in full. Violation: any surviving mutant on a leaf module that is not recorded as - equivalent — a missing `it.each` row or dead logic; name the mutant (file, line, + equivalent — a missing `it.each` row, an assertion on part of the message, or dead + logic; name the mutant (file, line, replacement from the `Survived` line) and the row that would kill it. From a7eed61844e4f33753ea8a8bb9628aee53f17003 Mon Sep 17 00:00:00 2001 From: Dan Mordechay Date: Sun, 18 Oct 2026 18:30:05 +0300 Subject: [PATCH 3/3] R7 Q7: the Python hand check reads the assertions of every leaf The Python R7 hunter judged Q7 one hit at a time. The only hit in tenant.py is the length comparison, and the table has rows for it, so the hunter cleared the file and did not read its error assertion. The hand check now reads the error assertions of each leaf file in scope, also where no hit row points. Co-Authored-By: Claude Opus 5.5 --- lang/python/rules/R7/falsifying-questions.md | 3 ++- python-linter-driven-development/rules/R7-test-placement.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lang/python/rules/R7/falsifying-questions.md b/lang/python/rules/R7/falsifying-questions.md index 1df827af..bdf72a52 100644 --- a/lang/python/rules/R7/falsifying-questions.md +++ b/lang/python/rules/R7/falsifying-questions.md @@ -58,7 +58,8 @@ and boolean conditions, check the parametrize table for a row at each boundary value and on each side of each condition, and name the row that is missing. Open the leaf's test file for this, also when the file is outside the review's scope. - Then read each error assertion. An assertion that matches only a word all the + Then read each error assertion of each leaf in scope, once per leaf file and not + only where a hit row points. An assertion that matches only a word all the leaf's errors share (`match="tenant"`) cannot tell one error path from another, so a removed check or a changed message survives. On a whole-repository review this question is not judged: a mutation run or a diff --git a/python-linter-driven-development/rules/R7-test-placement.md b/python-linter-driven-development/rules/R7-test-placement.md index 8cbc6d75..7c43210b 100644 --- a/python-linter-driven-development/rules/R7-test-placement.md +++ b/python-linter-driven-development/rules/R7-test-placement.md @@ -244,7 +244,8 @@ Answer each with evidence (`file:line`, command output) — never a bare verdict and boolean conditions, check the parametrize table for a row at each boundary value and on each side of each condition, and name the row that is missing. Open the leaf's test file for this, also when the file is outside the review's scope. - Then read each error assertion. An assertion that matches only a word all the + Then read each error assertion of each leaf in scope, once per leaf file and not + only where a hit row points. An assertion that matches only a word all the leaf's errors share (`match="tenant"`) cannot tell one error path from another, so a removed check or a changed message survives. On a whole-repository review this question is not judged: a mutation run or a