Files
fusion/packages/core/src
gsxdsm f53c9dbd39 fix(core): merge re-enqueue threw on every board with a renamed review column (#2819)
The single most consequential finding from the u12 seam-gate work,
picked up because batch-core (#2783) merged without it and it is now
unowned.

## The defect

`enqueueMergeQueueInTransaction` gates on the task's column being a
review column, and takes the board's review columns as an optional
trailing argument.

- `moves.ts:487` and `moves.ts:1153` — the automatic handoff-to-review
path — resolve and pass them.
- The public `enqueueMergeQueue` wrapper
(`async-merge-coordination.ts:246`), reached through
`store.enqueueMergeQueue`, **did not**, so it fell back to `new
Set(["in-review"])`.

This is not the quiet legacy-id degradation most of these seams produce.
The reject branch records `mergeQueue:enqueue-rejected` and **throws**
`MergeQueueInvalidColumnError`. Its production callers are
`merger.ts:7251` and `self-healing.ts:10329` — so on any board whose
review lane is renamed, the merge and recovery re-enqueue paths failed
outright while the handoff path kept working.

## Measured, not asserted

With the fix reverted, the renamed case fails with the exact predicted
error and the controls stay green:

```
× renamed vocabulary: a task in the RENAMED review lane enqueues for merge
  MergeQueueInvalidColumnError: Task KB-001 is in column 'checking', not 'in-review'; cannot enqueue
✓ default vocabulary: a task in the review lane enqueues for merge
✓ renamed vocabulary: a task in the WIP lane is still REJECTED
✓ default vocabulary: a task in the WIP lane is still REJECTED
  Tests  1 failed | 3 passed (4)
```

With it: `Tests 4 passed (4)`.

## About the suite

**Differential.** The fixture is the builtin coding workflow with only
its column ids renamed, so the sole difference between the two runs is
vocabulary — a hand-built graph would test the fixture's own transition
table as much as the code. It asserts the rename actually landed
(`checking` present, `in-review` absent), so a surviving literal cannot
pass by luck, and it walks the graph rather than jumping, because moves
are transition-validated.

**Both negatives included.** A WIP-lane task must still be REJECTED
under each vocabulary. Supplying the real columns must not degrade into
"every column is a review column", which would let work merge straight
out of the WIP lane — the failure mode a careless version of this fix
would introduce.

## Why nothing caught it

Partial supply. Two of three call sites passed the argument, so a check
asking "does SOME caller supply this?" reported the seam as satisfied,
and the lifecycle-column census counted the conversion as done. Closing
that one-supplier floor in `scripts/check-inert-flag-seams.mjs` (on
#2772) is what surfaced it.

## Verification

- `pnpm test:gate` green
- new suite 4/4; neighbouring merge-queue suites (`taskstore-lifecycle`,
`store-in-review-stall`, `runtime-lifecycle-async`) 29/29
- `tsc -p packages/core` 0, lint 0
- changeset included (`@runfusion/fusion` patch)

Note: #2772 still carries a TEMPORARY per-call-site exemption for this
seam. Once this lands, that exemption's staleness check will fail and I
will remove it there — it cannot outlive the fix.

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

---------

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