fix(engine): stuck-requeue no longer clobbers concurrently-recovered tasks

When SelfHealingManager.recoverCompletedTasks moved a task from
in-progress to in-review, the executor's stuck-kill cleanup running in
execute()'s finally block could fire 20s later, see a stale captured
task.column = "in-progress", and overwrite the recovery by tearing down
the worktree and moving the task back to todo with all step progress
reset. Both the outer-finally and step-session requeue blocks (and the
force-requeue setTimeout in markStuckAborted) now re-read the latest
column and skip cleanup entirely if the task has moved past
in-progress/todo.

Adds a new preserveProgressOnStuckRequeue setting (default: true,
toggle in Settings near the Stuck Task Timeout) so stuck-requeue passes
{ preserveProgress: true } to moveTask. Completed step statuses now
survive the bounce so the agent resumes from where it left off instead
of restarting every step from pending.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-05-06 09:07:52 -07:00
parent b1204576fd
commit c17c288cea
6 changed files with 241 additions and 49 deletions

View File

@@ -0,0 +1,18 @@
---
"@runfusion/fusion": patch
---
Fix a race in the stuck-task requeue path that could clobber a task back to
`todo` (with all step progress reset and worktree torn down) immediately
after `SelfHealingManager.recoverCompletedTasks` had already moved it to
`in-review`. The executor's stuck-kill cleanup ran in `execute()`'s
`finally` block and used a stale captured `task.column` snapshot, so it
would happily overwrite a fresh recovery. The cleanup now re-reads the
latest column and skips entirely when the task has moved past
`in-progress`/`todo`.
Also adds a new setting `preserveProgressOnStuckRequeue` (default: `true`,
toggle in Settings → Engine, near "Stuck Task Timeout"). When enabled, the
stuck detector's requeue passes `{ preserveProgress: true }` to `moveTask`
so completed step statuses survive the bounce and the agent can resume
from where it left off instead of restarting every step from pending.