fix(runner): stop replaying the current prompt as a prior turn#5489
fix(runner): stop replaying the current prompt as a prior turn#5489mmabrouk wants to merge 1 commit into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 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 |
The runner persists the inbound user message before it starts the engine, and acquiring a sandbox takes seconds, so by the time reconstruction reads the record log the current turn is already in it. Reconstruction returned that record as a prior turn and the inbound message was appended on top, sending the prompt to the model twice. It reproduced on every first turn of every conversation, including with the frontend flag off, because a first turn always carries exactly one message and the `messages.length > 1` guard does not stop it. Drop the current turn's records by `turn_id` before folding, and replace the message-count guard with a shared `carriesMinimalHistory` predicate that both this seam and the keep-alive check can agree on. The count alone also let reconstruction run for an empty array and for a lone assistant message. Claude-Session: https://claude.ai/code/session_01KM69J7uHafgciiN5zfG7qR
2f4cc5e to
b652316
Compare
Railway Preview Environment
|
The problem
Every first turn of every conversation sent the user's prompt to the model twice.
A one-line probe went in as
"Reply with exactly: PONG"and reached the model as:[{"role": "user", "content": "Reply with exactly: PONG"}, {"role": "user", "content": "Reply with exactly: PONG"}]The runner persists the inbound user message before it starts the engine (
server.ts), and acquiring a sandbox takes seconds, so by the time reconstruction reads the record log the current turn is already in it. Reconstruction returned that record as a prior turn, and the inbound message was then appended on top of it.Two things made this easy to miss. It is not gated by the frontend flag: a first turn always carries exactly one message, so the
messages.length > 1guard never blocked it, and it reproduced on 9 out of 9 first turns with the frontend flag off. And a duplicated prompt still produces a correct-looking answer, so every journey in the QA suite passed while it was happening.The same guard also let reconstruction run for an empty message array and for a lone assistant message, which CodeRabbit flagged separately.
The fix
Drop the current turn's own records by
turn_idbefore folding, so the log contributes only prior turns regardless of when the write lands.Replace the message-count guard with a shared
carriesMinimalHistorypredicate, so the runner and the keep-alive check agree on one definition of "this request carries only its own fresh user turn" instead of each inferring it from a count.Before / after
u, u, a, u, a, uu, a, u, a, uTesting
Runner unit suite passes with 4 new cases pinning the empty array, the lone assistant message, the current-turn exclusion, and a first turn whose only records are its own. Confirmed live on a deployed stack: QA results.