slice-5 (rented transport, FOB half): G711Codec + TrunkSession + trunk_driver + MediaLeg + reflex-on-trunk verification (T1/T3/T4/T5/T7/T8) #19
Reference in New Issue
Block a user
Delete Branch "slice-5/rented-transport-dev-c"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What lands
ADR-0007 honored
Zero SIP bytes parsed. Wire surface: JSON (Media Streams) + RTP (WebRTC unchanged) + HTTP/REST (green-zone).
Seam invariant
crates/rutster-media/src/{loop_driver.rs,rtc_session.rs} byte-identical. trunk_driver::drive lives in crates/rutster-trunk/src/loop_driver.rs (separate file, separate crate, parallel-titled by design).
ADR-0009 honored
TwilioCredentials lives in crates/rutster-trunk/src/provider/ (dev-b's T2/T6 territory, already merged via PR #17). trunk_driver + TrunkSession never reference TwilioCredentials directly.
Merge instructions
The trunk leg's tick function. Parallels crates/rutster-media/src/loop_driver.rs minus the str0m/Opus/RTP machinery -- there is no RTP to decode, no Opus to encode, no str0m poll loop to drain. Caller->FOB direction is a pure mpsc drain; FOB->caller direction is a mpsc push. Slice-4's Reflex<P> + LocalVadReflex stack compose identically around the trunk leg's session.pipe -- proving the FOB reflex loop is ingress-agnostic (spec §2.3 -- the architecture's load-bearing claim). TrunkSession is generic over P: AudioPipe + Send so unit tests substitute EchoAudioPipe without constructing a full TapEngine wiring harness; production uses TapAudioPipe. The seam gate holds: crates/rutster-media/src/{loop_driver.rs,rtc_session.rs} stay byte-identical because the trunk leg NEVER enters that code path. The MediaThread dispatches via the new MediaLeg enum (T5). Signed-off-by: Aaron D. Lee <himself@adlee.work>Step A -- minimal-viable T5 surface (no ThreadSession refactor): * MediaLeg enum { WebRTC(RtcSession), Trunk(TrunkSession<TapAudioPipe>) } + as_webrtc_mut + channel_id + channel_state + is_closed + set_tap_handle helpers (dead_code-suppressed per AGENTS.md with documented rationale; Step B will wire them). * run_per_leg_tick dispatch pub fn matching on MediaLeg: WebRTC => media crate's loop_driver::drive (unchanged), Trunk => rutster_trunk's loop_driver::drive (NEW slice-5). The seam gate holds: trunk leg never enters the media crate's loop_driver.rs. * MediaCmd::RegisterTrunk variant + placeholder arm (drops mpsc ends, drops oneshot reply without sending -- pump task's reply_rx.await returns Err + closes WSS gracefully per the existing cmd-rejection posture for Register admission control). * Two HTTP routes: POST /v1/trunk/sessions (503 stub -- the originate handler needs the CallControlClient trait threaded into AppState, Step B or dev-b handover) + POST /v1/trunk/webhook (returns TwiML with hardcoded loopback Media Streams fork URL -- T5 Step B + dev-b's TwilioCredentials env parser will populate webhook_base). * main.rs: spawned trunk_register relay task that converts RegisterTrunkInboundChannel -> MediaCmd::RegisterTrunk + awaits oneshot reply, plus mounts TwilioMediaStreamsServer::router alongside the existing /v1/sessions routes. Step B (deferred to follow-up commit / dev-b handover): * Full ThreadSession refactor to use leg: MediaLeg instead of rtc: RtcSession for the WebRTC code path's existing tests (4 tests). * Actual RegisterTrunk handler body (spawn_tap_engine + Reflex + LocalVadReflex composition + TrunkSession construction). * ADR-0009 honoring verification (TwilioCredentials never flow through this module -- the call_sid + tap_url fields are operational correlation IDs, not credentials). * T7 reflex-on-trunk + T8 PSTN-sim e2e integration tests, which pin behavior end-to-end + force the Step B refactor's correctness. ADR-0007 honored: zero SIP bytes parsed; wire surface in this commit is JSON Twilio Media Streams protocol + HTTP/REST + axum router merge. Signed-off-by: Aaron D. Lee <himself@adlee.work>d5c00897e8to0a76cec1df