Files
fusion/packages/dashboard/app/utils
gsxdsm d75de0fb80 fleet: complete the column-role helper set — 680 of 722 guards had no helper to convert to (#2685)
**Fleet blocker, measured before claiming a file — this unblocks 94% of
the work order.**

## The gap

The work order says *"conversion pattern: the existing role helpers
ONLY; no new abstractions"*. Measured against the census, those helpers
cover **42 of 722** backlog guards:

| role | guards | helper? |
|---|---:|---|
| `intake` / `hold` → `todo` | 42 | ✅ `isIntakeColumnRole`,
`isPreImplementationColumnRole`, `isHoldColumnRole` |
| `in-review` | 200 | ❌ |
| `done` | 195 | ❌ |
| `archived` | 147 | ❌ |
| `in-progress` | 138 | ❌ |

**680 guards — 94% — had no helper to convert to.** Every fleet worker
hits this on their first file. I hit it claiming `TaskCard.tsx`, whose
42 guards are `done` 13, `archived` 12, `in-progress` 9, `in-review` 7,
`todo` 1.

## Why this is not "a new abstraction"

It is the **same** abstraction — flags-first, legacy id only as the
documented no-metadata fallback — applied to the roles it did not yet
cover. The alternative is inlining a flags-plus-fallback expression at
680 sites, which recreates exactly the copy-paste drift these helpers
exist to remove: **three inline copies in `ListView` are what started
this file.**

Widening `ColumnRoleFlags` threads nothing new through any call site.
Callers already pass these flags — `TaskContextMenuColumnFlags` declares
all of them — the interface had only *declared* the two the earlier
helpers needed, so the type was dropping the rest on the floor.

## A correction I made mid-change

My first draft of that comment claimed the flags were "already carried
on `ColumnRoleFlags`". `tsc` disproved it immediately — `complete`,
`archived` and `countsTowardWip` did not exist on the type. Corrected
rather than quietly patched, because that claim *was* the justification
for calling this a completion rather than an addition.

## Each distinction is asserted, not just documented

- **`isCompleteColumnRole` does not count `archived`** — an archived
card is finished but not *completed*; surfaces counting throughput would
double-count it.
- **`isWipColumnRole` keys on `countsTowardWip`**, the same flag
capacity arithmetic uses, so a board cannot have a column that counts
toward WIP for capacity but not for this predicate.
- **`isReviewColumnRole` accepts either `mergeBlocker` or
`humanReview`** — separable traits, but every converted caller asks "is
this card in review", for which both qualify. A caller needing one and
not the other should read the flag directly rather than widen this.

Both directions are asserted in every case, so a helper returning
`false` unconditionally cannot pass.

## Verification

`columnRoles.test.ts` **10 → 14**. `pnpm test:gate` green (10 / 158 /
487 / 71). `pnpm check:lifecycle-columns` exits 0. `tsc -p
tsconfig.app.json` clean. `pnpm lint` clean.

No census movement — this adds capability, converts nothing. My
`TaskCard.tsx` conversion (42 → 0) follows on top of it.

No changeset: internal helpers, no user-facing change.

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