AppState now holds cmd_tx: Sender<MediaCmd> instead of DashMap<...>.
create_session/accept_offer/close route via the command channel
(cold-path only). main.rs spawns the MediaThread + shuts it down on
graceful exit.
Also update api_integration.rs to wire a real MediaThread (AppState::new
now takes the sender), and remove the function-call hangup dispatch test
from realtime_integration.rs: that path relied on the removed
drive_all_sessions poll-task architecture.
Signed-off-by: Aaron D. Lee <himself@adlee.work>
ARCHITECTURE.md mandate ("never the shared tokio pool") finally landed.
One std::thread owns all RtcSessions exclusively; axum routes via command
channel (Register/AcceptOffer/Delete/Shutdown). The Reflex<TapAudioPipe>
wrapper is wired here on Connected via RtcSession::set_pipe. loop_driver +
rtc_session untouched (seam holds).
Signed-off-by: Aaron D. Lee <himself@adlee.work>
This integration test proves wedge #1: loud synthetic caller audio
(samples = 1000, well above VAD_RMS_THRESHOLD = 500.0) sent through
LocalVadReflex<Reflex<TapAudioPipe>> trips the local VAD and kills
playout within one tick, without any brain advisory.
Cites README:98-100 and ARCHITECTURE.md:79-81 ("local reflexes that
don't need the brain"); see slice-4 spec §3.4, §6.1, and §7
done-criteria #8.
Signed-off-by: Aaron D. Lee <himself@adlee.work>