Files
fusion/packages/cli/src
gsxdsm 25b3c06d2d fix(plugins): compound-engineering pipelines stalled forever on a renamed board (#3022)
Closes #3020 — which I filed **instead of** fixing, on a rationale that
turned out to be wrong.

I said the plugin had no scaffolding for faking `CePipelineStore` +
`taskStore` together. It does: `_harness.ts` already builds a real
`PluginContext` over a live PostgreSQL layer. The gap was **two missing
readers on its task-store stub**, not missing infrastructure. I checked
the harness only after filing.

## The defect

`TERMINAL_COLUMNS` is `{in-review, done}`, and the reconciler advances a
pipeline only when **every** current-stage board task is in that set. On
a board whose review and completion lanes are renamed that's false for
every task, permanently:

- the pipeline never advances a stage
- it never creates its outbound task
- it sits `running` indefinitely

Nothing errors, so it reads as work that hasn't finished. Unlike the
display defects in this family (#3014, #3017), the CE flow actually
**stops**.

## Shape

The decision is extracted to an exported `isStageTerminalColumn` because
it *is* the whole decision. Left private it could only be reached
through a pipeline-state + links + board-tasks fixture, and the half
that needed proving is that a renamed board resolves to its own lanes
through this store.

It uses `resolveReviewColumns` rather than re-deriving the union — that
helper is the documented review **set** (`mergeOrchestration ∪
mergeBlocker ∪ humanReview`), so a board splitting those across a merge
lane and a human lane is covered without this site drifting from it.

## Two things my first attempt got wrong

**The fixture spelled traits in camelCase** — `{ trait: "humanReview"
}`. Trait **ids** are kebab-case (`human-review`, `merge-blocker`,
`wip`); the camelCase names are the resolved **flags**. Those columns
therefore resolved to *no roles at all*, silently, because an unknown
trait isn't an error. `complete` is spelled identically in both
vocabularies, which is exactly what made the first run look like
*"complete works, review is broken"* rather than *"the fixture is
wrong"* — I nearly went debugging the production union.

**The harness extension is additive** and inert until a test seeds it,
so all 24 existing plugin suites see the previous shape.

## Measured

| check | result |
|---|---|
| new suite | **4/4** |
| reverting to the literal-only gate | fails **exactly 2** — the
renamed-terminal case, and a board declaring a NON-terminal column named
`done` — while the legacy control and the WIP/intake negative still pass
|
| plugin suite | **24 files, 184 tests green** |
| `tsc` + all five gates | clean |

That second row is the one that matters: the `done`-without-`complete`
board is the only shape where a real resolution and a legacy fallback
disagree, so it's what separates the fix from a lucky agreement.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 01:20:04 -07:00
..