Repository navigation
fix(server): a Claude model change no longer blocks the next provider switch - #36641
jalay-mantys wants to merge 1 commit into
Conversation
… switch Claude Code replays set_model's "<local-command-stdout>Set model to …" as a user frame before the next turn attaches. The adapter buffered it as wake evidence, so hasPendingBackgroundWork stayed true and switching provider was refused after Stop. Made with Claude Opus 5.5 in Claude Code.
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — Replayed Claude local-command output is excluded from wake buffering so changing models no longer blocks the next provider switch. The filter targets a specific echo format while preserving other wake-message handling. You can add or adjust custom eligibility rules. Learn more. |
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
|
Thanks for this. #34498 makes the same change and is the one being kept. #34498 fixes the same replayed model-change echo being mistaken for background work. Its regression also drives a real model change through the adapter and verifies that an ordinary user frame still counts as wake evidence, which makes it the stronger version to keep. Your report independently confirms the same cause. The broader missing Stop control and other sources of stale background state remain separate problems. Closing as a duplicate. |
Problem
After changing the model inside a Claude thread (Opus 5.5 → Fable 5.2 on the same instance), pressing Stop, and then switching to another provider (Codex 6.1 Sol), the send is refused:
Nothing was running. The thread's
pendingBackgroundTaskswas empty, there were no subagents or background commands, and the Claude process had exited. Retrying, or pressing Stop again, gave the same refusal.Cause
On a live model change the adapter calls
set_modelbefore it attaches the new turn. Claude Code answers with a replayed user frame:{"type":"user","message":{"role":"user","content":"<local-command-stdout>Set model to `claude-fable-5-2`</local-command-stdout>"},"parent_tool_use_id":null,"isReplay":true}No turn is active yet, so the frame goes to
bufferWakeMessage, which treats anyuserframe as wake evidence. The buffered user frame makeshasPendingBackgroundWorkreturn true. An ordinary interrupt closes the query but does not clear the wake buffer, so the provider-switch guard inOrchestrator.tskeeps refusing.Change
bufferWakeMessageignores replayed<local-command-stdout>user frames. They are output echoed back from a command the adapter sent itself, not background work. Turns that are active still handle these frames as before, for example/compact's "Compacted" echo.Scope
A small bug fix with one root cause. Related: #36450 and #35240 report the same refusal. #36596 lets Stop clear background state that only the provider's probe sees; this PR removes one source of that false state, so Stop isn't needed in this case.
Verification
0.1.10-nightly.20261114.4669(macOS desktop). The provider event log shows theSet model toreplay at the model change, thenquery.interrupt→result: error_during_execution→query.close, with notask_startedor background frames at any point. The server trace shows every latermessage.dispatchfailing with the refusal above.ClaudeAdapterV2.test.ts, "does not count a live model change's stdout echo as background work". It fails onmain(hasPendingBackgroundWorkistrue) and passes with the fix.ClaudeAdapterV2.test.ts(425 tests),ClaudeReplayFixtures.integration.test.ts,ProviderSwitch.integration.test.tsandBackgroundWorkStop.integration.test.ts(109 tests) all pass. The compact fixtures that replay<local-command-stdout>Compactedare among them.vp run check:changedpasses.Made with Claude Opus 5.5 in Claude Code.