Ctrl+C Ctrl+V — TDD Extension
You are the same engineer. Same Friday flight to the Alps. Same system. You learned one thing the hard way: the code that gets you called back on Saturday is never the code you wrote — it is the code you forgot to test at the boundary.
So you built a second pattern library. Tests, like code, are copyable. A test for "rejects bad input" is the same shape whether the input is an email or a date. You copy, change the assertion, change the fixture, ship. You do not write every test. You write tests where it matters: at boundaries where someone else's code meets yours.
§ T0 — When TDD activates
The question is never "should I follow TDD?" — it is "who calls this?"
| Caller of the code under change | Tests? |
|---|---|
| Only this file (private helper) | NO |
| Only this module (internal use) | NO unless logic is non-trivial |
| Other modules in this project | YES — contract test |
| External processes, jobs, CLI users | YES — contract + boundary |
| Third-party API or DB (you call out) | YES — effect test (mock the boundary) |
| HTTP / RPC client (you are callee) | YES — contract + boundary + effect |
| Pure UI rendering, no logic | NO |
| UI with state machine or input validation | YES — contract on the logic |
Two-question fallback when the table feels ambiguous:
- If this returns the wrong value silently, who notices? "Nobody for a week" → write the test.
- If someone refactors this next month, what tells them they broke it? "Nothing" → write the test.
§ T1 — TDD integrates with task sizing
Same SMALL / MEDIUM / LARGE. TDD slots into each.
- SMALL — no tests. § 6 self-review is the gate.
- MEDIUM — § T0 decides. If YES: copy test pattern → RED → implement → GREEN → § 6 → § 7.
- LARGE — tests required and written FIRST. Active spec lists test cases as part of Plan. Boundary tests all RED before any implementation. Each task in Tasks: turns specific tests GREEN.
- REFACTOR — characterization tests written GREEN first (Feathers WELC Ch.13; aka pinning / golden master / approval); STAY GREEN through extract. See § T7.
When in doubt, the § T0 table decides — not vibes.
§ T2 — Find before you write (test version)
Same hierarchy as § 1. Different library.
0. ~/.claude/patterns/tests/INDEX.md → match? → copy that .md
1. THIS PROJECT grep "TEST PATTERN:" in tests/
2. FRAMEWORK does the test framework provide this fixture?
3. APPROVED DEPS approved test helper / mock library?
4. TEMPLATES tests/templates/ or example test files
5. OFFICIAL DOCS framework's docs → copy example
6. FROM SCRATCH last resort → save to patterns/tests/
Step 6 has the same tax as § 5: tag, save, index. Details → @${CLAUDE_SKILL_DIR}/playbooks/test-search.md
§ T3 — Test the boundary, not the implementation
Three categories. Pick by what the code does, not by framework.
| Category | Tests this question | Example shape |
|---|---|---|
| Input boundary | Does it reject bad input? | empty / null / oversize / wrong type / malicious |
| Contract | Does it return what callers expect? | shape, field names, error types, success vs failure |
| Effect | Does it produce the right side effect? | DB row, API call, file write, event emitted |
Write ONE of each. Not three of the same kind. Per-category templates → @${CLAUDE_SKILL_DIR}/playbooks/test-boundaries.md
§ T4 — Tests are also patterns
# TEST PATTERN: contract — pure function, happy + 2 errors
# USE WHEN: input → output mapping, no side effects
# COPY THIS: change function name, fixture, expected output, error cases
Tag, copy, save. Identical mechanics to § 3 and § 5. The library at
~/.claude/patterns/tests/ is your second library — grepped together
with code patterns, not separately.
Copying a pattern → @${CLAUDE_SKILL_DIR}/playbooks/test-copy.md
When no pattern fits → @${CLAUDE_SKILL_DIR}/playbooks/test-scratch.md
§ T5 — Test self-review
□ Test name reads as a sentence: "rejects_empty_email"
□ One test, one assertion focus
□ No test depends on another's order
□ No real network / real DB (mock the boundary)
□ Test fails for the right reason: comment out impl → does it RED?
□ TEST PATTERN tag added if shape is reusable
□ Test file mirrors source (framework convention)
□ Mock 是 Test Spy (captures call args + asserted), not just a Stub
configured to return a value (Meszaros 2007; Fowler "Mocks Aren't
Stubs" 2007). Use autospec / spec_set / interface-based mocks
(mockall / Mockito / gomock) so the type system enforces signature
drift detection.
□ Dependency substitution happens at the **use site**, not the
definition site. Python = `mock.patch("caller_module.dep")`;
Java = @MockBean / @InjectMocks; Go / Rust = constructor DI of an
interface / trait, with mock implementations from gomock / mockall.
Red flag: a test that still passes when you delete the implementation under test. That test is testing nothing.
Full checklist → @${CLAUDE_SKILL_DIR}/playbooks/test-review.md
§ T6 — LARGE tasks: spec drives tests drives code
LARGE task's Active spec gets one extra subsection:
### Test plan
- [ ] T1 boundary: rejects [bad inputs]
- [ ] T2 contract: returns [shape] when [condition]
- [ ] T3 effect: writes [row] to [table] on success
- [ ] T4 effect: does NOT call [external] when [condition]
All boundary tests RED before any implementation task starts. Each task maps to specific tests turning GREEN. "Verify" in Tasks checklist = "test Tn passes."
LARGE-specific rhythm → @${CLAUDE_SKILL_DIR}/playbooks/test-large.md
§ T7 — Characterization tests (refactor mode)
When the code under change is already working (LARGE refactor mode, ctrl-c-v § 9), TDD inverts: write tests that pass on the current monolith first (locking observed behavior), then refactor with those tests as the contract — any drift = revert.
Aliases (grep for all of these):
- characterization test — Feathers, WELC Ch.13 (canonical)
- pinning test — same shape, common in Java/Spring circles
- golden master — same, common in Approval Tests toolchain
- approval testing — Llewellyn Falco / Emily Bache framework family
Mnemonic: "GREEN-LOCK" — you lock the current GREEN state, refactor, stay GREEN. If a characterization test goes RED during refactor, you changed behavior; revert and split structural vs behavioral commits (Beck, Tidy First 2024).
Full mechanics + per-stack examples → @${CLAUDE_SKILL_DIR}/playbooks/test-refactor.md Execution sits at Step 2 of the extraction recipe → see ctrl-c-v/playbooks/refactor-extract.md
§ T8 — Adversarial test expansion (LARGE only)
After characterization tests are written GREEN but before the refactor starts, run an adversarial review of the test net itself: parallel LLM agents (or multiple human reviewers, or composed static tools — coverage diff + mutation testing + linters) hunt for must-add tests the spec missed.
Composes Fagan 1976 (inspection rigor) + Klein 2007 (pre-mortem timing) + LLM agent fan-out (cost). The LLM era reduces classical Fagan inspection from days-with-N-people to ~10 min, making pre-refactor review affordable for the first time.
Skip for SMALL / MEDIUM — ROI doesn't justify; the cost of the review exceeds the cost of the bug it would catch.
Full rhythm → @${CLAUDE_SKILL_DIR}/playbooks/test-refactor.md ("adversarial expansion" section)
— Completion criteria —
Boundary coverage — every external caller has a contract test. Every effect has an effect test.
Pattern reuse — second similar test next month: copy, change 5 lines, ship.
Refactor safety — colleague rewrites your function next quarter. Tests catch the break before merge. You stay on the slopes.
Source disclaimer: § T7 / § T8 derived from a single Python+FastAPI monolith characterization + extraction (gateway/server.py, 2026-06). Patterns generalized via Feathers/Beck/Meszaros/Fowler/Fagan/Klein classics. Non-Python stacks: § T7 generalizes cleanly; § T8 LLM-cost assumption holds in any framework; § T5 patch-where-used line has a Python-leaning example — see playbooks/test-refactor.md for Java/Go/ Rust/TS equivalents.
See you on the slopes. ⛷️