Imported from yanjunz/tdd-workflow (
skills/SKILL.md). Install upstream withnpx skills add yanjunz/tdd-workflow --skill skills. Copyright stays with the author.
TDD Workflow — Spec-Driven Full-Cycle Development
Command Overview
| Command | Purpose |
|---|---|
/tdd:auto <name> [--yolo] |
One-shot full cycle: chains new → ff → loop → e2e → done with 4 inter-stage checkpoints. --yolo skips checkpoints (real failures still halt). |
/tdd:new <name> |
Start new feature, interactive requirements gathering (collects UC framework) |
/tdd:ff <name> |
UseCase-first: generate usecases.md as primary output, then derive requirements → design → tasks from it |
/tdd:change |
Mid-course requirement change: analyze impact (UseCase dimension first), sync all 4 docs |
/tdd:spec |
Generate/update spec documents individually (preserves discovery context) |
| RED / GREEN / REFACTOR phases | Phase markers used inside /tdd:loop; not separate slash commands. See "Loop-internal phases" below for the rules each phase enforces. |
/tdd:loop |
Auto-cycle red -> green -> refactor until Phase 2 complete |
/tdd:e2e |
Derive E2E tests from usecases.md paths (each applicable path → E2E or an explicit unit/integration-only decision) |
/tdd:verify-setup |
Interactive project-level verify config (tdd-specs/.verify/project.md) |
/tdd:verify-local |
Interactive personal verify params (tdd-specs/.verify/project.local.md, gitignored) |
/tdd:cleanup [env] |
Manual cleanup — run pre_verify_cleanup without running verification itself |
/tdd:done |
4-stage verification: deterministic spec preflight → code checks → local E2E → staging → delivery (includes UC sync to paths.usecases.dir, default docs/usecases/) |
/tdd:notes |
Generate TDD practice notes — record decisions, pitfalls, lessons learned |
/tdd:bug [--fast|--standard|--full] |
Automatic triage before writes: Fast operational repair, Standard behavior TDD, or Full workflow |
/tdd:continue <name> |
Resume in-progress feature |
/tdd:archive |
Archive completed specs (warns if usecases.md not synced to docs/) |
Codex invocation: Select this skill with
$tdd-workflow, then state the workflow entry point, for example:$tdd-workflow Run /tdd:new checkout. In Codex, the/tdd:*names identify workflows inside this skill; they are not project slash commands. Codex implicit invocation is disabled for this skill, so an ordinary request such as "fix CI" does not opt into the workflow.
Command dispatch (mandatory)
Before executing any /tdd:<id> workflow other than /tdd:e2e, read
commands/<id>.md completely and follow it as the authoritative command
contract. This SKILL.md provides shared rules and routing summaries; it does
not replace command-specific compatibility, migration, or verification steps.
/tdd:auto loads each delegated command except at its E2E stage.
E2E information-boundary exception: the main/orchestrating agent must not
read commands/e2e.md. For manual /tdd:e2e, spawn from the boundary below;
for /tdd:auto, follow commands/auto.md Stage 4 and spawn directly. The
spawned Tester—not the main agent—reads commands/e2e.md completely and executes
it. The documented single-UC exception remains available without opening that
resource in the main context.
Active spec path safety
Before using a feature name or tdd-specs/.current in any path, require one
non-empty [A-Za-z0-9._-]+ name other than . or ... For an existing spec,
reject symlinked tdd-specs/ or tdd-specs/<name>/ directories. Stop on invalid
state; never normalize a parent traversal into an accepted path.
Discovery handoff and complete UseCases
discovery.md is an optional feature artifact. Create it when the current
conversation, a grill-me session, an Issue, or project notes contain useful
decisions before formal specification starts. It records settled decisions,
open questions, scope boundaries, assumptions, trade-offs, candidate
UseCases, acceptance signals, and negative/boundary cases. If no reusable
context exists, do not create an empty file.
UseCases must not stop at the happy path. For every UC, inspect input and validation, identity and permission, state and concurrency, external dependencies, timeout/recovery, and empty/boundary conditions. Model each applicable case as an alternate or failure path; record why a category is not applicable instead of silently omitting it.
Project Context Detection
Before starting any feature, detect project structure to determine test framework and directory conventions:
# Detect package management and test framework
ls package.json pyproject.toml Cargo.toml go.mod pom.xml build.gradle 2>/dev/null | head -5
grep -E '"test"|"jest"|"vitest"|"pytest"|"mocha"' package.json 2>/dev/null || true
# Detect source and test directories (monorepo-aware: check subdirs too)
find . -maxdepth 3 -name "package.json" -not -path "*/node_modules/*" \
-exec sh -c 'echo "$(dirname {})/src $(dirname {})/lib $(dirname {})/app"' \; 2>/dev/null | \
xargs -I{} sh -c 'ls -d {} 2>/dev/null' | sort -u | head -10
Adapt all subsequent commands based on detection results:
- Test commands:
npm test/npx jest/npx vitest/pytest/go test ./.../mvn test/cargo testetc. - Test directories:
test//tests//__tests__//spec/etc. - Source directories: read from
tdd-specs/.verify/project.md→paths.src_dirs(may be multiple paths for monorepos). Fall back to detection results if not configured. - Source directories:
src//app//lib/etc.
Test Output Frugality (token-saving)
Test runs are the biggest cache-invalidation driver in TDD. A single default-verbose npm test can emit hundreds of KB of output, blowing away the prompt cache and burning $1+ per re-build. Apply silent / minimal output by default; only re-run with details when a test fails.
Per-framework default flags (use silent unless tdd-specs/.verify/project.md already specifies a quieter command):
| Framework | Default (silent) | On failure (re-run only the failing test) |
|---|---|---|
| jest | npx jest --silent --reporters=summary |
npx jest <test-name> --verbose |
| vitest | npx vitest run --reporter=dot |
npx vitest run <test-name> --reporter=verbose |
| pytest | pytest -q --tb=line --no-header |
pytest <test-name> -vv --tb=long |
| go test | go test ./... -count=1 (Go is quiet by default) |
go test -v -run <test-name> <pkg> |
| cargo test | cargo test --quiet |
cargo test <test-name> -- --nocapture |
| mocha | npx mocha --reporter=min |
npx mocha --reporter=spec <test-file> |
| maven | mvn test -q |
mvn test -Dtest=<test-name> |
Rules (applied by main agent / Coder / Tester whenever running tests):
- Default silent: every RED test run, every GREEN full-suite run, and every
/tdd:doneregression run starts with the silent flags above. Expected output: a single summary line ("N passed, M failed, time"). - Re-run only the failure verbose: when a test fails in silent mode, re-run only the failing test name with verbose flags to get the stack trace. Do not re-run the full suite verbose.
- Cap large outputs: if a command emits > 100 lines of stdout, pipe to
| tail -100(or| head -50; echo ...; tail -50) before reading the result. Never read a multi-MB CI log fully into context. - Don't
catlog files:grep -E 'FAIL|Error' test.log | head -30is almost always what you actually wanted. - Per-project override: if
tdd-specs/.verify/project.mddeclarescommands.unitalready with silent flags, use it as-is. The defaults above are the floor, not a mandate to overwrite explicit config.
Why this matters: in a typical TDD cycle (200+ test runs across a feature), default-verbose can add several hundred dollars in cache_creation tokens vs default-silent — see CHANGELOG 3.13.1 rationale for the measured driver.
/tdd:new <name>
Start a new feature.
- If no
<name>, ask user what they want to build; derive kebab-case name from description - Create
tdd-specs/<name>/directory, write totdd-specs/.current - Enter requirements gathering (cannot be skipped)
Requirements gathering — all dimensions must be covered:
| Dimension | Question |
|---|---|
| Target users | Who will use this? (based on actual project roles) |
| Core scenarios | Top 1-3 most important use cases? |
| Input/Output | What does the user input? What does the system return? |
| Error handling | What situations cause failure? Expected error behavior? |
| Scope boundaries | What is explicitly out of scope? |
| Acceptance criteria | How do we know it's done? |
After each round of Q&A, reflect understanding back to user for confirmation. Scope must be confirmed before proceeding.
Output after collection:
- Feature name and path:
tdd-specs/<name>/ - Confirmation summary (user stories + acceptance criteria)
- Prompt: Run
/tdd:ffto generate all spec docs at once, or/tdd:specfor step-by-step
/tdd:ff <name>
Fast-forward: generate requirements -> design -> tasks in one shot.
-
If
tdd-specs/<name>/doesn't exist, first run/tdd:newrequirements gathering -
Step 1: Review known Issues (if project uses issues directory; path from
paths.issues.dirintdd-specs/.verify/project.md, defaults todocs/issues)# ISSUES_DIR resolves to paths.issues.dir (default: docs/issues). External-tool mode: skip local scan. ls ${ISSUES_DIR}/*.md 2>/dev/null | grep -v README || echo "No issues directory, skipping" grep -rl "<feature-keywords>" ${ISSUES_DIR}/ 2>/dev/null || true -
Step 2: Update UseCase docs (target dir from
paths.usecases.dir, defaultdocs/usecases/; external-tool mode prompts manual sync) -
Step 3: Generate
tdd-specs/<name>/requirements.md -
Step 4: Generate
tdd-specs/<name>/design.md(incorporating actual project tech stack) -
Step 5: Generate
tdd-specs/<name>/tasks.md(using actual project test commands and paths)CRITICAL — Vertical Slice Rule (mandatory):
Phase 2 tasks MUST be organized by UC (vertical slice), and each UC MUST include tasks for ALL technical layers it touches:
Layer Include when... Example tasks Database migration UC introduces new table/field CREATE TABLE, execute migration to local DBBackend service UC has business logic service unit test + implementation Backend controller UC has API endpoint controller/route test Frontend page UC actor is end-user (web/mobile/native app) page JS/HTML/CSS implementation Client app UC involves client-side processing Python/native test + implementation FORBIDDEN: Separating frontend into a standalone "Phase 4". All layers of a UC belong together in Phase 2.
Exception: Pure infrastructure setup (Phase 1: creating directories, Entity skeletons, module registration) is allowed as a separate phase since it's shared across all UCs.
Database migration execution rule: Phase 1 must include actually running the migration on local dev DB (not just writing the SQL file). After migration, run the project's schema dump command if one exists.
-
Step 6: Test coverage check (mandatory, cannot skip)
After generating tasks.md, immediately verify all 3 test layers have tasks. If any layer has 0, proactively add before continuing:
Layer Check Gap-fill direction Unit tests tasks.md has tasks with "unit test:" prefix Add pure function unit tests for core business logic Integration tests tasks.md has "integration test:" prefix tasks covering key HTTP endpoint chains + DB write verification Add: POST /api/xxxfull chain (request -> response -> DB state); concurrency safety; permission boundaries (4xx for unauthorized roles)E2E Phase 3 has E2E tasks Add key user flow end-to-end verification -
Show summary, wait for confirmation
Output format:
OK requirements.md — N requirements, N acceptance criteria
OK design.md — N modules, N interfaces
OK tasks.md — Phase 1: N items / Phase 2: N items (by UC, all layers) / Phase 3: N E2E items
Issues reviewed: <related IDs or "none">
Ready! Run /tdd:loop to start TDD implementation.
/tdd:spec
Generate or update spec documents individually. Same as /tdd:ff Steps 1-6, but checks existing files and asks whether to overwrite.
Loop-internal phases
The three phases below are NOT standalone slash commands. They are phase markers enforced inside /tdd:loop; Standard/Full /tdd:bug paths reuse RED/GREEN while commands/bug.md defines their verification breadth. Users do not invoke /tdd:red directly — the loop transitions through these phases automatically for each Phase 2 task.
The rules in each phase are the contract between the loop and the Coder/Reviewer it spawns.
RED phase
Write a failing test (TDD Red phase).
- Pick next
[ ]Phase 2 task fromtasks.md - Write test file (rules: one test at a time, test behavior not mocks, name describes behavior)
- Run immediately to verify it actually fails (using project's actual test command)
- Confirm failure reason is "feature not implemented" not syntax error
- Mark task as
[~]
Failure verification triple-check:
- The test fails (not errors out)
- Failure message matches expectation
- Failure is due to missing functionality, not typo
GREEN phase
Write minimum code to pass current test (TDD Green phase).
Step 0 (mandatory for /tdd:loop and Full /tdd:bug): Check Issues first (if project has issues tracking; path from paths.issues.dir)
grep -rl "<error-keywords>" ${ISSUES_DIR}/ 2>/dev/null || echo "No existing records"
- Write only the minimum code to pass the test — no premature abstraction
- Run tests (using project's actual command)
- All selected tests green, with no regressions in the selected surface, to
mark complete.
/tdd:loopuses its full configured suite;/tdd:buguses the Fast/Standard/Full verification breadth fromcommands/bug.md. - Mark task as
[x]
Three-Strike Protocol — triggered when same test fails 3 times:
WARNING: Three-Strike Protocol
Test: <test-name>
Attempt history:
1. <approach> -> <error>
2. <approach> -> <error>
3. <approach> -> <error>
Issues search result: <found/none>
Please choose:
A. Try a different approach (describe your idea)
B. Split into smaller test granularity
C. Mark [!] skip, move to next
D. Need more context
REFACTOR phase
Refactor (only when all tests are green).
- Eliminate duplication, improve naming, extract shared logic
- Run tests after each small change
- Follow project's existing conventions (reference lint/style config and issues prevention notes)
/tdd:loop
Auto-cycle until all implementation tasks (Phase 1 + Phase 2) are terminal.
Terminal means [x] or a documented [!]; do not silently retry [!] tasks.
Agent Team Design: Coder + Orchestrator separation
Orchestrator (main agent session)
├── Assigns tasks to Coder sub-agent(s)
│ - 1 Coder for sequential mode (default fallback)
│ - N parallel Coders for independent UC modules
│ (when host supports concurrent Agent tool calls, e.g. Claude Code)
│ Coder(s) read: tdd-specs/ + src/ + test/
│ Coder(s) write one phase per call: RED tests or GREEN implementation
│
└── Enforces the Observed RED gate, then reviews GREEN independently
commands/loop.md owns the Observed RED gate: RED and GREEN use separate Agent calls.
After RED-only work, the Orchestrator runs the narrowest relevant test itself and must
observe the expected non-zero before any GREEN implementation write. A Coder report is not
evidence. If that result is not observable, Mode A is unavailable; use Mode B or self mode.
commands/loop.md supports two execution modes, chosen at runtime based on host capability:
Mode A — Parallel multi-Coder (preferred, when host supports it)
Hosts that allow multiple Agent/sub-agent tool calls in the same turn to execute concurrently (Claude Code, and any other host with truly-async parallel agents) can dispatch one Coder per independent UC module:
- Same-turn spawn N Agent tool calls, one per independent UC
- Each Coder isolated via
worktree(no write conflicts on the file system) - Dispatch RED-only Coders, merge and observe each RED, then dispatch new GREEN-only Coders
- Orchestrator awaits all GREEN work, merges, then runs the full test suite
- Recommended N: 2–5 (API rate limits + token budget per agent)
When parallel is safe:
- 2+ UC modules with no shared files and no service-level dependencies
- Each UC's tests/impl live in distinct directories
When NOT to parallelize (must serialize):
- Shared DB schema changes — run Phase 1 migrations sequentially first, then parallel Phase 2
- UC-B depends on UC-A's service / module exports — process UC-A first, then UC-B
Mode B — Sequential single-Coder (fallback)
Hosts without parallel sub-agent execution (or no sub-agent tool at all — Cursor, CodeBuddy, Cline, Codex, GitHub Copilot) fall back to one-UC-at-a-time:
- One UC's vertical slice reaches a terminal state through separate RED and GREEN steps
- When Agent is available, RED and GREEN use separate Coder calls
- Same review checkpoints, same test discipline — only the dispatch fan-out is removed
- This is the supported mode on every host that runs the skill
The Orchestrator picks the mode at the start of /tdd:loop based on whether parallel Agent
dispatch is available; the rest of the loop logic (RED → GREEN → REFACTOR review checkpoints)
is identical in both modes.
WHILE tasks.md Phase 1 or Phase 2 has ANY [ ] or [~] task:
IF current task is Phase 1 (infrastructure):
Execute directly (no RED/GREEN cycle needed for migrations, scaffolding)
VERIFY then mark [x]
ELSE (Phase 2 implementation task):
IF task is a "unit test" task:
RED phase -> Write failing test
IF task is an "implement" task:
GREEN phase -> Implement to pass (with issues lookup)
IF task is a frontend page task:
Write page files directly (js/html/css or framework equivalent)
VERIFY then mark [x]
REFACTOR phase -> Refactor (if applicable)
IF same test fails 3 times:
STOP -> Three-Strike Protocol -> Await decision
IF Phase 1+2 have no [ ] or [~] tasks ([x] or documented [!]):
Run full test suite (project's actual command)
Output: completion report (N tests, Xs elapsed, plus every [!] blocker)
Prompt: Run /tdd:e2e for E2E acceptance
Marking [x] Verification Protocol (MANDATORY — cannot bypass):
Before marking ANY task [x], you MUST verify with evidence:
| Task type | Required evidence before [x] |
|---|---|
| Unit test | Test file exists + test runner shows it passes |
| Implementation | Source file exists + related tests pass |
| Frontend page | All page files exist (framework-appropriate: tsx/vue/svelte/html+js+css) + registered in router/config |
| Database migration | Schema inspection confirms new tables/columns exist |
| Any task | FORBIDDEN: marking [x] for incomplete work. Use [!] for blocked tasks. |
If you cannot complete a task, you MUST either:
- Mark
[!]with a documented blocker reason - Keep as
[ ]and ask user for guidance - NEVER mark
[x]for unfinished work
Key behavior: The loop processes ALL layers within each UC (backend test → backend impl → frontend page) before moving to the next UC. This ensures each UC is fully deliverable when its tasks complete.
Task completeness scan (runs once at loop start, cannot skip):
| Scenario Type | Check | Prompt |
|---|---|---|
| DB migration executed | Phase 1 has migration task AND local DB has the new tables? | Migration SQL exists but was never executed. Run it now and verify with SHOW TABLES or equivalent. |
| Real DB integration test | At least 1 test in tasks.md connects to real DB (not all mocked)? | All tests use mock repositories. Add at least 1 integration test that writes to real DB and reads back to verify schema correctness. |
| Error response parsing | Tests for "response structure doesn't match expected"? | Missing error response parsing test, suggest adding |
| Crash/restart recovery | Tests for "state recovery after process restart"? | Missing crash recovery test, suggest adding |
| External URL/Host changes | Tests for "external resource URL host mismatch"? | Missing URL host rewrite test, suggest adding |
| Network timeout | Tests for "return False/empty instead of throwing on timeout"? | Missing timeout handling test, suggest adding |
| Integration tests (HTTP chain) | tasks.md has "integration test:" tasks covering key endpoint HTTP request -> response -> DB write chain? | Missing integration test tasks, suggest adding: key endpoint e2e chain (with DB state verification), concurrency safety, permission boundaries |
DB Migration Verification Protocol (mandatory when Phase 1 has migration tasks):
When processing a Phase 1 migration task, the loop MUST:
- Execute the SQL file against local dev DB
- Verify tables/columns exist:
SHOW TABLES LIKE '<pattern>'or equivalent - Run the project's schema dump command if one exists
- Only mark task
[x]after verification passes
If DB is not running or migration fails → STOP and ask user to fix DB before continuing. Do NOT proceed with mock-only tests and claim "verification passed".
/tdd:e2e
Phase 3: E2E acceptance tests.
E2E Type Selection (mandatory first decision)
Before writing any test, classify each target:
- Type A — User-Flow E2E (controlled dev/CI env, seedable, 3rd-party mockable): follow Hard Rules 1–5 in this file.
- Type B — Staging Smoke (real external deps, real credentials, uncontrolled
data, cannot mock): MUST read
STAGING_SMOKE.md(sibling file) and follow its Hard Rules B1–B4 + producetdd-specs/<feature>/staging-smoke-design.mdwith the Negative-Proof Checklist filled in.
If a target involves real upstream dependencies that the dev/CI environment
cannot reach or mock (e.g. an external API only accessible from staging/prod
network, a vendor SDK requiring real credentials, a DB whose schema lives
outside your control), it is Type B by definition. Never write it as a Type A
test with weakened assertions like status < 500 or [200, 4xx] — that
pattern silently passes when the real dependency is fully broken. See
STAGING_SMOKE.md Anti-Patterns.
When both types are needed for one feature, produce two separate test files in the project's E2E directory. Do not merge.
MANDATORY FIRST STEP — Spawn Tester Agent (cannot skip)
Before writing a single line of test code, you MUST call the Agent tool to spawn a Tester Agent. Writing E2E tests directly as the main agent is FORBIDDEN when the feature has 2+ UCs.
REQUIRED:
Agent(
subagent_type: "general-purpose",
prompt: """
You are an independent Tester. Your ONLY job is to write and run E2E tests
for the feature described in tdd-specs/<feature>/usecases.md.
FIRST: Read the installed `commands/e2e.md` completely. You are already
the Task sub-agent named by its self-check, so do not spawn another Tester;
execute its Steps 1-N yourself.
ALLOWED to read:
- tdd-specs/<feature>/usecases.md (source of truth for test scenarios)
- tdd-specs/<feature>/requirements.md (acceptance criteria)
- API route files (route definitions only, not service implementations)
- DB schema files (table structure only)
- Existing E2E test files (for project structure/helper patterns)
- tdd-specs/.verify/project.md (test commands, health check URL)
FORBIDDEN to read (paths listed in tdd-specs/.verify/project.md → paths.src_dirs):
- Any implementation code under src_dirs
- Any unit test files created during Phase 2
IF you feel the need to read implementation code to understand behavior,
STOP — that means the spec is incomplete. Report back what is unclear
instead of reading the implementation.
STEPS:
1. Read usecases.md — derive test scenarios (1 per UC path)
2. Start services if needed (health check from project.md)
3. Write E2E tests starting from real user entry points:
- Navigate from home/index page, not direct URL injection
- No state injection bypassing UI interactions
- No mocking your own backend endpoints
4. Run ALL tests. Fix failures (Three-Strike Protocol applies).
5. Report: N passed / N failed / N skipped (with skip reasons)
"""
)
Exception — single-agent E2E is allowed only when:
- Feature has exactly 1 UC, and its path-coverage section explicitly proves there are no applicable alternate, failure, or boundary paths
- In that case, main agent writes tests but MUST commit in writing:
"I am writing this E2E as main agent. Feature has exactly 1 UC and no applicable alternate/failure/boundary paths. I have not read any src_dirs implementation since /tdd:e2e started."
Enforcement checklist (Orchestrator runs AFTER Tester Agent reports back):
| Check | Pass condition |
|---|---|
| Agent tool was called | Tool call log shows Agent invocation |
| Tests start from real entry points | No direct deep-link navigation bypassing home/app entry |
| No bulk skips | Skipped count ≤ 3, each skip has documented reason |
| No src_dirs reads in Tester prompt | Tester did not read implementation files |
If any check fails → mark Phase 3 tasks [!] blocked and report to user before continuing.
Why This Matters
When the Orchestrator writes E2E tests itself (without a separate Tester Agent), it tends to:
- Navigate directly to deep pages, bypassing real app entry flows
- Assume implementation correctness it just wrote (confirmation bias)
- Miss integration gaps that only appear when starting from a real user perspective
The Tester Agent boundary exists precisely to catch these integration gaps.
Tester Agent Information Boundary
Tester Agent
✅ Can read: tdd-specs/<feature>/usecases.md
✅ Can read: tdd-specs/<feature>/requirements.md
✅ Can read: API route definitions / interface signatures
✅ Can read: DB schema (table structure only)
❌ Cannot read: any implementation code (paths from paths.src_dirs in project.md)
❌ Cannot read: unit test files written by Coder
paths.src_dirs is the authoritative source for "what counts as implementation code". It may contain multiple paths for monorepos (e.g.
api/src,frontend/src,mobile/lib). Do NOT assume implementation lives undersrc/— always check project.md first.
⚠️ isolation: "worktree" does NOT achieve Tester blindness:
isolation: "worktree" prevents write conflicts between parallel agents.
It does NOT prevent reading implementation files — the worktree is a full code copy.
Tester blindness is enforced via prompt constraints only (FORBIDDEN list above).
E2E Mode: Real Stack First
Prefer running E2E against a real running service stack, not mocked responses.
REAL mode (default):
1. Detect service port (use auto-discovery from project config first;
only ask user if auto-discovery fails after 2 retries)
2. Verify service stack is running (health check from project.md)
3. Seed test data
4. Run E2E — no API response mocking (no route intercepts for your own endpoints)
5. Assert: UI state + API response + DB state (triple verification)
6. Teardown test data
MOCK mode (opt-in, requires justification):
- Acceptable for: 3rd-party payment APIs, SMS, email sends
- Unacceptable for: your own backend endpoints
- Each mock must have inline comment: // mocked because: <reason>
- If accumulated mocks > 3, create a test environment stub instead
Deriving E2E Test Cases from UseCases
For each UC in usecases.md:
Success path → 1 E2E test (full flow, verify postcondition)
Each alt path → 1 E2E test (verify error/boundary handling)
Each test must:
- Start from real user entry point (home page or app launch)
- Trigger via actual user action (tap/click/input)
- Have explicit assertions (not just navigate to page)
- Record function name in tasks.md (anti fake-checkoff rule)
tasks.md E2E task format:
# CORRECT (function name recorded before checking off)
- [x] 3.1 UC-01 success path — user completes <action>, system shows <result>
→ test_function_name (tests/e2e/flow.spec.ts:L142)
# WRONG (fake checkoff, cannot trace)
- [x] 3.1 UC-01 E2E
Hard Rules:
Rule 1: Must cover real network layer
Do not bypass the network layer with state injection or store manipulation. Trigger real user actions that cause actual API calls.
Rule 2: Skipped tests must have documented reasons
# WRONG: Silent skip
test.skip('env not supported')
# CORRECT: Document reason and reference UC
test.skip('UC-01 alt 4a: DB failure recovery — cannot simulate in local env, covered by unit test: <path>')
If accumulated skips exceed 3, must establish mock/stub environment to resolve — no more skip stacking.
Rule 3: Assert results after every key action
Rule 4: Assert specific values for critical business fields
Rule 5: Success path must reach the postconditions stated in usecases.md
A "success path" E2E test MUST run all the way to the UC's postconditions — not stop at an intermediate step.
WRONG — test stops before postcondition:
test "order full flow":
# Steps 1-3: inject uploaded state (OK to bypass file picker via test helpers)
set_uploaded_state(page, files)
assert page.data['allUploaded'] == True
# ← stops here, never calls POST /api/orders
# Named "full flow" but only covers half the UC
# DB constraints, order creation logic: completely invisible
CORRECT — must verify the postcondition:
test "order full flow":
# Steps 1-3: inject uploaded state (bypassing file picker is acceptable)
set_uploaded_state(page, files)
# Steps 4-5: proceed to confirmation, trigger the real write API
navigate_to_preview(page)
confirm_order(page) # triggers POST /api/orders with real HTTP call
# Assert postcondition from usecases.md:
# "orders record created, status=pending_payment"
wait_until(page, lambda d: d['orderId'] is not None)
assert page.data['orderId'] is not None, 'Postcondition: order ID must be returned'
Checklist before marking a success-path E2E as [x]:
- Every UC step that triggers a write operation (POST/PUT/DELETE) is actually executed (not skipped)
- At least one postcondition from
usecases.mdis asserted (DB record created, status field, returned ID, etc.) - Test name accurately reflects actual coverage depth — if it only covers setup steps, name it accordingly, not "full flow"
Rule 6: Type B targets defer to STAGING_SMOKE.md
Rules 1–5 above govern Type A (user-flow E2E in controlled environments). For any test target that hits real external dependencies which cannot be mocked or seeded (Type B per the type selection at the top of this section):
- Hard Rules 1–5 are not sufficient — Rule 1 ("real network layer") is satisfied, but assertion strength rules don't translate (no postcondition, no UC path, no seedable data).
- Apply
STAGING_SMOKE.mdHard Rules B1–B4 instead, and produce the requiredstaging-smoke-design.mdwith Negative-Proof Checklist answers before marking the task[x].
The Orchestrator enforcement checklist for Phase 3 gains one row when any Type B test exists in the feature:
| Check | Pass condition |
|---|---|
| Type B design doc | tdd-specs/<feature>/staging-smoke-design.md exists with B3 answers filled in |
Missing the design doc → Type B task stays [!] until produced.
/tdd:done
Phase 4: Delivery. Every check must pass before continuing.
Load and follow commands/done.md; in particular, retain its compatible CLI
selection and legacy-spec finalization rules. The list below is only a summary.
- Deterministic spec preflight — use the compatible CLI selection in
commands/done.mdto runverify --delivery-ready; stop on a non-zero exit code. This requires every Phase 1–3 task to be terminal ([x]or documented[!]) and verifies UseCase success + alternate/failure + path-coverage sections. It does not run project tests or external services. - Compilation verification (mandatory for compiled languages: TypeScript, Java, Go, Rust, etc.)
- Full unit tests with coverage (project's actual command) — coverage >= 80% (or project target)
- Full regression (if project has regression scripts)
- E2E (if applicable)
- Issue tracking judgment — create an Issue for bugs routed to Full, or when an explicit project policy requires one. Time spent and changed-file count alone do not promote a Fast/Standard fix to Full.
- Delivery checklist:
[ ] Compilation clean (if applicable) [ ] All tests passing, coverage >= 80% (or project target) [ ] Full regression passing (if project has regression scripts) [ ] E2E tests passing (if applicable) [ ] Feature docs updated (if project has usecases/docs directory) [ ] Issues logged (Full bugs or explicit project policy) [ ] Environment variable examples synced (if new env vars added) [ ] tdd-specs/<name>/tasks.md has no Phase 1–3 [ ]/[~]; every [!] is documented in the final report - Output delivery report — include the
spec-verification.mdpath and result - Prompt to run
/tdd:notesto capture practice notes, then/tdd:archive
/tdd:notes
Generate TDD practice notes — record the full development story.
When to use: after /tdd:done, or at any point you want to capture the development journey.
- Read
requirements.md,design.md,tasks.mdfrom current spec - Scan git history for feature-related commits, reverts, fix iterations
- Generate
tdd-specs/<name>/tdd-practice-notes.mdcovering:- Background: what the user wanted and why
- TDD process: Phase 1-4 record, each RED/GREEN cycle
- Pitfalls: real problems encountered (from git history, not hypothetical)
- File inventory: new/modified files + test coverage numbers
- Key lessons: 3-5 actionable lessons learned
- Cross-check: pitfalls ≥ 1, lessons ≥ 3, file list matches git diff
Guardrails:
- Must read spec docs first — don't fabricate from memory
- Pitfalls must reference actual problems (git reverts, fix commits, user reports)
- Lessons must be actionable ("do X" / "avoid Y"), not vague ("testing is important")
/tdd:bug
Explicit bug entry point with automatic, evidence-based routing. Load and
follow commands/bug.md. Before creating files or editing code, run its
read-only Step 0 and print the triage evidence.
| Mode | Select when | Default scope |
|---|---|---|
| Fast | Confirmed CI/environment/config/dependency/build/tooling cause; no behavior contract changes | Minimum repair + exact failing check |
| Standard | Stable, limited behavior regression; no high-risk flags | RED + minimum GREEN + targeted/affected regression |
| Full | Security/auth/payment/data/migration/concurrency/production risk, recurrence after a prior fix, or broad independent-boundary/external-contract impact | Issue + RED/GREEN + full regression + relevant E2E + escape analysis/prevention |
--fast, --standard, and --full express user intent, but evidence selects
the mode. Report any conflict, then proceed automatically with a definite Fast,
Standard, or Full decision. Ask for mode input only when bounded diagnosis
returns Unresolved or multiple mode flags make the request ambiguous. Announce
mode changes before continuing with the evidence-supported scope.
Three-Strike Protocol applies to repeated Standard/Full attempts.
/tdd:change
Mid-course requirement change flow.
-
Confirm current spec
-
Collect change description (interactive if not provided)
-
Analyze impact across all 3 spec docs
Output impact assessment:
## Change Impact Assessment ### Affected spec entries | Document | Entry | Impact Type | Description | ### Affected tasks | Task | Current Status | Action Needed | ### Risk notes - Completed tasks affected: N - Estimated additional work: small / medium / large -
Wait for user confirmation before modifying anything
-
Execute updates (requirements.md, design.md, tasks.md, UseCases if applicable)
- Completed but affected tasks: revert to
[ ]with note<- needs redo due to requirement change
- Completed but affected tasks: revert to
-
Output change summary
Guardrail: If change causes 10+ task reverts, suggest considering a fresh /tdd:ff
/tdd:continue <name>
Resume in-progress feature.
- Read
tdd-specs/<name>/tasks.md - Check
.harnessfirst:phase=delivermeans already shipped, so suggest notes/archive and stop - Otherwise find the first
[ ]or[~]task; do not silently retry a terminal[!] - Write to
tdd-specs/.current; route Phase 1/2 to/tdd:loop, Phase 3 to/tdd:e2e, and legacy Phase 4 to/tdd:done - If only
[!]tasks remain, report their blockers and continue to the next workflow gate - Output recovery summary (completed N/M tasks, current phase, next step)
/tdd:archive
Archive completed specs.
- Require
phase=deliverand terminal Phase 1–3 tasks; delivered legacy 4.x rows are historical, while[!]requires its blocker in the final report - Check for
tdd-practice-notes.md— if missing, prompt to run/tdd:notesfirst (recommend, don't block) - Move to
tdd-specs/archive/<YYYY-MM>/ - Clear
tdd-specs/.current
File Structure Convention
tdd-specs/
+-- .current <- Currently active spec name
+-- <feature-name>/
| +-- requirements.md <- Requirements (EARS format)
| +-- design.md <- Technical design
| +-- tasks.md <- Implementation checklist (live-updated)
| +-- tdd-practice-notes.md <- Practice record (generated by /tdd:notes)
+-- archive/
+-- YYYY-MM/
+-- <completed-feature>/
Task Status Markers
| Marker | Meaning |
|---|---|
- [ ] |
Not started |
- [~] |
In progress (RED written, GREEN incomplete) |
- [x] |
Completed |
- [!] |
Blocked or explicitly skipped; terminal only after the decision and reason are recorded |
YOLO Mode (/tdd:auto --yolo)
When .harness contains yolo=1 (and user-prompt-submit.sh shows Mode: yolo in the harness line), the main agent is in a /tdd:auto --yolo flow. Behavior changes vs default:
| Default behavior | YOLO behavior |
|---|---|
| Finish one UC's vertical slice → write 本轮报告 → ask "commit or continue UC-N+1?" | Don't checkpoint per UC. Report once Phase 1+2 have no [ ] / [~], including every [!] blocker. Proceed straight to next UC. |
| Three-Strike (3× same test fail) → halt, ask user A/B/C/D | Auto-pick C: mark task [!] with last failure as reason, log it, continue to next task |
| Task-completeness scan finds gaps → ask user to accept | Auto-accept all suggestions, append tasks |
| Reviewer rejects Coder output 2× in a row → escalate to user | Mark task [!] with reviewer feedback, continue |
YOLO does NOT change:
/tdd:donereal failures (compile / test / coverage / regression / E2E) → always halt- DB migration failures → always halt
- Tester Agent boundary in
/tdd:e2e→ main agent must spawn Tester via Task tool. Whilephase=e2e,pre-write-edit.shblocks main-agent Write/Edit outsidetdd-specs/and allows sub-agent writes with anagent_id. This is a narrow guard, not a complete sandbox: other write-capable tools remain governed by the workflow instruction. - Initial requirements intake in
/tdd:new→ must still run if nousecases.draft.mdexists
User exits yolo mid-flow by deleting the yolo=1 line from .harness, or by running /tdd:continue (which doesn't carry yolo forward). Each spec's .harness is feature-isolated, so yolo on feature-A never leaks into feature-B.
Mandatory Issues Lookup Timing
| Timing | Method |
|---|---|
Before /tdd:ff or /tdd:spec |
Browse project issues directory (if exists) |
Before each GREEN phase (inside /tdd:loop) |
grep -rl "<error-keywords>" <issues-dir>/ |
| After Three-Strike Protocol triggers | Full-text search + module filter |
Not Applicable For
- Ordinary CI, environment, dependency, build, or tooling diagnosis when the user did not explicitly invoke TDD Workflow
- Single-line typo fixes
- Pure documentation or simple style changes
- Diagnosis-only requests
Handle these directly with verification proportional to the change. If the
user explicitly invokes /tdd:bug, confirmed operational/configuration cases
normally route to Fast rather than the full TDD cycle.
Post-Delivery Development
After /tdd:done, the harness enters deliver state. Any source code change after this point must follow these rules to prevent test debt accumulation.
Scenario A: Bug found during integration testing
Run /tdd:bug triage before choosing the repair scope:
Bug found
-> Fast: confirmed operational cause -> minimum repair -> exact failing check
-> Standard: behavior regression -> RED -> minimum GREEN -> affected tests
-> Full: high-risk/prior-fix recurrence/broad independent-boundary impact
-> Issue + full workflow
Line count does not determine the mode. Standard and Full behavior fixes need a relevant RED test first; Fast requires confirmed non-behavioral evidence and a rerun of the exact failing check.
Scenario B: Adding functionality after spec delivery
# 1. Append tasks to tasks.md (annotate with: Post-delivery: <description>)
# 2. Reset harness back to green
sed -i 's/phase=deliver/phase=green/' tdd-specs/<spec>/.harness
# 3. Run normal loop → done flow
Modifying implementation code in deliver state without appending tests is not allowed.
Scenario C: Pure style / UX / config changes
May be done directly, but:
- Annotate commit message with
[style]/[ux]/[config] - Run the smallest check that directly verifies the changed surface; broaden verification only when the dependency surface justifies it
/tdd:done check: post-delivery change audit
# Read paths.src_dirs config (fall back to common dirs if not configured)
SRC_DIRS=$(grep -A20 'src_dirs:' tdd-specs/.verify/project.md 2>/dev/null | \
grep '^\s*-' | sed "s/.*- //;s/['\"]//g" | tr '\n' ' ')
[ -z "$SRC_DIRS" ] && SRC_DIRS="src app lib"
# List source files modified during this spec cycle
git log --oneline --name-only -- $(echo $SRC_DIRS | xargs -n1 printf "'%s/**' ") \
| grep -v "^[a-f0-9]" | sort -u | head -30
Cross-check against tasks.md:
- Every new business method → has unit test coverage
- Every new/modified API endpoint → has E2E test coverage
- Every bug fix during integration -> records triage mode and verification; Full bugs also have the corresponding Issue record
Uncovered logic found → stop delivery, add tests, re-run /tdd:done.
Pre-commit self-check
□ Does every new business method have a unit test?
□ Does every new/modified API endpoint have an E2E test?
□ Any external service integrations? → mock/stub tests?
□ Verification matches the selected Fast / Standard / Full mode?
□ Any behavior bug fixes? -> relevant RED for Standard/Full?
□ Any Full bug fixes? -> Issue, regression, and risk evidence recorded?