Skip to content

fix(rtmg/web): re-send the active loop band on reconnect#288

Open
seanhanca wants to merge 1 commit into
mainfrom
fix/resend-loop-band-on-reconnect
Open

fix(rtmg/web): re-send the active loop band on reconnect#288
seanhanca wants to merge 1 commit into
mainfrom
fix/resend-loop-band-on-reconnect

Conversation

@seanhanca

Copy link
Copy Markdown

Root cause (confirmed)

After an automatic reconnect (network blip), the active loop band is silently dropped server-side.

The WsReconnector success callback in demos/realtime_motion_graph_web/web/hooks/useStartSession.ts re-applies detected metadata, the LoRA catalog/cap/strengths, the network monitor, and timbre/structure refs (await restoreRefs(remote)), but never re-sends the loop band:

  • The loop band is not part of buildConfig (useStartSession.ts buildConfig, ~L100-136) — it carries prompts, LoRAs, fixture, source mode, client id, but no band.
  • The only code that calls remote.sendLoopBand(...) is the WaveformScrubBox sync effect (components/Performance/WaveformScrubBox.tsx ~L361-384), which depends on [bandState, bandLoopEnabled, hasPlayer, player]. On reconnect the AudioPlayer persists and loopBand/bandLoopEnabled are unchanged, so the effect does not re-run.

Result: the new RemoteBackend never learns the band. The worklet keeps looping the region locally while the new server session decodes against the full timeline → drift / a stale loop window at the seam.

sendLoopBand is a real SDK method (packages/demon-client/protocol.ts ~L1070), so the fix is wire-supported.

Fix

Mirror the established "re-apply session state on reconnect" pattern (restoreRefs):

  • Add restoreLoopBand(remote) next to restoreRefs. It reads the source-of-truth performance store (usePerformanceStore loopBand / bandLoopEnabled) and, when a band is active, re-sends it via remote.sendLoopBand(start, end).
  • The activity gate matches WaveformScrubBox exactly (band != null && bandLoopEnabled && end-start >= 0.05 && start >= 0) so we only re-send a band the listener is actually hearing loop.
  • Call it right after await restoreRefs(remote) in the WsReconnector success callback.

No new subsystem; ~30 lines including doc comment, following the same extracted-function shape as restoreRefs.

No regression

Extended tests/unit/wsReconnect.test.ts with a restoreLoopBand on reconnect block asserting:

  • the active loop band is re-sent with its exact region after a reconnect;
  • nothing is sent when no band is set;
  • nothing is sent for an armed-but-off (loop-disabled) band;
  • a degenerate sub-50ms region is ignored.

Verification

Run in demos/realtime_motion_graph_web/web/:

  • npx vitest run tests/unit/wsReconnect.test.ts11 passed (7 existing + 4 new).
  • npm run typecheck → clean (exit 0).
  • npm run build → compiled successfully (exit 0).

(The full tests/unit run shows 8 pre-existing failures in sliceEpoch.test.ts due to Math.f16round is not a function on this Node build — unrelated to this change; the touched test file passes.)

Made with Cursor

After an automatic reconnect (network blip), the active loop band was
silently dropped server-side. The reconnect success callback in
useStartSession re-applies detected metadata, the LoRA catalog/cap, the
network monitor, and timbre/structure refs via restoreRefs — but never
re-sent the loop band. The band is NOT part of buildConfig, and the only
other caller of sendLoopBand is WaveformScrubBox's sync effect, which
doesn't re-run on reconnect (the AudioPlayer persists and loopBand is
unchanged). So the fresh RemoteBackend never learned the band: the
worklet kept looping locally while the new server decoded against the
full timeline → drift / stale loop at the seam.

Mirror the established "re-apply session state on reconnect" pattern
(restoreRefs): add restoreLoopBand(remote), which reads the
source-of-truth performance store and, when a band is active (gate
matches WaveformScrubBox exactly), re-sends it via the existing
sendLoopBand SDK method. Call it right after restoreRefs in the
WsReconnector success callback.

Extends the reconnect test to assert sendLoopBand is invoked with the
active region after a reconnect, and is NOT called when no band is set,
when the band is armed-but-off, or when the region is sub-50ms.

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

1 participant