Stage 368: PR #2377 — Add run-journal replay timeline parity checks by @franksong2702
This commit is contained in:
commit
bcf44e586a
2 changed files with 63 additions and 2 deletions
|
|
@ -78,7 +78,10 @@ while WebUI still has multiple overlapping state stores.
|
||||||
assistant just acted.
|
assistant just acted.
|
||||||
5. **Replay is idempotent.** Replaying a run from a cursor must not duplicate
|
5. **Replay is idempotent.** Replaying a run from a cursor must not duplicate
|
||||||
transcript rows, thinking content, interim assistant text, tool cards, or
|
transcript rows, thinking content, interim assistant text, tool cards, or
|
||||||
compression cards.
|
compression cards. Replayed long-task events should enter the same
|
||||||
|
browser-facing timeline renderer as live SSE events so recovery does not
|
||||||
|
downgrade a structured Thinking / progress / tool / compression turn into a
|
||||||
|
separate flattened presentation.
|
||||||
6. **Compression is not current intent.** Automatic compression summaries and
|
6. **Compression is not current intent.** Automatic compression summaries and
|
||||||
reference cards are recovery/handoff material. They must not be treated as a
|
reference cards are recovery/handoff material. They must not be treated as a
|
||||||
new user request, active-turn content, or the default visible explanation for
|
new user request, active-turn content, or the default visible explanation for
|
||||||
|
|
@ -102,6 +105,8 @@ context reconstruction, or session metadata:
|
||||||
- What happens after browser refresh, session switch, SSE reconnect, and WebUI
|
- What happens after browser refresh, session switch, SSE reconnect, and WebUI
|
||||||
restart?
|
restart?
|
||||||
- Does replay rebuild the same scene without duplicates?
|
- Does replay rebuild the same scene without duplicates?
|
||||||
|
- Does replay use the same timeline-rendering path as live SSE for thinking,
|
||||||
|
interim assistant text, tool cards, compression cards, and terminal states?
|
||||||
- Can this change move a session in the sidebar without meaningful user or
|
- Can this change move a session in the sidebar without meaningful user or
|
||||||
assistant activity?
|
assistant activity?
|
||||||
- Can automatic compression or recovery text become visible active-turn content?
|
- Can automatic compression or recovery text become visible active-turn content?
|
||||||
|
|
@ -147,4 +152,3 @@ The two documents should be read together:
|
||||||
4. If #1925 introduces a new adapter-backed runtime layer, update this RFC or
|
4. If #1925 introduces a new adapter-backed runtime layer, update this RFC or
|
||||||
replace it with the accepted implementation contract so these invariants do
|
replace it with the accepted implementation contract so these invariants do
|
||||||
not live only in historical discussion.
|
not live only in historical discussion.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,3 +35,60 @@ def test_frontend_replay_cursor_uses_eventsource_last_event_id():
|
||||||
assert "source.addEventListener(_runJournalEventName,_rememberRunJournalCursor)" in MESSAGES_SRC
|
assert "source.addEventListener(_runJournalEventName,_rememberRunJournalCursor)" in MESSAGES_SRC
|
||||||
assert "after_seq=${encodeURIComponent(String(_runJournalReplayAfterSeq()))}" in MESSAGES_SRC
|
assert "after_seq=${encodeURIComponent(String(_runJournalReplayAfterSeq()))}" in MESSAGES_SRC
|
||||||
assert "after_seq=0" not in MESSAGES_SRC
|
assert "after_seq=0" not in MESSAGES_SRC
|
||||||
|
|
||||||
|
|
||||||
|
def test_replayed_long_task_events_enter_the_same_live_timeline_handlers():
|
||||||
|
"""Run-journal replay must not grow a parallel long-task renderer.
|
||||||
|
|
||||||
|
The run-state consistency contract depends on replayed journal events
|
||||||
|
flowing through the same EventSource handlers as live streams. Otherwise a
|
||||||
|
live long task can render as Thinking -> progress text -> tool cards, while
|
||||||
|
the same journaled event sequence replays as a flattened or reordered scene.
|
||||||
|
"""
|
||||||
|
wire_pos = MESSAGES_SRC.index("function _wireSSE(source)")
|
||||||
|
wire_block = MESSAGES_SRC[wire_pos : MESSAGES_SRC.index("async function _restoreSettledSession", wire_pos)]
|
||||||
|
replay_events = [
|
||||||
|
"reasoning",
|
||||||
|
"interim_assistant",
|
||||||
|
"tool",
|
||||||
|
"tool_complete",
|
||||||
|
"compressing",
|
||||||
|
"compressed",
|
||||||
|
"metering",
|
||||||
|
"done",
|
||||||
|
"apperror",
|
||||||
|
]
|
||||||
|
|
||||||
|
for event_name in replay_events:
|
||||||
|
assert f"source.addEventListener('{event_name}'" in wire_block, (
|
||||||
|
f"{event_name} must be handled by the shared live/replay SSE pipeline"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "updateThinking(" in wire_block, "reasoning replay should use the live Thinking card path"
|
||||||
|
assert "appendLiveToolCard(tc)" in wire_block, "tool replay should use live tool-card rendering"
|
||||||
|
assert "setCompressionUi({" in wire_block, "compression replay should use the compression card path"
|
||||||
|
assert "_runJournalReplayParams()" in MESSAGES_SRC, "replay attachments should enter _wireSSE via EventSource"
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_journal_cursor_tracks_every_long_task_timeline_event():
|
||||||
|
"""Every user-visible long-task event needs cursor tracking for parity replay."""
|
||||||
|
cursor_loop_pos = MESSAGES_SRC.index("for(const _runJournalEventName of [")
|
||||||
|
cursor_loop = MESSAGES_SRC[cursor_loop_pos : MESSAGES_SRC.index("]", cursor_loop_pos)]
|
||||||
|
timeline_events = [
|
||||||
|
"token",
|
||||||
|
"interim_assistant",
|
||||||
|
"reasoning",
|
||||||
|
"tool",
|
||||||
|
"tool_complete",
|
||||||
|
"compressing",
|
||||||
|
"compressed",
|
||||||
|
"metering",
|
||||||
|
"done",
|
||||||
|
"apperror",
|
||||||
|
"cancel",
|
||||||
|
]
|
||||||
|
|
||||||
|
for event_name in timeline_events:
|
||||||
|
assert f"'{event_name}'" in cursor_loop, (
|
||||||
|
f"{event_name} must advance the replay cursor to avoid duplicate timeline replay"
|
||||||
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue