Install
$ agentstack add skill-supermodo-skills-tdd ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
tdd — Test-Driven Development & Test-First Debugging
> Requires: the sibling protocols skill (shared protocol masters); uses skills.config.json when present. Missing protocols → tell the user to install the full supermodo package.
Two modes, one discipline: tests lead, production code follows.
| Invocation | Mode | |---------------|------| | tdd | Development — red-green-refactor while building a feature | | tdd --debug | Debugging — hypothesis-testing for an existing bug (below) |
> Supersedes the older tdd-debug skill name (its methodology is the > --debug mode here, unchanged).
Development mode (default)
Classic TDD, strictly played. Used standalone or by work during implementation.
- Red. Pick the smallest next behavior from the task at hand. Write ONE
test that specifies it. Run it and WATCH IT FAIL — for the right reason (the behavior is missing), not a typo/import error. A test you never saw fail proves nothing.
- Green. Write the MINIMUM production code that makes that test pass.
No speculative generality, no drive-by fixes. Run the test; run the affected suite.
- Refactor. With green as a safety net, clean what you just touched —
names, duplication, structure. Suite stays green throughout.
- Repeat until the behavior list is exhausted. Never write production
code without a failing test demanding it; never let a broken test linger.
Rules that bite:
- One failing test at a time — a wall of red is noise, not pressure.
- Test behavior through public surfaces, not implementation details; a
refactor (step 3) must not break tests.
- Assertions must be strong enough to fail on subtly wrong code — assert
values and effects, not just "doesn't throw".
- Edge cases (empty/null, boundaries, error paths) get their own red-green
cycles, not afterthought asserts.
- Zero tolerance: leaving any test red at the end of a session is a failed
session.
When a bug surfaces mid-development with an unclear cause, switch to --debug mode below, fix it, then resume the cycle.
Debugging mode (--debug)
Why This Workflow
Most debugging goes: see bug, guess cause, patch, hope. That fixes symptoms, not causes — and the same class of bug reappears elsewhere next week.
This workflow treats tests as diagnostic instruments, not just verification. By writing tests for every plausible hypothesis before touching production code, you accomplish two things: you discover exactly what's wrong (the failing test confirms the hypothesis), and you leave behind regression coverage that prevents the bug from returning. The codebase sweep at the end catches the systemic version — because if the pattern was wrong here, it's probably wrong elsewhere too.
The Cycle
1. HYPOTHESIZE ──► 2. TEST ──► 3. EXECUTE ──► 4. TRIAGE
▲ │
│ ┌────────────────────────────────┤
│ │ │
│ no failures, tests failing
│ bug persists │
│ │ ▼
└──────────┘ 5. FIX ──► re-run (3)
│
all pass
│
▼
6. SWEEP
│ │
similar issues clean
│ │
▼ ▼
restart (1) 7. AUDIT DOC ──► 8. QUESTIONS ──► 9. TEARDOWN ──► Done
The loop-backs are the point. When no test fails but the bug persists (Step 4), your hypotheses were incomplete — you go back and think harder with new information. When the sweep (Step 6) finds similar patterns, each gets the full treatment. These restarts aren't failure, they're the process working: each cycle eliminates possibilities and deepens understanding.
Step 1: Hypothesize
Generate ALL plausible causes before touching anything. Breadth over depth — cast a wide net so the tests can narrow it down.
First: Reproduce consistently. Before hypothesizing, confirm you can trigger the bug reliably. What are the exact steps? Does it happen every time? If it's intermittent, under what conditions? If you can't reproduce it, gather more data — don't guess. A bug you can't trigger is a bug you can't verify you've fixed.
Then generate hypotheses:
- Read error messages, stack traces, and the bug report carefully — don't skim
- Examine the code paths involved in the bug
- Think across layers: input validation, data transformation, state management, side effects, timing, environment
- Consider edge cases: empty/null inputs, boundary values, concurrent access, encoding, timezone differences
- Check recent changes:
git log,git diff— what changed that could cause this?
Output a numbered list of hypotheses. Each states:
- What might be wrong
- Where in the code it would manifest
- Why it could produce the observed behavior
Don't filter for likelihood — the "unlikely" hypothesis is often correct. The tests will sort it out. Aim for completeness.
After your hypothesis list: gather targeted evidence. Now that you've thought broadly, add diagnostic instrumentation at component boundaries if the system has multiple layers (e.g., pipeline → database → API → UI). Log what enters and exits each component. This narrows down WHERE the bug manifests without narrowing your thinking prematurely. The key is: hypothesize broadly first, then gather evidence to focus.
Restarting here from Step 4? Previous hypotheses were wrong or incomplete. Look at what you've eliminated. Consider interactions between components (not just individual ones). Question assumptions you took for granted last time.
Step 2: Test
Write tests that would fail if each hypothesis is correct. These are diagnostic instruments — each one either confirms or eliminates a hypothesis. But they're also more than that: these tests will outlive the debugging session and become permanent regression coverage that protects the codebase going forward.
What to do:
- For each hypothesis: write at least one test that would expose the bug if that hypothesis holds
- Add edge case tests around the bug's domain (boundary values, empty inputs, error paths)
- Use unit tests for isolated logic and E2E tests for integration/UI/full-path behavior
- Assert on the correct behavior — the test should fail against the current buggy code if the hypothesis is right
Test quality — write for the future, not just today:
- Minimal: Each test validates one hypothesis or edge case. If the name contains "and", split it.
- Clear: The name describes the behavior being tested, not the bug being fixed. A developer reading this test in 6 months should understand what it protects without knowing the debugging history.
- Real code: Use real code paths and realistic data. Avoid mocks unless external dependencies force it — mocks can mask the very bugs you're looking for.
- Regression-grade: These tests stay in the codebase permanently. Write them as if someone else will maintain them.
Test naming — describe behavior, never reference bug IDs:
GOOD:
"captures startTime at invocation, not construction"
"resets metrics between invocations"
"catches thrown exceptions from wrapped function"
"includes inactive assignments for historical attribution"
BAD:
"BUG-2: stale startTime closure"
"BUG-38: readAssignments only reads active assignments"
Test file placement:
- Tests go in
__tests__/.test.ts, matching the source file name - If a test file already exists for that source file, ADD your tests to it — group under the appropriate
describeblock for the function being tested - NEVER create bug-numbered test files like
bugs-38-42.test.tsorresilience-bugs.test.ts— these are debugging artifacts, not maintainable test infrastructure - If no test file exists yet, create one following the naming convention
Do not write the fix yet. Tests must compile and be runnable against the current (buggy) code. The purpose here is to let the tests tell you what's wrong, not to anticipate the fix.
Step 3: Execute
Run everything — both the new diagnostic tests AND the full existing test suite for the affected package or application.
What to do:
- Run the new tests first to see which hypotheses are confirmed (these fail)
- Run the complete package/app test suite to catch related breakage
- Record all failures with their messages — these are your evidence
Verify WHY each test fails. When a test fails, check that it fails for the right reason — the hypothesis being confirmed — not because of a typo, missing import, or setup error. A test that errors out on line 1 tells you nothing about the hypothesis. Fix the test error and re-run until it either fails correctly (hypothesis confirmed) or passes cleanly (hypothesis eliminated).
Why run existing tests too: The bug might already be partially covered by existing tests that are now failing. Or existing tests may reveal related issues you haven't hypothesized about. Both kinds of signal matter.
Step 4: Triage
Three possible outcomes. Each has exactly one correct next step — no judgment calls here.
No failures + bug is gone → Step 6 (Sweep)
Tests pass and the bug no longer reproduces. This can happen with environment-dependent issues or if it was fixed elsewhere. Still proceed to the Sweep — you want to check for similar patterns even if this instance resolved itself.
No failures + bug persists → Restart at Step 1
Your hypotheses missed the real cause. Before restarting, reflect:
- Did you test the right layer? (Maybe integration, not unit)
- Did test data actually trigger the condition?
- Are there unquestioned assumptions? (Config, environment, timing, data shape)
Each restart eliminates wrong hypotheses. This is progress — you now know what the bug is NOT.
Tests are failing → Step 5 (Fix)
Evidence found. The failing tests point to which hypotheses are correct. If multiple unrelated tests fail, start with the most fundamental failure — it may cascade to others.
Step 5: Fix
Fix the root cause revealed by the failing tests.
- Address the root cause, not the symptom
- Make the minimal change needed to pass the failing tests
- Don't refactor or "improve" nearby code — stay focused on the fix
- NEVER write
BUG-Nin code. Not in comments, not in JSDoc, not in variable names, not "temporarily." Track which bug you're fixing in your own reasoning — the code itself must never reference it. If a comment is needed, explain the design decision: "Promise-based singleton — avoids race condition on concurrent init", NOT "BUG-8 fix: Promise-based singleton." This applies to EVERY line you write, from the very first edit. Do not write BUG references with the intention of cleaning them up later — they will be missed. - New utilities go into existing files if the file is under ~300 lines and the utility logically belongs there. Only create a new file if it would be a separate concern or the existing file is already large. Never name a file after the bug it fixes.
- Reuse existing components. When fixing
.tsxfiles, list the project's component library directory recursively to discover available UI components. Never re-implement inline what the library already provides. When creating new UI elements, build them as generic reusable components in the library — never inline in route/page files. - After fixing: return to Step 3 and re-run ALL tests
After re-running:
- All pass → proceed to Harden (below), then Step 6 (Sweep)
- New failures appeared → your fix broke something. Analyze within this step, don't restart the whole cycle
- Original failures remain → fix didn't address the root cause. Rethink and try again within this step
If 3+ fix attempts have failed: Stop patching. Three failed fixes in a row usually means the design itself is wrong, not just the implementation. The repeated failures are revealing coupling, shared state, or architectural assumptions that no single fix can address. Step back and question whether the current approach is fundamentally sound before attempting fix #4. Discuss with the user.
Harden for Regression
This is a hard gate — you cannot proceed to the sweep until every item passes. This is not optional cleanup.
Step H1: Automated trace check (MANDATORY) Run this grep on every file you changed:
grep -rn 'BUG-\|BUG [0-9]\|bug-\|bug fix\|fix:'
If the count is anything other than zero, you are not done. Go back and replace every reference with a design-rationale comment or delete it. Repeat until the grep returns nothing.
Step H2: Test cleanup
- Test descriptions must describe behavior, not bugs: "captures startTime at invocation" not "BUG-2: stale startTime"
- Tests are in
__tests__/.test.ts, added to existing test files when present - No bug-numbered test files exist — merge into canonical files, delete the originals
- Group tests by function under
describeblocks
Step H3: Code cleanup
- Verify new utility functions are in the right file (existing file if ' --include='*.ts'` should show only the canonical source and its test
Found similar issues (not duplicates)? Each gets the full treatment — restart from Step 1 for that instance. Don't blindly apply the same fix, because the context may differ enough that a copy-paste fix would be wrong. Let the tests confirm.
Codebase is clean? Proceed to Step 7.
Step 7: Update the Source Audit Document
This step applies when the bugs came from a findings/audit document — a hunt report, audit file, findings list, or review doc (e.g., docs/*hunt*.md, an audit report the user pointed you at). If the bug came from a direct user report with no source document, skip to Step 8.
The audit doc is the record of what was found; after this session it must also record what was done. Update it now, while every change is fresh — not "later."
For EVERY finding you touched this session, update its entry in the doc with:
- Status — one of:
FIXED,WON'T FIX(with reason),FALSE POSITIVE(with evidence),DEFERRED(with why) - What changed — the files modified and a one-line description of the fix
- Tests added — the test file(s) and what behavior they now protect
- Deviations — if the actual root cause differed from what the finding described, correct the record
Also update any summary/counts section in the doc (e.g., "12 findings, 3 open") so totals match reality.
Note: bug IDs and finding numbers are BANNED in code (Step 5), but the audit doc is the opposite — it IS the audit trail. Reference finding IDs freely there.
Step 8: Resolve Open Questions (before final verification)
Before running the final Verification Checklist, review the whole session for open questions — ambiguous design choices, uncertain exports, behaviors you guessed at, deferred decisions ("if not obvious, ask the user"), or sweep findings you weren't sure how to treat.
- If any open questions remain: present them ALL in one batch using the
AskUserQuestiontool (not free-text prose). Then apply the user's answers — fix the remaining issues — and re-run the affected tests. - If none remain: proceed directly to the Verification Checklist.
Only after every open question is answered and every resulting fix is applied do you run the final verification round (the checklist below).
Step 9: Teardown
After verification passes and the bug is declared fixed, leave a clean terminal:
- Kill every background shell you started (watchers, dev servers,
run_in_backgroundcommands) — check
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: supermodo
- Source: supermodo/skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.