What changed

v0.0.2 — 2026-09-10

The decision path is domain code (ADR 0008). dialogue.py, brain.py, briefs.py, gate.py and demo_loads.py moved under app/domain/calling/, where the import-linter purity contract forbids I/O. The live bot no longer imports a benchmark script at start-up. The router and brain tests run in the main suite with no skip.

One venv. [dependency-groups] voice records the Pipecat install that was never written down. uv sync --group dev --group voice; 1,289 tests in one interpreter.

Ending the call: one mechanism. CallEnd arms on any hang-up decision, ends only on audio that started after arming plus a playout grace, and has a watchdog. Every path — model goodbye, canned goodbye, give-up, abuse — arms the same object. The session record carries ended_by (ori · watchdog · browser · idle) and the disposition.

A cut-off figure is held. "तो मेरा rate लगभग" is held 1.5 s and merged with its continuation before anything is decided.

The router is a table. Rules in precedence order, each returning its name; the page and the record show which rule fired. normalise() splits what the recogniser joined; every list is matched folded (nukta-insensitive) and word-bounded; verb forms come from one paradigm; unmistakable recordings precede the callback rule; abuse warns once and ends on the second strike. Both scripts in every list, because Flux writes "number" and "switch off" in Latin inside Hindi.

The brain has phases. Seven of them replace nine booleans. An out-of-range quote is questioned before the ladder closes; a good quote is squeezed once before the ladder takes it; the deferral is terminal; a particle during the probe gets one more ask; accepting the counter settles at the concession, never the ceiling. The wind-down line is canned and every brief forbids the model from saying it. The prompt carries the last three exchanges with her figures stripped.

Transliteration. Unknown words stay in Latin; the rule speller that produced दिसतनके for "distance" is gone. The model's 196-word vocabulary is a fixture and every word must have a pronunciation.

Comments. Every file on the decision path and in the ASR layer says why in one or two lines; the stories are in the incident ledgers. The code AST of each trimmed file is unchanged (scripts/comments_only.py). Emoji markers in source: 0 on the decision path.

Deleted. The push-to-talk glass box (app/api/ori.py, live.py, two pages, 17 tests), the CLI scaffold (poc/pc-tele, bot.py, Dockerfile, pcc-deploy), the stale poc/pipecat shadow package, a two-week-old handoff note.

Tests. tests/domain/calling/test_phases.py (17), test_call_end.py (16), test_normalise.py, test_router_stability.py (the perturbation ceilings), test_brain_abuse.py; scripts/mutate.py and make mutate; poc/e2e.py with seven audio scenarios; a CI workflow.

Measured.

what v0.0.1 v0.0.2
routings moved by joining the first two words (133 rows) 29, 18 money-critical 1, 1
live calls replayed where counter_once was reached (22) 3 5
calls the brain decided to end (22) 10 13
model turns handed a vague brief while a probe was open 7 1
end-to-end scenarios ending by Ori's decision 0 of 1 (the goodbye was clipped) 7 of 7
decision-path files with emoji markers 14 0
test suite 1,209 + 254 in two venvs 1,289 in one

Tooling. poc/converse_bakeoff.py (12 models, three passes, two judges), poc/judge_resume.py, poc/analysis/ (mine_calls, mine_words, replay_calls, perturb, probe_models), docs/site/build.py and deploy/docs.sh for this site.

Still open. The bake-off verdict; real call recordings; telephony; the semantic fallback under the router; the venv swap on the machine that runs the live bot.

v0.0.1 — 2026-09-10 (morning)

The first build confirmed end to end by Mehul: a call placed, negotiated, ended by the agent, and feedback saved against it with audio and a transcript. Defined once in version.py and stamped by the server into the page and every session record.