diff --git a/docs/development/review-checklist.md b/docs/development/review-checklist.md index 30ca4b851..b180ed5f7 100644 --- a/docs/development/review-checklist.md +++ b/docs/development/review-checklist.md @@ -54,3 +54,5 @@ 16. **A mechanism that explains the observation is not evidence for it — make it predict something, then check that.** When a state looks wrong, the tempting move is to find the machinery that would produce it. That machinery is often real, and every link in it independently true, and *none of that connects the chain to the thing you saw.* The tell is that the explanation was assembled after the observation and has not yet been asked to forbid anything. So before you commit a diagnosis or hand it to someone as actionable, name one consequence it *requires* — some other PR, row, or run that must look a particular way if you are right — and go look. If the chain is sound the check is cheap and you gain a second data point; if it is invented, this is the only step that will tell you. **The corollary is that a pipeline caught mid-flight is indistinguishable from a terminal state**, so a status read once is a snapshot, not a finding: re-read it after the in-flight work completes before you build anything on top of it. *(Earned: 2026-08-22, #1135. A docs-only PR read `MERGEABLE/UNSTABLE` with 10 checks against a sibling's 11; the missing one was `E2E Tests`, and `playwright.yml` really does filter on `frontend/** · backend/** · e2e/** · playwright.config.*`, which a `docs/**` diff really does fail to match, so the workflow really never dispatches. Every link true. The conclusion drawn — that such a PR can therefore never reach `CLEAN` and that a merge-only-when-`CLEAN` rule deadlocks on documentation — was false: `E2E Tests` is not required, and the `UNSTABLE` was one check still **pending**, not one missing. It cleared on its own. The invented mechanism made a checkable prediction — two sibling docs PRs must also never go `CLEAN` — and both settled `MERGEABLE/CLEAN` at 10 checks minutes later. That was one command, never run, because the chain already felt verified. It was committed to an audit entry and broadcast to the room as something to work around.)* 17. **A mutation test proves a term matters to the suite. It cannot tell you the suite's shape is real.** Deleting a term and watching a test go red is the standard way to show the term is load-bearing, and it is sound *within* the harness — which is exactly the boundary that gets skipped, because the red test feels like production talking back. It isn't: the harness chose the input shape, so a mutation only ever reports which terms that chosen shape reaches. If the shape is one production never produces, every result is valid and every conclusion is about a world that does not exist. **The check that closes it is one line and orthogonal to the mutation: grep what the real middleware assigns, and confirm the harness produces that same shape.** Two tells that it does not, both visible without running anything: the fixture *constructs* the auth object inline rather than calling the middleware, and — the loud one — the suite `jest.mock`s the real auth middleware into a pass-through, so the shape under discussion is not merely unexercised but deliberately excluded. Corollary for the fix: **a term found dead this way is often dead in the safe direction, so confirm what reviving it would do before calling it a bug.** *(Earned: 2026-08-22, the `tasksApi` identity terms. `resolveAgentInstanceId` reads `req.user.isBot`, while `agentRuntimeAuth` assigns only `req.agentUser` — so on every real agent call the term is undefined and `claimKey` falls through to the bot User's ObjectId. Dropping `req.user?.username` turned exactly one test red, and that was reported as the term being live; the harness sets `req.user = { id, _id, username, isBot }` from a test header and mocks `agentRuntimeAuth` to a bare `next()`, one line below. The reviewer had quoted that shim in their own review of the same file. Reviving the term would have made things worse, not better: `resolveHolder` only loads the holder's User row when `claimedBy` is a 24-hex ObjectId, so a readable instanceId key would null the holder, empty the `agentName` narrowing on the install lookup, and degrade the lease-rescue liveness check to buy a prettier column.)* + +23. **When a change alters the shape of a mocked value, the sweep must key on the mocked *path*, and it must not be a same-line conjunction.** A route's query chain changing shape (`find()` → `find().sort()`) breaks every suite whose mock returns the old shape, so the reviewer's sweep is "which suites mock this model" — and the obvious spelling of that sweep is the one that excludes the broken suite. Three populations over `backend/__tests__/**`, measured at `584442e9`: `jest.mock(.*AgentInstallation` on one line returns **7 files and omits the failing suite**; the bare symbol returns 96 and includes it; `jest.mock('…/models/AgentRegistry'` returns **74** and reproduces the failure. The reason the first misses is not that the symbol is absent — it is on the next two lines — but that `jest.mock` factories wrap, so a line-oriented conjunction can never see a symbol declared inside a multi-line call. **Both halves of the fix are load-bearing: key on the module path, because the changed export (`AgentInstallation`) is not the module (`models/AgentRegistry`); and drop the conjunction, because the anchor and the symbol are on different lines.** Generalises past mocks: any sweep written as `.*` on one line silently under-reports over wrapped calls, and rule 12 applies at full strength — the under-report renders as an all-green population, which is the answer the reviewer was hoping for. *(Earned: #1336, 2026-08-30 — a gate that passed on mutations plus the PR's own two suites turned main red for 17½ minutes across the whole fleet; the sibling sweep was run and returned a false all-clear before #1341 fixed the mock. Diagnosis by @sprint-review; the same-line/multi-line mechanism measured while writing this entry.)*