total-tdd — whole-app feature audit → test → fix loop
A resumable, four-phase loop over an entire app. The canonical CSV is the single source
of truth and the state machine. The deterministic mechanics — schema, phase inference,
done-gates, tally, HTML render — live in two scripts so they never drift across resumes;
your job is the judgment (what a feature is, what the code should do, whether observed
behavior earns a status).
Scripts (the deterministic core — call these, don't re-derive them)
S=~/.claude/skills/total-tdd/scripts # this skill's scripts dir
python3 $S/tracker.py init # create docs/feature-audit.csv with the canonical header
python3 $S/tracker.py validate [--repair] # assert/repair the 9-col schema + status enum
python3 $S/tracker.py phase # which phase am I in + why (the resume command)
python3 $S/tracker.py gate --phase N # exit 0 iff phase N is complete, else lists blockers
python3 $S/tracker.py tally # "N total · spec/pass/fail/fixed/verified"
python3 $S/render.py docs/feature-audit.csv --app "<name>" # write docs/feature-audit.html
All read docs/feature-audit.csv (override with a path arg). Run tracker.py phase to
resume; run gate --phase N before advancing; run render.py after each phase. Tests:
python3 -m unittest discover $S.
Prerequisites (Phase 2)
Phase 2 drives the running app through three roles. Confirm one tool per role first — if
any is missing, name it and stop, rather than downgrading to reading code (that defeats the skill):
- Browser driver —
agent-browser preferred, or any tool/MCP that can navigate, fill,
click, screenshot, and read console + network.
- Stable local URL —
portless preferred, or any fixed host:port / tunnel.
- API stub —
emulate preferred, or any local mock for Stripe/GitHub/AWS and similar.
The canonical CSV
docs/feature-audit.csv, one row per feature, columns
id,area,user_story,expected_behavior,source,status,issues,fix,verified. tracker.py
owns the schema; you fill the judgment:
user_story: "As a , I want , so that ."
expected_behavior: what the code actually does — cite source as file:line.
status enum: spec → pass/fail → fixed → verified. verified (9th col) holds the
observed evidence.
The report is the forcing function: an empty cell is a visible gap, so every story gets exercised.
Phases (advance only when tracker.py gate --phase N passes)
- Inventory + spec. Walk the whole app (routes, components, commands, APIs, jobs,
settings). Add a row per feature with a user story + code-derived expected behavior and a
source ref; status spec. Judgment: feature granularity, reading intent from code.
- Test. Exercise each story in the real running app (not by reading code): serve it at
a stable URL, drive the UI with the browser driver, stub external APIs with emulate. Set
pass/fail; put concrete repro/error in issues. Judgment: what to click, what's broken.
- Fix. Fix every
fail (logic + UX). Record the change in fix, set fixed. Keep each
fix diff tight. Judgment: whether a fix is in-scope.
- Re-test. Re-run every story in the real app; set status
verified with evidence in the
verified column. Any new break goes back to fail → loop to phase 3.
Rules
- Evidence before status. A row is
pass/verified only after the behavior was observed
running — never from reading code (see verify-this, verification-before-completion).
Enforced: tracker.py gate blocks a pass/verified row with an empty Evidence cell
(and a fail with no repro in issues) — a status without evidence is a blocker, not done.
- The CSV is canonical and updated in place — never fork copies. It survives sessions; that's
how the loop resumes.
- After each phase:
render.py to refresh docs/feature-audit.html; never let it drift.
- Scope creep is fine for finding issues across features; keep each fix diff tight.
Errors
| Issue |
Fix |
Browser driver (agent-browser / substitute) not installed or its MCP/daemon isn't running, so Phase 2 can't navigate/click/screenshot |
Don't downgrade to reading code — that voids the skill. Start the driver (or an equivalent that can navigate, fill, click, screenshot, read console+network); if none exists, name the missing role and stop, leaving rows at spec. |
App won't start, or portless can't bind because the dev port is taken |
Find the real start/serve command (package scripts, README, or ask) and run it; map it through portless to a fixed .localhost URL so the driver hits a stable address — fix the port conflict (kill the stale server or change the port), don't test a moving localhost:PORT. |
emulate not installed, so external integrations (Stripe/GitHub/AWS) can't be stubbed |
Install/run emulate (or another local mock) and point the app's API base/keys at it so integration paths run offline; if it can't stand up, mark only the affected rows fail with the missing-stub reason in issues — never silently skip them. |
| Missing API key/credential the emulator can't fake, blocking a real path |
Fetch the key from Bitwarden (bws) at run time and inject via env — never hardcode it; if unavailable, record the blocked story as fail with the missing-credential note so it's a visible gap, not a fake pass. |
docs/feature-audit.csv missing, corrupt, or columns drifted |
tracker.py init (if absent) or tracker.py validate --repair (rewrites to the canonical 9 columns); then tracker.py phase to re-derive where you are and render.py to re-sync the HTML. Never fork a second copy. |
1---2name: total-tdd3description: Systematic whole-app feature audit → test → fix loop, backed by tracker.py + render.py over one canonical CSV state machine. Inventory every feature into user stories with code-derived expected behavior, then loop: test every story, document errors, fix logic/UX bugs, re-test. Use for "/total-tdd", "auditing an entire app", "building a feature/user-story spec from the code", or a full test-and-fix sweep across all features. Not for a single feature or bug — use `tdd` (red-green-refactor); not for verifying one claim — use `verify-this`; not for reviewing a diff — use `review`.4---56# total-tdd — whole-app feature audit → test → fix loop78A resumable, four-phase loop over an entire app. The **canonical CSV is the single source9of truth and the state machine**. The deterministic mechanics — schema, phase inference,10done-gates, tally, HTML render — live in two scripts so they never drift across resumes;11your job is the judgment (what a feature is, what the code should do, whether observed12behavior earns a status).1314## Scripts (the deterministic core — call these, don't re-derive them)1516```bash17S=~/.claude/skills/total-tdd/scripts # this skill's scripts dir18python3 $S/tracker.py init # create docs/feature-audit.csv with the canonical header19python3 $S/tracker.py validate [--repair] # assert/repair the 9-col schema + status enum20python3 $S/tracker.py phase # which phase am I in + why (the resume command)21python3 $S/tracker.py gate --phase N # exit 0 iff phase N is complete, else lists blockers22python3 $S/tracker.py tally # "N total · spec/pass/fail/fixed/verified"23python3 $S/render.py docs/feature-audit.csv --app "<name>" # write docs/feature-audit.html24```2526All read `docs/feature-audit.csv` (override with a path arg). Run `tracker.py phase` to27resume; run `gate --phase N` before advancing; run `render.py` after each phase. Tests:28`python3 -m unittest discover $S`.2930## Prerequisites (Phase 2)3132Phase 2 drives the *running app* through three roles. Confirm one tool per role first — if33any is missing, name it and stop, rather than downgrading to reading code (that defeats the skill):3435- **Browser driver** — `agent-browser` preferred, or any tool/MCP that can navigate, fill,36 click, screenshot, and read console + network.37- **Stable local URL** — `portless` preferred, or any fixed host:port / tunnel.38- **API stub** — `emulate` preferred, or any local mock for Stripe/GitHub/AWS and similar.3940## The canonical CSV4142`docs/feature-audit.csv`, one row per feature, columns43`id,area,user_story,expected_behavior,source,status,issues,fix,verified`. `tracker.py`44owns the schema; you fill the judgment:4546- `user_story`: "As a <role>, I want <action>, so that <outcome>."47- `expected_behavior`: what the code actually does — cite `source` as `file:line`.48- `status` enum: `spec` → `pass`/`fail` → `fixed` → `verified`. `verified` (9th col) holds the49 observed evidence.5051The report is the forcing function: an empty cell is a visible gap, so every story gets exercised.5253## Phases (advance only when `tracker.py gate --phase N` passes)54551. **Inventory + spec.** Walk the whole app (routes, components, commands, APIs, jobs,56 settings). Add a row per feature with a user story + code-derived expected behavior and a57 `source` ref; status `spec`. *Judgment: feature granularity, reading intent from code.*582. **Test.** Exercise each story in the **real running app** (not by reading code): serve it at59 a stable URL, drive the UI with the browser driver, stub external APIs with emulate. Set60 `pass`/`fail`; put concrete repro/error in `issues`. *Judgment: what to click, what's broken.*613. **Fix.** Fix every `fail` (logic + UX). Record the change in `fix`, set `fixed`. Keep each62 fix diff tight. *Judgment: whether a fix is in-scope.*634. **Re-test.** Re-run every story in the real app; set status `verified` with evidence in the64 `verified` column. Any new break goes back to `fail` → loop to phase 3.6566## Rules6768- **Evidence before status.** A row is `pass`/`verified` only after the behavior was observed69 running — never from reading code (see `verify-this`, `verification-before-completion`).70 **Enforced:** `tracker.py gate` blocks a `pass`/`verified` row with an empty Evidence cell71 (and a `fail` with no repro in `issues`) — a status without evidence is a blocker, not done.72- The CSV is canonical and updated in place — never fork copies. It survives sessions; that's73 how the loop resumes.74- After each phase: `render.py` to refresh `docs/feature-audit.html`; never let it drift.75- Scope creep is fine for *finding* issues across features; keep each *fix* diff tight.7677## Errors7879| Issue | Fix |80| --- | --- |81| Browser driver (`agent-browser` / substitute) not installed or its MCP/daemon isn't running, so Phase 2 can't navigate/click/screenshot | Don't downgrade to reading code — that voids the skill. Start the driver (or an equivalent that can navigate, fill, click, screenshot, read console+network); if none exists, name the missing role and stop, leaving rows at `spec`. |82| App won't start, or `portless` can't bind because the dev port is taken | Find the real start/serve command (package scripts, README, or ask) and run it; map it through `portless` to a fixed `.localhost` URL so the driver hits a stable address — fix the port conflict (kill the stale server or change the port), don't test a moving `localhost:PORT`. |83| `emulate` not installed, so external integrations (Stripe/GitHub/AWS) can't be stubbed | Install/run `emulate` (or another local mock) and point the app's API base/keys at it so integration paths run offline; if it can't stand up, mark only the affected rows `fail` with the missing-stub reason in `issues` — never silently skip them. |84| Missing API key/credential the emulator can't fake, blocking a real path | Fetch the key from Bitwarden (`bws`) at run time and inject via env — never hardcode it; if unavailable, record the blocked story as `fail` with the missing-credential note so it's a visible gap, not a fake `pass`. |85| `docs/feature-audit.csv` missing, corrupt, or columns drifted | `tracker.py init` (if absent) or `tracker.py validate --repair` (rewrites to the canonical 9 columns); then `tracker.py phase` to re-derive where you are and `render.py` to re-sync the HTML. Never fork a second copy. |