docs(review): rule 18 — absence of code cannot be demonstrated by execution - #1209
docs(review): rule 18 — absence of code cannot be demonstrated by execution#1209lilyshen0722 wants to merge 3 commits into
Conversation
…cution @sprint-review's #1149 comment listed the backfillPending absence check as "defensible as a barrier" and stopped there, which is a verdict about one test rather than a rule anyone can reuse. The generalisation makes it decidable: a behavioural test can show a branch produces the right answer and cannot show a branch is gone. Dead code never runs, contributes to no assertion, and waits for the next reader to revive it. So the question is not "is this test grepping?" but "is the property behavioural or structural?" Keeps the default suspicion of source assertions intact and carves out the one class they are the only instrument for. Riders: label which kind the test is, or a reviewer applying the default suspicion correctly deletes it; and give an absence assertion a positive control, since a grep matching nothing because the pattern is wrong looks exactly like one matching nothing because the code is gone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
lilyshen0722
left a comment
There was a problem hiding this comment.
Approve. Verified at 4d7aa1de — every factual claim in the earned-note holds.
Checked:
- Rule 12 is indeed "verify the instrument before trusting a negative" — the cross-reference lands.
- #1149 is an OPEN issue ("Backfill cutoff tests pin text order, not control flow"); #1162 merged 2026-08-23 as "the read-failure safety property had no executing test." Both match the citation.
- The
backfillPendingabsence test is real:backend/__tests__/unit/models/threadUserState.test.js:219-226, and it satisfies rider one — labelled "stays textual ON PURPOSE and is the exception that shows the rule." - The ledger-read conversion was a genuine gain, as claimed.
threadStateReadContract.test.jsruns the handler, forces the ledger query to reject, and carries an explicittest('CONTROL: the same ledger row without a failure collapses them')plusexpect(p.threads.length).toBeGreaterThan(0)on both sides. And thecatchbranch really had no executing coverage — the file says so at the top of that block. - No rule renumbering: nothing references the checklist by count, and the two numeric inbound pointers (
REVIEW.md§7, ADR-019 rule 9) are unaffected by an append.
One note, non-blocking — the exemplar does not satisfy rider two.
Rider two requires an absence assertion to carry a positive control. The worked example is a bare
expect(CONTROLLER_SRC).not.toMatch(/backfillPending/);with no control. It is safe, but by a mechanism the rule never names: CONTROLLER_SRC comes from fs.readFileSync, which throws on a missing path, so the loudest failure mode — asserting absence against an empty string — is closed by the loader rather than by a control.
What is still open is the mode a control would not catch either: the assertion is scoped to one file, so a probe moved to a sibling controller goes green here while the dead code lives on.
The asymmetry is visible inside the PR's own two examples: the behavioural test got an explicit CONTROL:, and the structural one — the case rider two is specifically about — did not.
Suggestion: rider two would be more actionable as load the source in a way that fails loudly, and scope the claim to where the code could have moved, rather than "needs a positive control," which the exemplar does not have and does not need in that form.
Not verified: whether the backfillPending probe could plausibly live in another file today (I did not enumerate the controllers), and nothing about the rule's adoption status — the header still reads "Draft for adoption" with Sam's adopt-or-strike call pending, which rule 18 inherits.
@sprint-review's near-miss, running rule 17's closing check against the fix
rule 17 was written for. `agentRuntimeAuth.ts:98` is `.select('_id').lean()`
on an interleaved DM-pod `Pod.find`, thirty-seven lines below the
`User.findOne` — read as the User projection, it would have condemned
`req.agentUser?.username` as dead on arrival. Both `User.findOne` calls are
unprojected; the term is live.
Grepping for `.select(` near an assignment finds the wrong query whenever two
run in the same block, so the check needs the same discipline it imposes: read
the call the projection is chained to, not the nearest one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Second commit on this branch, same file, separate concern — folded in here rather than opened as a sibling PR because two PRs appending to Rule 17 rider: bind the projection to its own query, not to line proximity. @sprint-review's near-miss, from running rule 17's own closing check against the fix that earned rule 17. The general form: Observation and near-miss are @sprint-review's; the wording is mine. |
lilyshen0722
left a comment
There was a problem hiding this comment.
Re-gated at d02a68f2 — head moved after my 4d7aa1de review. 10/10 checks, CLEAN. Rule 18's text is untouched by this commit, so that half of my earlier review carries forward unchanged (including the non-blocking rider-two note, still open).
The new rider on rule 17 is accurate — every load-bearing claim in it verified against origin/main:
| claim | check |
|---|---|
agentRuntimeAuth.ts:98 is .select('_id').lean() |
✅ line 98 |
it belongs to an interleaved DM-pod Pod.find |
✅ Pod.find opens at line 95 |
"thirty-seven lines below the User.findOne" |
✅ User.findOne at line 61; 98 − 61 = 37, exact |
"Both User.findOne calls are in fact unprojected" |
✅ lines 61 and 185; the file's only .select( is the one at 98 |
The wording also represents the observation faithfully — it keeps the part that makes it a rule (bind the projection to the call it is chained to) rather than only the anecdote.
One thing I checked and was wrong about, recording it because the negative is the useful part. I expected the nested *(Rider added …)* inside the outer *(Earned: …)* to break the emphasis — an inner * closing the outer span is a real Markdown failure mode, and this is a docs-only diff where markup is the deliverable.
It doesn't. Rendered the exact construction through GitHub's own renderer (gh api /markdown) and it produces properly nested <em> inside <em>:
<p><em>(Earned: … same file. <em>(Rider added … the term is live.)</em> Reviving the term …)</em></p>No defect. Worth noting only that nested <em> renders identically to the outer italic, so the rider is not visually distinguished from the text around it — cosmetic, not a blocker.
Not verified: that line 61 and line 185 are the only two User.findOne calls in the file (I read the grep for .select( as complete, which bounds the projection claim, but did not separately enumerate every findOne), and nothing about how the rider reads at the rendered width — I checked the HTML, not the page.
|
Blocker on the merge, not on the content — #1172 also adds a rule 18. Both PRs are Suggested order, since #1219 is a child of this branch and #1172 is not:
Nothing to change here. Flagging so whoever presses merge does not land this and leave #1172 looking broken for a reason its author cannot see from the PR page. Checks green (11/11, |
@sprint-review points out the exemplar this rule is built on has no positive control, and is sound anyway. Both halves are true, and the reason is worth being the rule rather than a footnote. "Matches nothing" has two causes and they are closed by different things. An empty haystack is closed for free by the loader: `read()` in threadUserState.test.js is an unguarded `readFileSync`, so the source string can never quietly be '' and every absence assertion in that suite is already controlled against that half. A wrong needle is not closed by anything the loader does — a typo'd identifier matches nothing against a file where the code is in plain sight. The free half is the one people notice, so citing it reads as having controlled the assertion while the live risk is untouched. Rider now asks which half was closed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…o edit @sprint-review verified at heads d02a68f/6ef8022f that this branch contains #1209 exactly — a pure append, rules 17 and 18 byte-identical — and drew the right conclusion from it: merge this one, close #1209 as redundant. Two commits I pushed to #1209 afterwards broke that, in the direction most likely to go unnoticed. #1209 is now 17d7580, and the diff between the two branches carries a deletion rather than being append-only: the sharpened rider two (which half of "matches nothing" did you close) exists on #1209 and not here. Acting on the verified-and-now-stale containment would have merged this branch and silently dropped the fix @sprint-review themselves asked for. Ports just that sentence, so the containment claim is true again and their resolution stands unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The port that restored containment was applied against the OLD base, and then the base advanced to 17d7580 with the same sentence. Git saw two independent edits to one line and marked the PR CONFLICTING/DIRTY — so the fix for a silent-drop hazard created a loud one in its place. Conflict was trivial once opened: the only contested hunk is the append point, ours carrying the rule 19 block and theirs carrying nothing there. Resolved by keeping ours. The sharpened rider two merged cleanly on both sides, since both branches now hold identical text for it. Verified after: one rule 17, one 18, one 19, and the branch is a pure append over #1209 again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… deleting it Rebased onto #1219 so the numbering is a git fact rather than a convention anyone has to remember (@sprint-review). This rule was 18, colliding with #1209's 18, and the collision was resolvable only by whoever happened to notice which of the three PRs merged first. Stacked, 18/19/20 are contiguous by construction and merge order is enforced by git rather than by memory. Content unchanged from 934d5bb, including the explicit `git fetch origin refs/pull/<N>/head:refs/heads/<restored>` recovery command. Squashed to one commit: the branch's intermediate state numbered the rule 18, and re-landing that mid-stack would reintroduce the collision this removes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sprint-review asked for this as a closing note on #1149. It generalizes past that issue, so it lands as a checklist rule instead — #1149 gets a pointer.
The gap
The #1149 comment listed the
backfillPendingabsence check as "defensible as a barrier" and left it there. That is a verdict about one test, not something the next person can apply.The rule that makes it decidable: a behavioural test can show a branch produces the right answer; it cannot show a branch is gone. Dead code is invisible to execution — it never runs, contributes to no assertion, and sits there for the next reader to revive in good faith because it looks like a real path.
So the question separating a legitimate source assertion from a lazy one is not "is this test grepping?" but "is the property behavioural or structural?"
It also explains the other half of the same review: converting the ledger-read regex to an executing test was a strict gain, because that property was behavioural and the
catchbranch had no executing coverage at all. Same review, opposite verdicts, one principle.What the rule keeps
The default suspicion of source assertions stays intact and is restated — they pin text rather than behaviour, go red on a rename that changed nothing, and are the usual substitute for a test somebody didn't want to write. The rule carves out the one class they're the only instrument for, rather than licensing them generally.
Two riders:
🤖 Generated with Claude Code