This is the merged-board half of the evidence assignment. **It is red,
deliberately, and the red is the finding.** Do not merge it to make the
red go away — the assertions are correct and `main` is broken.
## Escalation first: #2613 broke the default board, and the gate did not
notice
`6a33d8f8c` — *"Phase B — TAKING task-creation.ts: intake classification
by trait (4 sites → 0)"* (#2613) — regressed four E2E cases, including
**the default-vocabulary full lifecycle**, which is scenario 1 of the
whole E2E assignment.
Attribution is a clean single-file revert, not a guess:
```
HEAD (main): 4 failed | 39 passed
HEAD with ONLY 6a33d8f8c's task-creation.ts reverted: 28 passed (both files fully green)
```
Failing:
1. `scenario 1 — DEFAULT vocabulary … persists the card in the expected
column at every stage`
2. `scenario 2 — RENAMED vocabulary … writes the same column-transition
audit trail as the default`
3. `releases a card out of the merged lane on capacity — the release is
not a self-move`
4. `does not re-release a card that already left the merged lane`
**`pnpm test:gate` is green on this branch — exit 0, 695 tests.** #2613
merged through a green gate, and its own tests pass. This is the eighth
time this program a test has passed without exercising its subject, and
the first one an E2E family caught rather than review.
### Mechanism
`isIntakeColumn` in `task-creation.ts` decides whether a new card gets a
**bootstrap** prompt (freeform, "triage will plan this later") or a
**specified** prompt (planned, executable). #2613 rewrote it as:
```ts
const isIntakeColumn = (intakeFacts.intake !== undefined && task.column === intakeFacts.intake) || …
```
where `intakeFacts.intake` falls back to the **default workflow's**
intake when the create supplies no `workflowId`. Post-U11 the default
workflow's intake **is `todo`**. So any card created directly in `todo`
is now classified as intake and gets a bootstrap prompt — unplanned.
Unplanned cards do not advance through the graph (no `NodeEntered` audit
rows → failures 1 and 2) and hold-release will not release them
(FN-7648: no unplanned card enters a processing column → failures 3 and
4).
Before U11 this was safe: `triage` was intake and `todo` was a distinct
lane, so creating in `todo` meant "planned work". The merge deleted that
distinction.
### Why this is the exact trap you warned about
You said you did not want *"a conversion that swaps the literal for a
trait lookup WITHOUT checking what the guard was for."* The old
`task.column === "triage"` guard meant **"is this card unplanned?"** On
a merged board, intake-vs-hold **cannot answer that question at all** —
one column is both. The distinguishing fact is not the column; it is
whether the caller supplied a spec. Resolving the role faithfully still
gets the wrong answer, because the question was never really about the
column.
Not fixing it from here: `task-creation.ts` is #2613's owner's file, and
the fix is a design call about which fact replaces the column test.
## What the evidence itself adds
Three families extended to the U11 shape — one column carrying **both**
intake and hold. That breaks a class of guard renamed boards
structurally cannot reveal:
| shape | consequence |
|---|---|
| `intake && !hold` | **unsatisfiable** — silent |
| hold → intake release | **self-move**, re-fires every poll — loops |
| `intake && column !== "triage"` | inverts to **always-true** — silent
|
Two are silent and one loops, so every case sweeps **twice** and asserts
no re-release; a single pass cannot tell a no-op from a self-move.
### A fixture that could not fail
My first merged row used `MERGED_VOCAB`, which is *faithful* to U11 — it
reuses the legacy ids, because that is what the default lineage has.
That fidelity **destroyed its discriminating power**: its hold column
*is* `todo`, so a guard falling back to the `todo` literal returns the
same answer as one resolving the role. The "hold but not intake"
mutation left all 23 green.
Added `MERGED_RENAMED_VOCAB` — merged *structure*, renamed *vocabulary*
— the only combination where the collapse is observable **and** the
literal is wrong. Same mutation now fails exactly 1 of 23. Both
vocabularies stay: one asks *"does the collapse break the release
path"*, the other *"is the role actually resolved"*.
One rebound mutation was **genuinely unobservable** rather than
undetected — `hold` is also the first column in that fixture, so the
fallback chain lands there regardless. Pointing rebound at `complete`
instead fails 9 of 15. Recorded rather than papered over.
## For the CAPACITY worker before `self-healing.ts` is marked done
`self-healing.ts:2952` and `:9134` query `listTasks({ column: "triage"
})`. Converting the 10 guards leaves those sweeps **blind** — they never
see a renamed card, so the guard is correct and unreachable. Query and
guard convert together or not at all.
There are **137** such `column: "<legacy id>"` sites repo-wide, 52 in
that one file, and the 45→0 grep counts none of them because they are
object properties, not comparisons. **The bar can reach zero with sweeps
still unable to fire.**
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>