Opus advisor caught that the new run-journal replay path could double-render
when the live stream errors mid-stream:
- Live SSE frames emitted by _sse() in api/streaming.py:2296 carry no 'id:'
field. Only _sse_with_id() (used in _replay_run_journal at routes.py:5853)
emits IDs.
- During live streaming, EventSource.lastEventId stays empty, so the frontend's
_lastRunJournalSeq stays at 0.
- If the server dies mid-stream, the error reconnect handler opens replay with
after_seq=0 — server replays every journaled event from seq 1.
- assistantText accumulator (closure scope in messages.js) carries over from
the live phase. The token handler unconditionally appends d.text. Double-
rendered text.
Fix: reset assistantText, reasoningText, liveReasoningText, segmentStart, and
set _smdReconnect=true before opening the replay EventSource. Next live token
clears assistantBody.innerHTML to match the reset accumulator.
4 regression tests added in test_stage364_opus_replay_doublerender_fix.py.
Revert-fix verification confirms 3/4 tests fail against reverted code.
This is the TWO-LAYER catch in action: agent self-verified the producer→
consumer chain works end-to-end (Step 3 in agent-side-empirical-verification.md
PASSED for #2283), and Opus independently caught a separate frontend coupling
issue. Both checks required and both fire.