Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when implementing any feature or bugfix, before writing implementation code. Enforces RED-GREEN-REFACTOR cycle with test-first approach.
.claude/skills/graniet-test-driven-development/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 115% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 136% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 236% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 105% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 133% | 0% |
This skill is repo-local and stays inactive until explicitly activated.
When the original instructions refer to legacy tool names, use these Kheish mappings:
terminal => bashweb_extract => web_fetch, plus web_search when discovery is neededsearch_files => grep_search and glob_searchbrowser_* tools require a browser-capable surfaced tool or MCP; if none is available, use the closest available surface and say so explicitlyWhen the instructions mention local helper files, resolve them from ${KHEISH_SKILL_DIR}.
Write the test first. Watch it fail. Write minimal code to pass.
Core principle: If you didn't watch the test fail, you don't know if it tests the right thing.
Violating the letter of the rules is violating the spirit of the rules.
Always:
Exceptions (ask the user first):
Thinking "skip TDD just this once"? Stop. That's rationalization.
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRSTWrite code before the test? Delete it. Start over.
No exceptions:
Implement fresh from tests. Period.
Write one minimal test showing what should happen.
Good test:
pythondef test_retries_failed_operations_3_times(): attempts = 0 def operation(): nonlocal attempts attempts += 1 if attempts < 3: raise Exception('fail') return 'success' result = retry_operation(operation) assert result == 'success' assert attempts == 3
Clear name, tests real behavior, one thing.
Bad test:
pythondef test_retry_works(): mock = MagicMock() mock.side_effect = [Exception(), Exception(), 'success'] result = retry_operation(mock) assert result == 'success' # What about retry count? Timing?
Vague name, tests mock not real code.
Requirements:
MANDATORY. Never skip.
bash# Use terminal tool to run the specific test pytest tests/test_feature.py::test_specific_behavior -v
Confirm:
Test passes immediately? You're testing existing behavior. Fix the test.
Test errors? Fix the error, re-run until it fails correctly.
Write the simplest code to pass the test. Nothing more.
Good:
pythondef add(a, b): return a + b # Nothing extra
Bad:
pythondef add(a, b): result = a + b logging.info(f"Adding {a} + {b} = {result}") # Extra! return result
Don't add features, refactor other code, or "improve" beyond the test.
Cheating is OK in GREEN:
We'll fix it in REFACTOR.
MANDATORY.
bash# Run the specific test pytest tests/test_feature.py::test_specific_behavior -v # Then run ALL tests to check for regressions pytest tests/ -q
Confirm:
Test fails? Fix the code, not the test.
Other tests fail? Fix regressions now.
After green only:
Keep tests green throughout. Don't add behavior.
If tests fail during refactor: Undo immediately. Take smaller steps.
Next failing test for next behavior. One cycle at a time.
"I'll write tests after to verify it works"
Tests written after code pass immediately. Passing immediately proves nothing:
Test-first forces you to see the test fail, proving it actually tests something.
"I already manually tested all the edge cases"
Manual testing is ad-hoc. You think you tested everything but:
Automated tests are systematic. They run the same way every time.
"Deleting X hours of work is wasteful"
Sunk cost fallacy. The time is already gone. Your choice now:
The "waste" is keeping code you can't trust.
"TDD is dogmatic, being pragmatic means adapting"
TDD IS pragmatic:
"Pragmatic" shortcuts = debugging in production = slower.
"Tests after achieve the same goals — it's spirit not ritual"
No. Tests-after answer "What does this do?" Tests-first answer "What should this do?"
Tests-after are biased by your implementation. You test what you built, not what's required. Tests-first force edge case discovery before implementing.
| Excuse | Reality | |--------|---------| | "Too simple to test" | Simple code breaks. Test takes 30 seconds. | | "I'll test after" | Tests passing immediately prove nothing. | | "Tests after achieve same goals" | Tests-after = "what does this do?" Tests-first = "what should this do?" | | "Already manually tested" | Ad-hoc ≠ systematic. No record, can't re-run. | | "Deleting X hours is wasteful" | Sunk cost fallacy. Keeping unverified code is technical debt. | | "Keep as reference, write tests first" | You'll adapt it. That's testing after. Delete means delete. | | "Need to explore first" | Fine. Throw away exploration, start with TDD. | | "Test hard = design unclear" | Listen to the test. Hard to test = hard to use. | | "TDD will slow me down" | TDD faster than debugging. Pragmatic = test-first. | | "Manual test faster" | Manual doesn't prove edge cases. You'll re-test every change. | | "Existing code has no tests" | You're improving it. Add tests for the code you touch. |
If you catch yourself doing any of these, delete the code and restart with TDD:
All of these mean: Delete code. Start over with TDD.
Before marking work complete:
Can't check all boxes? You skipped TDD. Start over.
| Problem | Solution | |---------|----------| | Don't know how to test | Write the wished-for API. Write the assertion first. Ask the user. | | Test too complicated | Design too complicated. Simplify the interface. | | Must mock everything | Code too coupled. Use dependency injection. | | Test setup huge | Extract helpers. Still complex? Simplify the design. |
Use the terminal tool to run tests at each step:
python# RED — verify failure terminal("pytest tests/test_feature.py::test_name -v") # GREEN — verify pass terminal("pytest tests/test_feature.py::test_name -v") # Full suite — verify no regressions terminal("pytest tests/ -q")
When dispatching subagents for implementation, enforce TDD in the goal:
pythondelegate_task( goal="Implement [feature] using strict TDD", context=""" Follow test-driven-development skill: 1. Write failing test FIRST 2. Run test to verify it fails 3. Write minimal code to pass 4. Run test to verify it passes 5. Refactor if needed 6. Commit Project test command: pytest tests/ -q Project structure: [describe relevant files] """, toolsets=['terminal', 'file'] )
Bug found? Write failing test reproducing it. Follow TDD cycle. The test proves the fix and prevents regression.
Never fix bugs without a test.
Production code → test exists and failed first
Otherwise → not TDDNo exceptions without the user's explicit permission.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-03 | fail→fail | 12,741 | 3,761 | -70% | 1 | 1 | 0% | 2,218 | 3,174 | +43% | 0 | 0 | — |
case-01 | fail→fail | 18,105 | 9,371 | -48% | 1 | 1 | 0% | 4,434 | 2,879 | -35% | 0 | 0 | — |
case-02 | fail→fail | 3,612 | 4,540 | +26% | 1 | 1 | 0% | 233 | 2,778 | +1092% | 0 | 0 | — |
case-04 | fail→fail | 8,479 | 3,856 | -55% | 1 | 1 | 0% | 1,452 | 3,119 | +115% | 0 | 0 | — |
case-05 | fail→fail | 10,526 | 5,088 | -52% | 1 | 1 | 0% | 1,789 | 3,306 | +85% | 0 | 0 | — |
case-06 | fail→pass | 9,680 | 7,868 | -19% | 1 | 1 | 0% | 1,805 | 3,882 | +115% | 0 | 0 | — |
case-07 | pass→pass | 8,446 | 5,859 | -31% | 1 | 1 | 0% | 1,540 | 3,545 | +130% | 0 | 0 | — |
case-08 | pass→pass | 9,159 | 4,325 | -53% | 1 | 1 | 0% | 1,676 | 3,254 | +94% | 0 | 0 | — |
case-09 | pass→pass | 7,788 | 3,480 | -55% | 1 | 1 | 0% | 1,326 | 3,145 | +137% | 0 | 0 | — |
case-10 | fail→pass | 8,980 | 6,903 | -23% | 1 | 1 | 0% | 1,533 | 3,612 | +136% | 0 | 0 | — |
case-11 | fail→pass | 22,030 | 6,141 | -72% | 1 | 1 | 0% | 1,079 | 3,628 | +236% | 0 | 0 | — |
case-12 | pass→pass | 9,036 | 3,866 | -57% | 1 | 1 | 0% | 1,655 | 3,159 | +91% | 0 | 0 | — |
case-13 | pass→pass | 9,882 | 4,658 | -53% | 1 | 1 | 0% | 1,638 | 3,376 | +106% | 0 | 0 | — |
case-14 | fail→pass | 10,137 | 8,278 | -18% | 1 | 1 | 0% | 1,877 | 3,848 | +105% | 0 | 0 | — |
case-15 | pass→pass | 11,757 | 7,314 | -38% | 1 | 1 | 0% | 2,054 | 3,741 | +82% | 0 | 0 | — |
case-16 | pass→pass | 8,029 | 3,537 | -56% | 1 | 1 | 0% | 1,428 | 3,099 | +117% | 0 | 0 | — |
case-17 | pass→pass | 4,599 | 2,885 | -37% | 1 | 1 | 0% | 849 | 2,997 | +253% | 0 | 0 | — |
case-18 | pass→pass | 9,799 | 5,734 | -41% | 1 | 1 | 0% | 1,693 | 3,448 | +104% | 0 | 0 | — |
case-19 | fail→pass | 9,752 | 6,221 | -36% | 1 | 1 | 0% | 1,545 | 3,595 | +133% | 0 | 0 | — |
case-20 | pass→pass | 5,623 | 2,035 | -64% | 1 | 1 | 0% | 995 | 2,767 | +178% | 0 | 0 | — |
case-21 | pass→pass | 5,636 | 1,656 | -71% | 1 | 1 | 0% | 999 | 2,761 | +176% | 0 | 0 | — |
case-22 | pass→pass | 6,838 | 1,288 | -81% | 1 | 1 | 0% | 1,083 | 2,641 | +144% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 19 counted toward the lift figure. The other 3 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +23 percentage points is the difference between those two pass rates over the 19 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.