tdd · pandoscope — скилл для Claude Code: что делает и как установить
Принуждает агента вести разработку строго по циклу red-green-refactor и фиксировать каждый шаг в git-коммитах. Защищает кодовую базу от непроверенного кода.
npx skills add pandoscope/skills --skill tdd -a claude-codeЧто делает tdd
Скилл запрещает писать продакшен-код до создания падающего теста. Работа идёт исключительно вертикальными срезами: один тест, один коммит реализации, следом рефакторинг. Если код случайно написан раньше теста, правило требует стереть его и начать заново.
Каждый шаг отражается в истории коммитов отдельным префиксом: test(red):, chore(stub): для пустых сигнатур и feat: либо fix: для прохождения тестов. Чтобы падающий тест не ломал проверку типов, скилл настраивает маркеры вроде @pytest.mark.xfail(strict=True) или test.fails().
Для контроля структуры коммитов и маркеров в репозиторий добавляется скрипт lint-red.sh. Однако хуки проверяют лишь заголовки и файлы, а сам запуск теста перед коммитом остаётся на вашей совести.
Какие задачи решает
- внедрить строгий цикл red-green-refactor при написании фич и багфиксов
- исключить появление продакшен-кода без предварительного падающего теста
- настроить строгие маркеры ожидаемого падения для pytest, jest и vitest
- автоматизировать проверку чистоты коммитов через скрипт
lint-red.sh
Примеры запросов агенту
- Сделай фичу добавления скидки в заказ строго по TDD.
- Исправь баг с расчётом суммы через red-green-refactor и зафиксируй шаги в git.
- Напиши падающий тест на валидацию пароля и подготовь коммит
test(red):.
Как установить tdd
1Через Skills CLI (Vercel)
npx skills add pandoscope/skills --skill tdd -a claude-code2Вручную: положить файлы
~/.claude/skills/tdd/SKILL.mdПапка скилла целиком; для одного проекта — .claude/skills/tdd/
1Через Skills CLI (Vercel)
npx skills add pandoscope/skills --skill tdd -a codex2Вручную: положить файлы
.agents/skills/tdd/SKILL.mdДля всех проектов — ~/.agents/skills/
1Через Skills CLI (Vercel)
npx skills add pandoscope/skills --skill tdd -a cursor2Вручную: положить файлы
.cursor/skills/tdd/SKILL.mdCursor читает и .agents/skills/, и .claude/skills/
1GitHub CLI
gh skill install pandoscope/skills tdd2Вручную: положить файлы
.github/skills/tdd/SKILL.md1Через Skills CLI (Vercel)
npx skills add pandoscope/skills --skill tdd -a gemini-cli2Командой платформы
gemini skills install https://github.com/pandoscope/skills --path derived/tddВручную: положить файлы
.devin/skills/tdd/SKILL.mdСтарый путь .windsurf/skills/ тоже читается
Через Skills CLI (Vercel)
npx skills add pandoscope/skills --skill tdd -a opencodeClaude.ai
Упакуйте папку в zip и загрузите в Settings → Capabilities → Skills
По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов
Вопросы о tdd
Как установить tdd в Claude Code?
Выполните команду npx skills add pandoscope/skills --skill tdd -a claude-code. Файл настроек сохранится по пути ~/.claude/skills/tdd/SKILL.md.
Что делает скилл tdd?
Он задаёт протокол разработки через тестирование: агент пишет тест, коммитит его с маркером падения, пишет минимальный код для зелёного статуса и только потом проводит рефакторинг.
Что происходит, если интерфейс функции ещё не объявлен?
Создаётся отдельный коммит с префиксом chore(stub):, куда входит только сигнатура функции с генерацией NotImplementedError. Это позволяет пройти проверку типов до написания логики.
Исходный текст
SKILL.md · MIT
---
name: tdd
description: Test-driven development via red-green-refactor with an auditable commit protocol. Use when implementing any feature, bugfix, refactor, or behavior change before writing implementation code, or when the user mentions TDD, red-green-refactor, or test-first development.
metadata.derived-from: merge of mattpocock/skills' `tdd` (https://github.com/mattpocock/skills/blob/7afa86d3a5dd96edde06ffa014e16c64e733681e/skills/engineering/tdd/SKILL.md) + obra/superpowers' `test-driven-development` (https://github.com/obra/superpowers/blob/030a222af19c1f3a93c6eb876a7422d8e4fc0162/skills/test-driven-development/SKILL.md)
metadata.derivation-note: Commit protocol (red/green commits, markers, lint-red.sh) and the vertical-slice framing are the load-bearing original parts. Diff prose against upstream, not the protocol.
---
# Test-Driven Development
Write test first. Commit it red. Write minimal code to pass. Commit green. Refactor.
**Core principle:** tests verify behavior through public interfaces, not implementation. Code can change entirely; tests shouldn't. A test that breaks when you rename an internal function — with no behavior change — was testing implementation. Delete it.
No watched failure = no proof the test tests the right thing. Commit history is the evidence; hooks check its structure.
See [tests.md](tests.md) for examples and [mocking.md](mocking.md) for mocking guidelines.
## Iron Law
NO PRODUCTION CODE WITHOUT A FAILING TEST COMMITTED FIRST.
Wrote code before the test? Delete it. Implement fresh from tests. Delete means delete. Exceptions (throwaway prototypes, generated code, config) need human sign-off. Thinking "skip TDD just this once"? That is rationalization.
## 1. Plan (before any code)
Use the project's domain glossary so test names and interface vocabulary match the codebase; respect ADRs in the area you touch.
- List the *behaviors* to test, not implementation steps. Prioritize critical paths and complex logic — you can't test everything.
- Design interfaces for [testability](interface-design.md); identify opportunities for [deep modules](deep-modules.md) (small interface, deep implementation)
- Confirm the public interface and the priority behaviors with the user, then proceed.
## 2. Vertical slices, not horizontal
DO NOT write all tests, then all implementation. That is horizontal slicing and it produces crap tests: written in bulk they test *imagined* behavior and the *shape* of things (signatures, data structures), go insensitive to real changes, and commit you to test structure before you understand the code.
Work in vertical slices — one test → one implementation → repeat. Each test responds to what the last cycle taught you.
```text
WRONG (horizontal): RED: t1 t2 t3 t4 GREEN: i1 i2 i3 i4
RIGHT (vertical): t1→i1 t2→i2 t3→i3 ...
```
The first slice is a tracer bullet: it proves the path works end to end.
## 3. Commit protocol
One behavior = one RED commit + one GREEN commit. Multiple cycles per branch is fine; prefer one cycle in flight in Go/Rust repos (see markers).
**Every commit — RED included — leaves the tree lint-clean.** After each
commit, run the repo's full hook suite over the whole tree (e.g.
`prek run --all-files`): exit code 0 and no files modified. An auto-fixer
changing files IS a failure — apply the fix and amend it into the commit that
introduced the problem before starting the next cycle. CI evaluating only the
PR head never excuses a dirty intermediate commit; per-commit hygiene is
verified here, at commit time, or not at all.
### STUB commit (only when the RED test needs a missing API surface)
A RED commit is red on **tests only**: lint, format, and type checks still
pass on it. When the new test calls a function, method, or parameter that does
not exist yet, type checking would fail — so first commit a minimal
signature-only stub as its own commit, prefix `chore(stub):`. Signature +
docstring, zero behavior: a new callable raises `NotImplementedError`; a new
parameter is accepted and ignored. This is the one sanctioned exception to the
Iron Law: a stub defines *interface*, and must contain no *implementation* —
any logic in a stub commit is production code without a failing test. Skip
this step entirely when the surface already exists.
### RED commit
- Write one failing test. One behavior, clear name, real code — no mocks unless unavoidable.
- Run it *unmarked*; watch it fail for the **right reason** (feature missing — behavior wrong, assertion fails, or the stub's `NotImplementedError` — never a typo, import error, or missing symbol). Passes immediately? It tests existing behavior — fix the test.
- Add the marker (below), suite green, commit. Tests only, prefix `test(red):`.
| Language | Marker | Strict? |
| --- | --- | --- |
| Python (pytest) | `@pytest.mark.xfail(strict=True)` | yes — XPASS fails suite |
| TS/JS (vitest) | `test.fails(...)` / `it.fails(...)` | yes |
| TS/JS (jest) | `it.failing(...)` | yes |
| Go | `//go:build red` + `TestRed` prefix + `red-tests` job | aggregate only |
| Rust | `#[ignore = "red"]` + `red_` prefix + `red-tests` job | aggregate only |
| Other | find a strict expected-failure mechanism; none exists → commit unmarked, tell the human the repo lacks red enforcement | n/a |
Go/Rust build-tag/ignore markers *exclude* tests from the normal suite, so a `red-tests` CI job must run only the marked tests and expect failure (no-op when none exist). Its exit code is aggregate: two red tests in flight, one wrongly passing → the job still passes. Strict markers catch this per-test; the job doesn't.
### GREEN commit
- Simplest code that passes. YAGNI — no speculative generality.
- Remove this cycle's red markers. No other test changes in this commit.
- Full suite green, output pristine. Fails? Fix the code, not the test. Prefix `feat:`/`fix:`.
### REFACTOR (after green only)
Remove duplication, improve names, extract helpers, deepen modules. Tests stay green, no new behavior, separate commit. **Never refactor while red.** Then start the next cycle.
After all tests pass, look for [refactor candidates](refactoring.md):
## Enforcement — and its limits
`lint-red.sh` (ships with skill; wire into prek + CI): `staged`/`commit` modes check that `test(red):` commits touch only test files and add a marker, others add none; `merge` mode rejects any markers in tree; if Go/Rust markers are present, a `red-tests` job must exist.
Hooks verify commit *structure* and marker hygiene. They do **not** verify you ran the unmarked test and watched it fail for the right reason — that stays on you. Same for per-commit lint: an installed pre-commit hook blocks dirty commits at commit time, but where no hook is installed (fresh remote environments), the after-every-commit `--all-files` run is manual discipline — do it anyway. Strict markers partially compensate (XPASS catches tests of already-existing behavior). "Lint passed" ≠ "TDD verified."
## Good tests
| Quality | Rule |
| --- | --- |
| Behavioral | Exercises a real path through the public API; survives refactors |
| Minimal | One thing. "and" in the name? Split it. |
| Clear | Name states the behavior, not `test1` |
| Honest | Tests the code, never the mock |
```ts
// Good — tests real behavior (vitest; jest: it.failing)
test.fails('retries failed operations 3 times', async () => {
let attempts = 0;
const op = () => { attempts++; if (attempts < 3) throw new Error('fail'); return 'ok'; };
expect(await retryOperation(op)).toBe('ok');
expect(attempts).toBe(3);
});
```
## Red flags — STOP, delete, restart
Unmarked test passes before implementation exists · can't explain why the test failed · weakening an assertion in GREEN to make it pass · testing the mock · "just this once" · "I'm being pragmatic, TDD is dogmatic."
Read [testing-anti-patterns.md](testing-anti-patterns.md) to avoid common pitfalls.
## Rationalization table
| Excuse | Reality |
| --- | --- |
| "Too simple to test" | Simple code breaks. The test takes 30s. |
| "I'll test after" | Tests-after are biased by the implementation: "what does this do?" not "what should this?" |
| "Deleting hours of code is wasteful" | Sunk cost. Unverified code is debt. |
| "Test is hard to write" | Hard to test = hard to use. Listen to it; simplify the interface. |
| "Must mock everything" | Too coupled. Inject dependencies. |
| "Lint passed, TDD done" | Hooks check structure, not that you watched the failure. |
## When stuck / debugging
Don't know how to test → write the wished-for API and assertion first. Found a bug → write a failing test reproducing it, then run the full protocol. Never fix a bug without a test.
© pandoscope · MIT · pandoscope/skills
Похожие скиллы
Все скиллы по теме «Тесты и QA» →Сначала — tdd от других авторов.