Files
fusion/packages
gsxdsm 126cee7e6d engine: finalization parked ALREADY-MERGED work as failed on a renamed board (#2964)
**The worst symptom in this family: the branch landed, and the board
says the task failed.**

`project-engine`'s merge-confirmed finalization spread the task's
**real** column into `getTaskHardMergeBlocker` with no `reviewColumns`,
so the identity check ran against the literal `in-review`. On a renamed
board it returned `task is in 'signoff', must be in 'in-review'`, and
the caller parked the card:

```
status: "failed"
error:  "Merge confirmed but finalization blocked: task is in 'signoff', must be in 'in-review'"
```

For work that had already merged.

## Its sibling had already solved this

`auto-merge-finalization.ts` passes the **review-eligible sentinel**
instead of the card's own column, with the reasoning recorded at that
site: `getTaskHardMergeBlocker` asks *"is this card blocked by anything
other than where it sits?"*, and its callers are recovery paths for
landed work that a graph crash can leave resting in any column.
`project-engine` simply never got the same treatment.

## One name instead of two spellings

Rather than write the sentinel a second time, it is exported once as
`REVIEW_ELIGIBLE_SENTINEL_COLUMN` next to the helper whose contract
gives it meaning, and both recovery paths use it. **Two sites
independently spelling a magic value is how one of them came to be
missing it** — that is the actual root cause here, not the literal
itself.

This also answers the census, which flagged the new literal — correctly.
Its guidance (which I wrote, in #2909) is to hoist a deliberate literal
into a *declaration*, where a `DELIBERATE-LITERAL` marker actually
attaches, instead of leaving it mid-expression where the marker is
silently ignored. The shared constant is exactly that, and it lowers
`auto-merge-finalization`'s literal count too.

## Revert result

| | reverted → |
| --- | --- |
| sentinel replaced by the card's own renamed column | reproduces the
shipped string |

The middle test asserts that string deliberately — it is what landed in
`task.error`, so a regression reports what the operator would actually
have seen. A third case checks the sentinel does **not** suppress
genuine blockers: incomplete steps still block finalization in any lane.

These drive the helper directly; reaching `project-engine`'s
finalization end to end needs a live engine, a merge run and a real
repo, while the defect is entirely in *what the blocker is asked*.

## Verification

`pnpm test:gate` 161 + 487 + 13 + 71; `project-engine` +
`auto-merge-finalization` + the new suite, 207; `tsc` clean on core and
engine; lint, census `--strict`, FNXC gate, changesets all clean.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Fixed merge-confirmed tasks being finalized correctly when boards use
renamed workflow columns.
* Prevented already-merged tasks from being incorrectly marked as failed
due to custom review-column names.
  * Preserved enforcement of genuine incomplete-step blockers.

* **Tests**
* Added coverage for finalization on renamed lanes and legitimate merge
blockers.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

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