Skip to content

fix(executor): honor stop-after over pending pauses and carry it on resume - #8745

Open
sudoKrishna wants to merge 1 commit into
simstudioai:mainfrom
sudoKrishna:fix/stop-after-pausing-stop
Open

sudoKrishna wants to merge 1 commit into
simstudioai:mainfrom
sudoKrishna:fix/stop-after-pausing-stop

Conversation

@sudoKrishna

Copy link
Copy Markdown

Problem

Two pre-existing bugs in stopAfterBlockId (the --stop-after / "run until
block" feature), from #8661:

  1. Stop-after vs a pending pause. If the stop block completes while another
    branch is paused at a human-in-the-loop block, the run returns paused.
    Nothing downstream runs, before or after resume.
  2. Resume drops the stop target. If the stop block is itself a pausing
    block, resuming continues past it.

Fix

1. Stop-after wins over a pending pause. The engine records that the stop
target was reached; at the end of the run it clears any pending pause points and
returns a completed run, so no unusable resume is persisted. Downstream of the
stop target still does not run, as intended.

2. A pausing stop target is rejected upfront. stopAfterBlockId that targets
a human-in-the-loop or wait block cannot be honored — the run pauses there and
the resume path prunes the paused block's outgoing edges, so the target would be
lost. execution-core now rejects it with a clear message instead of starting a
run whose stop target resume cannot keep. New helper
isPausingStopTarget in executor/utils/stop-after.ts.

3. The stop target is carried across pause/resume. stopAfterBlockId is
persisted in the pause snapshot and passed back into the resumed run, so a run
that pauses on a different branch still ends after its original stop target.

Tests

  • executor/execution/engine.test.ts — stop-after completes instead of pausing
    when another branch pauses; status is not paused and no pause points
    survive.
  • executor/execution/snapshot-serializer.test.ts — the stop target is written
    into the pause snapshot.
  • executor/utils/stop-after.test.ts — human-in-the-loop v1/v2 and wait targets
    are rejected; normal blocks and unknown ids are allowed.

Existing coverage: 336 tests in executor/execution + the HITL manager pass
unchanged.

Closes #8661

…esume

Three changes for issue simstudioai#8661:

1. The engine now lets stop-after win over a pause still pending on another
   branch. Reaching the stop target completes the run and drops the pause
   points instead of returning 'paused' and persisting a resume that can never
   finish. (executor/execution/engine.ts)

2. stopAfterBlockId is rejected upfront when it targets a block that pauses
   (human-in-the-loop or wait). Such a target cannot be honored: the run pauses
   there and the resume path prunes its outgoing edges, so the stop target would
   be lost. (executor/utils/stop-after.ts, execution-core.ts)

3. stopAfterBlockId is persisted in the pause snapshot and passed back into the
   resumed run, so a run that pauses on another branch still ends after its
   original stop target. (execution/types.ts, snapshot-serializer.ts,
   human-in-the-loop-manager.ts)

Tests: an engine case for stop-after vs a pending pause, a serializer case for
the carried stop target, and unit coverage for the pausing-target check.
@vercel

vercel Bot commented Oct 7, 2026

Copy link
Copy Markdown

@sudoKrishna is attempting to deploy a commit to the Sim Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps

greptile-apps Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

[Medium risk] Fixes stop-after behavior across pause and resume cycles.

Fix the rejection of synchronous Wait targets before merging.

Findings

  1. P1 Synchronous Wait targets fail ▶

Summary

This PR makes stop-after completion take priority over pauses on other branches. It also saves the stop target in pause snapshots and restores it on resume.

  • Adds regression coverage for pause cleanup and saved stop targets.
  • Rejects HITL and Wait stop targets.
  • The Wait guard needs to distinguish synchronous waits from waits that suspend the run.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Run with stop target] --> B{Target rejected?}
  B -->|HITL or any Wait| C[Return error]
  B -->|No| D[Execute workflow]
  D --> E{Stop target reached?}
  E -->|Yes| F[Clear pending pause points]
  F --> G[Complete run]
  E -->|No, another block pauses| H[Save snapshot with stop target]
  H --> I[Resume with saved stop target]
  I --> D
Loading

Reviews (1) · Last reviewed commit: "fix(executor): honor stop-after over pen..." · Reviewed by Greptile

stopAfterBlockId: string
): boolean {
const blockType = blocks.find((block) => block.id === stopAfterBlockId)?.metadata?.id
return isHumanInTheLoopBlock(blockType) || blockType === BlockType.WAIT

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Synchronous Wait targets fail

isPausingStopTarget rejects every wait block, including the default mode where async is off. In that mode, WaitBlockHandler sleeps in-process and returns status: 'completed' without _pauseMetadata, so the engine can honor the stop target normally. This guard makes previously valid run-until requests fail before execution. Reject only waits that suspend the run, and cover the synchronous case in the tests.

This branch has not been deployed

No deployments
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.

Stop-after: pending pauses and resume don't honor the stop target

1 participant