RIFF · call replay & layered atlas · use-case-first spec · 2026-07-07 · TDD twin: tests/test_call_replay.py
Call Replay & Layered Atlas — Use Cases
One command turns any call — live, archived, or simulated — into a self-contained page a stranger can read. The success criterion for everything below: answer "why did this call do that?" in five minutes without opening flow YAML.
slot_write ledger: who set/erased what),
VERDICTS (per-decision transition arbitration: winner + shadowed edges).
Command: .venv/bin/python scripts/call_replay.py <call-id | session-dir | transcript.json> [--out page.html]
- Inputs. call-id resolves to
data/sessions/<id>/. A session dir supplies any of:transcription.txtor transcript JSON (HEAR),call_export_events.jsonl(PATH checkpoints),bus_events.jsonl(SLOTS/VERDICTS). A transcript JSON (eval/phantom) supplies turns with per-turnstate(PATH+HEAR) andfact_events(SLOTS). Whatever is absent → that lane is "not recorded on this call". - Ordering. Sort key is
(ts, seq); events withouttskeep arrival order viaseq; equal keys keep source-file order. Lanes never re-sort a source that is already ordered. A re-entered state appears in PATH once per visit — replays show what happened, never deduplicate history. - PII, globally (Codex #3): no public (non-
_) slot VALUE may appear anywhere in ANY output mode — body text, embedded JSON,data-*attributes, titles, or comments. The ledger's redactions (<str:12>) render verbatim; the page adds no new disclosure surface.
Case R1 — "Why did this call do that?" TR1
- The operator has a call id (from the phone log, companion page, or postcall dir).
call_replay.py <call-id>finds the session artifacts underdata/sessions/and writes one self-contained HTML file.- The page shows the four lanes in time order; every FSM state the call visited appears in PATH, in visit order; every slot_write appears in SLOTS with its writer.
Pass: page exists, self-contained (no external fetches), lanes present, states in order. Never: a lane silently empty — a lane with no data says "not recorded on this call" out loud.
Case R2 — Pre-lane archived calls degrade honestly TR2
- The operator replays a call recorded BEFORE the telemetry lanes existed (e.g. the 2026-07-07 incident calls).
- The page renders HEAR + PATH from what exists, and marks SLOTS / VERDICTS "not recorded on this call".
Pass: old artifacts still render; missing lanes named. Never: an absent lane rendered as an empty (implying clean) lane.
Case R3 — Layered atlas: the flow that mutates TR3
- An engineer wants to see how one flow differs for guest / ANI-known / verified callers — without any call at all.
call_replay.py --atlas flows/legal_intake.yamlrenders the compiled graph with a layer toggle: each state is tagged with the trust layers it can serve (data-layer="guest known verified"), and the welcome gate zone (welcome_*) is visually marked as gate machinery.- Toggling a layer dims states that can't occur on that layer (e.g.
welcome_backnever happens for a guest;welcome_firstnever for a known caller).
Pass: every state carries an explicit layer tag from
guest | known | verified | shared | unattributed; gate zone marked; the welcome
branches tagged to their layers (welcome_first→guest-entry,
welcome_back→known, welcome_unlock_granted→verified-edge).
Never: a state with no tag at all — unattributed is
allowed but must be visibly called out (it is a to-do, not an error; Codex #4).
Case R4 — Live calls carry the lanes automatically TR4
- A live call ends. At teardown the phone server dumps this session's bus events
(
slot_write,transition_verdict, and the rest) todata/sessions/<call-id>/bus_events.jsonl. - Replaying that call id renders all four lanes with no extra flags.
Pass: the dump function writes only THIS session's events, appends nothing to other sessions' files, and is safe when the bus buffer has rolled (writes what remains + a truncation notice). Never: another session's events in the file; a teardown crash because telemetry failed (dump errors are logged, not raised).
Case R5 — Sim conversations replay the same way TR5
- An eval or phantom-caller conversation produced a transcript JSON with
fact_events(the PR1 lane). call_replay.py path/to/transcript.jsonrenders the same page: HEAR from the transcript turns, PATH from per-turn states, SLOTS from fact_events.- This is the shift-left half: the same page that explains a live incident explains a failing eval — one literacy for both.
Pass: phantom/eval transcripts render; SLOTS lane populated from fact_events.
Never: public slot VALUES anywhere in any mode's output
(see the global PII rule above) — the ledger's redaction (<str:12>) must
survive rendering verbatim.
Case R6 — The PAGE lane: follow-along vs voice, one clock TR6
- The companion page already reports its life to the server (
view= node entered,resync_skip,menu_poll) — but those events died in the phone log. The server now persists them per call tocompanion_events.jsonl(wall-clock stamped) beside the other artifacts. - Replay renders a fifth lane, PAGE: node enter/exit intervals (an enter ends the previous node — the operator's exact ask: "track when we entered a specific page and when we exited"), aligned to the call clock.
- Replay computes divergence windows: any span > 5s where the page's current node and the
FSM's current state disagree while both are known is flagged in red with its duration — "the page
showed
menufor 41s while the voice walked the Riders Club" becomes one visible bar.
Pass: PAGE lane renders enter/exit intervals; divergence windows named with durations; a session without the file says "not recorded on this call". Never: page and voice each rendering plausibly with no comparison — the lanes exist to disagree loudly.
Test matrix (TDD: these exist and fail before the tool does)
| id | case | asserts |
|---|---|---|
| TR1 | R1 session-dir replay | page written, self-contained, 4 lanes, states in visit order |
| TR1n | R1 Never | a lane without data renders "not recorded on this call" |
| TR2 | R2 pre-lane artifacts | renders; SLOTS+VERDICTS marked not-recorded |
| TR3 | R3 atlas layers | every state layer-tagged; welcome_* gate-marked; branch↔layer mapping |
| TR4 | R4 teardown dump | session-filtered file; other sessions excluded; errors logged not raised |
| TR5 | R5 sim transcript | fact_events → SLOTS lane; PII redactions verbatim; no raw public values |
Contract discipline: implementation cites the case it satisfies; a behavior change without a case change here is a bug by definition. Sibling contract: Session Facts — end-flow behavior.