## The headline number stopped tracking work
I have hand-computed this breakdown every round of the phase to decide
what to claim. Making it a first-class mode so nobody else has to, and
so "census before/after" in a PR body means something.
On current `main`:
```
COLUMN guards (the backlog): 51
TRIAGE (heuristic, opt-in; changes no count and no exit code)
documented deferral (flag note within 40 lines): 13
unexamined: 38
unexamined, by file — this is the list to pick work from:
21 packages/engine/src/self-healing.ts
4 packages/engine/src/executor.ts
2 packages/engine/src/auto-merge-finalization.ts
2 packages/engine/src/scheduler.ts
1 packages/core/src/eval-signal-collector.ts
...
```
**51 reads as a lot of available work. It is not.** 13 carry an explicit
reason for staying a literal, and 21 of the remaining 38 are
`self-healing.ts` — which has **eight** open PRs on it. What is actually
loose is roughly a dozen scattered singletons, most of them in
synchronous listeners where the only available resolver is inert.
That gap is not cosmetic; it is causal. A worker told to "claim the
largest cluster" reads 51, finds little that is both unclaimed and
convertible, and reaches for whatever moves the number. That is exactly
how #3051 converted ten `scheduler.ts` guards to
`resolveTaskWorkflowIrSync` — inert under PostgreSQL, refuted end-to-end
in #3058 — and how five open PRs came to share the same helper.
## Design constraints I held to
- **Opt-in.** No flag, no change. Verified: default output is
**byte-identical** to `main` (`diff` clean), and `--json`, `--strict`
and `--compare` all still exit 0.
- **Beside the totals, never inside them.** It changes no count and no
exit code — the same discipline `traitFallbackCount` already documents
two lines above, and for the same reason: a deferred guard is still a
guard.
- **Nothing downstream consumes it.** It is a triage aid for choosing
work, not a gate.
## Stated limits
Classification is **comment proximity**: an FNXC note within 40 lines
above the guard whose text marks a deliberate deferral. It cannot tell a
good reason from a bad one, and a note that sits far above its guard
reads as unexamined. That is why it is opt-in and why no gate reads it.
## The bug I shipped into my own draft, and what it cost
The first version reported **`documented deferral: 0`** — for a tree I
knew had them, because I had counted them by hand that morning. Cause:
it referenced an undefined path constant, and my `try/catch` turned the
`ReferenceError` into an empty file list, so every proximity window was
the empty string and nothing ever matched.
That is the same silent-catch shape I have flagged in review twice this
phase. The catch is now gone: **a triage aid that fails to zero is worse
than one that throws**, because zero reads as a clean answer rather than
a broken instrument. Post-fix it reports 13/38, which matches the hand
counts I have been posting all phase.
## Census before / after
```
before: COLUMN guards (the backlog): 51
after: COLUMN guards (the backlog): 51
```
Unchanged by construction — this converts nothing. It makes the number
interpretable.
## Verification
`test:gate` exit 0 · default census output byte-identical to main ·
`--json` / `--strict` / `--compare` exit 0 · `pnpm lint` clean. One
script; no production file touched.
Related and still open: **#3079** (makes the five inert
`resolveMoveFanoutColumnsSync` guards fail the build instead of
registering as a census win) and **#3095**.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added an optional triage mode to classify column findings as
documented deferrals or items requiring review.
- Added aggregate and top-file triage results to make findings easier to
assess.
- Added guidance for the new command-line option.
- **Bug Fixes**
- Improved source-reading error handling so failures are reported
clearly instead of being silently ignored.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->