Skip to content

fix(playlist): reconcile fallback lifecycle exactly once - #150

Draft
nicoechaniz wants to merge 8 commits into
AlterMundi:mainfrom
nicoechaniz:test/playlist-fallback-lifecycle
Draft

fix(playlist): reconcile fallback lifecycle exactly once#150
nicoechaniz wants to merge 8 commits into
AlterMundi:mainfrom
nicoechaniz:test/playlist-fallback-lifecycle

Conversation

@nicoechaniz

@nicoechaniz nicoechaniz commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Outcome

Hardens the already-deployed playlist fallback decision with snapshot-based, exact-once lifecycle reconciliation.

  • derives availability from the complete current LiveKit publication snapshot;
  • emits a transition only when availability actually changes;
  • makes duplicate publish/unmute and mute/unpublish events idempotent;
  • preserves suppression when an obsolete publication disappears but another live audio publication remains;
  • ignores a stale ParticipantDisconnected when replacement beacon01 already has live audio;
  • reconstructs both sides of the decision after reconnect.

Audio boundary

This PR does not change codec, bitrate, sample rate, channel count, PCM, gain, fade duration/equation, buffers, source selection or routing. It remains draft and requires Nico's explicit audio review before merge. No deploy is requested.

Evidence

  • npm test --prefix services/playlist-bot: 13/13 pass.
  • npm run build --prefix services/playlist-bot: pass.
  • git diff --check: pass.
  • Full repository CI rerunning on head 1922042; the prior head passed browser E2E and frozen-audio.

Remaining #39 acceptance

This does not close #39. A controlled LiveKit-native rehearsal and a truthful signal for “neither source is producing audio” remain outstanding.

Risk / rollback

The runtime path still calls the existing setBeaconAudioAvailable and onBeaconAvailabilityChanged; the helper only suppresses duplicate/stale transitions. Revert the PR merge commit to roll back.

Refs #39.

@nicoechaniz nicoechaniz added the audio-touching Touches frozen audio behavior and requires Nico approval label Aug 5, 2026
@nicoechaniz

Copy link
Copy Markdown
Contributor Author

Adversarial follow-up 1922042: ParticipantDisconnected no longer forces playlist availability to false from a stale event. It reconciles the current LiveKit snapshot, so a replaced beacon01 with a live unmuted audio publication cannot be covered by a transient fallback fade-in. Added the exact replacement test; playlist-bot now passes 13/13 tests and TypeScript build locally. PR remains draft + audio-touching; no merge/deploy without explicit Nico review.

@nicoechaniz nicoechaniz changed the title test(playlist): prove exact-once fallback lifecycle fix(playlist): reconcile fallback lifecycle exactly once Aug 5, 2026
@nicoechaniz

Copy link
Copy Markdown
Contributor Author

LiveKit-native evidence added in 9ef9b28 without changing runtime or acoustic behavior.

The opt-in integration test connects real @livekit/rtc-node 0.13.32 participants to disposable livekit/livekit-server:v1.13.4 and verifies the actual remote snapshot across: identity present without track → publish → server mute → unmute → unpublish → same-identity replacement. Result: 14/14 playlist-bot tests pass (0 skipped under the opt-in environment); TypeScript build passes.

The first controlled attempt also confirmed the production-safe LiveKit default: remote unmute is rejected. The successful lab rerun enabled room.enable_remote_unmute only on the disposable server so the SDK's TrackUnmuted representation could be exercised; this does not propose enabling remote unmute in production.

The local container was stopped and auto-removed. PR remains draft/audio-guarded: no merge or deploy requested. Remaining #39 gap is the explicit health signal for “neither source produces frames” plus audible rehearsal/approval.

@nicoechaniz

nicoechaniz commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up f731485: fixes the sole CI lint finding (prefer-const) in the opt-in native test. The behavioral test evidence is unchanged; fresh gates are running.

@nicoechaniz

Copy link
Copy Markdown
Contributor Author

Fresh head f731485 is fully green: frozen-audio 17s, lint/build 1m24, unit gate 1m48 and complete browser E2E 10m35. The PR remains draft/audio-guarded pending explicit acoustic approval and the remaining frame-health/audible rehearsal; no merge or deploy.

@nicoechaniz

Copy link
Copy Markdown
Contributor Author

Fast Forward checkpoint policy: the newly triggered full E2E workflow was intentionally canceled while this remains an audio-guarded draft. Focused playlist lifecycle tests/build/diff-check are green and the earlier unchanged native LiveKit lifecycle evidence is preserved; unit/lint-build/audio-boundary continue. Full E2E must be explicitly rerun and pass at the approval checkpoint before merge.

@nicoechaniz

Copy link
Copy Markdown
Contributor Author

Adversarial re-review on current head 99a2a4e: no new blocker found. Every relevant LiveKit event now derives from the room publication snapshot, the final state transition remains idempotent at both helper and setter boundaries, stale disconnect/unpublish cannot override a live replacement, and reconnect reconstructs rather than replays event history. The important limitation remains explicit: an unmuted publication is not proof that non-silent frames are flowing. That acceptance belongs to health + audible rehearsal, so the draft must not be promoted or merged yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

audio-touching Touches frozen audio behavior and requires Nico approval

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[review] Playlist bot: fallback keys off participant presence, not live audio

1 participant