| name | tdd |
| description | Implement changes using Test-Driven Development (Red-Green-Refactor). Use for bug fixes, new features, or any code change that should have test coverage. |
TDD
Implement code changes using strict Red-Green-Refactor. Iron law: no production code without a failing test first.
Wrote code before a test? Delete it. Start over from a failing test.
Available skills and subagents
/e2e — Use when the change needs a Playwright E2E test instead of a unit test.
/verify — Run after completing all TDD cycles to ensure everything passes across the monorepo.
When to use
- Bug fixes — write a test that reproduces the bug before fixing
- New functions, methods, or utilities
- Refactoring existing logic that lacks tests
Skip for: pure UI components (we don't test React components), config files, generated code.
For UI rendering bugs, prefer extracting or using a pure helper and testing that helper. Add Playwright only when the behavior is truly visual or integration-level. Avoid adding React component tests just to assert DOM output; that does not match this project's testing convention.
Determine test scope
Choose the right level:
- Unit: pure logic or isolated service behavior.
- Integration: handler/service/repository boundaries, SQLite-backed flows, filesystem behavior, or process boundaries.
- E2E: critical user-facing browser flows; keep these focused and use
/e2e.
Prefer state/output assertions over interaction assertions. Mock only slow, nondeterministic, or external boundaries; use real implementations or fakes when they keep the test deterministic.
Steps
1. RED — Write a failing test
- Identify the single behavior to implement or bug to reproduce
- Write the smallest test that asserts the expected behavior — one assertion, clear name
- Run the test and confirm it fails with the expected assertion error (not a compile/import error)
- If it passes immediately, the test is not testing new behavior — revise it
For bug fixes, use the Prove-It Pattern: reproduce the bug with a failing test before changing production code. A fix without a regression test is not complete unless the change is explicitly untestable and you say why.
2. GREEN — Minimal code to pass
- Write the minimum production code to make the failing test pass
- Do not add extra logic, handle other edge cases, or refactor yet
- Run the test again and confirm it passes
- If it fails, fix the production code (not the test)
3. REFACTOR — Clean up
- Improve production code: extract helpers, rename, simplify — without changing behavior
- Improve tests: table-driven tests (Go) or
describe/it blocks (TS), remove duplication
- Run the test after each change to confirm still green
In tests, prefer DAMP over DRY: each test should read like a small specification. Shared helpers are fine when they remove noise, but not when they hide the scenario.
4. Repeat
Return to step 1 for the next behavior or edge case. Continue until the feature or fix is complete.
5. Final verification
Run /verify to ensure all formatters, linters, typechecks, and tests pass across the monorepo.
Testing anti-patterns
Don't test implementation details:
- Assert behavior, state, API response, DB row, emitted event, or UI outcome. Avoid assertions that only prove a helper was called or an internal query string happened to be built a certain way.
Don't test mock behavior:
- If your assertion checks a mock element (
*-mock test ID, mock return value), you're testing the mock, not the code. Test real behavior or don't mock it.
Don't add test-only methods to production code:
destroy(), reset(), _testHelper() that only tests call — put these in test utilities, not production classes.
Mock minimally and understand dependencies:
- Before mocking, ask: what side effects does the real method have? Does the test depend on any of them?
- Mock the slow/external part (network, disk), not the method the test depends on.
- If mock setup is longer than test logic, consider an integration test instead.
Don't use incomplete mocks:
- Mock the complete data structure as it exists in reality, not just fields your test uses. Partial mocks hide bugs when downstream code accesses omitted fields.
Never swallow errors in tests:
try/catch that silently ignores failures in test helpers or setup — these hide real failures.
Don't repeat unchanged passing commands for reassurance:
- After a clean targeted run, re-run only after code or test inputs change. Move to the next required verification step instead.
Red flags
- Writing production code before a failing test exists — delete and start over
- Test passes on first run — it tests nothing new, revise the test
- Fixing a test to make it pass instead of fixing the production code
- Large jumps — multiple behaviors implemented between test runs
- Skipping the refactor step
- Mock setup longer than test logic — consider integration test
- Asserting on mock elements instead of real behavior
- "All tests pass" but no relevant test was actually run