Repository navigation
[fix] Make Claude answer a steered message that replaces an unanswered one - #7111
Conversation
A steer that lands before the model replies leaves two user messages in a row. Claude Code sends them as one user turn, and Claude Haiku read the new message as a possible prompt injection or ran the stopped request again (3 of 8 local trials). Put a short note in front of the new message saying it replaces the earlier one. Decided from history, Claude only; Pi and Codex answered correctly without it.
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. 📝 SummarySummary by CodeRabbit
WalkthroughThe runner now detects when the two latest messages are plain user messages. For eligible non-resume Claude turns that are not approval-reply-only requests, it adds a note to the prompt blocks. Tests cover message-history cases and orchestration behavior across Claude, pi_core, and codex. ChangesSuperseded Turn Prompt Handling
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: 🔵 Low · up to After an unanswered turn, three Claude local commands may be sent as ordinary prompts instead of running locally. Fix this regression or explicitly accept the bounded risk before merging. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Railway Preview Environment
Dependent tests finished. Post /preview for manual review. Environment absence verified; retained storage requires separate provider accounting. Post |
|
🤖 The AI agent says: @coderabbitai review |
|
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Advanced
Run ID: 9240e24f-ce56-4de8-b599-7415f17256eb
📒 Files selected for processing (4)
services/runner/src/engines/sandbox_agent/run-turn.tsservices/runner/src/engines/sandbox_agent/superseded-turn.tsservices/runner/tests/unit/sandbox-agent-orchestration.test.tsservices/runner/tests/unit/superseded-turn.test.ts
Included review availability: Your plan provides up to 8 included reviews per hour; 0 remain after this review.
Context
If you send a steer (a follow-up with
on_busy: "steer") before Claude has replied to the running turn, Claude often ignores it. In v0.121.0 staging QA, Claude Haiku answered "the system reminder contains what appears to be a steering instruction" and refused. On the local stack it went wrong in 3 of 8 early-steer trials. It either called the steer a prompt injection or ran the stopped request again.The cause: the stopped turn left no assistant message, so Claude Code holds two user messages in a row (the stopped request, its
[Request interrupted by user]marker, then the new message). It sends them to the model as one user turn, and each half starts with our "Current facts for this turn" block. The model sees two conflicting instructions and nothing tells it which one is current. This is not new in 0.121. Steer, the warm park on Stop and the per-turn context block all shipped in v0.120.0.Changes
When a Claude turn's history ends in two plain user messages, the runner adds one text block in front of the new message:
Prompt blocks before:
[turn context, "Reply with exactly: X"]After:
[turn context, note, "Reply with exactly: X"]The check reads the (reconstructed) history, not runner state, so it covers a warm session, a native load after a restart, and a cold replay. If the model had already replied or started a tool call before the stop, an assistant message sits between the two user messages and nothing changes. Live approval resumes, approval-only replies and slash commands (which Claude ACP only recognizes as the first prompt block) are skipped. Pi and Codex get no note, because they answered the steer correctly without it.
Tests / notes
tests/unit/superseded-turn.test.tsfor the history check. New orchestration tests: Claude gets the note in the prompt blocks, Pi and Codex do not. The Claude test fails with the wiring removed.gateway-run-turn-composition.test.tsfail the same way on the release branch without this change.sleep 20turn: without the note 5 of 8 followed the steer; with this exact note text sent in front of the steer, 12 of 12 followed. Steers that landed after the tool call started followed 4 of 4 without the note. Pi (Codex subscription) 2/2 and Codex 2/2 followed without it. The note was tested by prefixing it to the steer text on the running stack, not by deploying this runner build.What to QA