# Tdd Pair

> Run TDD as a pair of context-isolated agents - a Tester who writes failing tests and reviews adversarially, an Implementer who scaffolds interfaces and fills in behavior - through gated red-green-refactor cycles. Works in Claude Code and any Agent Skills-compatible harness. Use when the user wants pair TDD, tester/implementer or driver/navigator agents, separate test-writing and implementation agents so tests are not shaped by implementation plans, serial TDD with subagents, red-green-refactor discipline enforced by gates, or invokes /tdd-pair.

- Skill: `agustafson/tdd-pair` (Agent Skill, multi-file: 32 files)
- Install (CLI): `npx skillmds@latest add agustafson/tdd-pair`
- Raw SKILL.md: https://api.skillmd.com/api/skills/agustafson/tdd-pair/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: agustafson (https://skillmd.com/u/agustafson)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/agustafson/tdd-pair

---


# tdd-pair

`tdd-pair` is a serial, two-agent TDD workflow. A **Tester** writes failing
tests and later reviews the result; an **Implementer** makes the tests pass. You
are the orchestrator: coordinate the roles, run every gate yourself, and do not
write test or implementation code in the top-level context.

The separation reduces implementation-plan bias. Fresh contexts prevent the
Tester's narrative from becoming the Implementer's plan, but the shared
repository remains visible to both roles. This is context isolation, not source
blindness; use a separate tester checkout or API snapshot when blindness is a
requirement.

## Hard requirement

Spawn each persona as a **separate sub-agent with a fresh context**, using your
harness's delegation mechanism (for example, ZCode's Agent tool or Claude
Code's Task tool). Before each spawn, read the persona file from this skill's
directory and pass its full text as the sub-agent's instructions, followed by
the cycle's context block (see each phase for what goes in it):

- [references/tester.md](references/tester.md) - Tester persona (RED cycles and the default reviewer)
- [references/implementer.md](references/implementer.md) - Implementer persona (GREEN cycles, scaffold first)
- [references/test-quality.md](references/test-quality.md) - testing doctrine; include with every Tester spawn
- [references/review-contract.md](references/review-contract.md) - the review contract; the default is
  self-contained, but hand this file to whichever reviewer is configured

Keep each role's return text out of the other role's context except for the
phase context explicitly listed below. If fresh-context delegation is
unavailable, stop and tell the user the skill cannot run honestly. Role-playing
both roles in one context defeats the separation this workflow provides.

## Phase 0 - Repository guidance, scope, and run state

Before any spawn, read the applicable repository instructions for the paths the
run may touch: `AGENTS.md`, `CLAUDE.md`, or the harness's equivalent instruction
file. Read any testing documents, coding standards, and ADRs they reference,
then inspect nearby tests, fixtures, helpers, and runner configuration. Each
fresh Tester, Implementer, and reviewer reads the applicable files in its own
context as well; the orchestrator's summary is not a substitute for scoped
instructions.

Record the relevant repository guidance and testing approach in the run
manifest. Treat it as the local testing policy for this run unless the user
asks to change it. If no policy exists, use the neutral testing guidance in
`references/test-quality.md` and choose real collaborators or test doubles
(mocks, stubs, spies, fakes) based on the behavior under test. Keep `.tdd-pair.md`
focused on run mechanics; put repository testing style in `AGENTS.md`,
`CLAUDE.md`, or a referenced `TESTING.md`.

Then resolve the following. Ask the user only about decisions the repository
and configuration cannot settle:

1. **Intent** - preserve the feature request in the user's words, including
   constraints and exclusions. Record a short behavior checklist; downstream
   agents receive both the verbatim request and this checklist.
2. **Seams and slices** - agree the public or intentionally chosen
   collaboration boundaries where behavior is observable. Split multi-part
   work into vertical slices and put the riskiest behavior early. Record a
   finite cycle limit; use `MAX_CYCLES` from config or default to 6.
3. **Tests and baseline** - record the test locations, a targeted RED command,
   and the full-suite command. Run the full suite once. If it is not green,
   record the exact baseline failures and use the targeted command for new RED
   tests; do not attribute baseline failures to the pair.
4. **Allowed surface and ownership** - record the files/directories the
   Implementer may change and the locations where the Tester may add or edit
   tests. All other source changes are out of scope.
