RIFF · developer tutorial · 2026-07-07 · code: scripts/fixlog.py · records: docs/fixlog/ · contract: docs/fixlog/CONTRIBUTING.md
The Fix Journal: How This Codebase Remembers Its Mistakes
Written so anyone new to the project can use it in five minutes. Short version: every bug we fix gets a little story written about it, a robot can search those stories, and every so often we turn the stories into rules. Result: the same bug never costs us a day twice.
The big idea, as a picture
The two gold boxes are the habits this system enforces. Searching first means you inherit every past debugging session for free. Writing the story means the next person (human or AI) inherits yours.
A real example, start to finish
This actually happened on 2026-07-07. A caller pressed 0 to update their profile. The system said "I'll text you a code" — and no code ever arrived. Here's what the caller's phone showed:
Why? The first text (just a link, no code — that's on purpose) accidentally checked the internal box that says "a verification code has been sent." So when the caller asked for a real code, the system looked at the box, saw it checked, and skipped sending one. The caller was asked to read a code from a message that never existed. One checkbox, shared by two different features.
Step 1 — Search before touching anything
$ .venv/bin/python scripts/fixlog.py search "code text never arrived" [semantic] top matches: FX-008 Texts 'sent' but never delivered (10DLC) FX-011 Verify chain skipped the real code (welcome text claimed verification)
Ten seconds in, you already know: this smells like FX-008 (a carrier problem) or FX-011 (a bookkeeping problem) — and each record tells you exactly how to check which one, because the last person wrote down their evidence.
Step 2 — The story that got written (anatomy of an entry)
**Symptom**: Pressed 0, said yes to "text you a code" — no code ever arrived. ← the caller's words
**Root cause**: the link-only welcome text set _caller_verification_sent=True
with an EMPTY code; the verify chain saw "already sent" and skipped. ← the mechanism
**Fix**: verification-sent now strictly means A CODE IS OUTSTANDING. ← what changed
**Why this way**: fixing the MEANING of the claim beats special-casing
the skip check — every future reader gets the truth. ← alternatives were weighed
**Shift-left gate**: tests/test_link_only_verification_isolation.py ← can't happen again
**Test**: the guard's exact path::name ← required on new entries
**Success**: pending — next live call, press 0, code arrives ← closed at retro
**Evidence**: call v3:ByaxLead, slot ledger, commit 84f0a649
Step 3 — The retro (where stories become rules)
Every ~10 entries (or before a deploy, or at a session wrap), whoever's working reads the new stories
and asks: is there a pattern? Patterns get one line each in
docs/fixlog/PRINCIPLES.md. Today's first retro produced nine. Three examples:
| rule | born from | plain English |
|---|---|---|
| Deterministic paths need deterministic audio | FX-004, 006, 007 | If a keypress can reach a state, that state's voice must be a pre-recorded file — keypresses mute the AI's mouth. |
| A claim slot must mean exactly what it says | FX-001, 002, 011 | If a flag is called "verification sent," it better mean a code is really out there — lies in bookkeeping become bugs downstream. |
| Every fix ships its gate | all 13 | A fix without a test that would catch the regression is a fix you'll do twice. |
How the rules are enforced (not the honor system)
| habit | enforced by | what happens if skipped |
|---|---|---|
| Annotate every fix, all fields | tests/test_fixlog_schema.py | CI fails — the build is red until the story is complete |
| Search before changing | AGENTS.md (read by every AI agent at session start) + persistent memory | the agent's own instructions call it mandatory |
| Retro on cadence | 10 entries · before deploy · session wrap | pending Success fields pile up and the schema test surfaces them |
🧪 Try it yourself (2 minutes)
# 1. Search like a debugger would: .venv/bin/python scripts/fixlog.py search "menu kept repeating" # 2. Read the whole catalog: .venv/bin/python scripts/fixlog.py list # 3. Start a new entry when you fix something: .venv/bin/python scripts/fixlog.py new my-bug-slug # → creates docs/fixlog/FX-014-my-bug-slug.md with the template # 4. Prove your entry is complete: .venv/bin/python -m pytest tests/test_fixlog_schema.py -q