docs: the census total is a floor — 25 membership predicates it structurally cannot see, one a live defect (#2763)

Docs only, extending the entry #2748 landed. Opening it because the
fleet reads the census total as its completion bar, and that total
excludes a whole predicate class — a measurement that should not live in
a chat reply.

## Measured on `origin/main`

- **47** array/Set literals of two or more lifecycle ids, in 35 files.
- **25 are membership predicates against a task's column** —
`SET.has(task.column)` / `ARRAY.includes(task.column)` — in 19 files.
Two are documented fallbacks behind a resolved primary, so **~23 are
unconverted guards**.
- The census scans `===` / `!==` against a column. **None of these is a
comparison, so none is counted.**

| file | constant |
| --- | --- |
| `cli/src/commands/task.ts` (3) | `retryReviewColumns` |
| `dashboard/app/components/TaskCard.tsx` (2) | `TIME_INDICATOR_COLUMNS`
|
| `engine/src/eval-followups.ts` (2) | `OPEN_COLUMNS` |
| `engine/src/merger.ts` (2) | `sourceTerminal` |
| `engine/src/task-revert.ts` (2) | `REVERTABLE_COLUMNS` |
| `core/src/agent-role-policy.ts` (1) | `IMPLEMENTATION_TASK_COLUMNS` |

## One is a proven live defect

`isImplementationTask` is
`IMPLEMENTATION_TASK_COLUMNS.has(task.column)`, and
`evaluateImplementationTaskBind` short-circuits to `allowed: true` when
it returns false. **On a renamed board every agent is bind-compatible
with every task** — the role check that stops a liaison being handed
implementation work (the NEXT-871 loop FN-7851 fixed) does not apply.

It surfaced only because a reviewer questioned a coverage claim in one
of my dispatch tests (#2739). Passing an agent wasn't proof the
evaluator ran, so I asserted a `custom`-role agent must be *refused* —
and that test failed against production. Flagged at the site in #2739,
not fixed: `isImplementationTask` is a sync pure predicate with no
store, and making the routing policy async is a behaviour change to
agent admission.

## What this does and does not argue

The census is the right instrument — AST-based, honest about what it
measures, and it has caught real drift in both directions (it failed on
me in #2724 when merged conversions moved an inventory *down*). This is
not an argument against it.

It is an argument against reading **"backlog: N" as "N guards remain"**.
The same shape already appeared in the archived gate (#2724), where the
rule is additionally encoded in Drizzle predicates and raw `sql`
templates that no comparison scan can see. Two independent classes now,
found the same way — by looking at what the instrument's definition
excludes.

**Extending the census to count membership predicates is deliberately
left to you, not done here.** It would move every worker's number
mid-fleet, and deciding which sets are lifecycle guards versus
board-config definitions or type unions is exactly the judgement
`DELIBERATE-LITERAL` exists for — 47 collections would each need that
call.

## Verification

`pnpm lint` clean · census `--strict` exits 0 · no code changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-07-30 08:12:51 -07:00
committed by GitHub
parent 86a2b48968
commit 245086dad6

View File

@@ -107,6 +107,69 @@ which is the same churn already removed from the census baseline by dropping its
So the requirement lives here, as a review criterion, until someone finds a sound signal. The honest
version of the automated attempt is recorded above so it is not re-attempted from scratch.
## The census counts comparisons, so a literal COLLECTION is invisible to it
Measured on `origin/main` while assessing the next conversion target, and worth stating because the fleet
treats the census total as the completion bar:
- **47** array/Set literals of two or more lifecycle ids, in 35 files.
- **100** membership tests against a task's column overall — `SET.has(task.column)` /
`ARRAY.includes(task.column)`. MOST ARE ALREADY CORRECT: the set is RESOLVED at runtime from the task's
IR, which is the converted shape this program produces.
- **19** of them, in 16 files, test a set built from HARDCODED ids. Three are seeds or documented fallbacks
sitting behind a resolved primary (`triage.ts`, `mission-feature-sync.ts`, `branch-group-ops.ts`), so
**~16 are unconverted guards**.
- The census scans `===` / `!==` against a column. **None of these is a comparison, so none is counted.**
They behave exactly like the guards the census does count. The largest concentrations:
<!--
FNXC:LifecycleColumnCensus 2026-07-30-20:10 (PR #2763 review):
COUNTS ARE MEMBERSHIP USES, not occurrences of the identifier — the declaration line does not count.
Re-measured every row against the tree rather than only the one the review questioned:
grep -c '<CONST>.has(' <file>
Two were wrong and are corrected above: `retryReviewColumns` 3 -> 4, `OPEN_COLUMNS` 2 -> 1. The row
the review questioned (`TIME_INDICATOR_COLUMNS`) is CORRECT at 2, and the constant is present at
TaskCard.tsx:333 with uses at 1712 and 3070.
-->
| file | constant |
| --- | --- |
| `dashboard/app/components/TaskCard.tsx` (2) | `TIME_INDICATOR_COLUMNS` |
| `engine/src/eval-followups.ts` (1) | `OPEN_COLUMNS` |
| `engine/src/task-revert.ts` (2) | `REVERTABLE_COLUMNS` |
| `core/src/agent-role-policy.ts` (1) | `IMPLEMENTATION_TASK_COLUMNS` |
<!--
FNXC:LifecycleColumnCensus 2026-07-30-20:40 (self-correction after the #2763 review):
THE REVIEW FIXED THE COUNTS; TWO ROWS WERE WRONG IN KIND. `retryReviewColumns` and `sourceTerminal` are
already RESOLVED — `columnsWithFlag(ir, ...)` with a legacy array as the no-IR fallback — and my scan
classified a constant by whether its initializer text contained only lifecycle ids, so the FALLBACK array
matched and they read as hardcoded. Six sites across the tree were miscounted that way, which is why the
headline moved 25 -> 19.
That is this section's own thesis one level up: my instrument counted syntax and called it meaning. The
corrected rule excludes any initializer deriving from an IR or a resolver at runtime. Rows removed rather
than renumbered, because a wrong KIND is not fixed by a better count.
-->
**One is a proven live defect.** `isImplementationTask` is `IMPLEMENTATION_TASK_COLUMNS.has(task.column)`,
and `evaluateImplementationTaskBind` short-circuits to `allowed: true` when it returns false — so on a
renamed board every agent is bind-compatible with every task and the role check that stops a liaison being
handed implementation work does not apply. It surfaced only because a reviewer questioned a coverage claim
in a dispatch test; asserting the claim properly made the defect fail a test.
**So the census total is a floor, not a total.** That is not an argument against it — it is the best
instrument here and it is AST-based and honest about what it measures. It is an argument against reading
"backlog: N" as "N guards remain". The same shape appeared in the archived gate (PR #2724), where the rule
is also encoded in Drizzle predicates and raw `sql` templates that no comparison scan can see.
Extending the census to count membership predicates is a coordinator-level call, not a worker one: it would
move every worker's number mid-fleet, and the classification work (which sets are lifecycle guards versus
board-config definitions or type unions) is exactly the judgement the `deliberate` marker exists for.
## Related
- `docs/solutions/test-failures/store-fake-defects-that-masquerade-as-production-bugs.md` — the adjacent