gate(census): say WHY role and status are not backlog, where the numbers print (#3275)

## The hazard

The column backlog is **0**. The two largest numbers the census prints
are now `ROLE (12)` and `STATUS (185)`, sitting directly beneath it,
labelled only `(not guards)`.

That is a verdict with no reason. For a worker under a directive to
drive a census down — finding the backlog line already at zero and two
bigger numbers underneath — "(not guards)" is thin protection. This PR
puts the reason where the numbers are.

## Why they are genuinely not backlog

Both classify by **receiver**, not by the literal
(`ROLE_RECEIVER_TOKENS` = `role, agentType, agent, lane, capability,
sessionPurpose, surface, purpose, agentRole`; status matches
`/status/i`). A legacy column id next to one of those is a different
domain that happens to share vocabulary with the old board. Sampled from
the current tree, not reasoned:

| site | receiver | what it actually is |
| --- | --- | --- |
| `packages/cli/src/commands/task.ts:529` | `outcome === "archived"` | a
task **outcome** |
| `.../routes/register-chat-routes.ts:894` | `type === "done"` | a chat
**message type** |
| `.../cli-agent/telemetry-hub.ts:304` | `kind === "done"` | a telemetry
**kind** |
| `packages/cli/src/commands/goals.ts:178` | `status === "archived"` | a
**goal's** status |
| `packages/cli/src/commands/mission.ts:145` | `status ===
"in-progress"` | a **mission's** status |

None is a task column, so none has a workflow lane to resolve against.
Converting a goal's `status === "archived"` to a column trait would not
remove a legacy id — **it asks the wrong object for a lane it does not
have**, and the resulting bug would be invisible on the default board
for precisely the reason every inert conversion is. That is 185
opportunities to inject a real defect while a number goes down.

## Output-only, verified

Counts, JSON, baseline comparison and exit codes are untouched:

```
--strict exit=0
json totals: {"column": 0, "role": 12, "status": 185, "deliberate": 150}   # byte-identical
```

60 census tests pass (`lifecycle-column-census.test.ts`,
`census-reclassification-message.test.ts`). `lifecycle-columns`,
`move-target-literals`, `inert-sync-lanes`, `quarantine-ledger` all exit
0.

## Provenance

I raised "role/status have no inertness proof behind them" several times
as a reason not to touch them, which was too weak — it implied the work
might be valid pending proof. Rather than leave that hanging I went and
looked. They are not unproven conversions; they are **not conversions at
all**. Correcting my own earlier framing, and putting the finding where
the next person will hit it instead of in a report they will not read.

No changeset — internal tooling.
This commit is contained in:
gsxdsm
2026-07-31 17:12:10 -07:00
committed by GitHub
parent 29eb512d57
commit 79b08a2e99

View File

@@ -277,8 +277,31 @@ if (json) {
} else {
console.log(`lifecycle-column-census: scanned ${files.length} source files\n`);
console.log(` COLUMN guards (the backlog): ${summary.totals.column}`);
console.log(` ROLE comparisons (not guards): ${summary.totals.role}`);
console.log(` STATUS comparisons (not guards): ${summary.totals.status}`);
/*
FNXC:LifecycleColumnCensus 2026-07-31-23:57 (these two are NOT a second backlog — do not convert them):
Both classify by RECEIVER, not by the literal. A legacy column id appearing next to a role- or
status-named receiver is a DIFFERENT DOMAIN that happens to share vocabulary with the old board.
Measured samples from the current tree:
outcome === "archived" packages/cli/src/commands/task.ts a task OUTCOME
type === "done" .../routes/register-chat-routes.ts a chat MESSAGE TYPE
kind === "done" .../cli-agent/telemetry-hub.ts a telemetry KIND
status === "archived" packages/cli/src/commands/goals.ts a GOAL's status
status === "in-progress" packages/cli/src/commands/mission.ts a MISSION's status
None is a task column, so none has a workflow lane to resolve against. "Converting" a goal's
`status === "archived"` to a column trait does not remove a legacy id — it asks the wrong object for
a lane it does not have, and the resulting bug is invisible on the default board for the same reason
every inert conversion is.
Why this needs saying where the numbers PRINT rather than only in the classifier: the two counts sit
directly under the backlog with larger values (12 and 185 against a backlog of 0). A worker told to
drive a census down, finding the column line at zero, has two bigger numbers in front of them and
"(not guards)" alone does not explain why they are off-limits. The label states the verdict; this
states the reason.
*/
console.log(` ROLE comparisons (NOT backlog — agent/chat/telemetry receivers, not columns): ${summary.totals.role}`);
console.log(` STATUS comparisons (NOT backlog — goal/mission/step status, not columns): ${summary.totals.status}`);
console.log(` DELIBERATE-LITERAL (reviewed): ${summary.totals.deliberate}`);
/*
FNXC:LifecycleColumnCensus 2026-08-01-01:40: