Reference solution
The findings below are ordered by priority.
Findings
1. An in-progress join can register after shutdown
Location: src/live-video-manager.ts:10-17,19-28
The stopping check occurs before acceptJoin, but the pending handshake is not registered or abortable. Shutdown snapshots existing sessions, resolves, and then the await can complete and add fresh resources to a newly created session.
Track a provisional join and controller before the first await, include it in shutdown, and recheck ownership before registration while ensuring rejected registration tears the resources down.
2. Concurrent removal runs teardown more than once
Location: src/live-video-session.ts:20-31
The participant stays in the map until all awaits finish. A disconnect callback, moderator action, and session close can each retrieve the same resources and concurrently stop or close them multiple times.
Atomically remove or replace the entry with one shared teardown promise before the first await, and have every trigger join that promise.
3. Closing the peer precedes recorder finalization
Location: src/live-video-session.ts:26-28
The peer is closed before recorder.stop finishes the final media segment. Closing its source first can truncate the recording and violates the explicit ordering guarantee.
Detach callbacks, await recorder finalization, stop local tracks, and only then close the peer.
4. One cleanup rejection skips every later resource
Location: src/live-video-session.ts:26-30
The sequential awaits have no failure aggregation. If peer close or recorder stop rejects, tracks remain active, signaling stays subscribed, and the participant remains registered.
Attempt each ordered stage with per-stage error capture and a final ownership release, then report an aggregate after all resources settle.
5. Disconnect listeners remain attached
Location: src/live-video-session.ts:14-18
onDisconnected returns the required unsubscribe function, but it is discarded. The callback retains the session and can fire during or after intentional teardown, adding races and preventing timely collection.
Store the unsubscribe handle in participant state and call it when teardown is claimed, before asynchronous cleanup.
6. Session shutdown fails fast instead of waiting for every participant
Location: src/live-video-session.ts:34-37, src/live-video-manager.ts:21-28
Both levels use Promise.all. A single participant or session rejection makes closing reject immediately, so manager shutdown can return while other cleanups are still running and does not report their later failures together.
Use all-settled aggregation at both levels and surface a combined result only after every teardown settles.
7. The deadline does not cancel or join timed-out work
Location: src/live-video-manager.ts:19-28
The timer merely wins Promise.race. It supplies no abort signal to handshakes or resource methods and shutdown resolves while they retain sockets, tracks, and subscriptions in the background.
At the deadline abort all outstanding controllers, then await their cleanup settlement before resolving and report which resources exceeded graceful time.
Reasonable non-findings
- Setting the stopping flag before beginning the shutdown snapshot is correct.
- Keeping sessions in a map is appropriate when their participant lifetimes are coordinated.
- Individual media APIs may be asynchronous; the contract intentionally requires joining them rather than assuming close is immediate.