Files
fusion/packages/core/src
gsxdsm f47fc167ee convert(core/task-store/comments-ops.ts): triage guards 3 → 1, and the dead approval-invalidation it hid (#2608)
**Taking `packages/core/src/task-store/comments-ops.ts`** (announced for
collision avoidance). Two commits: a behaviour-identical extraction,
then the conversion.

| File | triage column comparisons before | after |
|---|---|---|
| `packages/core/src/task-store/comments-ops.ts` | **3** | **1** |

`pnpm test:gate` green.

## The bug the literal was hiding

`builtin:coding` → `BUILTIN_STEPWISE_FINAL_REVIEW_CODING_WORKFLOW_IR`,
whose merged Planning column keeps the id **`todo`** and declares **no
`triage` column**. So `task.column === "triage" && task.status ===
"awaiting-approval"` never matched a default card. The damage was
graded:

- **with a real spec** — the card fell through to the re-triage arm.
Same `needs-replan` write, but audited as *"requested re-specification
of planned task"* instead of *"invalidated spec approval"*.
- **with a bootstrap-stub spec** — `hasRealPrompt` was false and
**neither arm fired**, so a user comment on a card awaiting spec
approval invalidated **nothing**. The approval silently stood.

That second case is the real regression; the wording is cosmetic. I
checked both rather than assuming the first one was the whole story.

## The conversion

The column was never the discriminator. Callers reach this only after
establishing the card sits in a pre-implementation column, so re-testing
it inside was redundant before U11 and wrong after. **Status carries the
distinction** — the same conclusion `spec-staleness.test.ts` already
reached for its sibling guard.

**Red-green:** the 3 new cases fail with the literal reinstated (**3
failed / 4 passed**) and pass without it. Two assert the merged-Planning
card is now invalidated; the third uses a `planning`-named column to
show no column id remains in the decision at all.

**The 1 remaining literal is deliberate:** the caller's gate `column ===
"todo" || column === "triage"` names *both* vocabularies, so it still
fires for default cards, and narrowing it to traits needs an IR the
caller doesn't have.

Commit 1 is move-only — the extracted body is the inlined expression
verbatim, `triage` literals included, so the moved logic diffs empty
apart from field renames. Behaviour change is entirely in commit 2.

---

## Census correction — the 48 is 41, and "reach ZERO" is wrong as stated

I re-measured before picking a file, and the shared number needs three
corrections. Same-scope method: `packages/*/src`, `.ts`, tests excluded,
**comments stripped**.

| Measurement | Count |
|---|---|
| raw `=== "triage"` / `!== "triage"` | 54 |
| …comments stripped | **48** ← matches your figure |
| …of those, genuine **column** comparisons | **41** |
| …non-column identifiers that must NOT be converted | **7** |

The 7 are `role === "triage"` ×3 (`agent-prompts.ts`), `agentType ===
"triage"` ×2 (`usage-limit-detector.ts`), `sessionPurpose === "triage"`
(`skill-resolver.ts`), `surface === "triage"` (`tool-availability.ts`).
**The triage service keeps its name; only the column id was merged
away.** Converting these would break the triage lane, so the bar cannot
be literal zero — it's zero *column* comparisons, with those 7
documented as permanent.

Two I nearly misclassified and hand-checked: `col === "triage"`
(`cli/commands/task.ts`, indexes `COLUMN_LABELS`) and `from ===
"triage"` (`executor.ts`, a `moveTask` from-column) **are** columns
despite their names.

## Of the 41, which are actually dead

Splitting by whether a `todo` companion arm sits in the same condition:

- **27 have one** → still fire for default cards. Real but lower
priority.
- **14 have none** → candidates for silently-dead. But on inspection
that set shrinks further:
- `register-task-workflow-routes.ts` ×5 compare against a *resolved*
`approveIntakeColumn`/`refineIntakeColumn` variable **plus** a legacy
`"triage"` fallback, so they still fire via the variable;
- `spec-staleness.ts:40` is a **deliberate R11 compat retention** —
`spec-staleness.test.ts` already carries a "U11 proof" block concluding
the guard is carried by status, not column, and that other workflows
still declare `triage`. Converting it would be wrong;
  - `self-healing.ts` ×7 is U4's file;
  - `comments-ops.ts` ×1 was genuinely dead — this PR.

**So the actionable dead set is far smaller than 14, and
`self-healing.ts` holds most of it.** I'd suggest whoever takes
`self-healing.ts` starts from that 7 rather than its 11 total.

## Files I evaluated and did NOT convert

- **`replan-target.ts`** — my first pick, then both its "sites" turned
out to be **comment text**. Zero real sites; already trait-resolved via
`workflowHasColumn`.
- **`mission-feature-sync.ts:88`** — `(column === "triage" || column ===
"todo")` still fires via the `todo` arm. The genuine gap is a
custom-named planning column, but `reconcileMissionFeatureState`'s store
is narrowed to `Pick<TaskStore,"getTask">`, so trait resolution means
plumbing through `scheduler.ts` — **U5's file**. Left to avoid the
collision, per KTD-2's warning that most sites have no IR in scope.
- **`tool-availability.ts` / `skill-resolver.ts` /
`usage-limit-detector.ts`** — non-column identifiers, see above.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 21:07:28 -07:00
..
…