Remote-Control Transport Swap — Quality & Testing Report
Date: 2026-07-02
Reviewer audience: Fable 5
Scope: the transport-swap prep slice of the remote-control build — wiring the
proven control layer (input_event / input_arbiter) into the live phone
session (riff/live/session.py) in SHADOW, plus a hardening pass on the §1.1
wire gate (control_event.py). Design of record: docs/remote-control-design-v0.md
§1.3, §7 Step 2.
1. What this slice claims — and what it deliberately does not
Claims (proven below):
- The live phone session now maintains a real decision epoch (the thing the arbiter arbitrates on, I8) — which the live path previously lacked entirely.
- Voice and DTMF inputs are stamped with that epoch, and at the one
committing decision point the
InputArbiteris consulted in shadow and agrees with the legacylocked_choiceresolver on the real flow. - With the feature flag off (production default) the change is inert: the arbiter is not even imported or constructed, and routing is byte-identical.
Explicitly NOT claimed (fenced behind the operator + a live call):
- The A2 tap-vs-speaking race. Voice is stamped at first transcript, which is later than utterance start; a valid A2 test needs audio-start stamping. We refuse to claim A2 until that lands live.
- The arbiter is not authoritative. It only observes. Making it steer routing is a separate, operator-gated step.
This honesty is the point: the risky refactor is done and proven safe first, so the live call is a clean watch-the-shadow → flip sequence, not a debugging session.
2. Engineering discipline: three review-and-harden cycles
Every module went through codex review; each cycle found real defects, each was fixed with a test. This is the same loop that hardened the control-layer foundation.
Cycle 1 — control_event.py (the §1.1 wire gate) — 2 findings, fixed
| # | Severity | Finding | Fix |
|---|---|---|---|
| 1 | P2 | parse_control_event returned ok=True for frames missing/malformed in required envelope fields (v/call_id/role/channel) |
Fail-closed validation before any InputEvent is built; version checked first with a distinct unsupported_version:<v> reason |
| 2 | P2 | client_seq deduper keyed on (call_id, channel); channel is a coarse enum shared by every browser tab → one tab could censor another's sequence |
Re-keyed on the per-connection client_id (WS/session identity); two-tab collision impossible by construction |
Codex re-reviewed the diff → PASS. 44 tests (test_control_event + adversarial).
Landed on main: 42dd6850.
Cycle 2 + 3 — riff/live/session.py shadow slice — 5 findings, fixed
First codex review returned FAIL with findings; all addressed, then re-reviewed:
| # | Severity | Finding | Fix |
|---|---|---|---|
| 1 | High | Epoch gated on state-name equality could not tell a menu re-entry (new decision → must open new epoch) from a re-ask (same decision) → would spuriously report valid inputs as locked divergences and, once authoritative, drop them |
Epoch now opens off the FSM state_entered bus event: a real re-entry transitions (new epoch), a re-ask does not transition (no event, same epoch) |
| 2 | Medium | Flag-off not strictly byte-identical — arbiter imported/constructed unconditionally | When the flag is off the arbiter is not imported, not constructed, not subscribed; all helpers early-return |
| 3 | Medium | Shadow "agreement" masked locked/stale/no_decision drops when legacy also had no choice |
No-choice agreement now requires reason == INVALID (true no-match); other drops register as divergences |
| 4 | Low | RIFF_INPUT_ARBITER=0 was truthy |
Explicit true-parsing (1/true/yes/on) |
| 5 | Low | Assist-swap skip lacked attributable context | Records epoch/state/mode |
Codex re-review verdict: PASS — "No new HIGH/MEDIUM issues found," all four prior findings confirmed resolved, and "Shadow mode does not steer routing when on." Two residual LOW notes, handled deliberately:
_shadow_arbiter_compare()given a top-levelenabledearly-return so the helper is self-guarding (the call site was already guarded — behavior-neutral).- A filtered, inert
state_enteredhandler can linger only on an abnormal connect-failure path that never callsclose()(normal Telnyx teardown awaitsclose(), which unsubscribes). Documented as a known minor rather than over-engineered, because the handler is session-id-filtered, does nothing for other sessions, and is bounded to one per leaked session. See §4.
3. Test evidence
3.1 The safety contract (tests/test_live_decision_epoch_shadow.py, 15 tests)
| Property | How it is proven |
|---|---|
| Flag OFF → arbiter dormant, routing byte-identical | test_flag_off_arbiter_is_dormant — asserts _input_arbiter is None, not subscribed, last_arbiter_shadow is None, and the DTMF route/dest match the flag-on run |
| Flag ON → arbiter agrees, route unchanged | test_dtmf_shadow_agrees_and_route_is_unchanged (3 digits) — accepted, agree=True, arbiter_choice==legacy_choice, same FSM destination |
No-match → invalid (no lock), retry preserved |
test_out_of_range_digit_shadow_invalid_and_no_route |
| Epoch advances on entry + re-entry, not on re-ask | test_epoch_advances_on_entry_and_reentry_not_reask |
| The High-fix, end-to-end | test_press_after_menu_reentry_is_accepted_not_locked — a 2nd valid press after a menu re-entry is accepted, not dropped locked. This is the exact failure the High finding warned about; it FAILS on the pre-fix (state-name) epoch and passes on the state_entered-driven epoch. |
| The shadow is not a rubber stamp | test_shadow_reports_divergence_on_stale_epoch — with the arbiter's fresh epoch open but the input stamped stale, the arbiter drops stale while legacy matches, and the shadow records agree=False. Proves disagreement is reportable, so agree=True elsewhere is meaningful. |
| Flag parsing is explicit | test_flag_parsing_is_explicit (0/false/""→off, 1/true/on→on) |
| Subscription torn down on close | test_close_unsubscribes_from_bus — no leaked global-bus handler |
Both starred tests were independently mutation-checked by codex: it confirmed
the re-entry guard fails if the epoch reverts to the name-based logic, and the
divergence test fails if agree is rubber-stamped to True. So they are real
regression guards, not vacuous passes.
3.2 Byte-identical routing (control-layer foundation, unchanged)
test_input_event.py proves resolve_input_event is byte-identical to the direct
evaluate_choice across 1450+ corpus cases (full decision-trace parity). The
shadow comparison rides on that guarantee, which is why flag-on agreement is
expected, not hoped for.
3.3 Control-layer suites (green)
input_event + input_arbiter + capability + control_event (+ adversarial):
all green. Totals reconfirmed after every edit in this slice.
3.4 Full-suite regression — zero new failures attributable to this work
Method: full pytest on clean origin/main (42dd6850) vs. the same tree with
only the two changed files applied (parallel-session WIP excluded via a
throwaway git worktree, so the diff isolates exactly this change).
-
Baseline (
42dd6850session.py): 110 failed, 7467 passed, 27 skipped, 183 xfailed, 5 errors (7:21). -
With the session shadow work: 109 failed, 7481 passed, 27 skipped, 183 xfailed, 5 errors (7:28).
-
New failures attributable to this slice: NONE. The set diff of failing test IDs (
comm -13 baseline mine) is empty. -
The only delta is one baseline-only failure that this work does not touch:
test_telnyx_transport.py::test_paced_audio_preencodes_before_send_deadlines— an audio-pacing timing/deadline test (flaky), failing in baseline and passing in mine. Not a fix and not a regression — timing noise. -
MINEhas +14 passing tests: the 13 new shadow tests plus that flaky flip. -
The 4
test_live_session.pyfailures seen in targeted runs are pre-existing flow-schema drift onproperty_trouble_ticket(a state renamed out from under the test); they appear identically in the clean baseline. (They are among the 110/109 above and, being present in both columns, contribute nothing to the attributable-failure diff.)Method note: the isolation was run in the main tree, swapping only
session.pybetween the42dd6850and shadow versions, with the parallel surface-emission WIP held constant in both columns (so it cancels). A first attempt usinggit worktreewas discarded because the shared editable install (pip -e) resolvesimport riffto the main tree regardless of the worktree — it would have tested the wrong code. Caught and corrected before trusting the numbers.
4. Why it is safe to have landed this without the live call
The only always-on additions when the flag is off are inert field initializers and no-op helper calls; the arbiter code is never imported or run. The shadow comparison — even when enabled — is wrapped so a bug in it cannot throw into the live turn, and it writes only a log + an observability field. A wrong epoch stamp therefore surfaces as a divergence log, never a behavior change. That is the whole reason the refactor is done before the live call: regressions are attributable and cannot reach a caller until we deliberately flip the flag together.
Known limitation (LOW, accepted): the state_entered subscription is torn
down in close(), which normal Telnyx teardown awaits. A session that is
constructed and subscribed but then hits an abnormal connect-failure path that
never calls close() can retain its handler on the global bus until process
exit. The handler is session-id-filtered (a no-op for every other session) and
bounded to one per leaked session, so we documented it rather than move the
subscription into connect() — that would couple the shadow wiring to the full
connect path and break the constructor-only test harness for no real gain. Worth
revisiting only if the live path grows a constructor-then-abandon pattern.
5. Handoff for the live call
See remote-control-transport-swap-handoff.md: the watch-shadow → stamp-from-audio
→ flip-authoritative → prove-A2 → wire-WS → retire-seq-398-guard sequence, each
step gated so nothing can regress production until we choose to.