Files
fusion/scripts
gsxdsm 51934931e1 fix(board): the awaitingPlanning badge only ever worked on a lane named "todo" (#2845)
Converts the one site in `register-task-workflow-routes.ts` that a
previous pass **deliberately deferred**, and does it in the shape that
note asked for.

## What the deferral said

```
FNXC:WorkflowResolvedColumns 2026-07-29-00:00 (U12 — R8, DELIBERATELY NOT CONVERTED):
… I converted it and then REVERTED: resolving each task's hold column needs a
per-task workflow read, and this is the board-load path whose own comment above
exists because unbounded reads here "turn a board load into thousands of reads".

Converting it properly needs the hold column resolved per WORKFLOW from data the
board payload already carries, not per task from the store.
```

That was the right call and the right diagnosis.
`resolveProjectColumnsForRoles(store, ["hold"])` is exactly the
project-scoped shape it names: **one** `listWorkflowDefinitions()` read
per board load, flat in task count. The expensive part — a PROMPT.md
read per row — is untouched and still bounded by
`AWAITING_PLANNING_ENRICH_LIMIT`.

The test asserts the flatness directly (`listWorkflowDefinitions` called
exactly once), so a future per-task regression fails here rather than
being discovered as board latency.

## What was broken

The filter named `todo`, so on a board whose waiting lane is called
anything else **no row was enriched at all** — no error, no log line,
just a silent fall back to the client's `steps.length === 0` heuristic.
That heuristic is precisely what this enrichment was added to correct,
so the card most likely to be mislabelled — real spec, zero parsed
steps, already a scheduler dispatch candidate — sat on "Queued to plan"
indefinitely.

Over-inclusion is the safe direction and is chosen deliberately: a card
in some other workflow's hold lane gets annotated as waiting, which is
what a waiting card in a waiting lane should show.

## Revert proof (measured)

Restore `task.column === "todo"`:

```
FAIL  register-task-workflow-routes.awaiting-planning.test.ts
  > enriches a card in a RENAMED hold lane, not only one literally named todo
  expected undefined to be false
```

The other 8 cases in the file stay green — their harness store declares
no `listWorkflowDefinitions`, so they run the degraded legacy-`todo`
path. That compatibility is half the contract, which is why the new case
brings its own store rather than widening the shared harness.

## Census

| file | before | after |
|---|---|---|
| `packages/dashboard/src/routes/register-task-workflow-routes.ts` | 3 |
2 |

The 2 remaining in that file are documented trait-fallback branches, not
unconverted debt. The baseline also picks up
`packages/core/src/task-store/moves.ts` 2 -> 0, already true on main and
not from this diff.

## Verification

- `pnpm test:gate` — 161 / 487 / 13 / 71 passed
- `pnpm lint` — clean
- `tsc --noEmit -p tsconfig.json` (`@fusion/dashboard`) — clean
- `node scripts/lifecycle-column-census.mjs --strict` — exit 0
- targeted: `register-task-workflow-routes.awaiting-planning.test.ts`
9/9

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

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 14:48:00 -07:00
..