Files
hermes-webui/docs/pr-media/6469-sidebar-handoff

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.