fix(core): let answers land on a failed-then-resumed run - #10
Merged
Conversation
The answer path decides whether a run is terminal by folding the whole journal, but nothing in that fold clears a historical run.failed when the run is resumed. Once a run has failed once (e.g. a step timeout), every subsequent gate answer is refused with "run is already failed — resume it before answering" forever — including after resuming. A resumed step that parks on a permission gate then sits unanswerable until it hits its own step timeout, which fails the run again: a guaranteed deadlock loop (timeout -> resume -> gate -> unanswerable -> timeout). Fix: in both fold sites (live-engine and suspended-run), a run.status: "executing" event — which resume appends — resets the terminal flag, so the answer guard reflects the run's actual liveness instead of its history. Observed in the wild: two long agent workflows deadlocked exactly this way; with this patch both accepted their gate answers and ran to completion. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DSa5GP2q5WNtNJ1Mt4V2z4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
Engine.answer()decides whether a run is terminal by folding the whole journal, but nothing in that fold clears a historicalrun.failedwhen the run is resumed. Once a run has failed once (e.g. a step timeout), every subsequent gate answer is refused forever with:— including after resuming, which is exactly what the error tells you to do. A resumed step that parks on a permission gate then sits unanswerable until it hits its own step timeout, failing the run again:
A guaranteed deadlock loop for any failed-then-resumed run whose steps use
risk-gated tools.Reproduction (observed in the wild)
Two long agent workflows (design agents using gated Bash) hit a 30-minute step timeout, were resumed, parked on their next permission gate, and could not be answered by CLI (
weft answer) or daemon (POST /api/runs/:id/answer) — both raised the terminal-run guard — until the step timed out again. With this patch applied, both runs accepted their gate answers on the next resume and ran to completion.The fix
In both fold sites (the live-engine path and the suspended-run path), a
run.status: "executing"event — which resume appends — resets theterminalflag, so the answer guard reflects the run's actual liveness rather than its history. 9 lines, comments included.Related observation (possibly its own issue)
Time spent parked on a human gate counts against the step's
timeout. That is what turned this bug into a hard deadlock, and even without the bug it means slow human approvals can kill an otherwise healthy step. Splitting gate-wait time out of step execution time may be worth considering separately.🤖 Generated with Claude Code
https://claude.ai/code/session_01DSa5GP2q5WNtNJ1Mt4V2z4