Skip to content

fix(stream): keep the on-air panel live through auto-reconnects - #823

Draft
anneoneone wants to merge 2 commits into
mainfrom
claude/cool-tesla-rf8cxu
Draft

anneoneone wants to merge 2 commits into
mainfrom
claude/cool-tesla-rf8cxu

Conversation

@anneoneone

@anneoneone anneoneone commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

A connection drop that the stream socket is auto-reconnecting no longer ends the show in the live panel. Before, the panel switched to "Stream Ended" and could leave the stream on air after the host clicked "Done". When the show really ends, the microphone is now released.

Issue: none filed. This is the follow-up listed under "Not in this PR" in #819.

Validation (in frontend/):

  • npm run lint: clean.
  • npm run typecheck: clean.
  • npm test -- --run: 24 files, 183 tests passed (2 new).
  • Both new tests fail against main's useStreamSocket.ts.
  • tsc and ESLint here don't cover .vue files, so I also compiled the app with vite build to check FlowOnAir.vue. It builds cleanly.
Implementation and review

This is an ordinary fix to main, not architecture-epic work, so the epic fields do not apply.

Epic: n/a
Executor: n/a
Lane: n/a
Reviewer: n/a
Leaf IDs: n/a

Bugs (frontend/src/admin/pages/flow/FlowOnAir.vue).

  1. A reconnecting drop ended the show. onDisconnected set streamEnded on every socket close, including drops that useStreamSocket was already auto-reconnecting. Since fix(stream): keep reconnect backoff going and reset it on success #819 those reconnects usually succeed. When one did:
    • Audio kept flowing through the new socket, but the page stayed on "Stream Ended".
    • The end-time auto-stop was skipped, because updateEndTimeCountdown only stops when !streamEnded.
    • "Done" unmounted the page without stopStream() / stopCapture(), because onUnmounted guards both with !streamEnded. The stream could stay on air after the host had left.
  2. A final disconnect left the mic open. useAudioCapture is a singleton with no unmount hook, onUnmounted skips stopCapture() once streamEnded is set, and flow.reset() doesn't touch it. After the socket really closed, the microphone stayed captured, even after "Done".

Fix.

  • useStreamSocket: onDisconnected now receives willReconnect. It is true when an automatic reconnect is already scheduled, and false for a final close: retries used up, an error: frame, a clean close, or stopStream() / cleanup(). The argument is optional to read, so the other consumers (StreamPage.vue, RecordingPage.vue, LiveSetupTest.vue) behave exactly as before.
  • FlowOnAir.vue: closes with willReconnect are ignored. The streaming view, elapsed timer, metrics and end-time stop keep running. onLive fires again when the stream is back. If the retries run out, the final close ends the show as before, and now also calls audioCapture.stopCapture().
  • FlowOnAir.vue status bar: while the socket is connecting or connected (not yet live) during a live show, it shows a yellow "Reconnecting…" instead of "LIVE". The producer is off air at that point. The same label also appears during a manual reconnect.

Tests (frontend/tests/useStreamSocket.test.ts).

  • tells onDisconnected whether an auto-reconnect is scheduled: true for the drop and for each failed attempt that still has one queued, false once the last attempt fails.
  • reports error-frame closes and stopStream() as final disconnects

There is no test that mounts FlowOnAir.vue. The repo has no component-test setup (@vue/test-utils isn't installed), and mounting the page would mean mocking the router, the host flow, audio capture, the APIs and four child components. The page changes are a guard on the new argument plus one stopCapture() call in the existing final-disconnect branch.

GitNexus impact. Not run: the GitNexus MCP server failed to connect in this session and there is no local index. By text search, onDisconnected is passed to useStreamSocket by FlowOnAir.vue, StreamPage.vue, RecordingPage.vue, LiveSetupTest.vue and FlowStreaming.vue. No route uses FlowStreaming.vue. Only FlowOnAir.vue reads the new argument.

Not in this PR.

  • After a final disconnect, the "Stream Ended" screen still says the show "has finished", even when the connection was lost rather than stopped. This behaviour is unchanged.
  • FlowStreaming.vue has the same onDisconnected pattern but is not routed, so I left it alone.

🤖 Generated with Claude Code

https://claude.ai/code/session_01N2RWiX6eBm5kyYsSc8Vghq

FlowOnAir ended the show on every socket close, including drops that
useStreamSocket was already auto-reconnecting. After a successful
reconnect audio kept flowing but the page stayed on "Stream Ended":
the end-time auto-stop was skipped, and "Done" unmounted the page
without stopStream()/stopCapture() (both guarded by !streamEnded), so
the stream could stay on air after the host left.

onDisconnected now gets `willReconnect`, true when an automatic
reconnect is already scheduled. FlowOnAir ignores those closes and only
ends the show on a final disconnect (gave up, error frame, clean close).
While the socket is reconnecting, the status bar shows "Reconnecting…"
instead of LIVE.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N2RWiX6eBm5kyYsSc8Vghq
@vercel

vercel Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
live-moafunk-de Ready Ready Preview Oct 19, 2026 7:50am UTC

A final disconnect set streamEnded but left audio capture running. The
capture is a singleton with no unmount hook, onUnmounted skips
stopCapture() once streamEnded is set, and flow.reset() does not touch
it, so the microphone stayed open even after "Done".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N2RWiX6eBm5kyYsSc8Vghq

This branch was successfully deployed

1 active deployment
Preview — ad349d04 Deployed Oct 19, 2026 by vercel[bot]
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