The merger maintains a single mergeActive slot per project; while a task
holds it, no other in-review task can be picked up. When the merger
sets task.status="merging" but the task is then completed via a
self-healing recovery path instead of the merger's own completeTask(),
the status field is never cleared. The mergeActive slot stays leaked
indefinitely, and the entire merger queue stalls.
Concrete failure: FN-5052 landed via FN-4499 auto-recover-branch-misbound
on 2026-05-18 15:48 with mergeConfirmed=true. Status field stayed at
"merging". 12.5 hours later, FN-5083, FN-5060, FN-5053, FN-5092, and
every other in-review task were waiting on the merger queue with no
merger activity at all.
Two self-healing → done paths were missing the status clear:
- recoverBranchMisboundInReviewTasks() (self-healing.ts ~5031):
branch-tip-misbound recovery moves an in-review task to done after
detecting its work landed on main via a different commit. The merger's
completeTask() correctly clears status before moveTask(); this path
did not.
- proven-no-op finalize in recoverNoOpReviewTasks() (self-healing.ts
~3460): for tasks proven already on main (FN-4500 classifier), the
updateTask({ mergeDetails, modifiedFiles }) patch did not include
status: null. Same leak class.
Both now follow the pattern from merger.ts completeTask() and
project-engine.ts auto-merge already-confirmed:
updateTask({ status: null, error: null, paused: false }) before
moveTask("done").
FN-5052 task.json patched to clear the existing stale status as part
of this commit so the running engine releases its mergeActive slot
on next sweep.
Fusion-Task-Id: FN-5092