The reclaimSelfOwnedBranchConflicts sweep was force-pausing actively-running
tasks. Production failure shape on FN-4819:
1. Self-healing sweep runs every cycle and inspects branch conflicts.
2. For FN-4819, inspection classified the conflict as 'tip-already-merged'
(the task's branch tip was already on main).
3. Sweep called removeWorktree({ reason: SelfHealingBranchConflict }).
4. The FN-4811 active-session gate correctly refused: the worktree was
bound to FN-4819/executor (a live agent session was using it).
5. The thrown ActiveSessionWorktreeRemovalError was caught by the outer
reclaim catch block.
6. The catch escalated to AutoRecoveryDispatcher with class
'branch-conflict-unrecoverable'.
7. decision.action === 'pause' marked the task failed + paused +
pausedReason='branch-conflict-unrecoverable' + moved to in-review.
Net effect: the FN-4811 gate (which is correct \u2014 you can't yank a live
worktree) became a regression source because the self-healing sweep
interpreted the refusal as fatal. Tasks that were actively making progress
got paused with a misleading 'branch conflict unrecoverable' error.
Fix: at the top of the per-task reclaim loop in
reclaimSelfOwnedBranchConflicts, check
activeSessionRegistry.isPathActive(task.worktree) and continue for any
task whose worktree is currently bound to a live session. The reclaim
will retry on the next sweep (sweeps run every cycle) once the session
has finished using the worktree. No data is lost, no decision is forced.
Test added under
packages/engine/src/__tests__/reliability-interactions/reclaim-defers-on-active-session.test.ts
covering:
- The skip path: when activeSessionRegistry has a registration for
task.worktree, the sweep MUST NOT call inspectBranchConflict,
removeWorktree, or isUsableTaskWorktree. The task MUST stay in
in-progress, not be marked failed/paused, not be moved to in-review.
- Control: with no registration, the sweep DOES proceed and reaches
inspectBranchConflict (preserving existing behavior).
Full engine suite: 314 files, 5061 tests pass, 1 skipped. Lint clean.
Build clean.
Fusion-Task-Id: FN-4811
1.1 KiB
@runfusion/fusion
| @runfusion/fusion |
|---|
| patch |
fix(FN-4811): defer self-healing reclaim when worktree has an active session
The reclaimSelfOwnedBranchConflicts sweep was force-pausing actively-running tasks. When a task's branch tip was already on main (the tip-already-merged inspection), the sweep tried removeWorktree({ reason: SelfHealingBranchConflict }). The FN-4811 active-session gate correctly refused (the worktree was still bound to a live executor session), but the outer catch escalated the thrown error to AutoRecoveryDispatcher with class branch-conflict-unrecoverable. The dispatcher's pause decision then marked the task failed + paused + pausedReason="branch-conflict-unrecoverable" — even though the executor was making real progress (FN-4819 reproduction).
Fix: at the top of the per-task reclaim loop, check activeSessionRegistry.isPathActive(task.worktree) and continue for any task whose worktree is currently bound to a live executor/merger/step session. The reclaim retries on the next sweep when the session has finished and the worktree is genuinely free.