slice-4 (dev-a): Reflex<P> + LocalVadReflex<P> (Task 2 + 2b) #8

Merged
alee merged 4 commits from slice-4-dev-a-reflex into main 2026-07-03 13:50:42 +00:00

4 Commits

Author SHA1 Message Date
58410675ea feat(media): LocalVadReflex — primary barge-in, zero brain round-trip
All checks were successful
CI / fmt (pull_request) Successful in 1m50s
CI / clippy (pull_request) Successful in 2m58s
CI / test (1.85) (pull_request) Successful in 4m17s
CI / test (stable) (pull_request) Successful in 6m41s
CI / deny (pull_request) Successful in 1m32s
The wedge-#1 proof. RMS/energy VAD in on_pcm_frame on the dedicated
thread, in the 20ms loop — caller speech trips SpeechStarted locally,
without any brain round-trip. Debounce (N=3 frames = 60ms) filters
transients. Composes as LocalVadReflex<Reflex<TapAudioPipe>>; both
the local VAD + the brain's advisory feed the same advisory_tx mpsc
(spec §6.1). Revised after adversarial review (initial brainstorming
was advisory-only, which contradicts ARCHITECTURE.md:79-81; wedge-#1
mandate at README:98-100). Spec §3.4 (VAD trigger) + §6.4 (wrapper-
not-inline) load-bearing.

Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-02 23:57:26 -04:00
10c0547431 refactor(media): fix stale Task-1 forward-ref comment + strengthen Reflex test 6
Fix 1: removes a Task-1 forward-reference comment in reflex.rs that
contradicted itself once Task 2 added the imports it claimed were
"intentionally absent."

Fix 2: MockPipe::on_pcm_frame was a no-op, so Test 6 could not
distinguish "delegated to inner" from "dropped on the floor." Add
last_inbound_frame: Option<PcmFrame> capture + assert delegation so
the reflex's inbound-passthrough guarantee is verified, not assumed.

Addresses the two Important findings from the Task 2 review.

Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-02 23:50:29 -04:00
488b6ab423 feat(media): Reflex<P> barge-in state machine (slice-4 §3.2, §3.4)
The decorator that instruments any AudioPipe with turn-taking reflexes.
SpeechStarted -> muted=true + barge_in_flush + epoch bump. First fresh
audio_out -> un-mute. SpeechStopped is observational (no toggle). Inbound
audio (on_pcm_frame) is NEVER gated — the brain still hears the caller
during barge; the FOB only kills its OWN playout. loop_driver +
rtc_session byte-identical (seam holds).

barge_epoch is load-bearing THIS slice (spec §6.1, commit 86b7460) — the
local VAD (Task 2b) fires ~0 ms after caller speech; the brain's slower
ASR advisory fires ~300 ms later on the SAME barge; the epoch
disambiguates a fresh re-barge from the late confirmation.

Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-02 23:46:09 -04:00
f5a5a6dd3c chore(media): add tokio dep for Task 2 Reflex<P> consumer (slice-4 §3.2)
Reflex<P> owns a tokio::sync::mpsc::Receiver<AdvisoryEvent> (the advisory
channel the TapEngine feeds from its tokio task), so tokio must be a
runtime dep of rutster-media at lib-compile time, not dev-only. The
crate itself makes no tokio runtime calls — the dedicated media thread
drives Reflex, not an executor (ARCHITECTURE.md; slice-1 spec §3.4).

Signed-off-by: Aaron D. Lee <himself@adlee.work>
2026-07-02 23:45:56 -04:00