Commit Graph

9 Commits

Author SHA1 Message Date
d5c00897e8 test(trunk): reflex-on-trunk verification (T7) + PSTN sim e2e (T8) — slice-5
All checks were successful
CI / fmt (pull_request) Successful in 54s
CI / clippy (pull_request) Successful in 1m58s
CI / test (1.85) (pull_request) Successful in 4m57s
CI / test (stable) (pull_request) Successful in 7m23s
CI / deny (pull_request) Successful in 1m26s
T7: proves slice-4's Reflex<TapAudioPipe> + LocalVadReflex decorates the
trunk leg's TapAudioPipe identically; barge-in fires on PSTN caller speech
through the same state machine as WebRTC caller speech.

T8: MockRealtimeBrain + BrainShim drives a synthetic PSTN caller through the
FOB reflex loop end-to-end: loud PCM -> local VAD trips -> barge kills ->
brain reply -> un-mute -> idle timeout (caller hangup) closes the session.

Dev-dependencies added to rutster-trunk/Cargo.toml so the integration tests
reach MockRealtimeBrain, futures-util, and tokio-tungstenite without pulling
FOB source into the trunk crate.

Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-05 04:02:56 -04:00
b76f0dcc4b fix(trunk): trait imports + remove unused (AudioSink/AudioSource) in session tests (T4 cleanup)
Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-05 03:36:20 -04:00
a6abe24995 fix(trunk): trait imports (AudioSink, AudioSource) for trunk_driver + clippy let_and_return on test helper (slice-5 T4)
Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-05 03:33:12 -04:00
ed6686e50a feat(trunk): TrunkSession + trunk_driver::drive -- FOB-side trunk-leg tick (slice-5 T4)
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>
2026-07-05 03:31:22 -04:00
02385a410f fmt(trunk): rustfmt reflow on twilio_media_streams (T3)
Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-05 03:29:20 -04:00
e534d23ad5 feat(trunk): TwilioMediaStreamsServer -- axum WSS handler (slice-5 T3)
Accepts Twilio's inbound WSS connections; parses the JSON envelope
(connected/start/media/stop per Twilio's documented protocol); decodes
base64 µ-law via G711Codec; ferries decoded PCM frames to a per-call
inbound mpsc. Concurrently drains the outbound mpsc + sends back JSON
media frames. Same tokio/std-thread split as slice-2's TapEngine: tokio
owns IO; the std thread owns the 20ms tick via trunk_driver::drive (T4).

ADR-0009 honored: TwilioCredentials never reach this module -- only
per-call CallSid (operational log correlation only) + tap_url (operator
configured brain WS URL) flow through the RegisterTrunkInboundChannel.

Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-05 03:28:50 -04:00
ed8db38199 feat(trunk): TwilioMediaStreamsServer skeleton (slice-5 T3 placeholder commit)
Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-05 03:28:01 -04:00
8fe7ce7ee5 feat(trunk): G711Codec — µ-law encode/decode + 8kHz↔24kHz linear-interpolated resampling (slice-5 T1)
In-core ~30-line table-driven codec (no dep). The ITU-T G.711 µ-law
companding formula is a piece of telephony history worth teaching (AGENTS.md
learner-facing comment mandate). 3× linear upsample on decode; 3× decimation
downsample on encode. The resampler artifacts are below the barge-in trigger
threshold (LocalVadReflex only needs RMS energy); rubato lands in a post-
spearhead refinement if a downstream consumer needs better (spec §6.6).

Task T1 of slice-5 — T3 (TwilioMediaStreamsServer) consumes this codec.

Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-05 03:27:00 -04:00
opencode controller
3c3197e57a refactor(workspace): rutster-signaling-sip → rutster-trunk; edition 2024; repo URL = git.adlee.work
ADR-0007 lands in code:
- Rename crates/rutster-signaling-sip → crates/rutster-trunk. The crate was
  a stub anyway (lib.rs with a doc-comment + crate_compiles() test); the
  rename locks the new boundary shape — the future rented-transport
  ingress (CPaaS media-leg fork / out-of-tree SBC glue, **no SIP stack**)
  lands here at spearhead step 5.
- rutster-trunk/src/lib.rs doc-comment now describes the ADR-0007 split:
  CPaaS media-leg adapter as primary, out-of-tree SBC for on-prem
  graduation. Cross-refs ADR-0007 + ADR-0008 (FOB vs. green zone: trunk
  is green zone; SIP lives outside the trust boundary).
- rutster-spend/src/lib.rs doc-comment updated to quote ADR-0007's
  'rutster mediates both the provider call-control API and the brain tap'
  framing — the spend gate sits in that boundary, structurally preventing
  a runaway brain from exceeding spend/pacing. Pulling spend out into a
  service re-introduces the 3-vendor structural hole.

Cargo bumps:
- edition = '2024' (slice-1's pinned Rust 1.85 + edition-2024 floor
  already requires this; rutster-media's let-else pattern in
  OpusDecoder::decode · Slice-2's let-else in AcceptOffer's Uuid
  parse_str · all rely on edition 2024's stabilized let-chains / let-else).
- repository = 'https://git.adlee.work/alee/rutster' (the self-hosted
  Gitea remote — matches 'git remote -v' origin). The github.com/anomalyco
  URL was stale from the pre-pivot copy.
- Cargo.lock regenerated by cargo for the rename.

No behavioral code changes — the trunk crate's body is still the stub
crate_compiles() test. FOB membership (per ADR-0008) is unchanged:
rutster-trunk will be FOB-internal at step 5 because it's the media-leg
ingress (hot path); spend-spend stays FOB because spend is
security-constitutive.
2026-06-29 20:26:37 -04:00