Skip to content

fix(plans): honor abort during parallel wave slot-wait#85

Merged
ersinkoc merged 1 commit into
mainfrom
fix/plan-wave-abort-responsiveness
Jun 5, 2026
Merged

fix(plans): honor abort during parallel wave slot-wait#85
ersinkoc merged 1 commit into
mainfrom
fix/plan-wave-abort-responsiveness

Conversation

@ersinkoc
Copy link
Copy Markdown
Collaborator

@ersinkoc ersinkoc commented Jun 5, 2026

Summary

Found during a deep-read audit of plans/executor.ts (a minor observation I'd flagged earlier, now fixed).

In executeStepsInWaves, when every maxConcurrent slot was full of slow steps, a cancelled plan kept spinning the slot-wait poll (while (executingSteps.size >= maxConcurrent) await sleep(10)) until a step happened to finish — the wave-top signal.aborted check can't fire while the inner scheduling loop holds control. Cancellation was still eventually honored at the next wave top, but delayed, and the rest of the wave's ready steps were scheduled anyway.

Fix

The slot-wait and the step-scheduling loop now re-check signal.aborted and stop.

The subtle part: they break, not throw. Throwing mid-loop would orphan the already-pushed stepPromisesPromise.all (below) is what attaches their rejection handlers — so a later-failing in-flight step would surface as an unhandled rejection (the exact class just fixed in #83). Instead we break out of scheduling, still await Promise.all(stepPromises) on what was scheduled, then throw 'Plan execution aborted' → the catch surfaces 'cancelled'.

Test plan

New regression test (wave execution abort): maxConcurrent=1, stepA's tool gated to hold the slot; aborting while stepB waits leaves stepB unscheduled (calledTools === ['tool_a']) and resolves the plan 'cancelled' (not failed, no plan:failed). Without the fix, the freed slot after release would schedule tool_b — failing the assertion.

  • ✅ 61/61 executor tests pass (60 existing + 1 new), no type errors
  • ✅ typecheck + eslint clean

🤖 Generated with Claude Code

In executeStepsInWaves, when all maxConcurrent slots were full of slow
steps, a cancelled plan kept spinning the slot-wait poll until a step
finished — the wave-top abort check can't fire while the inner scheduling
loop holds control. Cancellation was still eventually honored (next wave
top), but delayed, and the remaining ready steps were scheduled anyway.

The slot-wait and the step-scheduling loop now re-check signal.aborted and
break. Crucially they break rather than throw mid-loop: throwing would
orphan the already-pushed stepPromises (Promise.all is what attaches their
rejection handlers), reintroducing the unhandled-rejection class just
fixed in JobQueueService. Scheduled steps are still awaited via
Promise.all, then the abort is surfaced as 'cancelled'.

Regression test: with maxConcurrent=1 and stepA gated, aborting while
stepB waits for the slot leaves stepB unscheduled and resolves the plan
'cancelled' (not failed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ersinkoc ersinkoc merged commit db741bb into main Jun 5, 2026
2 checks passed
@ersinkoc ersinkoc deleted the fix/plan-wave-abort-responsiveness branch June 5, 2026 18:19
@ersinkoc ersinkoc mentioned this pull request Jun 5, 2026
HoneyAdam pushed a commit to HoneyAdam/OwnPilot that referenced this pull request Jun 6, 2026
Patch release shipping the post-0.7.1 fix:

Fixed
- Plan cancellation is now prompt during parallel wave execution: the
  maxConcurrent slot-wait and step-scheduling loop re-check signal.aborted
  and stop scheduling immediately; scheduled steps are still awaited via
  Promise.all so no in-flight promise is orphaned. (ownpilot#85)

Version bumped across all workspace packages + docs/ARCHITECTURE.md +
start scripts. release:verify green (17240 gateway tests, 8/8 turbo).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant