2.9 KiB
Sidebar idle / terminal handoff evidence
Failure and scope
The independent /api/sessions response can report idle before the chat SSE's
terminal frame reaches the page. Three sidebar paths were retiring chat state:
idle reconciliation, INFLIGHT purging, and optimistic-row retirement. The idle
path clears the Worklog and active stream ID; the subsequent done is rejected
as stale, so no settled Anchor scene is persisted.
A real-HTTP barrier reproduces this on both published PR head
75c8ff8d2a991f22389e3e4263ee03fb8d8f67a0 and frozen upstream
b09fded7b8c98b549a49aaaece3721015337d307. Applying only the sessions.js
product fix to the frozen upstream also passes the identical oracle: this is a
shared lifecycle defect, not evidence that artifact replay introduced it. The
historical hosted run lacks this instrumentation, so its exact interleaving
cannot be proved retrospectively; its empty-surface/no-persistence signature
matches the controlled reproduction.
Reproduction
LIFECYCLE_SCENARIO=normal LIFECYCLE_TEST_BITE=settle-worklog-frame-proof \
.venv/bin/python tests/browser_conversation_lifecycle.py
./scripts/test.sh tests/test_sidebar_terminal_handoff.py -q
The existing frame proof now uses a test-only server wrapper to hold the actual
done HTTP write. The real worker finishes; the real browser receives an idle
sidebar response through renderSessionList() while its native EventSource
remains open. The test verifies pane, INFLIGHT and registry ownership, then
releases done and retains all existing painted-frame, final-answer, semantic
activity and hard-reload assertions. The fixture always releases/cleans up and
has a bounded wait. Normal production server startup is unchanged.
No renderer, terminal payload or persistence response is fabricated. Runtime events come from the existing deterministic localhost Gateway fixture, not a live model/provider. All state and files are disposable. These are browser viewport checks, not physical-device acceptance.
Before / after
Each screenshot is taken after the idle sidebar response, before the terminal
HTTP frame is released. The exact published source fails at both widths. The
patched source preserves three live activity rows and then passes completion
and hard reload. results.json records the predicate results without machine
paths or credentials.
| Viewport | Before | After |
|---|---|---|
| 1440 x 900 | Empty surface | Owned handoff retained |
| 390 x 844 | Empty surface | Owned handoff retained |
The fix only defers these sidebar cleanups for the current pane's exact OPEN chat transport. Missing, CONNECTING, CLOSED and mismatched sources still take the existing stale recovery path. It does not add a separate recovery timer or claim to redesign transport liveness; prolonged network failure remains the responsibility of the existing stream error/recovery machinery.