Best for
- Adding a new function, method, or behavior
- Fixing a bug (the bug needs a regression test before the fix)
- Refactoring a unit whose current behavior is unclear
event4u-app/agent-config/src/skills/test-driven-development/SKILL.md
Implementing a feature, fixing a bug, refactoring — failing test first, then the code. For a WRONG test, `testing-anti-patterns` wins.
Decision brief
Implementing a feature, fixing a bug, refactoring — failing test first, then the code. For a WRONG test, `testing-anti-patterns` wins.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/event4u-app/agent-config --skill "src/skills/test-driven-development"Inspect the Agent Skill "test-driven-development" from https://github.com/event4u-app/agent-config/blob/6a5670b7881a676c0da90d2afb950298087c4ccb/src/skills/test-driven-development/SKILL.md at commit 6a5670b7881a676c0da90d2afb950298087c4ccb. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Plain TDD (red → green → refactor) is the right size for most work. A small subset benefits from a gated 5-phase wrapper — Spec → Pseudocode → Architecture → Refine → Complete — where each gate produces a written artifact before the next phase runs.
State in one sentence: "When X happens, the system should do Y."
Adding a new function, method, or behavior Fixing a bug (the bug needs a regression test before the fix) Refactoring a unit whose current behavior is unclear Any task where expected behavior can be expressed as an assertion
Drive implementation from a verified-failing test, not from the agent's belief that the code "should work". Catch edge cases before they become production bugs. Leave every change with a regression test that runs in CI.
If step 2 is skipped, the test is not trusted — a test that has never failed proves nothing about the code under test.
Permission review
The documentation asks the agent to run terminal commands or scripts.
npx vitest run --testNamePattern "rejects empty email"The documentation asks the agent to run terminal commands or scripts.
Re-run the same targeted command. Required:Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 92/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 9 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Do NOT use when:
.md, AGENTS.md, README)agent-config package on skill/rule markdownPlain TDD (red → green → refactor) is the right size for most work. A small subset benefits from a gated 5-phase wrapper — Spec → Pseudocode → Architecture → Refine → Complete — where each gate produces a written artifact before the next phase runs.
Decision tree — escalate if any branch is true:
When escalating: drop the Spec artifact in agents/roadmaps/ (or the
project's planning location), capture it as an ADR via adr-create
when the decision is load-bearing, then run plain TDD inside each
Refine cycle. Do not skip RED→GREEN inside a SPARC phase — the
wrapper adds gates, not exemptions.
For everything else (single-AC ticket, leaf-module change, bug fix), stay on plain TDD — the section above.
1. Write ONE failing test that describes the desired behavior.
2. Run it. WATCH it fail for the right reason.
3. Write the MINIMUM production code to make it pass.
4. Run it again. Watch it pass.
5. Clean up (rename, deduplicate) while keeping the test green.
If step 2 is skipped, the test is not trusted — a test that has never failed proves nothing about the code under test.
UNTESTED CODE THIS TASK JUST WROTE, AND A TEST IS NEEDED — DELETE THE CODE,
WRITE THE TEST, REIMPLEMENT. NEVER KEEP IT "AS REFERENCE".
THIS LAW COVERS YOUR OWN UNTESTED OUTPUT. IT IS NOT A LICENCE TO DELETE
PRE-EXISTING CODE, AND NEVER OVERRIDES A REUSE VERDICT.
Reading the existing implementation while writing its test is test-after-the-fact with extra steps. Which code that applies to has three answers, and only the first is a deletion:
| The code is | Do |
|---|---|
| untested, written by this task | delete it, write the test, reimplement — the Iron Law above |
| pre-existing and tested | keep it. Its tests are the record of its behaviour; deleting it to re-derive the same thing discards evidence and contradicts the reuse verdict |
| pre-existing and untested | do NOT delete. Write a characterization test pinning the behaviour it has today — including the behaviour you think is wrong — then change it under that test |
The middle row is the one this law used to get wrong: unqualified, it read as
a standing instruction to delete tested legacy that a reuse verdict would
keep. Case detail, the characterization-test procedure, and the 12-row
anti-rationalization table are in
testing-anti-patterns/process-anti-patterns.md,
which keeps this skill under the 400-line sunset trigger.
The flow runs as four modes. Each Forbidden item names how a reviewer checks it from the diff — an unverifiable prohibition does not ship.
| Mode | Goal | Activities | Forbidden (diff check) | Output contract |
|---|---|---|---|---|
| Design | One-sentence behavior + enumerated cases | Steps 1–2 | No production code (diff touches no src/** production path) · no test bodies yet | Case list (happy/boundary/error) |
| Test-Red | A failing test that fails RIGHT | Steps 3–4 | No production edits (diff = tests/** only) · the failure must be about the behaviour under test. Valid: a failing assertion · a missing target — class-not-found, or a compile/type error naming the unimplemented symbol · a contract failure (wrong shape, wrong status, unmet interface). Invalid: a broken fixture · a syntax error in the test · a missing unrelated dependency · a runner or environment fault | Failing test + its observed failure, named as one of the three valid classes |
| Implement | Minimum code to green | Steps 5–6 | No test edits (no tests/** paths in Implement-phase diffs — changing the assertion to fit the code is the canonical violation; genuinely-wrong test → STOP and ask, never silently edit) · no scope beyond the one case | Green run output |
| Debug | Fix a defect found later | (re-enter at 3) | No bugfix before a reproducing regression test exists (the fix commit contains a tests/** addition that fails without the fix) | Regression test + fix, verified red→green |
The discriminator is whether the failure is about the behaviour under test, never where in the run it surfaces. A class that does not exist yet can only fail at load, so demanding an assertion would force a production stub before the first test — the exact thing this skill forbids. The four invalid classes are failures of the harness: they would fail identically with the behaviour fully implemented, so they measure nothing about it. Unsure → re-read the failure output and name which of the seven it is; an unclassified red is not a RED.
Step 0 of any resume: if agents/runtime/state/HANDOFF.md exists, resume
from its Mode + Contract-owed fields (see /agent-handoff file mode) instead
of re-deriving. Otherwise infer the mode from observable state — never
assume Design:
| Observed state | Resume in |
|---|---|
| No test for the target behavior | Design |
| Test exists, currently failing at an assertion | Implement |
| Test exists + passing, defect reported | Debug |
| Test failing at load because the target does not exist yet | Implement (that is a valid RED) |
| Test failing on a harness fault — fixture, syntax, unrelated dependency, runner | Test-Red (fix the test, not the code) |
At every mode transition, one consent-checkpoint sentence (per
ask-when-uncertain / autonomous-execution — no new mechanism): name the
mode you are leaving, the output contract you hand over, and the mode you
enter; under an autonomous mandate the sentence is stated, not asked.
State in one sentence: "When X happens, the system should do Y."
If you cannot state it in one sentence, the scope is too big — split into multiple tests, each covering one sentence.
Before the first test is written, run the
test-case-discovery funnel for the
behavior: dimension scan → case synthesis → optional subagent cross-check →
prioritization. Do not proceed to step 3 with fewer than the floor:
Each case from the list then gets its own RED → GREEN cycle (steps 3–6). A behavior whose only test is the happy path is not done — it is the first item of an unfinished case list.
Write the smallest test that expresses the sentence from step 1.
it_rejects_empty_email, not test_email_1.Execute the single test (targeted, not the full suite):
# PHP/Pest
./vendor/bin/pest --filter=it_rejects_empty_email
# JS/Vitest
npx vitest run --testNamePattern "rejects empty email"
Required observations before proceeding:
Add just enough production code to make the test green. No extra features, no unrelated refactoring, no "while I'm here" cleanups.
If you feel the urge to add a parameter, edge case, or helper not covered by the current test — stop. That belongs in the next RED step, not this GREEN step.
Re-run the same targeted command. Required:
With all tests green, you may:
Do not add new behavior during refactor — that needs its own failing test first. Re-run tests after the refactor to confirm still-green.
Back to step 1 with the next single-sentence behavior.
Twelve common rationalizations that fire before the test is written —
plus the delete-and-restart Iron Law — live in
testing-anti-patterns/process-anti-patterns.md.
Read the table when:
For mock-isolation failure modes (separate concern), see
testing-anti-patterns.
// tests/Unit/EmailValidatorTest.php — RED
it('rejects empty email', function () {
$result = (new EmailValidator())->validate('');
expect($result->isValid())->toBeFalse();
expect($result->error())->toBe('Email required');
});
Run: ./vendor/bin/pest --filter='rejects empty email' → fails
(EmailValidator does not exist yet, or returns isValid()=true).
// app/Validators/EmailValidator.php — GREEN (minimum)
final class EmailValidator
{
public function validate(string $email): EmailResult
{
if (trim($email) === '') {
return EmailResult::invalid('Email required');
}
return EmailResult::valid();
}
}
Run the filter again → passes. No additional rules (format, MX, length) until a next failing test drives them.
// src/retry.test.ts — RED
import { retry } from './retry';
it('retries a failing operation up to 3 times', async () => {
let attempts = 0;
const op = async () => {
attempts += 1;
if (attempts < 3) throw new Error('transient');
return 'ok';
};
await expect(retry(op)).resolves.toBe('ok');
expect(attempts).toBe(3);
});
Run: npx vitest run --testNamePattern "retries a failing" → fails
(retry is undefined).
// src/retry.ts — GREEN (minimum)
export async function retry<T>(op: () => Promise<T>): Promise<T> {
let lastError: unknown;
for (let i = 0; i < 3; i += 1) {
try { return await op(); } catch (e) { lastError = e; }
}
throw lastError;
}
Run again → passes. Configurable attempt count, backoff, and jitter all wait for their own failing tests.
expect() with three or four assertions on unrelated fields describes
multiple behaviors. Split them.it('works') — no behavior describedtest-case-discoveryquality-toolspest-testing/tests:executesystematic-debuggingBefore marking TDD work complete:
See also developer-like-execution
for the broader think → analyze → verify loop this skill plugs into.
Frequently asked questions
Implementing a feature, fixing a bug, refactoring — failing test first, then the code. For a WRONG test, `testing-anti-patterns` wins.
The source record exposes this install command: npx skills add https://github.com/event4u-app/agent-config --skill "src/skills/test-driven-development". Inspect the command and pinned source before running it.
Static rules flagged exec-script in the source; the page lists the matching lines and excerpts.
Alternatives
obra/superpowers
Use when implementing any feature or bugfix, before writing implementation code
garrytan/gbrain
End-to-end discipline for turning any large data source (audio libraries, email takeouts, document corpora, chat exports, API dumps) into brain pages at scale. The lifecycle spine: SCHEMA → ACCESS → TRIAL → EVALUATE → IMPROVE → CODIFY → TEST → SKILLIFY → BULK → MONITOR. State is tracked in a durable JSON manifest (see MANIFEST-PATTERN.md) so any crash, session boundary, or subagent fan-out resumes from ground truth instead of memory.
alirezarezvani/claude-skills
App Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklist
dotnet/skills
Migrates .NET test projects from VSTest to Microsoft.Testing.Platform (MTP). Use when user asks to "migrate to MTP", "switch from VSTest", "enable Microsoft.Testing.Platform", "use MTP runner", set OutputType=Exe only for test projects in Directory.Build.props, or mentions EnableMSTestRunner, EnableNUnitRunner, or UseMicrosoftTestingPlatformRunner. USE FOR: MTP behavioral differences vs VSTest (exit code 8, zero tests discovered, --ignore-exit-code, TESTINGPLATFORM_EXITCODE_IGNORE); centralizing