Hai TDD
For Chinese readers, see SKILL.zh_CN.md. The English SKILL.md is the execution source of truth.
Overview
Drive development with tests: write a failing test first, confirm it fails for the right reason, write the smallest implementation that passes, then refactor while keeping tests green. This is not "add tests at the end" — TDD defines behavior before implementation.
Core Principle
Red, then green, then refactor.
Do not write production code before a failing test. A test that never failed first has not proven it constrains the target behavior — so the RED failure is the evidence, not a formality. If implementation already exists without a test, do not call the process TDD; either label it as tests-after or return to a test-first path.
Do not invent a test merely to satisfy the ritual. TDD is for behavior and stable contracts, not for creating ad hoc structural tripwires around a one-off cleanup.
When TDD does not fit
Select honest non-TDD verification without pausing for permission when:
- The work is a throwaway prototype or a spike, or the user asked for code reading before implementation.
- The change is pure configuration, copy, or styling.
- The behavior cannot reasonably be verified automatically yet.
- The change is purely structural and has no meaningful behavior-level RED, such as deleting a field, narrowing an interface, moving a type, renaming a symbol, removing an exported helper, or changing package ownership.
In these cases, say TDD does not fit this slice. Use compile failures, existing tests, static checks,
or tests-after verification instead of manufacturing a RED test. Ask the user only when the choice
would materially change the requested behavior or scope.
No Fake RED
Never add tests whose only purpose is to make a structural refactor look like TDD. Do not add:
- AST/regex/reflection scans for a one-time shape cleanup.
- White-box guards that inspect source rather than a durable behavior or policy boundary.
- Tests that duplicate compiler checks unless the repository already maintains that policy class.
- One-off guards for renames, package moves, aliases, or visibility changes.
Architecture/static boundary tests are allowed only when all of the following are true:
- The boundary is a durable policy the project intends to keep checking over time.
- The test failure would catch a likely future regression, not just document today's edit.
- The user explicitly wants that kind of guard, or the repository already has an established policy-test pattern.
When no legitimate RED exists, prefer this sentence over a fake test: "No TDD for this slice: this is a structural refactor. I will validate it with compiler errors, existing tests, and focused runtime checks."
Workflow
Define the target behavior.
- Pick one minimal behavior slice.
- State input, output, side effect, boundary, and failure condition.
- If the slice is only structural, stop here and report "not TDD" instead of forcing RED.
- If the work is too broad, use
hai-goal to split it into verifiable phases.
RED: write the failing test first.
- The test name should describe behavior, not say "works".
- Test one behavior at a time.
- Prefer public APIs, user-observable behavior, or stable boundaries — they keep the test stable when you refactor internals.
- Avoid mocks unless external systems, time, network, randomness, or permissions force them; mocks tie the test to implementation and hide real behavior.
Verify RED.
- Run the smallest relevant test command and confirm the test fails.
- Confirm it fails because the target behavior is missing, not because of syntax, imports, bad test code, or environment setup — that distinction is what makes the RED a real constraint rather than a broken test.
- If the test passes immediately, do not damage it merely to manufacture RED. Determine whether
the behavior already exists, the reproduction is wrong, or this is regression/tests-after work,
then label the evidence honestly.
GREEN: write the minimal implementation.
- Write only enough code to pass the current test. Extra code is unverified by any failing test, so it falls outside TDD's safety net.
- Do not add future features and do not mix in unrelated refactors.
- Do not skip the minimal implementation step for a larger "complete" design.
Verify GREEN.
- Rerun the relevant tests.
- Confirm the new test passes.
- Based on risk, run broader tests for the directory, module, or full suite.
REFACTOR.
- Refactor only after green.
- Improve duplication, naming, structure, or boundaries.
- Rerun tests after refactoring.
Continue with the next behavior.
- Every new behavior returns to RED.
- Do not put multiple behaviors into one large test.
Test Quality Bar
Good TDD tests:
- Test behavior rather than implementation details.
- Have names that communicate business or system meaning.
- Fail with a message that points to the missing behavior.
- Are stable under refactoring.
- Are small, but not brittle white-box tests.
- Serve as behavior documentation for future maintainers.
Common Mistakes
Traps not already caught by the workflow steps above:
- Forcing TDD onto structural cleanup by writing AST/grep/reflection guard tests that do not protect user-visible behavior or a stable contract.
- Distorting the production API just to make tests convenient — the test should adapt to a good design, not the design to the test.
- Testing only whether a mock was called, not the real behavior, so the test passes even when the behavior is wrong.
Use a different skill when
- The work is too broad to slice into one verifiable behavior, or the goal/phasing is unclear — use
hai-goal to turn it into verifiable phases first, then return here to drive each phase.
- The question is module boundaries, abstraction depth, or dependency direction rather than behavior under test — use
hai-architecture.
- You are deciding whether the feature is worth building at all — use
hai-idea.
- You are choosing the name of the unit, function, or concept under test — use
hai-naming.
Output
Report using references/output-template.md — fill every RED / GREEN / REFACTOR field with real evidence when TDD applies; do not collapse it to "tested, passing". If no legitimate RED exists, use the template's "No Legitimate RED" path instead of pretending. Read the template before finalizing.
1---2name: hai-tdd3description: Drives a real behavior change through red-green-refactor and records the failing RED evidence, minimal GREEN implementation, refactor decision, and verification commands. Use when the user explicitly asks for TDD/tests first, adds or fixes testable behavior, or needs regression protection(先写测试、红绿重构、补回归测试). Do not force TDD onto documentation, configuration, styling, renames, package moves, or other purely structural changes; report honest non-TDD verification instead.4---56# Hai TDD78For Chinese readers, see `SKILL.zh_CN.md`. The English `SKILL.md` is the execution source of truth.910## Overview1112Drive development with tests: write a failing test first, confirm it fails for the right reason, write the smallest implementation that passes, then refactor while keeping tests green. This is not "add tests at the end" — TDD defines behavior before implementation.1314## Core Principle1516Red, then green, then refactor.1718Do not write production code before a failing test. A test that never failed first has not proven it constrains the target behavior — so the RED failure is the evidence, not a formality. If implementation already exists without a test, do not call the process TDD; either label it as tests-after or return to a test-first path.1920Do not invent a test merely to satisfy the ritual. TDD is for behavior and stable contracts, not for creating ad hoc structural tripwires around a one-off cleanup.2122## When TDD does not fit2324Select honest non-TDD verification without pausing for permission when:2526- The work is a throwaway prototype or a spike, or the user asked for code reading before implementation.27- The change is pure configuration, copy, or styling.28- The behavior cannot reasonably be verified automatically yet.29- The change is purely structural and has no meaningful behavior-level RED, such as deleting a field, narrowing an interface, moving a type, renaming a symbol, removing an exported helper, or changing package ownership.3031In these cases, say TDD does not fit this slice. Use compile failures, existing tests, static checks,32or tests-after verification instead of manufacturing a RED test. Ask the user only when the choice33would materially change the requested behavior or scope.3435## No Fake RED3637Never add tests whose only purpose is to make a structural refactor look like TDD. Do not add:3839- AST/regex/reflection scans for a one-time shape cleanup.40- White-box guards that inspect source rather than a durable behavior or policy boundary.41- Tests that duplicate compiler checks unless the repository already maintains that policy class.42- One-off guards for renames, package moves, aliases, or visibility changes.4344Architecture/static boundary tests are allowed only when all of the following are true:4546- The boundary is a durable policy the project intends to keep checking over time.47- The test failure would catch a likely future regression, not just document today's edit.48- The user explicitly wants that kind of guard, or the repository already has an established policy-test pattern.4950When no legitimate RED exists, prefer this sentence over a fake test: "No TDD for this slice: this is a structural refactor. I will validate it with compiler errors, existing tests, and focused runtime checks."5152## Workflow53541. Define the target behavior.55 - Pick one minimal behavior slice.56 - State input, output, side effect, boundary, and failure condition.57 - If the slice is only structural, stop here and report "not TDD" instead of forcing RED.58 - If the work is too broad, use `hai-goal` to split it into verifiable phases.59602. RED: write the failing test first.61 - The test name should describe behavior, not say "works".62 - Test one behavior at a time.63 - Prefer public APIs, user-observable behavior, or stable boundaries — they keep the test stable when you refactor internals.64 - Avoid mocks unless external systems, time, network, randomness, or permissions force them; mocks tie the test to implementation and hide real behavior.65663. Verify RED.67 - Run the smallest relevant test command and confirm the test fails.68 - Confirm it fails because the target behavior is missing, not because of syntax, imports, bad test code, or environment setup — that distinction is what makes the RED a real constraint rather than a broken test.69 - If the test passes immediately, do not damage it merely to manufacture RED. Determine whether70 the behavior already exists, the reproduction is wrong, or this is regression/tests-after work,71 then label the evidence honestly.72734. GREEN: write the minimal implementation.74 - Write only enough code to pass the current test. Extra code is unverified by any failing test, so it falls outside TDD's safety net.75 - Do not add future features and do not mix in unrelated refactors.76 - Do not skip the minimal implementation step for a larger "complete" design.77785. Verify GREEN.79 - Rerun the relevant tests.80 - Confirm the new test passes.81 - Based on risk, run broader tests for the directory, module, or full suite.82836. REFACTOR.84 - Refactor only after green.85 - Improve duplication, naming, structure, or boundaries.86 - Rerun tests after refactoring.87887. Continue with the next behavior.89 - Every new behavior returns to RED.90 - Do not put multiple behaviors into one large test.9192## Test Quality Bar9394Good TDD tests:9596- Test behavior rather than implementation details.97- Have names that communicate business or system meaning.98- Fail with a message that points to the missing behavior.99- Are stable under refactoring.100- Are small, but not brittle white-box tests.101- Serve as behavior documentation for future maintainers.102103## Common Mistakes104105Traps not already caught by the workflow steps above:106107- Forcing TDD onto structural cleanup by writing AST/grep/reflection guard tests that do not protect user-visible behavior or a stable contract.108- Distorting the production API just to make tests convenient — the test should adapt to a good design, not the design to the test.109- Testing only whether a mock was called, not the real behavior, so the test passes even when the behavior is wrong.110111## Use a different skill when112113- The work is too broad to slice into one verifiable behavior, or the goal/phasing is unclear — use `hai-goal` to turn it into verifiable phases first, then return here to drive each phase.114- The question is module boundaries, abstraction depth, or dependency direction rather than behavior under test — use `hai-architecture`.115- You are deciding whether the feature is worth building at all — use `hai-idea`.116- You are choosing the name of the unit, function, or concept under test — use `hai-naming`.117118## Output119120Report using `references/output-template.md` — fill every RED / GREEN / REFACTOR field with real evidence when TDD applies; do not collapse it to "tested, passing". If no legitimate RED exists, use the template's "No Legitimate RED" path instead of pretending. Read the template before finalizing.