TDD you can't skip: a phase-gate inside your coding agent
May 22, 2026 · 3 min read · tdd, claude-code, workflow
Most “AI writes tests” stories are test-after in disguise: the model implements, then back-fills assertions shaped to whatever it already wrote. That’s not TDD — it’s a green checkmark with none of the design pressure.
Zensu takes a different line. When you approve a plan that adds executable code,
Zensu asks whether to run the strict /zensu:tdd flow. Say yes and it runs in
the main thread under a PreToolUse phase-gate — a small finite-state machine:
RED_WRITE— you may create the failing test.RED_FAIL— the test ran and failed for the right reason.IMPL— production edits are allowed only after aRED_FAILfor this step.GREEN_PASS— the test passes.
Try to edit a source file before its test has failed and the gate denies the write. Not a lint warning you can ignore — the edit doesn’t happen.
> edit src/auth/session.ts # no failing test yet
✗ gate state=RED_WRITE — production edit denied
write the failing test for this step first
> add test/auth/session.test.ts
✓ RED_WRITE test created
> run
✗ RED_FAIL expired token still accepted (fails for the right reason)
> edit src/auth/session.ts
✓ IMPL edit allowed — a test is now demanding it
> run
✓ GREEN_PASS 1 passed
Why a gate beats a prompt
Prompt discipline degrades. Ten steps into a session, “remember to write the test first” has fallen out of the context window. A gate doesn’t forget. It keys off a per-session state file, so the rule holds on step one and step fifty alike.
It’s opt-in — and off by default
Strict TDD is a choice, not a tax on every edit. Out of the box Zensu runs in vanilla mode: you implement directly, and the parts that always pay for themselves stay on — the evidence audits (build, coverage, a witness that cross-checks what actually ran) and the full review chain. What’s switched off is the red-green ceremony and the edit gate.
You turn the gate on when you want it: answer “yes” to the question at plan
approval for a single session, or set hooks.tddImplementation: true in
~/.zensu/config.json to make strict the default for a repo. The mode is frozen
per session when the flow arms — flipping the config mid-flight changes nothing
already in motion.
The ~30% tax
Strict TDD is not free. Every step becomes write-the-test → run → watch it fail → implement → run → watch it pass, plus the phase markers the gate reads. That’s more turns and more tokens than a direct implementation — in our runs, on the order of 30% more. On a metered agent that’s a real line item, and the reason the gate is a deliberate opt-in rather than an always-on default.
When it earns its keep
Spend the 30% where a regression is expensive and the invariants are subtle: auth and sessions, billing and money movement, permissions, state machines, parsers — anything where a silent break ships quietly and bills you later. There the design pressure shapes a cleaner interface and you walk away with a permanent regression net. The tax pays for itself the first time a future edit trips a test instead of production.
For exploratory spikes, throwaway scripts, UI glue, and low-stakes changes, vanilla is the right call: keep the review chain, skip the ceremony, save the tokens.
What it feels like
You still drive. You write the test, watch it fail, implement, watch it pass — the same loop you’d run by hand, except the harness refuses to let you cut the corner when you’re tired. The payoff is a diff where every line of production code is there because a test demanded it.
That’s the whole point: tests that shape the code, not tests that rationalize it. Skip it when the stakes are low. Turn it on when a regression would hurt — and then you can’t skip it.