You have already internalized the red-green-refactor loop; this skill does not repeat the tutorial. It only holds the two gates most easily lost when rushing for progress, and connects the source of test cases to the failure list. Baseline fact: without disciplinary constraints, an agent under time pressure will implement, do a smoke pass, and declare completion, with zero automated tests.
The two gates
- Before implementing code, first have a test that will fail.
- Watch it go red with your own eyes, and paste the red output, then make it go green. A test that has never gone red proves nothing—it may be always true, may test the wrong thing, or may not have been run at all. Red is the test of the test itself. Pasting the failure output is auditable evidence of test-first: no record of red output = TDD was not followed, and anyone afterward (including yourself) can verify it.
Test cases come from the failure list
The philosophical core of test-first is reverse thinking—do not ask "how should it work", first ask "how would it count as broken".
- When there is a spec (such as the output of clarify): translate the acceptance criteria one by one into tests, at least one test per criterion.
- When there is no spec: before starting, first ask yourself "under what circumstances would this change count as a failure", list 3–5 failure scenarios—they must cover both dimensions of the new behavior being correct and the old behavior not regressing—then translate them one by one.
- Small steps in rhythm: one failure scenario → one red test → minimal implementation to green → next one. Do not accumulate a large batch of tests and write them all at once.
- When a test written afterward goes green on the first try(the implementation already covered that scenario incidentally): do not accept it on that basis—temporarily break the corresponding implementation path, rerun, watch it go red with your own eyes, then restore. A test that cannot be made red no matter how you break it is a bad test; delete it and rewrite it.
Recommended mode when there is a spec: separate the test author from the implementer
The root cause of writing tests afterward is confirmation bias—tests written against the implementation encode "what the code does", not "what it should do". The structural solution: when the environment supports subagents or multiple models, hand the spec's acceptance criteria to anindependent context that cannot see the implementationto produce tests, and the implementation side is only responsible for going green. The test author has no implementation to copy from, so the tests necessarily encode "what it should do". When the environment does not support this, fall back to the two gates in this session.
Definition of done
Only when all failure scenarios have corresponding tests and all are green is it done. "I ran it manually once and there were no errors" is a smoke test, not done—a smoke test leaves no regression defense line, and the next change will have no one to protect this behavior.
Exceptions (honest exit)
One-off scripts, pure configuration changes, and exploratory spikes may skip TDD, but you must say it out loud ("this is a spike, not following TDD"), and when the spike ends, choose one of two: add tests and promote it, or mark it throwaway and delete it. There is no third option of "keep it for now and decide later".
Red flags—stop when these thoughts appear
| Thought | Reality |
|---|---|
| "Finish writing first, then add tests" | Tests written afterward ask "what does this code do"; tests written beforehand ask "what should it do". Adding tests afterward is not TDD |
| "It's too simple to need tests" | Simple code can break too. If it truly isn't worth testing, take the exception path and say so out loud; don't silently skip it. |
| "I've verified it manually" | That's a smoke test. See the definition of done. |
| "The user is pushing" | Being pushed on progress is exactly when dropping the bar happens most. A test that goes red to green is also the fastest way to "prove it's done." |