zebra-report/test
Russell Ballestrini caa0548113
zebra-spaces: fresh-stream wrap on mic ontrack + mute-gate mesh swap (kill fedora-chrome silence)
Fox 2026-06-07 telemetry on blanka-chrome ruled out the sink-routing
hypothesis from 467146a:

  audio via AudioContext 085b target=0.5s ctxState=running sink=default
  mesh stream swapped into worklet for 085b
  ...
  aud.recv pkt=14622 lost=1 bytes=9117575 jitter=0.0100 level=0.000 jbuf=? lp=0.1s

50 pps stereo Opus arriving on the SFU sub PC, sink routed to the
picked device, but jbuf=? (jitterBufferEmittedCount=0) + level=0 — the
native decoder consumes nothing because no MediaStreamSource is bound
to the SFU receiver's track. fxhp-phone same room same window decodes
fine (jbuf=0.49s level=0.001), so the SFU is healthy and the defect is
per-client wiring.

Two coupled defects:

(A) tracks=0 race in handleRemoteSfuTrack mic branch. Chrome can fire
ontrack with ev.streams[0] still empty at handler-time (the live track
arrives a microtask later, or MSID-supplant merges new+dead tracks).
Caching ev.streams[0] then handing it to attachSfuTrack drops the
listener into the silent <audio> fallback — telemetry: "sfu attach
b1a7 fresh=1 tracks=0" + "meter for b1a7: MediaStream has no audio
track". Screen/camera/game already wrap ev.track in a fresh
MediaStream; mic now does the same. Single track, guaranteed live,
every time.

(B) Mesh ontrack unconditionally swaps the worklet's source to the
mesh stream — even when the mesh track is still muted (no RTP). The
SFU receiver becomes orphaned (no decoder), and if the mesh track
never unmutes the listener hears silence with no automatic SFU
restore. Gate the swap on track-not-muted: swap immediately if mesh
is already flowing, otherwise wait for 'unmute'. Add a mute-watchdog
that swaps back to the cached SFU stream after MESH_MUTE_WINDOW_MS
of mesh silence — same shape as the existing
connectionState=='failed' restore path, but driven by track-level
mute instead of PC-level failure.

Hard refresh + leave/enter worked because the fresh negotiation
delivered the mesh track already unmuted, so the eager swap landed
on a live source. Now the same swap waits for live source instead of
hoping for one.

Tests:
  - handleRemoteSfuTrack: ev.streams[0] empty but ev.track live →
    fresh MediaStream wrap rescues the attach (chain built, audible
    path verified)

Ticket 0001 updated with the telemetry comparison and the new
diagnosis. Mesh-mute-watchdog has no dedicated test yet (the mesh
ontrack lives in connectToPeer which the listener test harness
doesn't extract) — pin in multi-peer-mesh.test.js next pass.
2026-06-07 11:33:57 -04:00
..
functional.c phase 1: unfirehose reconstruction from session JSONL ingest 2026-05-27 13:51:14 -04:00
integration.c phase 1: unfirehose reconstruction from session JSONL ingest 2026-05-27 13:51:14 -04:00
jitter-buffer-worklet.test.js zebra-spaces: clean shrink-retarget (drop to target, skip-couple video) 2026-06-06 14:55:03 -04:00
listener-audio-attach.test.js zebra-spaces: fresh-stream wrap on mic ontrack + mute-gate mesh swap (kill fedora-chrome silence) 2026-06-07 11:33:57 -04:00
mod-action-serializer.test.js test: pin the mod-action serializer race fix 2026-06-04 10:46:55 -04:00
multi-peer-mesh.test.js zebra-spaces: rebuild <video> on MSID supplant — autoplay grant resets 2026-06-03 16:08:01 -04:00
self-listener-fsm.test.js zebra-spaces: formalize self-listener as FSM — pure spec + observer-driven side effects + 12 unit tests 2026-06-04 13:10:29 -04:00
sendbye-fsm.test.js zebra-spaces: send 'bye' on pagehide for listeners even on bfcache (kill closed-firefox roster zombies) 2026-06-06 14:45:39 -04:00
test.h phase 1: unfirehose reconstruction from session JSONL ingest 2026-05-27 13:51:14 -04:00
unit.c phase 1: unfirehose reconstruction from session JSONL ingest 2026-05-27 13:51:14 -04:00
video-track-removal.test.js zebra-spaces: 120s mute window for screen + game (static content), 15s stays for camera 2026-06-03 10:33:28 -04:00
web-protocol.test.js test: node web protocol suite (no devices) + exponential retransmit backoff 2026-05-28 12:44:20 -04:00
zebra-fsm.test.js test/zebra-fsm: MuteFSM transition tests — 14 new cases (102 total) 2026-06-04 14:14:52 -04:00
zebra-spaces.test.js zebra-spaces: JS↔Go protocol parity + crypto tests 2026-05-31 11:06:59 -04:00