Skip to content

fix: make server truth end stale web streams - #973

Merged
SamSaffron merged 2 commits into
SamSaffron:mainfrom
sam-saffron-jarvis:feat/stream-truth-reconciliation
Jul 28, 2026
Merged

fix: make server truth end stale web streams#973
SamSaffron merged 2 commits into
SamSaffron:mainfrom
sam-saffron-jarvis:feat/stream-truth-reconciliation

Conversation

@sam-saffron-jarvis

@sam-saffron-jarvis sam-saffron-jarvis commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

What

Make stalled response recovery converge on server truth without allowing an old idle sample to cancel a newer send:

  • reconnect-loop state reconciliation failures are caught and treated as unknown, so replay keeps retrying
  • a rejected resume detaches only the matching failed transport, then performs selected-session reconciliation
  • sidebar status detecting “server idle, client still owns a server-issued response ID” confirms through /state
  • idle /state may retire a transport only when its controller, generation, session ID, and non-empty response ID are unchanged across the state round trip
  • unacknowledged POST /v1/responses ownership (currentStreamResponseId === '') is never retired by idle polling
  • normal state polling remains suppressed while a healthy transport is attached; no extra active-run poll loop

Why

Session 3137 exposed the failure sharply:

  • the final response was persisted at 18:40:12 AEST
  • the client’s last /events connection had ended at 18:28:24
  • the browser still claimed the run was active
  • Stop at 18:58:29 received 404 because the response had already been done for 18m 16s
  • reload showed the correct durable transcript immediately

The likely root failure was a rejected reconciliation killing the nominally infinite reconnect loop. The fix makes that loop exception-safe and adds an independent, ownership-safe server-truth path.

Race invariants

  1. A stable stale transport with a server-issued response ID can be retired when /state confirms idle.
  2. An idle response sampled before a newer local send cannot abort that send.
  3. Sidebar idle status cannot abort a response POST before the server has issued its response ID.
  4. A transient reconciliation exception cannot terminate heartbeat recovery.

Verification

  • node internal/serveui/static/app_sessions_test.js
  • node internal/serveui/static/app_stream_test.js
  • go test ./internal/serveui/...
  • go build .
  • all three differential race probes from the first Opus review now pass:
    • newer run survives stale idle state
    • in-flight response POST survives sidebar idle status
    • no perpetual 1.2-second /state polling loop

@SamSaffron
SamSaffron merged commit edbdd49 into SamSaffron:main Jul 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants