| name | tdd-loop |
| description | Design, create, run, and verify deterministic TDD loop-engineering workflows using the tdd_loop extension. Use when the user asks to build a loop, codify expected coding functionality as tests, create a TDD loop, split a complex feature into test-backed subloops, define completion verification, or run /tdd_loop commands. |
TDD Loop
Turn a coding task into a controlled, verifiable TDD loop. Pair with the tdd skill for execution.
Core: codify expected functionality as behavior tests, one vertical slice at a time — write the test and the code in the same pass. The passing test command plus validation pass is the completion proof.
Do not write the whole suite first. Use tracer bullets: one failing behavior test → smallest fix → verifier and Fallow validation pass → next behavior.
Every TDD iteration should also satisfy the test-quality contract: identify the behavior slice, consider likely failure modes and relevant edge cases, write one public-interface behavior test, confirm the red phase fails for the expected reason when practical, then implement only the smallest fix.
Choose the shape
| Task | Shape | Key fields |
|---|
| Small, one test | no steps | prompt, check, attempts |
| Medium, one suite | no steps | loop until the suite passes |
| Large, ordered steps | steps[] without dependsOn | steps[], attempts, finalCheck? |
| Large, independent parts | steps[] with dependsOn | steps[] with dependsOn, parallelism |
| Fuzzy / subjective | any | first loop writes the test or checker, then stop for human approval |
Before you loop (loopability gate)
A loop pays off only if all are true:
- The task repeats across iterations.
- The expected behavior can be expressed as a deterministic check (a test command).
- Extra tokens/time are acceptable.
- The verifier can actually run in this environment.
If the behavior can't be tested yet, the first loop goal is to find the first behavior test — not to bulk-write tests.
Spec anatomy
Single-test:
{
"name": "fix-login-bug",
"goal": "Fix the login bug",
"prompt": "Write/update one behavior-focused failing test for the login behavior, including its predictable failure mode, then implement the smallest fix. End by calling tdd_loop_report.",
"check": "npm test -- login.test.ts",
"validationCommand": "fallow audit --format json --quiet --fail-on-issues",
"attempts": 5
}
Plan: same top-level fields plus steps[] (legacy: plan[]). Each step carries its own prompt + check. If any step has dependsOn[], the loop is inferred as DAG; otherwise it runs in order. parallelism (default 1) caps how many ready DAG tasks are shown per iteration. taskWorktrees: true optionally gives each DAG task its own git worktree/branch. finalCheck? runs a full-suite check after the last step passes. Every verifier success is followed by validationCommand (default: fallow audit --format json --quiet --fail-on-issues); validation failure output is returned in the next task prompt for mitigation. Set validationCommand to an empty string only when a repo cannot run Fallow.
{
"name": "onboarding",
"goal": "Implement the onboarding flow",
"parallelism": 2,
"taskWorktrees": false,
"attempts": 3,
"steps": [
{ "id": "draft", "name": "Create draft applicant", "dependsOn": [], "prompt": "Write/update the draft test, then the smallest fix.", "check": "npm test -- onboarding.draft.test.ts" },
{ "id": "validate", "name": "Validate fields", "dependsOn": [], "prompt": "Write/update the validation test, then the smallest fix.", "check": "npm test -- onboarding.validate.test.ts" },
{ "id": "kyc", "name": "Submit to KYC", "dependsOn": ["draft"], "prompt": "Write/update the KYC test, then the smallest fix.", "check": "npm test -- onboarding.kyc.test.ts" }
],
"finalCheck": "npm test -- onboarding"
}
Drop dependsOn for the same shape to run strictly in order. Full worked examples live in README.md.
Create and run
/tdd_loop <name-or-path> # create/edit if needed, then start; resumes unfinished runs automatically
/tdd_loop fresh <name-or-path> # force a new run instead of auto-resuming
/tdd_loop status # show active loop status
/tdd_loop edit <name-or-path> # edit an existing spec
Each iteration: do one focused unit of work (write/update one failing behavior test → smallest fix per the tdd skill), then call tdd_loop_report exactly once.
Test-quality checklist per iteration:
- Identify the smallest behavior slice under test.
- Consider happy path, boundary/empty/invalid inputs, regression/predictable failure, and ordering/idempotency/concurrency/persistence/permission risks if relevant.
- Test observable behavior through the public interface; avoid private methods and mock choreography.
- Run the targeted test before implementation when practical; confirm it fails for the expected reason.
- Before reporting, check the test would fail against the old/broken behavior and mention intentionally deferred edge cases.
You do not decide completion. After your report the extension runs the current verifier, then the configured Fallow validation pass:
- verifier + validation exit
0 → step/task done → next eligible work (or loop complete)
- non-zero verifier/validation → the failure reason is returned in the next task prompt for mitigation, then retry the same step/task (until
attempts / maxIterations)
blocked: true → loop stops as blocked
After a step verifier and Fallow validation pass, tdd_loop auto-commits the completed step using only file paths listed in tdd_loop_report.artifacts. Failed iterations are kept uncommitted for the next attempt.
In test-plan, the next step unlocks only after the current step's verifier and Fallow validation pass. In dag-plan, only ready tasks (all dependsOn done) are shown; set tdd_loop_report.taskId if you pick a ready task other than the suggested one.
tdd_loop_report fields
| Field | When |
|---|
summary | always — what changed this iteration |
blocked | true only for human input, missing access, ambiguity, or unsafe action |
taskId | dag-plan only — the ready task you worked on |
nextPrompt | optional hint if the verifier or validation still fails |
artifacts | changed file paths first; commands/URLs may be included after paths |
lessonsLearned | reusable lessons worth persisting |
Never claim completion — passing the verifier and Fallow validation is the proof.
Delegated DAG execution
For large independent work, use steps[] with dependsOn and let the main agent act as orchestrator. Delegation is safe only when task ownership is explicit.
Main-agent responsibilities:
- Decompose the goal into DAG steps with narrow
checks.
- Put expected file ownership / no-touch boundaries in each
prompt.
- Delegate only ready DAG tasks whose file ownership is disjoint.
- Review returned work before reporting.
- Call
tdd_loop_report for exactly one completed ready task; the extension verifies and advances.
Subagent contract:
You own only this TDD loop task.
Allowed file ownership: <paths/globs>.
Write or update one behavior-focused failing test through the public interface.
Implement the smallest fix for this behavior only.
Do not refactor unrelated code or touch files outside ownership; stop and explain if required.
Run: <check>.
Return: changed files, command result, old-behavior failure rationale, deferred edge cases, and integration risks.
Start with parallelism: 2. Parallelize leaf UI/tests/adapters/docs. Serialize core domain services, schema, migrations, shared types, fixtures, and any task likely to touch the same files.
For stronger isolation, set taskWorktrees: true on a dag-plan. The extension creates a sibling worktree and branch for each task, prompts the agent to work there, runs the task verifier plus validation there, commits reported artifacts on that branch, merges the task commit into the orchestrator worktree, then reruns the task verifier plus validation in the orchestrator worktree before marking the task done. If merge or post-merge verification fails, the task is retried instead of completed.
Stop
/tdd_loop stop
Resume
If Pi is interrupted or restarted mid-loop, the run state is still on disk at .pi/loops/runs/<name>/. Continue with the same command:
/tdd_loop fix-login-bug
The extension picks up the latest unfinished run. Use /tdd_loop fresh fix-login-bug to force a new run from the spec.
Guardrails
- Keep
attempts / maxIterations, and parallelism low until the loop proves useful.
- Prefer specific test commands over broad suites.
- Break subjective work into sub-loops with an explicit checker or approval gate.
- Prefer specialist skills for execution; the loop orchestrates, tests verify.
Built-in safety
- DAG cycle detection: dependency cycles are detected at startup and rejected with a clear error message.
- Vacuous pass guard: set
verifyOutputPattern to a regex that must match verifier output. Exit 0 without matching output counts as failure.
- Dirty-tree warning: warns before starting if the working tree has pre-existing uncommitted changes; auto-commit only commits paths reported in
artifacts.
- Abort safety: cancelled verifier runs preserve loop state without advancing or committing.
- History truncation: prompts show the last 10 iterations to bound token usage.
- Auto-commit: completed steps create a local git commit when reported artifact paths have changes.
- Task worktrees:
taskWorktrees: true for DAG plans isolates task edits in per-task worktrees/branches and requires a successful merge plus post-merge verifier and Fallow validation before completion.
autoCommitNoVerify: defaults to false (respects pre-commit hooks). Set true to bypass hooks.
autoCommitAddUntracked: defaults to false (tracked files only). Set true to run a last-moment git add -N -- <reported paths> so untracked files can be included by git commit --only.
Run memory is written to .pi/loops/runs/<name>/ (a .json state file + a .md log per run). Live status shows in the Pi UI widget.