Merged
Conversation
… untested and nothing said so (#188) Its header claimed a tool added later was covered without anyone remembering. That was true of flags -- each script's own argument loop is parsed for arms carrying `shift 2` -- and false of scripts, which were four hand-written drive_script() calls. A new tool under scripts/ was silently untested by the suite that exists to sweep every tool: nothing errored, the totals went up as assertions were added elsewhere, and the lost coverage appeared in no number the suite printed. #183 added the jit-doctor.sh line and a comment naming the gap. The list is now `git ls-files -- scripts`, the enumeration tests/test-dogfood-entries.sh already ran for rule coverage. Enumeration turns "not in the list" into "must be driven", so classify_script() gives every tracked file one of four verdicts read out of its own bytes: drive the canonical loop, with valued flags in it no-flags no loop and no sign of flag parsing -- printed BY NAME, so "nothing to drive here" and "never ran" are different lines loop-no-flags the loop is there and nothing could be read out of it -- red flags-elsewhere no loop, but getopts / shift 2 / a dash case arm -- red No skip list, deliberately: a skip list is the hand-written list again one indirection further out, stale in the same silence. The cost is that a tool taking flags in an unfamiliar shape is red rather than quietly skipped. The sweep own guarantee is driven rather than assumed, against a throwaway git repository holding three scripts this suite has never heard of, one per verdict: the enumeration finds them, the classifier sorts them, and the unlisted tool actually goes red for want of a positive control -- paired with an assertion that the same fixture refusal path PASSED, so the red cannot be a broken fixture reading as a working guard. Two floors (at least one driven and at least one no-flags in the real tree) and an independently written cross-check (every file mentioning `shift 2` is one the sweep drove) catch a classifier stuck on one answer or quietly dropping a single script. The header no longer overstates itself: both halves are now read from the repository, and it says which one was typed until when. paths/00-manual/tooling.md gains the consequence for whoever adds the sixth tool -- `while [ $# -gt 0 ]` is now a shape, not a habit. Body-only edit, no frontmatter change, so no index rebuild. 70/70 in the suite, all suites green, shellcheck clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ached the tally (#188) Two findings from the audit of the previous commit, both real. 1. `loop-no-flags` -- "the loop is there and yielded nothing" -- had no fixture anywhere, and worse, it was wrong for a shape that is perfectly ordinary: a tool whose flags are all booleans has a loop, no `shift 2` arm, and nothing for #114 to happen to. It would have been told its parser had rotted. Split in two on evidence read from the same loop -- does any arm reach for `$2`? -- so `boolean-flags-only` is a named pass and `loop-no-flags` keeps its FAIL for the case where a value IS consumed in a shape valued_flags() cannot see. Both now have a fixture; all five verdicts do. 2. When the throwaway git repository could not be built, the section printed a SKIPPED block and left PASS/FAIL untouched -- so the tally at the bottom, and the exit code run-all.sh reads, were byte-identical to a run where the sweep self-test passed. That is this issue own defect one level up, inside its own fix. The summary line now counts skipped sections and the suite exits 2, which run-all.sh already renders as "NOT a clean result". Driven, not reasoned: forcing the fixture build to fail gives exit 2 and "62/62 passed, 0 failed, 1 section(s) SKIPPED"; inverting loop_consumes_value() either way reddens exactly one of the two new assertions. 74/74, all suites green, shellcheck clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 18, 2026
Merged
fdaviddpt
pushed a commit
that referenced
this pull request
Aug 18, 2026
…ching (#191) A `<file>.sh:NNN` pointer is exact the day it is written and wrong on the next PR that inserts a line above it. It rots silently: a rotted citation reads exactly like a live one, so the reader follows it and lands on a plausible comment rather than on an error. Counted on main at 98386f1, outside the assembled changelog: nine citations of that shape. Six pointed at the wrong thing, one had drifted off the block it named, two were still right. #191 names three of the six; the sweep added here found the other three. #185 added one of them three hours before #190 moved it. Every citation in an enforced file now points at something greppable -- a function name, a distinctive literal, or the issue number, which additionally says why rather than where. tests/test-line-citations.sh has three outcomes rather than two. It FAILS on a citation in a tracked scripts or tests shell file; it REPORTS WITHOUT FAILING over every other tracked file; and it names the assembled changelog as deliberately not swept, because that file is generated and a finding in it is unactionable. The advisory half is advisory rather than enforced for one stated reason: its only remaining finding lives in an entry held by PR #192, and reddening a file this change may not edit is how a check gets disabled in its first week. Widening it is one awk pattern. The false-positive surface was costed before the check was written: over all 96 tracked basenames and the whole tree, 10 hits and 0 false. A bash diagnostic, a shellcheck line, an awk error and a longer basename are driven as controls that must not match, alongside a planted citation that must. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
fdaviddpt
pushed a commit
that referenced
this pull request
Aug 18, 2026
A reason that has been spent reads exactly like a reason that still holds -- which is the defect class this whole branch is about, in the file that exists to refuse it. The suite printed "advisory only while #192 holds one of the files it would flag" on every run, and #192 merged at eb9cf71. Replaced with what is true: widening is a scope decision nobody has taken, not a blocked one. Deliberately NOT widened here. It is unrequested scope on a pull request already in review, and the question it settles -- whether every future doc, template, example and jit-context entry is bound by this rule, in every installed project's contributor path -- is a design decision this branch was not briefed to make. Filed for the maintainer with the exact cost instead: two awk alternations in the selectors, one line in the entry currently flagged, and zero measured false positives on that surface across two commits. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
fdaviddpt
added a commit
that referenced
this pull request
Aug 18, 2026
…ses new ones (#191) (#197) * Cross-file line-number citations rotted silently, and nothing was watching (#191) A `<file>.sh:NNN` pointer is exact the day it is written and wrong on the next PR that inserts a line above it. It rots silently: a rotted citation reads exactly like a live one, so the reader follows it and lands on a plausible comment rather than on an error. Counted on main at 98386f1, outside the assembled changelog: nine citations of that shape. Six pointed at the wrong thing, one had drifted off the block it named, two were still right. #191 names three of the six; the sweep added here found the other three. #185 added one of them three hours before #190 moved it. Every citation in an enforced file now points at something greppable -- a function name, a distinctive literal, or the issue number, which additionally says why rather than where. tests/test-line-citations.sh has three outcomes rather than two. It FAILS on a citation in a tracked scripts or tests shell file; it REPORTS WITHOUT FAILING over every other tracked file; and it names the assembled changelog as deliberately not swept, because that file is generated and a finding in it is unactionable. The advisory half is advisory rather than enforced for one stated reason: its only remaining finding lives in an entry held by PR #192, and reddening a file this change may not edit is how a check gets disabled in its first week. Widening it is one awk pattern. The false-positive surface was costed before the check was written: over all 96 tracked basenames and the whole tree, 10 hits and 0 false. A bash diagnostic, a shellcheck line, an awk error and a longer basename are driven as controls that must not match, alongside a planted citation that must. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Address the audit: an advisory sweep that could report coverage it never had (#191) Four findings from the self-review, all in tests/test-line-citations.sh unless noted: - The advisory sweep had no floor guard on its file count, so a broken selector would print `Clean across 0 file(s)` and exit 0 -- byte-identical, to run-all.sh which reads only the exit code, to a genuinely clean tree. It now takes the same `< 10 -> exit 2` floor the enforced sweep already had. - The enforced selector was `[^/]*`, so a shell file landing in a subdirectory one day would fall silently into the report-only bucket and stay there. Now `.*`, which closes the shape rather than half of it. - A tracked path that is not a readable file was skipped in silence in both loops, while the header above claimed a count the sweep never reached. Both are counted now; an unreadable path in the enforced set fails the suite. - The needle accepts an optional directory prefix, and no enforced file exercises that branch -- every real citation was bare-basename -- so a dead prefix branch would have read as clean. It has its own positive control now. scripts/jit-dry-run.sh named `jit_log_name()` and `jit_row_id()` as being "in pre-tool-hook.sh". They are defined in common.sh and only called there, which is an anchor wrong in the same way the line numbers were. Reworded to what is true: pre-tool-hook.sh routes every refused row through them. The 96-basename and 10-hit figures are now pinned to 98386f1 in all three places that carry them, rather than stated in the present tense. They were already stale by two the moment this branch added a file, which is the exact failure mode the change is about. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Two prose findings from the re-review (#191) - CLAUDE.md read as attributing the move to #191 itself, because the issue citation sat immediately after "the PR that moved it". #185 added it and #190 moved it three hours later; the sentence now says so. - scripts/jit-dry-run.sh: "which #7 is the reason for" -> "which exists because of #7". Accurate either way, clunky one way. A third finding was argued down rather than applied. The reviewer recounted the audit as 3 right / 1 drifted / 5 wrong and read the tooling.md citation as correct, on the strength of that entry own sentence "The truncation is pre-tool-hook.sh:127-144 and it is correct" -- where "it" is the truncation BEHAVIOUR being deliberate, not the line range being accurate. The same range in scripts/jit-dry-run.sh was judged wrong in the same review, which is the contradiction: at 98386f1 those lines are the #182 subagent_type comment, and the truncation is at 162-185. The count stands at 6 wrong / 1 drifted / 2 right. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * README said "Two suites are the exception" and this change made it three (#191) The test section enumerates the suites that are about this repository rather than the hooks, and the enumeration was a count. Adding tests/test-line-citations.sh made the sentence wrong in the quietest possible way -- a reader counting two and finding three has no reason to think anything is missing. Named rather than counted where it can be: the third one is called out by name and by what it refuses, and the "assembler they are about" clause now says which two it means. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Rebase onto 5b46095: two citations #194 wrote while this branch was open (#191) CI caught these, and the check that caught them is the one this branch adds. Neither is a defect in this diff -- they are in tests/test-session-markers.sh, section J, which #194 (issue #177) added to main after this branch was cut. - `session-start-hook.sh:32 parses the payload with jit_json_fields + jit_session_key` -- the line number was ALREADY WRONG on the day it was written: line 32 is mid-sentence in the LC_ALL paragraph, and the parse claim is three lines earlier. The comment names its own functions, so the number was carrying nothing the prose did not already carry. Dropped. - `the comment at session-start-hook.sh:25-29` -- accurate today, and a pointer to a comment rather than to code, which is the shape most likely to move. This one wanted more than a rename: it is a claim about TWO hooks agreeing, so it now names the pair of functions the claim is about and says which half of the claim this leg proves. The counts pinned to 98386f1 are re-pinned to 5b46095 in all three places that carry them, and the second measurement is kept beside the first rather than replacing it. Nine citations at 98386f1, eleven at 5b46095; 96 basenames / 10 hits / 0 false, then 98 / 12 / 0. Pinning was already the right call and the rebase is what proved it: the pin went stale in under a day and SAID SO, where a present-tense number would have read true and been wrong. That interval is now the argument the suite header leads with. The citations this check first went red on in CI were not the ones it was written for, they were written by somebody else while it was in review, and one of the two was born broken. A cleanup would not have caught either. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * The advisory half said it was blocked on #192, which has landed (#191) A reason that has been spent reads exactly like a reason that still holds -- which is the defect class this whole branch is about, in the file that exists to refuse it. The suite printed "advisory only while #192 holds one of the files it would flag" on every run, and #192 merged at eb9cf71. Replaced with what is true: widening is a scope decision nobody has taken, not a blocked one. Deliberately NOT widened here. It is unrequested scope on a pull request already in review, and the question it settles -- whether every future doc, template, example and jit-context entry is bound by this rule, in every installed project's contributor path -- is a design decision this branch was not briefed to make. Filed for the maintainer with the exact cost instead: two awk alternations in the selectors, one line in the entry currently flagged, and zero measured false positives on that surface across two commits. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Florian DAVID <fdavid@digital-village.fr> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #188.
What was wrong
tests/test-arg-flag-values.shsweeps every tool for the missing-flag-value hang of #114. Its header said a thing added later was covered without anyone having to remember it. That was true of flags — each script's own argument loop is parsed for arms carryingshift 2— and false of scripts, which were four hand-writtendrive_scriptcalls at the bottom of the file.So a new tool under
scripts/was silently untested by the suite whose job is to sweep every tool, and untested in the way this repository is named after: nothing errored, the totals went up as assertions were added elsewhere, and the coverage that was lost appeared in no number the suite printed. #183 added thejit-doctor.shline and a comment naming the gap; the gap itself stayed open.What changed
The script list is now
git ls-files -- scripts, the same enumerationtests/test-dogfood-entries.shalready ran for rule coverage.Enumeration turns "not in the list" into "must be driven", so a tool that legitimately takes no flag arguments needs an answer of its own.
classify_script()gives every tracked file one of five verdicts, read out of that file's own bytes, and each of the five prints a line naming the script:driveshift 2armsno-flagsboolean-flags-only$2loop-no-flags$2, noshift 2arm to readflags-elsewheregetopts/shift 2/ a dash case armToday's tree: 4 driven, 6 with no flags.
There is no skip list, deliberately. The issue named the choice between an explicit skip list and a sweep that can tell "no flags to test" from "never ran". A skip list is the hand-written list again, one indirection further out, stale in the same silence — so the second was built. What it costs, stated rather than hidden: a tool parsing flags in a shape this suite cannot read is red, not skipped, and there is no opt-out short of changing the classifier.
The header comment no longer overstates itself. Both halves are read from the repository now, and it says which one was typed until when.
Proving it catches an unlisted script
Observing that the sweep passes on today's tree says nothing — today's tree is exactly what the old hand-written list already covered. So the enumeration and the classifier are driven on every run against a throwaway git repository holding five scripts this suite has never heard of, one per verdict. The enumeration finds them, the classifier sorts them, and the unlisted tool actually goes red for want of a positive control — paired with an assertion that the same fixture's refusal path passed on the same run, so the red cannot be a broken fixture reading as a working guard.
The sweep's own coverage is guarded two ways that fail differently: two floors (at least one
driveand at least one no-flag verdict in the real tree) catch a classifier stuck on one answer, and an independently written cross-check — every file mentioningshift 2anywhere must be one the sweep drove — catches a classifier that quietly loses a single script.When the fixture cannot be built at all, the suite exits
2and its summary line counts the skipped section. A tally identical to a clean run would be this issue's own defect one level up, inside its own fix.Mutation evidence
Every mutation applied to a copy outside the tree:
tracked_scripts()reduced to the old four-script hand listhas_arg_loop()always falseparses_flags_somehow()always falseloop_consumes_value()inverted either waygit initforced to fail62/62 passed, 0 failed, 1 section(s) SKIPPEDThe first row is the one that matters: reverting to the arrangement this issue is about reddens the suite.
Also in the diff
.claude/jit-context/paths/00-manual/tooling.md— the consequence for whoever adds the sixth tool:while [ $# -gt 0 ]is now a shape, not a habit. Body-only edit, no frontmatter change, so no index rebuild.changelog.d/188.fixed.md.Nothing under
scripts/is touched.Checks
bash tests/run-all.sh— all suites passed; this suite 74/74 (was 65/65).shellcheck -S warning scripts/*.sh tests/*.sh— clean.python3 .oss/assemble_changelog.py --check— ok.awk,gawkandmawkall agree on thehas_arg_loopidiom (observed); a CRLF line still matches the loop pattern (observed). Windows and Linux legs are reasoned, not observed.Reviewed by an
Explorereviewer (no findings) andoss:auditor(two findings, both accepted and fixed in the second commit — a skipped section that could not reach the tally, and aloop-no-flagsverdict that had no fixture and was wrong for an all-boolean tool).Verified by the maintainer
The load-bearing mutation, re-run by me in a fresh worktree at
origin/mainwith only this suite file copied in: unmutated it is74/74 passed, 0 failed, 0 SKIPPED, exit 0. Revertingtracked_scripts()to the four-script hand list this issue is about gives58/63 passed, 5 failed, exit 1. That is the claim that matters — a suite whose enumeration cannot be reverted without reddening is a suite that is actually enumerating.And one accident worth reporting, because it exercised a guard nobody wrote a fixture for. My first attempt staged the tree with
git archiverather than a worktree, so there was no.gitandgit ls-filesreturned nothing. The suite did not pass over an empty sweep — it failed and said so in as many words: "and this suite would have reported a clean run over an empty sweep." That is the floor doing exactly its job against a case I produced by mistake, which is better evidence than a fixture I would have built to agree with it.The design call is right, and it is the one I could not settle when I briefed it. A skip list is the hand-written list one indirection further out, stale in the same silence. Five verdicts read from each file's own bytes is the harder build and the correct one. The cost is stated plainly in the body rather than buried: a tool parsing flags in a shape the classifier cannot read goes red, not skipped, with no opt-out short of changing the classifier. I accept that trade — a false red is debuggable in one run, and the alternative failure is a tool with flags reading as a tool with none, which is this issue verbatim.
loop-no-flagswas a real bug, not just an untested arm. The auditor found the missing fixture; acting on it turned up that the verdict would have failed any all-boolean tool with the message "the parser rotted". That is the second time today an audit finding has been the thread that pulled a defect out of a diff, and it is the argument for the two-reviewer arrangement over one.Filed rather than carried: #193 — the classifier reads bash argument-loop shapes, so a tool written in another language under
scripts/classifies as no-flags and goes quiet rather than red. Correct today only becauseCLAUDE.mdforbids anything but bash, awk and perl there, and nothing connects the two rules.