fix #71992: [Bug]: Control UI webchat duplicates every assistant reply on 2026.4.21 — regression from #5964/#39469#88786
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 2, 2026, 1:27 PM ET / 17:27 UTC. Summary PR surface: Source +5, Tests +30. Total +35 across 2 files. Reproducibility: yes. source-level. On current main, active-run final/aborted handling can assign chatMessages while chatStream is still non-null, and the renderer appends the active stream separately. Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the focused Control UI ordering fix for the client-side WebChat duplicate-render path while keeping backend transcript dedupe as separate work. Do we have a high-confidence way to reproduce the issue? Yes, source-level. On current main, active-run final/aborted handling can assign chatMessages while chatStream is still non-null, and the renderer appends the active stream separately. Is this the best way to solve the issue? Yes. Reordering terminal lifecycle reconciliation before the committed assistant-message append fixes the state overlap at the owner boundary; renderer-side dedupe would only hide the duplicate output and backend transcript dedupe is a separate issue. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against f789081bae12. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +5, Tests +30. Total +35 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
2fe5f20 to
095b9b4
Compare
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
095b9b4 to
f6244fb
Compare
|
Maintainer verification for
Best-fix verdict: best narrow fix for this UI race. Renderer-side dedupe would hide symptoms and backend transcript dedupe is a different bug class. |
Summary
chatStreamandchatRunIdare cleared.Root Cause
final/abortedevents could append an assistant message intochatMessageswhile the same assistant content still existed in the livechatStream/chatRunIdstate. Because the Control UI renders committed history and the live stream item separately, Lit batching could briefly render the completed assistant reply twice.Real behavior proof
Behavior or issue addressed: Control UI WebChat should render a completed assistant reply exactly once after a send completes, with no leftover streaming bubble for the same assistant turn.
Real environment tested: Local OpenClaw gateway serving the real Control UI from this PR head (
095b9b496f1e320b9ff61617c2b2b4eb0f92e186) on loopback, driven by Playwright in Chromium. The gateway used the repo's deterministicqa mock-openaiprovider so the proof exercises a real browser WebChat send without depending on external model billing/auth.Exact steps or command run after this patch:
pnpm openclaw qa mock-openai --host 127.0.0.1 --port 44180.node scripts/run-node.mjs gateway runwithOPENCLAW_CONFIG_PATH=/tmp/openclaw-dev-pr88786-mock-proof/.openclaw-dev/openclaw.jsonand gateway port19190.node /media/vdc/code/ai/aispace/openclaw-pr-88786-evidence/control-ui-send-proof-after-sync.mjswithOPENCLAW_PROOF_SESSION=dailyfix-88786-mock-proofandOPENCLAW_EXPECTED_REPLY=daily-fix-88786-proof.Evidence after fix: Copied proof result from
/media/vdc/code/ai/aispace/openclaw-pr-88786-evidence/control-ui-send-proof-after-sync.jsonplus screenshot artifact/media/vdc/code/ai/aispace/openclaw-pr-88786-evidence/control-ui-after-one-reply-after-sync.png(sha256:05db04780c0b9b707d78920c714fae19b34d9586ddc448d0a40cbeb755eadc14).{ "url": "http://127.0.0.1:19190/chat?session=dailyfix-88786-mock-proof#token=<redacted>", "prompt": "reply exactly `daily-fix-88786-proof`", "expectedReply": "daily-fix-88786-proof", "before": { "messageCount": 0, "userCount": 0, "assistantCount": 0, "chatRunId": null, "chatStream": null, "liveStreamingBubbleCount": 0 }, "after": { "messageCount": 2, "userCount": 1, "assistantCount": 1, "messageSummaries": [ { "role": "user", "text": "reply exactly `daily-fix-88786-proof`" }, { "role": "assistant", "text": "daily-fix-88786-proof" } ], "assistantGroups": [ { "text": "daily-fix-88786-proof Assistant 2026年6月3日 0:45", "bubbleCount": 1, "streamingBubbleCount": 0 } ], "chatRunId": null, "chatStream": null, "liveStreamingBubbleCount": 0 }, "result": { "newUserMessages": 1, "newAssistantMessages": 1, "newAssistantTexts": ["daily-fix-88786-proof"], "chatRunIdCleared": true, "chatStreamCleared": true, "liveStreamingBubbleCount": 0, "completedAssistantReply": true, "exactlyOneCompletedAssistantReply": true } }Observed result after fix: One WebChat send added exactly one user message and exactly one completed assistant message. The completed assistant text was
daily-fix-88786-proof;chatRunIdandchatStreamwere bothnull;.chat-bubble.streamingcount was0; the rendered assistant group had one bubble.What was not tested: This proof does not claim to fix the separate persisted-transcript structural duplicate shape described in BUG: Control UI (webchat) double-records assistant messages in session JSONL #39469. It also does not depend on a third-party live model response; model-provider billing/auth behavior is outside this UI rendering fix.
Review findings addressed
Regression Test Plan
ui/src/ui/controllers/chat.test.ts.node scripts/run-vitest.mjs ui/src/ui/controllers/chat.test.ts.pnpm tsgo:test:ui.git diff --check.Merge risk
app: web-ui,size: S,impact:session-state,impact:message-loss.