$npx -y skills add eduwxyz/my-awesome-skills --skill tddDrives feature work and bug fixes through a tight failing-test-first loop. Trigger when implementing, fixing, or refactoring behavior in a codebase that already has tests. Skip for spikes, visual-only edits, throwaway scripts, and generated files.
| 1 | # TDD |
| 2 | |
| 3 | One small failing test, just enough code to pass, then a look. Repeat. |
| 4 | |
| 5 | ## Install |
| 6 | |
| 7 | Drop in `~/.claude/skills/tdd/` (per-user) or `<repo>/.claude/skills/tdd/` (per-project). |
| 8 | |
| 9 | ## Skip TDD for |
| 10 | |
| 11 | Spikes, copy/style/visual edits, one-off scripts, generated files. If the codebase has no tests at all, see [untested-code.md](untested-code.md) first. |
| 12 | |
| 13 | ## Two rules that keep TDD honest |
| 14 | |
| 15 | 1. **Never start a second test before the first one passes.** |
| 16 | 2. **Never edit production code while red, except to make red green.** |
| 17 | |
| 18 | If either slips, back up. |
| 19 | |
| 20 | ## What a good test looks like |
| 21 | |
| 22 | The name describes a capability, not a method. Read it out loud — it should sound like something a user or caller would care about. |
| 23 | |
| 24 | ``` |
| 25 | ✅ logged_out_user_cannot_publish_a_post |
| 26 | ✅ schedule_overlap_returns_409 |
| 27 | |
| 28 | ❌ post_service_calls_repo_save |
| 29 | ❌ schedule_returns_object_with_status |
| 30 | ``` |
| 31 | |
| 32 | The body has three sections in order — set up, do the thing, check what's observed: |
| 33 | |
| 34 | ``` |
| 35 | test "expired tokens are rejected": |
| 36 | token = issue_token(ttl_seconds: 60) |
| 37 | advance_clock(seconds: 120) |
| 38 | |
| 39 | result = verify(token) |
| 40 | |
| 41 | assert result.ok == false |
| 42 | assert result.reason == "expired" |
| 43 | ``` |
| 44 | |
| 45 | If renaming a private function tomorrow would break the test even though no behavior changed, the test was tied to internals. Rewrite or delete it. See [test-anatomy.md](test-anatomy.md). |
| 46 | |
| 47 | ## The cardinal mistake: tests in bulk |
| 48 | |
| 49 | Writing five tests up front and then five implementations produces tests that describe an *imagined* system. They lock you into the wrong shape and stop pulling their weight once any pair shares a code path. |
| 50 | |
| 51 | Each test must exist because of something you learned writing the previous one. |
| 52 | |
| 53 | ``` |
| 54 | Wrong: RED t1 t2 t3 t4 t5 → GREEN c1 c2 c3 c4 c5 |
| 55 | Right: t1→c1, t2→c2, t3→c3, ... |
| 56 | ``` |
| 57 | |
| 58 | Same mistake in miniature: writing one test and reaching inside to "also handle" something it doesn't cover. Don't. |
| 59 | |
| 60 | ## Before the cycle — wire up the test command |
| 61 | |
| 62 | The Stop hook runs your tests at the end of every turn (green allows the turn to end, red blocks it). It reads the command from `.agents/tdd/test-command.txt` at the project root. |
| 63 | |
| 64 | If that file is missing, set it up before starting the cycle: |
| 65 | |
| 66 | 1. Read `CLAUDE.md` first — projects often document the canonical test command there. |
| 67 | 2. Otherwise check `package.json` (`scripts.test`), `pyproject.toml` / `pytest.ini`, `Makefile` (`test:` target), `Cargo.toml`, or analogous config files for the project's stack. |
| 68 | 3. If multiple plausible commands exist (e.g. `test:unit` vs `test:e2e`), ask the user which one to use for TDD. |
| 69 | |
| 70 | Once decided, create `.agents/tdd/` if missing and write the command on a single line to `.agents/tdd/test-command.txt`. Confirm with the user the first time. |
| 71 | |
| 72 | The user can edit the file at any time to change the command (e.g. switching from `npm test` to `npm run test:unit`). |
| 73 | |
| 74 | ## The cycle |
| 75 | |
| 76 | **1. Decide.** If a `spec/<slug>.md` exists for this task, read it — Behaviors + Acceptance criteria are your queue. Otherwise list the behaviors to verify in rough order with whoever cares. |
| 77 | |
| 78 | **2. Smoke run.** Pick the first behavior. Test → fails → minimum code → passes. If this first cycle takes more than 15 minutes, the slice is too big. |
| 79 | |
| 80 | **3. Each next behavior.** Same shape. One test at a time. No speculation. No "while I'm here." If a rule slips, see [smells.md](smells.md#warning-signs-while-you-work). |
| 81 | |
| 82 | **4. Cleanup.** Tests green? Invoke the `simplify` skill on the recent changes — it reviews code for reuse, quality, and efficiency and applies any improvements found. After simplify, see [cleanup.md](cleanup.md) for additional cleanup. Cleanup only on green; never add behavior during cleanup. (The Stop hook will also remind you to invoke `simplify` once per session if you forget.) |
| 83 | |
| 84 | ## When you're done |
| 85 | |
| 86 | Stop adding tests when all are true: |
| 87 | |
| 88 | - Every acceptance criterion has a test. |
| 89 | - Every interesting branch has a test (skip getters, plumbing). |
| 90 | - The domain edge cases (empty input, expired things, races, boundaries) are covered. |
| 91 | - Reading just the test file would teach the feature. |
| 92 | |
| 93 | 100% coverage with bad tests is worse than 70% with good ones. |
| 94 | |
| 95 | ## Per-cycle checklist |
| 96 | |
| 97 | - [ ] Test names a capability, not a method. |
| 98 | - [ ] Test only touches the public API. |
| 99 | - [ ] Test would survive a rename of internals. |
| 100 | - [ ] Code written is the minimum for this test. |
| 101 | - [ ] No second test queued before this one is green. |
| 102 | |
| 103 | ## Map |
| 104 | |
| 105 | - [test-anatomy.md](test-anatomy.md) — body shape, assertions per test, naming. |
| 106 | - [smells.md](smells.md) — bad-test patterns + warning signs while you work or review. |
| 107 | - [boundaries.md] |