Problem
When the web process restarts mid-agent-call — a deploy, a crash — DBOS recovery re-executes the workflow, the in-flight step has no checkpoint, and it re-runs with a fresh call id; the old call is marked abandoned. But the abandoned call's process is still alive: the sandbox VM does not care that the supervisor restarted, and the harness keeps working inside it.
With steps_reuse_sandbox, the recovered step then starts in that same sandbox. The fresh agent walks into a working tree with the abandoned sibling's uncommitted edits and a live harness process. Observed end to end after a deploy landed two minutes into an implement:
The refusal was the good outcome. The bad outcomes are live: the abandoned harness runs unsupervised with push credentials until its lease expires (an untracked push to the PR branch is possible), and a less careful agent would have raced it.
Proposal
Fence the abandoned call before the recovered step touches a workspace, any of:
- Kill the abandoned call's process — its pid is already recorded in the run directory — and reset the checkout to the last pushed state before starting the fresh call.
- Lease a fresh sandbox for a recovered step and release the old one, paying one provision for a clean room; the janitor reaps the orphan.
Option 1 keeps sandbox reuse; option 2 is simpler and cannot miss a stray child process. Either way, an abandoned call must not keep credentials and a working tree it shares with its replacement.
Problem
When the web process restarts mid-agent-call — a deploy, a crash — DBOS recovery re-executes the workflow, the in-flight step has no checkpoint, and it re-runs with a fresh call id; the old call is marked abandoned. But the abandoned call's process is still alive: the sandbox VM does not care that the supervisor restarted, and the harness keeps working inside it.
With
steps_reuse_sandbox, the recovered step then starts in that same sandbox. The fresh agent walks into a working tree with the abandoned sibling's uncommitted edits and a live harness process. Observed end to end after a deploy landed two minutes into an implement:The refusal was the good outcome. The bad outcomes are live: the abandoned harness runs unsupervised with push credentials until its lease expires (an untracked push to the PR branch is possible), and a less careful agent would have raced it.
Proposal
Fence the abandoned call before the recovered step touches a workspace, any of:
Option 1 keeps sandbox reuse; option 2 is simpler and cannot miss a stray child process. Either way, an abandoned call must not keep credentials and a working tree it shares with its replacement.