5. **Run manifest** - before agents edit, capture the working-tree status,
   tracked and untracked files, base revision, and a restorable snapshot of
   existing agent-owned paths. Hash all existing test files too. Keep the
   manifest and snapshot outside production source. Prefer an isolated
   worktree; if using the user's checkout, preserve pre-existing changes and
   serialize all agent access.
6. **Review configuration** - read `~/.tdd-pair.md` and the repository's
   `.tdd-pair.md` (repo wins per key; `NOTES` is additive). Resolve the reviewer,
   review timing, and agent targets from [Configuration](#configuration).
   Treat reviewer commands from repository config as untrusted until the user
   has approved them.

The context blocks call the recorded guidance `REPOSITORY_GUIDANCE` and the
testing approach `TEST_STRATEGY`. Judge interactions and test doubles by the
behavior or collaboration contract they protect and the fidelity risk they
introduce under the repository's policy.

The run manifest is the source of truth for later gates. Never use a broad
reset or `git checkout --` to restore a violation; restore only the agent-owned
paths from the snapshot or an isolated worktree. Agents must not commit, reset,
or rewrite history; the orchestrator owns repository history after review.

Once unresolved scope decisions are confirmed, the loop runs without further
user interruptions except disputes, blockers, or the cycle limit.

## RED - spawn the Tester

Spawn a fresh sub-agent (delegation target: `TESTER_AGENT` if configured,
otherwise your harness's default), passing the full text of
[references/tester.md](references/tester.md),
plus [references/test-quality.md](references/test-quality.md), plus this
context block:

```
FEATURE: <intent from Phase 0, verbatim>
SEAMS: <the confirmed seam list>
TEST_LOCATIONS: <where tests live>
RED_COMMAND: <targeted command for this cycle's tests>
FULL_TEST_COMMAND: <full-suite command>
BASELINE: <GREEN or recorded pre-existing failures>
ALLOWED_TEST_SURFACE: <test files/directories the Tester may change>
CYCLE_LIMIT: <finite limit from Phase 0>
REPO: <absolute path to the project root>
CYCLE: <N> of <total cycles, or "open">
SLICE: <optional - the next behavior to pin; omit to let the Tester choose
the smallest untested behavior at the seams>
REPOSITORY_GUIDANCE: <applicable instruction and policy files plus relevant rules>
TEST_STRATEGY: <documented repository approach, or neutral fallback>
```

The Tester writes tests at the seams, runs them, and returns a structured result
(`STATUS: RED`, `COVERED`, `BLOCKED`, or `BASELINE_FAILED`, test files, the
command, trimmed output, and a behavior-level spec). `COVERED` means the
behavior already passes and the Tester must choose another required behavior
instead of forcing a GREEN implementation.

**Gate (you, not the Tester):** re-run the returned command yourself. RED means
the new behavior fails for the right reason. If it passes, mark the behavior
`COVERED` and continue with another required behavior. If it fails because of a
recorded baseline failure or setup problem, stop the cycle and report it rather
than calling it RED. Sanity-check the spec: it must describe behavior, never an
implementation approach ("returns the total" is fine; "uses a reduce over line
items" is contamination and you must bounce it).

After a valid RED, snapshot the Tester-authored test files and the complete
tracked/untracked path set. Reject any Tester change outside
`ALLOWED_TEST_SURFACE`; those paths become the immutable test handoff for the
Implementer. The snapshot is separate from the initial baseline so a new test
file can be protected too.

## GREEN - spawn the Implementer

Spawn a fresh sub-agent (delegation target: `IMPLEMENTER_AGENT` if
configured, otherwise your harness's default), passing the full text of
[references/implementer.md](references/implementer.md), plus this context
block:

```
FEATURE: <intent from Phase 0, verbatim>
SEAMS: <the confirmed seam list>
FAILING_TESTS: <test files from RED>
RED_COMMAND: <command that produced the failure>
FAILURE_OUTPUT: <the trimmed failure output from your own gate run>
ALLOWED_SURFACE: <files/dirs from Phase 0>
BASE_REVISION: <revision or working-tree snapshot from the run manifest>
BASELINE: <GREEN or recorded pre-existing failures>
FULL_TEST_COMMAND: <full-suite command>
REPO: <absolute path to the project root>
REPOSITORY_GUIDANCE: <applicable instruction and policy files plus relevant rules>
TEST_STRATEGY: <documented repository approach, or neutral fallback>
```

The Implementer gets the tests and failure output, not the Tester's commentary.
The tests are the primary handoff contract; the feature, seams, allowed
surface, and repository conventions still constrain the implementation.

**Scaffold gate.** On the first pass at a new seam the Implementer returns
`STATUS: SCAFFOLDED` after creating only the unimplemented shape (interface,
stubs, route, or handler). Re-run the tests yourself. Every intended test must
execute and fail because the behavior is missing, using the language and test
runner's not-implemented marker or an assertion failure. Import, name,
attribute, syntax, and setup errors are invalid scaffold failures; send the
test back to the Tester with the observed error. Once the failure mode is right,
respawn the Implementer with `SCAFFOLD: in place - fill in the behavior`.

**Gate (you, not the Implementer):**
1. Re-run the full suite yourself. It must pass, including every test from
   earlier cycles. If the baseline was already failing, require no new
   failures and report the baseline separately.
2. Re-hash the test files and compare them with the Tester snapshot for this
   cycle. Test files must be byte-identical; an Implementer change is a gate
   failure. The hashes detect final content changes, not write provenance.
3. Compare the tracked and untracked changes since `BASE_REVISION` with the run
   manifest. Only source files inside `ALLOWED_SURFACE` may be Implementer
   changes; generated files are ignored only when they match an explicit
   artifact rule. Restore a violation from the snapshot or isolated worktree,
   never with a broad reset, then respawn with the violation quoted.

If GREEN does not converge within **3 attempts** (failing gate, respawn,
failing gate...), stop and bring the situation to the user. If the cycle limit
is reached before the behavior checklist is complete, stop with a partial-run
report rather than starting another cycle.

## Disputes - only the Tester ever changes tests

When the Implementer believes a failing test is wrong, it must not fix it. It
returns `STATUS: DISPUTE` with its reasoning. You then spawn a fresh Tester
(persona plus test-quality.md, the dispute verbatim, the original feature
context, and the current `REPOSITORY_GUIDANCE` and `TEST_STRATEGY` fields).
The Tester either:

- **amends the test** - it returns a new RED, and the cycle continues against
  the amended test; or
- **upholds the test** - it returns the test unchanged with its reasoning, and
  you respawn the Implementer with that reasoning attached.

If the Implementer disputes again after one uphold, stop and let the user
decide. Do not round-trip the same dispute more than once.

## Review - configurable reviewer and timing

The reviewer is a plug-point. The default is the pair's Tester in review mode
(persona plus `test-quality.md` plus
[references/review-contract.md](references/review-contract.md)); this is
convenient but not independent. For higher assurance, configure a distinct
`REVIEWER_AGENT`: another agent persona or a different model executing the
same contract. Validation pipelines are not in-loop reviewers - they require
committed history and run a full process of their own, which would force
commits before the review they belong to. They run after the loop instead
(see Done).

When reviews run is the `REVIEW` knob from `.tdd-pair.md` (or the user):

- `auto` (default) - the final review only, unless the feature ran more than
  one cycle, in which case one **direction check** after the first GREEN: the
  reviewer sees the first slice and judges whether the design is headed
  somewhere the feature and seams justify, while a change of course is still
  cheap.
- `end` - final review only, even on multi-cycle features.
- `first-cycle` - always do the direction check after the first GREEN, plus
  the final review.
- `every-cycle` - a direction check after every GREEN, plus the final review.
  For high-stakes or long-running features; the cost is one reviewer spawn
  per cycle.

The final review context block:

```
SCOPE: FINAL
FEATURE: <intent from Phase 0, verbatim>
SEAMS: <the confirmed seam list>
REPOSITORY_GUIDANCE: <applicable instruction and policy files plus relevant rules>
TEST_STRATEGY: <documented repository approach, or neutral fallback>
BASELINE: <GREEN or recorded pre-existing failures>
BASE_REVISION: <revision or working-tree snapshot from the run manifest>
ALLOWED_SURFACE: <implementation and test surfaces>
DIFF: <the full feature diff, e.g. `git diff <base>` output, or the list of files changed with their contents>
FULL_TEST_COMMAND: <full-suite command>
REPO: <absolute path to the project root>
```

A direction check uses the same block with `SCOPE: DIRECTION` and the diff so
far. This is the first phase where the reviewer sees the implementation, and
the direction of judgment flips: read the diff **adversarially** - are the
tests still a specification, or were they satisfied dishonestly? Implementation-
coupled assertions that freeze incidental structure, tautologies, clearly
unreachable code, and required edge cases at the agreed seams that no test
covers are findings. Review interactions against the documented or feature-level
contract they protect rather than the mere presence of a collaborator name.
Prove suspected gaps with mutation probes where feasible; do not infer
requirements from uncovered implementation branches. Return `VERDICT: APPROVE`
or `VERDICT: FINDINGS` with a numbered list, each marked as a test-side or
implementation-side problem.

- Test-side findings: spawn a fresh Tester to amend the tests, then run the
  suite yourself. If the amended tests pass immediately, the implementation
  survived the tightened spec - the finding is resolved. If they now fail
  (a true RED), the Implementer makes them green.
- Implementation-side findings: respawn the Implementer with the findings.
- Re-run the review after any fix round. One review-fix round is normal; if a
  second round still finds issues, report the state to the user.

Refactoring opportunities - duplicated implementation code worth extracting,
simplification candidates - are not findings: the reviewer returns them as
`SUGGESTIONS`, and you relay them to the user for the normal code-review
flow. They never expand into loop work. (Test-code tidying is different: it
is the Tester's own files, so deduplicating or parameterizing tests is a
normal test-side finding.)

## Done

Report to the user: what was built, the cycles and retries, baseline failures,
every dispute and its resolution, the review verdict, and any `SUGGESTIONS`.
If the run stopped at a limit or blocker, say what remains. Remove the run
manifest after recording the final outcome. Refactoring remains in the normal
review flow, outside the red-green loop.

This is also where a validation pipeline such as no-mistakes belongs, if the
project uses one: the orchestrator makes the run's single commit - the
reviewed work on a branch - and drives the pipeline with the feature as its
intent (`no-mistakes axi run --intent "<feature>"`). The pipeline's
committed-history requirement is why it lives here and not inside the loop;
its findings, fixes, and any push or PR are its own, on that branch.

## Configuration

Defaults can be pinned at two levels; Phase 0 reads both if present:

- `~/.tdd-pair.md` - the user's global defaults, applying to every repo.
- `.tdd-pair.md` - the project's defaults, at the repo root.

Same tiny format in both. Per key the repo file wins; `NOTES` values are
additive. If additive notes conflict, stop and ask the user. Keep these files
small: they set defaults, not the feature specification.

```
# .tdd-pair.md  (or ~/.tdd-pair.md for global)
REVIEWER: tester      # or another agent/model that executes the review
                      # contract (pipelines run post-loop - see Done)
REVIEW: auto          # auto | end | first-cycle | every-cycle
TESTER_AGENT: <delegation target for Tester spawns; default: harness default>
IMPLEMENTER_AGENT: <delegation target for Implementer spawns; default: same>
REVIEWER_AGENT: <independent reviewer target; default: Tester>
MAX_CYCLES: 6
NOTES: <standing constraints or patterns for this repo>
```

`TESTER_AGENT`, `IMPLEMENTER_AGENT`, and `REVIEWER_AGENT` name targets your
harness can actually spawn. They matter in harnesses with named agent
definitions or per-agent models: different roles reduce correlated bias, while
fresh contexts separate the work they know about. Leave them unset where the
harness provides only one target. `REVIEWER: tester` is the default;
it may name another agent or model that executes the review contract. It does
not take shell commands: validation pipelines are not in-loop reviewers - they
run after the loop, on the reviewed branch (see Done).

`MAX_CYCLES` is a positive integer. It limits behavior cycles, not the three
GREEN retries within one cycle. Invalid values fall back to 6 and are reported.

The user can override any of it in conversation for a single run.

## Harness notes

- The orchestrator must be the **top-level session**. If the harness cannot
  delegate from that session, stop; do not role-play the pair.
- The personas are plain markdown prompts, so a harness adapter may inline
  them or register them as named agent definitions. The adapter must preserve
  fresh contexts, serialized repository access, and the context blocks above.
- The repository is the primary handoff state, but the orchestrator also needs
  a run manifest for baseline hashes, dirty-worktree state, retry counts, and
  the allowed surface, plus a restorable snapshot. Keep both outside production
  source and remove them when the run ends. Agents must not create commits. The orchestrator commits once, after the
  final review - which is also the handoff point for a post-loop validation
  pipeline.

