← Choice Health remote-control-transport-swap-quality-report.md raw .md

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):

Explicitly NOT claimed (fenced behind the operator + a live call):

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:


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).


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.