Repository navigation
fix(server): let Claude abort a stopped v2 turn before closing the CLI - #1008
Merged
Merged
Conversation
Contributor
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
Re-port the #916 interrupt grace lost in the orchestrator v2 adoption. ClaudeAdapterV2.interruptTurn now waits up to CLAUDE_INTERRUPT_GRACE (3s) after query.interrupt for the active turn to settle, so Claude records the prompt in its transcript before Stop closes the process. A failed or hung interrupt no longer skips the close. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Catch the full cause around query.interrupt so a defect, not only a typed failure, still reaches close. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
rynfar
force-pushed
the
fix/claude-v2-interrupt-grace
branch
from
October 4, 2026 00:26
8c7b9c1 to
9edd807
Compare
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.
Regression
#916 (93ba1cf) made Claude's Stop call
query.interrupt()and then wait up to 3s for the turn to finish before closing the process. That gave Claude time to write the prompt to its transcript. The orchestrator v2 adoption (eed7e97) dropped this.ClaudeAdapterV2.interruptTurnnow didyield* query.interrupt; query.closewith no wait.User impact
If you stopped a thread's first turn before Claude saved it, the thread kept a resume cursor for a session Claude never stored. The next turn always resumes (
providerTurnOrdinal > 1forcesresumeinopenQuery), so every later message failed with "No conversation found" and the thread couldn't be used again. Separately, ifquery.interruptfailed,closewas never called and the CLI process stayed alive.Fix
ActiveClaudeTurnContextnow has asettledDeferred, whichfinalizeActiveTurnresolves.interruptTurnwaits forsettledfor at mostCLAUDE_INTERRUPT_GRACE(Duration.seconds(3)), then closes. A hung interrupt also counts against the same grace.orchestration-v2.claude-query-interrupt-failed) and the process is still closed. The existing 10s close timeout and fallback finalization are unchanged.Tests
New tests in
ClaudeAdapterV2.test.ts. Each one fails without the fix:interruptTurn lets Claude abort the turn before closing the process: Stop does not close while the turn is unsettled, even though the TestClock is frozen. It closes once Claude's abortedresultsettles the turn, and the terminal isinterrupted.interruptTurn closes the process when Claude never aborts the turn: a hung interrupt closes afterTestClock.adjust(CLAUDE_INTERRUPT_GRACE).interruptTurn still closes the process when Claude's interrupt fails/... defects(the defect case fails if the cause is only caught as a typed error).Replay:
turn_interrupt_restart/claudeAgent(first-turn Stop, then a new message that resumes) still passes through OrchestratorV2. That fixture was recorded with a tool call already persisted, so it doesn't reproduce the bug itself. A fixture that reproduces "No conversation found" would need a new live recording.Validation: the focused suites
ClaudeAdapterV2.test.ts,ClaudeReplayFixtures,OrchestratorReplayFixturesandOrchestratorReplayRecoverypass (261 tests).vp run -F t3 typecheckpasses, and scoped fmt/lint report nothing new.Follow-up (not in this PR)
Recovery for threads that are already broken is not included. That would mean: when a resume fails with "No conversation found" after an interrupted first turn, start a fresh native session instead. It needs a session-existence check or error classification in the query runner, plus re-offering the prompt mid-turn, which is too much for this fix. Threads broken before this change stay broken until that lands.
#1005 also edits
ClaudeAdapterV2.tsand the wake harness in the test file. Whichever merges second needs a rebase. The harnessinterruptoption now takessdkMessages.🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.