Unify the call/huddle session lifecycle across the clients (LC-613) #574
Loading…
Reference in a new issue
No description provided.
Delete branch "refactor/lc613-converge-rtc-clients"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Workstream 4 of LC-596: converge
call.jsandvoice.js"where practical". This does the session-lifecycle convergence (the ticket's headline) and leaves the media layer surface-specific by design.The duplication this removes
call.js(1:1, oneRTCPeerConnection) andvoice.js(huddle, mesh or SFU) each published their own real-time-session state, andtranscribe.jsreconciled the two surfaces. Two concrete things were duplicated:Two session-room globals.
__lcCallRoomand__lcVoiceRoomnever coexisted (you are in one real-time session at a time), had identical set-on-enter / null-on-exit lifecycles, and their only shared reader already coalesced them with||. They become one__lcSessionRoom, sotranscribe.js's resolution drops fromdata-lc-room || __lcCallRoom || __lcVoiceRoomtodata-lc-room || __lcSessionRoom. That reconciliation was the ticket's opening complaint.Two lifecycle event pairs.
lc:call-active/lc:voice-joined->lc:rtc-session-started;lc:call-ended/lc:voice-left->lc:rtc-session-ended.transcribe.js's four listeners collapse to two.The one real difference, kept via a flag not two names
A 1:1 call is sole-participant, so its end must finalize the shared transcript for everyone; a huddle end must only stop our capture, because the session continues for whoever is still on the line. Rather than keep two event names to encode that, both events carry
detail.surface('call' | 'voice') andtranscribe.jsbranches on it.huddle_ring.js's start listener guards onsurface === 'voice', so a DM call no longer reaches it - matching the oldlc:voice-joined-only trigger exactly.AC4: one fewer global (two room globals to one).
Deliberately NOT converged
Per the ticket's own "forcing one abstraction would be worse":
call.jsdrives oneRTCPeerConnection;voice.jsloops a mesh or delegates to the SFU.toggleMute/toggleCamera/toggleScreendiffer by exactly that single-PC vs per-peer-loop vs SFU boundary. Merging them would push mesh/SFU branching into the DM path for no benefit.watchBus) and control-click-dispatch machinery. These are textbook duplication and safe to extract, but doing so means a new shared-loaded JS file, and bundling that into an untested-client refactor adds blast radius for little value. Filed as LC-616 so the remaining intent is tracked, not dropped.Verification (no JS test harness)
node --check).just checkclean.🤖 Generated with Claude Code
https://claude.ai/code/session_01FhDVMViNHqUbwmfe2aXTv5
Workstream 4 of LC-596, the "converge where practical" cut. call.js (1:1, single peer connection) and voice.js (huddle, mesh or SFU) duplicated the real-time-session plumbing, and transcribe.js had to reconcile the two surfaces. This unifies the parts that are genuinely one thing and leaves the parts that are genuinely two. Converged: - The two session-room globals `__lcCallRoom` and `__lcVoiceRoom` become one `__lcSessionRoom`. They never coexisted (you are in one real-time session at a time), had identical set-on-enter / null-on-exit lifecycles, and their only shared reader (transcribe.js) already coalesced them with `||`. That reconciliation is now gone: `data-lc-room || __lcCallRoom || __lcVoiceRoom` is `data-lc-room || __lcSessionRoom`. - The two lifecycle event pairs become one: `lc:call-active` / `lc:voice-joined` -> `lc:rtc-session-started`, and `lc:call-ended` / `lc:voice-left` -> `lc:rtc-session-ended`. Both carry `detail.surface` ('call' | 'voice'). transcribe.js's four listeners collapse to two. The one behaviour that genuinely differs is preserved via `detail.surface`, not by two event names: a 1:1 call is sole-participant, so its end finalizes the shared transcript for everyone; a huddle end only stops the local capture, because the session continues for whoever is still on the line. transcribe.js branches on the surface to keep both. huddle_ring.js's start listener guards on `surface === 'voice'` so a DM call no longer reaches it, matching the old `lc:voice-joined`-only trigger exactly. Net globals: one fewer (two room globals to one), satisfying AC4. Deliberately NOT converged, per the map's verdicts and the ticket's own "forcing one abstraction would be worse": - The media operations. call.js drives one `RTCPeerConnection`; voice.js loops over a mesh or delegates to the SFU. `toggleMute`/`toggleCamera`/`toggleScreen` differ by exactly that single-PC vs per-peer-loop vs SFU boundary; merging them would push mesh/SFU branching into the DM path for nothing. - The bus-drain (`watchBus`) and control-bar click-dispatch machinery. These are textbook duplication (identical but for a few string tokens) and safe to extract into a shared module, but bundling a new shared-loaded file into untested client JS adds blast radius for little value. Left as a tracked mechanical follow-up. Verification: no JS test harness exists, so the DM-path contract is pinned by the LC-614 server tests (green: they assert the WS frames, which this does not change), every old global/event name is gone from live code (grep-confirmed; only comments reference them), all four files parse, and an adversarial review confirmed each behaviour-preservation claim - the surface-branch in the ended handler, the single started attach, the huddle-ring surface guard, the dedup global swap, and symmetric set/null on every path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhDVMViNHqUbwmfe2aXTv5