Merged
Conversation
…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>
…ver 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>
- 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>
…ree (#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>
…pen (#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>
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>
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 #191. Rebased onto
5b46095.The defect
A
<file>.sh:NNNpointer is exact the day it is written and wrong on the next pull request 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-looking comment rather than on an error. That is this repository's own defect class pointed at its own source.The rot rate is measured, and it was measured twice
At
98386f1there were nine citations of that shape outside the assembled changelog: six wrong, one drifted, two right.At
5b46095— this branch's rebase target, hours later — there were eleven: seven wrong, one drifted, three right. #194 added the two new ones, intests/test-session-markers.sh, a file this change does not otherwise touch. One of the two was already wrong on the day it was written, its line number landing three lines past the sentence it names.CI is what found them, and the check that found them is the one this PR adds. The citations this sweep first went red on were not the ones it was written for. That is the argument for a check rather than a cleanup, and it is a measurement rather than a prediction.
What replaces a line number
A function name, a distinctive literal, or the issue number. All three are greppable, all three survive an insertion above them, and the issue number additionally says why rather than where. The trade is taken knowingly — none is as precise as a line. I found no citation where the number was genuinely the only useful pointer; every one named something with a name.
One of the two new ones wanted more than a rename.
the comment at session-start-hook.sh:25-29is a claim about two hooks agreeing rather than a pointer to one site, so it now names the pair of functions the claim is about and says which half of it that test leg proves.The check
tests/test-line-citations.sh. Three outcomes, not two:scripts/ortests/. A hit fails the suite.CHANGELOG.md, stated rather than silently skipped: it is assembled by.oss/assemble_changelog.pyand never hand-edited, so a finding in it is unactionable by construction.The false-positive surface was costed before the check was written, and re-measured on the rebase.
98386f1: 96 basenames, 10 hits, 0 false.5b46095: 98, 12, 0. Every hit both times was a real citation. These shapes were checked as non-matching and are driven as controls the suite fails on:One honest residual, documented and deliberately not allowlisted:
grep -noutput quoted verbatim in a comment would match. The escape hatch is prose, because an allowlist rots at exactly the rate the numbers do.Counts are pinned to a commit rather than written in the present tense. The rebase is what proved that was right: the pin went stale in under a day and said so, where a present-tense number would have read true and been wrong. Both measurements are kept, not replaced.
One thing for you to decide
The advisory half was advisory because
tooling.mdwas held by PR #192, and reddening a file this branch may not edit is how a check gets disabled in week one. #192 has since landed, so that reason is spent. I did not widen it: that is unrequested scope on a PR 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 I was not briefed to make.Both the suite and
CLAUDE.mdnow say that in as many words, so a spent reason cannot read as a live one. The cost if you want it: twoawkalternations in the selectors, plus one line intooling.md, which is the single remaining advisory finding. Measured false positives on that surface: zero, twice.The suite cannot pass vacuously
Three controls run before a single real file is read, and the first
exit 1s rather than printing a sweep nobody performed: a planted bare-basename citation must be seen; a planted path-qualified one must be seen (the dir-prefix branch is otherwise never driven, since every real citation is bare-basename); the five forms above must not match. A needle that fails to compile lands on the same branch. Both file sets carry a floor guard, and an unreadable tracked path is counted rather than skipped in silence.Review
The auditor found four real defects in the first commit — the advisory set had no floor guard where the enforced set did, the enforced selector was flat-only, unreadable paths were dropped silently, and the dir-prefix branch had no control. All four fixed.
One reviewer finding was argued down: it recounted the audit as 5-wrong-not-6 and read the
tooling.mdcitation as correct on the strength of that entry's own sentence "The truncation ispre-tool-hook.sh:127-144and it is correct". That "it" is the truncation behaviour being deliberate, not the range being accurate — and the same review judged the identical range wrong when cited fromscripts/jit-dry-run.sh.Also
README.mdsaid "Two suites are the exception" and this makes it three.CLAUDE.mdcarries the convention.Verified by the maintainer
CI caught what neither of us could have locally, and it caught it with this PR's own check. The ubuntu leg failed on two citations in
tests/test-session-markers.shthat PR #194 wrote today, in a file this branch did not contain, in the window between the branch point and the review. One of the two was already wrong the day it was written. That is the rot rate measured twice — nine citations at98386f1, eleven at5b46095— rather than asserted, and it is better evidence for this change than any fixture.The argued-down finding is the one I checked by hand, before the rebase. The reviewer read
tooling.md's "The truncation ispre-tool-hook.sh:127-144and it is correct" as the entry vouching for its own range. I read the sentence in the tree: that clause is about the truncation behaviour being deliberate — the following clause says "what was wrong is that the row read as enforced" — and the same review called the identical range wrong when cited fromjit-dry-run.sh. Refusing it was right.Red re-run, mine, against
mainwith only the suite copied in:exit 1,3/4, six real stale citations, all three controls green in the same run. A needle that failed to compile would have gone quiet; the controls are what make the red mean something.Costing the false-positive surface before writing the check is why it will survive — 96 basenames, whole tree, 10 hits, 10 real, 0 false, and zero again after the rebase. The
grep -n-quoted-in-a-comment residual stays in prose rather than in an allowlist, and I agree with that: an allowlist rots at the rate the numbers do.The scope call handed back to me is answered in #198, not here. Widening the advisory half now costs two
awkalternations and one line intooling.md— but it binds every future doc, template, example and entry in every installed project's contributor path, and that belongs in its own change with its own red, not as a rider on a PR already reviewed. What this branch does instead is the part that could not wait: the spent reason no longer reads as a live one, in both the suite output andCLAUDE.md.Force-pushed by me after the rebase,
1dc43bf→7cd0237, with--force-with-lease. A rewrite, on a branch that is this loop's own, opened twenty minutes earlier, that nobody else has fetched — and the prior head is still reachable from the PR timeline. Recorded because a force-push should never be silent, not because it was in doubt.