Files
fusion/packages/engine
gsxdsm 7bdeaa8b0c fix(engine): the new worktree ledgers count terminal lanes by NAME — renamed boards stall (#3296)
## Census

**Before: `COLUMN guards (the backlog): 2`, `--strict` RED. After:
`BACKLOG ZERO`, all five gates green.**

Two commits from last night's `maxWorktrees` rollout copied the same
holder ledger, both with literals:

| commit | file | gate |
|---|---|---|
| `374956ef23` | `triage.ts` | planning admission |
| `6c7467a78d` | `executor.ts` | `fn_spawn_agent` |

```ts
t.column !== "done" && t.column !== "archived"
```

## What it costs

Both exclude terminal lanes because a finished card's worktree is
**cleanup-owned, not capacity**. On a renamed board neither literal
matches, so every finished card keeps counting as a live holder. The
count only grows, the gate reaches zero room on a board with free slots,
and planning admission is withheld forever / every spawn is refused.

That is the **mirror** of the breach these commits fixed, and strictly
worse: 8 planners on a 4-slot board is visible; a permanent stall is
silent. The recorded reason even names the worktree budget, which the
operator then checks and finds has room.

## The conversion

`resolveProjectColumnsForRoles(store, ["complete", "archived"])` —
project-level, because the ledger spans the whole board with no single
task to resolve against. Matches triage's existing use in
`sweepStalePlanningStatuses` and executor's at the wip gates.
Legacy-seeded, so a default board still excludes exactly `done` and
`archived` — byte-identical there.

## Both conversions were UNCOVERED when written

Measured with #3214's blinding procedure **before** writing tests:
reverting either to the literals left **all 19 tests in the capacity
suites green**. Nothing in the tree could tell the conversion from what
it replaced — which is how the literals got there in the first place.

Each now has a renamed-board case that fails when blinded:

```
triage    converted 2 passed  |  BLINDED 1 failed | 1 passed  |  restored 2 passed
executor  converted 8 passed  |  BLINDED 1 failed | 7 passed  |  restored 8 passed
```

## The pairing earned itself immediately

Both new cases assert an **absence** (no throttle / no refusal), so each
is paired with a positive proving the gate still fires on the same
renamed board when a card genuinely holds the last worktree.

That caught a real defect in my own fixture: the candidate scan resolves
each task's **own workflow selection**, not `listWorkflowDefinitions`,
so my first version fell back to the default board where `drafting`
isn't a hold lane. No card was eligible, nothing throttled, and the
absence assertion **passed for the wrong reason**. The positive failed
and exposed it. Recorded at the fixture so the next reader doesn't
reintroduce it.

## Verification

```
42 tests across 6 capacity suites             pass
check-fnxc-future-dates                       green
check-inert-sync-lane-conversions             green
check-lane-wiring                             green
check-sql-column-literals                     green
census --strict                               green   (BACKLOG ZERO restored)
```

No changeset: internal engine fix, no published-package surface change.

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

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 19:16:07 -07:00
..
2026-07-31 18:03:13 -07:00
2026-07-26 18:11:47 -07:00