Skip to content
Development
Skill

/tdd

Runs test-driven development with a red-green-refactor loop, an opt-in tool for logic where test-first helps. Use when the user wants to build features or fix bugs using TDD, mentions "red-green-refactor", wants integration tests, or asks for test-first development.

From plugin
gaia
2619 skills18 agents12 commands

Claude Code

$ npx -y skills add gaia-harness/gaia --skill tdd --agent claude-code

SKILL.md

tdd.SKILL.md
name: tdd
description: Runs test-driven development with a red-green-refactor loop, an opt-in tool for logic where test-first helps. Use when the user wants to build features or fix bugs using TDD, mentions "red-green-refactor", wants integration tests, or asks for test-first development.

Test-Driven Development

An opt-in tool for logic where test-first helps, not a rule: the principle is Test What Matters (`.claude/rules/coding-guidelines.md`, section 3). The skill verifies its own red step: run the new test, see it fail for the right reason, then implement. No hook or ledger records it.

Contents: Selecting a Stack Reference; Philosophy; Anti-Pattern: Horizontal Slices; Workflow; Checklist Per Cycle.

Selecting a Stack Reference

Before writing the first red test, consult the reference for your stack:

  • **React / Vitest / MSW / Storybook** → the `tdd-react` skill (`apps/web/.claude/skills/tdd-react/SKILL.md`)
  • **Shell / bats** → `.claude/skills/tdd/references/tests-shell.md`

Add a new stack reference (a skill or a `references/tests-{stack}.md`) when adopting a new stack. The stack reference covers concrete patterns, test layers, mocking rules, and good/bad examples specific to that environment.

Philosophy

**Core principle**: tests verify behavior through public interfaces, not implementation details. Good tests are integration-style: they exercise real code paths through public APIs, read like a specification ("user submits a valid form and sees a success toast"), and survive refactors. A test that breaks on a behavior-preserving refactor is coupled to implementation.

Anti-Pattern: Horizontal Slices

**DO NOT write all tests first, then all implementation.** This is "horizontal slicing", treating RED as "write all tests" and GREEN as "write all code."

Tests written in bulk test _imagined_ behavior, not _actual_ behavior. You outrun your headlights, committing to structure before understanding the implementation, producing tests insensitive to real changes.

**Correct approach**: vertical slices via tracer bullets. One test → one implementation → repeat.

WRONG (horizontal):
  RED:   test1, test2, test3, test4, test5
  GREEN: impl1, impl2, impl3, impl4, impl5

RIGHT (vertical):
  RED→GREEN: test1→impl1
  RED→GREEN: test2→impl2
  RED→GREEN: test3→impl3
  ...

Workflow

1. Planning

Before writing any code:

  • [ ] Confirm which layer owns this test (see stack reference for layer breakdown)
  • [ ] Confirm which behaviors to test (prioritize)
  • [ ] Name the user-observable behaviors and the one acceptance test that owns each; one test may own several (not implementation steps, not one test per function)
  • [ ] Get user approval on the plan with `AskUserQuestion`: `Approve the plan` (Recommended) or `Revise`

Ask: "What should the public interface look like? Which behaviors are most important to test?"

**You can't test everything.** Focus on critical paths and complex logic, not every edge case.

2. Tracer Bullet

Write ONE test that confirms ONE thing end-to-end for this layer. `RED → GREEN`. The tracer bullet confirms the testing infrastructure wires up before adding real coverage.

3. Incremental Loop

For each behavior your task names (a plan's task doc, or the plan approved in step 1): `RED → GREEN`, extending the test that already owns it, if one does, rather than writing a second. Write a new test only for those behaviors and their acceptance tests, plus a unit test for logic meeting both unit-test conditions (`wiki/decisions/Test Layering.md`): the integration layer cannot cover its cases cheaply, and the logic is important or likely to fail. Only enough code to pass the current test. Don't anticipate future tests.

**Bound the green chase.** If a test won't pass after a few focused attempts, stop and reassess instead of thrashing the implementation: the test, the interface, or an assumption may be wrong. Surface the blocker rather than looping indefinitely to force green.

Running an honest red step

For every new test, run it before implementing and look at the failure: `pnpm -C <package dir> test --run <test-file>` (path relative to the package), or the stack reference's runner. It must fail for the right reason, an assertion that the missing behavior breaks (or the stub's throw), never a collection or import error.

**Author the test against a stub of the implementation symbol.** First write a stub of each new exported symbol the test imports, under its final name and with its final signature (async-ness, parameter and return types; a Promise-returning function is stubbed async, since type-aware lint on the test reads the signature), returning a wrong value (throwing only when the symbol has no return value to get wrong). Write the test against the stub, lint both, then run it: it fails on the assertion (or the stub's throw) because the implementation is missing. That failure is the honest red, not a manufactured one. Then write the implementation that turns it green. Never point the test at a module file that does not exist yet: vitest fails the file at collection, no test body runs, and nothing shows the test can fail.

RIGHT:  stub parseAmount() returning a wrong value → write the test → lint → run → fails on the assertion → implement → green
WRONG:  write the test importing a parse-amount module that does not exist → run → collection error, no honest red
WRONG:  implement parseAmount() → write the test → break parseAmount() to force red → restore it → green

**Never break working production code to force a red, then restore it.** Nothing enforces this, so it is an authoring obligation.

**Single-pass-author exemption.** When the implementation already exists in the same change with no prior failing observation (a single-pass author landing impl and test together), author the test honestly against the existing behavior and route it to the worthiness audit, exactly as an emergent test is routed.

4. Refactor

Af

Read more
@gaia-react
Ships withgaia

Claude is raw power. GAIA is order and focus. A serious harness for Claude Code. It keeps Claude-shipped code production-grade as your team scales. The React frontend is handled. You build the rest of your app on top.

2619 skills18 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
26
Star history of gaia-react/gaia on GitHub, a chart by star-history.com

Other skills on gaia.

file-tech-debt

file-tech-debt

Files a new tech-debt GitHub issue for an out-of-scope code-review finding. Builds the dedup key, skips an open or…

@gaia-react@gaia-reactInstall Skill
gaia-wiki

gaia-wiki

Runs GAIA wiki maintenance (sync, consolidate, lint with fixes) in one command and lands it as one commit and PR. Trigger on…

@gaia-react@gaia-reactInstall Skill
naming-conventions

naming-conventions

Applies descriptive, self-documenting naming in any language (TypeScript, bash, workflow YAML, Dockerfiles). Use this skill…

@gaia-react@gaia-reactInstall Skill
release-notes

release-notes

Maintainer-only. Rewrites a version's GAIA CHANGELOG entries as plain-language public release notes for the marketing site…

@gaia-react@gaia-reactInstall Skill
update-deps

update-deps

Remediates dependencies in place of Dependabot version and security updates. Discovers outdated packages and open security…

@gaia-react@gaia-reactInstall Skill
update-gaia

update-gaia

Pulls the latest GAIA release into this project without clobbering customizations, with a three-way merge per file using…

@gaia-react@gaia-reactInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills10d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc11d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about tdd.

Which coding agents can use tdd?
tdd installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.