Files
Skills/tdd/SKILL.md
T
Brummel 7a58a530b1 feat(pipeline): route to the lightest correct methodology; move execution loops onto the Workflow substrate
The selector forced every task through the heaviest methodology's
critical path: a behaviour-preserving, type-enumerable change paid the
same specify -> planner -> implement front-half as a novel feature,
because it was neither new behaviour (tdd) nor an observed bug (debug)
and so fell to specify by elimination. Two coupled defects — a selector
with no verification axis, and an all-or-nothing executor — kept the
existing lighter path unreachable and uneconomical. This fixes both.

Part A — verification-keyed selector (boss/SKILL.md):
- Replace the three-way "design line" with an ordered cascade that adds
  a verification/enumeration axis ahead of the settled-vs-fork question.
  Each lighter arm carries a positive trigger matched by signature, not
  reached by elimination.
- New `compiler-driven` arm: a type/signature edit at a definition site
  that propagates mechanically. Observe-then-bounce — make the edit,
  build, run the suite; clean build AND suite green unchanged commits;
  a hole bounces up (specify for a design choice, tdd for discovered
  test-specifiable new behaviour); a regression bounces to debug.
- The observed-bug RED-first gate is first in the cascade, so a
  mechanical-looking fix cannot bypass it.
- The straddle rule ("add an enum variant") is codified as a rule:
  mechanical/forwarding -> compiler-driven; encodes new behaviour ->
  tdd/spec; doubt routes up.
- The executor is the elevated inline carve-out plus a shipped workflow,
  not a heavy new skill ("the largest concrete win is small").

Part B — Workflow substrate (implement/workflows/):
- implement-loop.js: the per-task loop as a deterministic script. Each
  phase (implementer -> spec-compliance -> quality, + tester for E2E) is
  a separate top-level agent() call, so a single phase is independently
  invokable and inter-phase aggregation/re-loop is code. Retires the
  implement-orchestrator agent's inline-role-switch workaround (the four
  phase agents survive as the agent-types the script dispatches).
- compiler-driven-edit.js: the observe-then-bounce loop.
- install.sh / uninstall.sh symlink shipped workflows into
  ~/.claude/workflows/.
- specify and brainstorm stay prose + interactive (human-intent oracle);
  only the autonomous/mechanical loops moved. try-and-error is deferred.

Docs (pipeline taxonomy, design, agent-template, migration, README) and
all selector<->executor cross-references updated; the arm and its
executor are co-located so a future re-route through the full loop is a
visible regression.

Verified by an adversarial multi-agent pass: PASS on all six acceptance
criteria; two coherence concerns fixed. The shipped scripts are
syntax-validated but exercised only in a downstream target project (the
skills repo is not itself a pipeline target).

closes #7
2026-06-17 12:27:51 +02:00

226 lines
12 KiB
Markdown

---
name: tdd
description: Use when a feature description or tracker issue has test-specifiable behaviour — the "how it should work" can be pinned as a single failing test. Authors a minimal RED executable-spec and hands the GREEN side to implement mini-mode, exactly as a bug fix is handled. The alternative entry path to brainstorm→specify→planner for test-specifiable work; bounces back to brainstorm the moment the behaviour is not test-specifiable (genuine design forks). Distinct from the per-task TDD the implementer already practices inside implement.
---
# tdd — executable-spec-first entry path
> **Violating the letter of these rules is violating the spirit.**
## Overview
For work whose desired behaviour is expressible as a test, the
test *is* the spec. This skill is the alternative to the
`brainstorm → specify → planner` design path: instead of a prose spec the
user approves, it produces a single minimal **RED executable-spec**
that says "this is how it should work", then treats that test
exactly like a bug to be fixed — handing the GREEN side to the
`implement` skill in mini mode, the same two-stage RED-first
handoff `debug` uses for bugs.
Skipping the executable-spec stage — jumping straight to
implementation because "the feature is obvious" — produces code
with no failing test that ever pinned the intended behaviour, the
exact gap RED-first exists to close.
**This is not the per-task TDD the implementer already practices.**
Inside `implement`, the implementer writes a test per plan task as
an inner-loop discipline. This skill is the *entry-point* form: it
authors the one headline executable-spec that defines what the
whole iteration delivers, before any plan or task exists. The two
compose — this skill's headline test sits above the implementer's
per-task tests, which drive it green from underneath.
The substantive process — turning a description into a minimal,
autonomous RED test, the design-fork bounce-back, and the reactive
decomposition into BLOCKER sub-tests when one iteration can't reach
GREEN — lives in `agents/tdd-author.md`. That file is the single
source of truth for the discipline; this skill file governs only
trigger, dispatch, the orchestrator-side loop, and handoff.
## When to Use / Skipping
Trigger this skill on:
- a feature description whose desired behaviour can be stated as a
single falsifiable assertion ("calling X with Y returns Z",
"endpoint /a rejects payload b with 422", "flag --foo makes the
CLI emit bar")
- a tracker issue (`feature` / `bug` / unlabelled concrete task)
whose body pins one observable behaviour
**Use `tdd` instead of `brainstorm` only when the behaviour is
test-specifiable.** The honest test is the test of this: if you
can write one minimal assertion that, green, means "the feature
works as described", the work is test-specifiable and `tdd` owns
it. If writing that assertion forces a choice between two or three
plausible behaviours with real trade-offs, the work has a genuine
design fork — that is `brainstorm`'s job, and `tdd` must bounce
back (see The Bounce-Back below). `tdd` never ratifies a *guessed*
design into a test; guessing-a-design-as-a-test is the exact
reactive-deference failure the `brainstorm` hard-gate exists to
prevent, and it is no less a failure for being dressed as a test.
**Skip `tdd` (and use the named alternative) for:**
- an observed bug — use `debug` directly (it is already RED-first;
`tdd` is for new behaviour, not a regression of existing
behaviour).
- a cycle with genuine design forks — use `brainstorm`.
- a tidy iteration — use `audit`.
- a trivial mechanical edit — per the project's CLAUDE.md
"trivial mechanical edits" carve-out.
This skill is a standard entry path, always available — one of the
three design entries alongside `brainstorm → specify → planner` and
`specify → planner`. Which one a given iteration uses is a fit
decision per item, not a project setting.
## The Iron Law
```
THE EXECUTABLE-SPEC (RED TEST) COMES FIRST — BEFORE ANY IMPLEMENTATION.
THE RED TEST IS MINIMAL AND AUTONOMOUS, AND IT ENCODES THE DESIRED BEHAVIOUR ("HOW IT SHOULD WORK"), NOT THE CURRENT ONE.
IT MUST FAIL FOR THE RIGHT REASON — FEATURE ABSENT — NOT A SYNTAX OR SCAFFOLDING ERROR.
NO FIX ATTEMPT IN THIS DISPATCH — THE GREEN SIDE GOES TO `implement` MINI-MODE.
IF THE BEHAVIOUR IS NOT TEST-SPECIFIABLE (DESIGN FORKS SURFACE), STOP AND BOUNCE TO `brainstorm`. DO NOT GUESS A DESIGN INTO THE TEST.
IF ONE ITERATION CANNOT REACH GREEN, DECOMPOSE INTO BLOCKER SUB-TESTS — DO NOT PUSH HARDER ON THE HEADLINE.
```
Every clause is non-negotiable.
## Dispatch
Dispatch the `tdd-author` agent with the carrier fields it defines
under **Carrier contract** in `agents/tdd-author.md``source`,
`intent`, `mode`, `blocked_report`. That table is the authoritative
definition of those fields; it is deliberately not restated here,
so the two files cannot drift.
The agent writes the RED test to the working tree (uncommitted)
and reports the handoff carrier for `implement` mini-mode. The
agent does NOT commit anything, and does NOT write the feature —
splitting RED (this skill) and GREEN (`implement` mini-mode) across
two dispatches keeps the spec honest: the test is written before
any implementation, so it genuinely pins the intended behaviour,
not the post-hoc code path. The orchestrator decides whether to
commit the RED test as a separate audit-trail commit before
dispatching `implement` mini-mode, or to hand the dirty working
tree directly to mini-mode (the mini-mode orchestrator's Phase-0
clean-tree check refuses the latter — so for an audit-trail flow
the orchestrator commits the RED test first; for a streamlined flow
it commits the combined RED+GREEN at the end of mini-mode). This is
the same commit-shape decision `debug` hands the orchestrator.
## The orchestrator-side loop
```
1. Dispatch tdd-author (mode: author).
- BLOCKED (design fork) → go to The Bounce-Back.
- NEEDS_CONTEXT → ask the user / read the issue, re-dispatch.
- DONE → headline RED test in the working tree.
2. Dispatch `implement` mini-mode on the handoff carrier (drive GREEN).
- DONE → the headline test is green; the feature shipped. Commit.
- PARTIAL / BLOCKED → one iteration could not reach GREEN.
Re-dispatch tdd-author (mode: decompose) with the implement
BLOCKED report as `blocked_report`. Go to step 3.
3. tdd-author (decompose) carves the headline into a ladder of
BLOCKER sub-tests and marks the headline blocked-on them.
- DONE → the first sub-test is the next implement target.
Return to step 2 with that sub-test as the carrier; when all
sub-tests are green the headline test is un-blocked and
re-targeted.
- BLOCKED (design wrong) → go to The Bounce-Back.
```
The headline test is never edited to make it pass — it is the
contract. It only changes shape via a deliberate decompose
re-dispatch, which adds sub-tests below it; the headline's own
assertion stays fixed.
## The Bounce-Back
`tdd` returns to `brainstorm` whenever the work turns out not to be
test-specifiable: either the `tdd-author` cannot write one honest
headline assertion without choosing between plausible designs
(`author` mode), or decomposition reveals the design itself is
wrong rather than the granularity (`decompose` mode). The
orchestrator:
1. Discards the working-tree test changes the agent left
(`git checkout -- .` / `rm` the new test files — they were
never committed, and a test that encodes a guessed design is
worse than no test).
2. Invokes `brainstorm` with the design question the agent
surfaced as the cycle request. From there the normal
`brainstorm → specify → planner → implement` path runs.
The bounce-back is not a failure of this skill; it is the skill
correctly refusing to ratify an un-chosen design into a test.
## Handoff Contract
| Direction | Carrier |
|-----------|---------|
| user / issue → `tdd` | feature description or issue ref with test-specifiable behaviour |
| `tdd``implement` mini-mode | `red_test_path` + `spec_summary` + minimal-feature constraint (the carrier the agent defines under **Output format**) |
| `tdd``tdd-author` (decompose) | the `implement` BLOCKED report, on a GREEN that one iteration could not reach |
| `tdd``brainstorm` (bounce-back) | the design question the agent surfaced; working-tree test discarded |
Anything beyond the one headline behaviour (a broader feature set,
a refactor, a doc rewrite) is OUT of scope for the executable-spec
iteration and gets queued for a separate one.
## Common Rationalisations
| Excuse | Reality |
|--------|---------|
| "The feature is obvious, just implement it — skip the RED test" | An obvious feature is 30 seconds to assert and 30 seconds to leave as a regression guard. Without the RED test, no test ever pinned the intended behaviour, and the next change that breaks it is silent. |
| "I'll write the feature and the test together" | Test-after proves nothing about whether the test would have failed before the feature existed. RED before GREEN — always; the split across two dispatches is the whole point. |
| "There are two plausible behaviours but I'll pick one and test it" | Picking one is a design decision, and a guessed design baked into a test is harder to dislodge than a prose spec. That is the bounce-back trigger — stop and go to `brainstorm`. |
| "One iteration didn't reach green, push harder on the headline test" | Pushing harder is hunch-driven. The disciplined move is to decompose into BLOCKER sub-tests; if decomposition can't find a test-specifiable smallest step, the design is wrong, not the effort. |
| "This is a bug, I'll use tdd because I like the flow" | A regression of *existing* behaviour is `debug`'s job. `tdd` is for *new* behaviour. Using the wrong entry path muddies which tests guard regressions vs which define new contracts. |
| "Edit the headline test a little so implement can pass it" | The headline test is the contract. Editing it to pass is moving the goalposts. It changes only via a decompose re-dispatch that adds sub-tests below it; its own assertion is fixed. |
| "Decomposition keeps splitting — one more level will bottom out" | Two failed decomposition rounds means the design is wrong, not the granularity. Bounce to `brainstorm` rather than splitting a fourth time. |
## Red Flags — STOP
- "Skip the executable-spec, the feature is obvious"
- About to write any implementation before the headline test is RED
- The headline test fails on a syntax / import / scaffolding error,
not because the feature is absent
- Writing one assertion required silently choosing between two or
three plausible behaviours (→ bounce to `brainstorm`)
- About to edit the headline test so `implement` can pass it
- Pushing a third or fourth decompose round instead of bouncing to
`brainstorm`
- Using `tdd` for a regression of existing behaviour (→ `debug`)
- About to `git commit` from inside the `tdd-author` agent (it
never commits)
## Cross-references
- **Agent dispatched:** `agents/tdd-author.md` — carries the
authoring process, the design-fork bounce trigger, the reactive
decomposition into BLOCKER sub-tests, the Common Rationalisations
and Red Flags. The orchestrator does not execute these phases
directly.
- **Hand-off target (GREEN):** `../implement/SKILL.md` — runs the
GREEN side as the `implement-loop` workflow in `mode: "mini"`, after
the orchestrator decides whether to commit the RED test separately or
as part of the final commit. This is the same mini-mode handoff
`../debug/SKILL.md` uses.
- **Bounce-back target:** `../brainstorm/SKILL.md` — reclaims the
work when the behaviour is not test-specifiable.
- **Sibling fast path:** `../specify/SKILL.md` — the other bounded
entry path that shares this skill's bounce-to-`brainstorm`-on-fork
discipline. Where `tdd` owns work whose behaviour one assertion can
pin, `specify` owns work whose design the sources already resolve;
both fall back to `brainstorm` when their precondition fails.
- **Sibling RED-first skill:** `../debug/SKILL.md` — same two-stage
RED→GREEN shape, but triggered by an observed bug rather than a
new-behaviour description.
- **Pipeline:** `../docs/pipeline.md``tdd` is a standard,
always-available entry path; the graph and skip rules are fixed
(not per-project).