## What
One new source-level audit test, 3 cases. **No production file is
touched.** Fourth instance of the optional-role-parameter class (#2795,
#2798, #2799) — and the only one so far where the source **annotation
asserts the opposite of the fact**.
`packages/engine/src/__tests__/auto-heal-review-lane-callsite-audit.test.ts`
## The finding
`project-engine.ts`'s `hasAutoHealableVerificationBufferFailure` takes
the review-lane answer as an optional parameter defaulting to
`task.column === "in-review"`. Its `DELIBERATE-LITERAL` note says:
> "Both call sites pass the resolved answer; the default exists so an
unconverted caller keeps exactly today's behaviour rather than silently
changing meaning."
**There are three call sites, not two:**
| site | passes the resolved lane? |
|---|---|
| `canMergeTask:2657` (threads its own param) | ✅ |
| ← `canMergeTask:2903` | ✅ `t.column === reviewLane` |
| ← `canMergeTask:3334` | ✅ `task.column === mergeLoopReviewLane` |
| **merge loop `:3655`** — direct call | ❌ **nothing** |
The note counts the two gating callers and misses the healing one. Note
which half is converted: **the sites deciding whether a card MAY merge
resolve the lane; the site that would RECOVER a stuck card does not.**
The consequence is in the same comment: on a renamed board *"a task
whose merge verification died on a buffer-overflow error was never
auto-healed — it sat retry-exhausted until a human reset it. The failure
is invisible because 'no auto-heal' looks identical to 'nothing to
heal'."*
## Why this is a source audit and not an E2E
The predicate and its caller are both **private methods** of
`ProjectEngine`. The three sibling files in this series each carry a
live behavioural differential because their predicates are exported;
this one cannot, and inventing a mock `ProjectEngine` to assert a
private method would prove only that the mock behaves as written. Stated
plainly rather than substituted for — the finding is a call-site fact,
and a call-site fact is what is asserted.
The third case deliberately pins the **false note itself**, so the audit
fails when someone corrects the sentence — forcing them to also decide
what to do about the third site rather than fixing the prose and leaving
the gap.
## A self-correction, forced by the mutation run
The first version filtered call sites on whether the argument text
contained `isReviewColumn` / `ReviewLane`. Converting the unconverted
site to pass a plain `true` left the count at one and **the suite stayed
green** — the "alarm in both directions" the header claims did not
exist.
Now it counts **arguments** (depth-aware, so nested calls and object
literals do not confuse it), which is the property actually being
asserted and cannot be spelled around. Re-verified:
| state | result |
|---|---|
| main | 3/3 pass |
| site 3655 converted to pass a third argument | **fails** |
Recorded in an FNXC note next to the helper, because the first version
is the exact mistake this series exists to catch.
## Not done, and why
**No fix.** Passing the resolved lane at `:3655` means resolving the
task's review column inside the merge loop; whether that resolution
belongs there or should be hoisted alongside `mergeLoopReviewLane`
(already computed nearby, which is what makes the omission look
accidental rather than considered) is a decision for the file's owner.
## Verification
- new suite — **3/3 passed**, mutation-verified in both directions
- `pnpm lint` — clean
- Unit lane, no PostgreSQL required; adds no gate surface.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>