| name | tdd-loop |
| description | Run when the user says "implement X", "add a feature with tests", "make the tests pass", "fix this bug test-first", "red-green-refactor", or picks up any ticket whose Verify-by is a test. The discipline: write the FAILING test first and watch it fail, make the smallest change that turns it green, refactor only with tests green — and NEVER weaken an assertion to get to green, which is this repo's no-overclaim rule in miniature. Use even for changes that feel too small to test-drive — "too small to test" is how untested paths accumulate. Slash alias: /tdd-loop.
|
| metadata | {"short-description":"Red-green-refactor in this repo's script-mode test shape; never weaken an assertion to pass"} |
TDD loop (red-green-refactor, this repo's shape)
Repo-specific test shape (read this before writing the test)
This repo's CI runs hundreds of tests as python tests/test_*.py — script
mode, NOT pytest, NO PYTHONPATH. Every new test module needs the repo-root
sys.path bootstrap and a main() runner, exactly as ci-script-test-bootstrap
specifies; a test that passes under pytest locally but lacks the bootstrap
fails closed in CI. Run python tools/lint_script_tests.py before pushing.
Consult ci-script-test-bootstrap FIRST whenever the loop below says "write a
test".
The loop
- Agree the seam. Decide which public boundary the test exercises (the
function/CLI/gate a real caller uses — not private internals) and confirm
it with the user if there is any doubt. A test at the wrong seam survives
the wrong refactors and breaks on the right ones.
- RED — write one failing test. One behavior, named for the behavior
("registry rejects a promoted adapter without a receipt"), expected values
from an independent source of truth (a known-good literal, a worked
example, the spec) — never recomputed the same way the implementation
computes them, or the test proves nothing.
- Watch it fail, and read the failure. Run the exact CI invocation:
python tests/test_<name>.py. It must fail for the reason you predicted.
A test that passes before the implementation exists, or fails with an
import error, is not red — fix the test first.
- GREEN — smallest change that passes. Only enough production code to
turn this one test green. No speculative parameters, no adjacent cleanup,
no "while I'm here". Resist generalising ahead of a test that demands it.
- Watch it pass, then re-run the neighbors. The new test green, plus the
other test files touching the same area. Full local gate sweep comes at
PR time via
verification-recipe-runner.
- REFACTOR — only on green. Improve names, remove duplication, tighten
structure, with the tests as the invariant: they stay green throughout and
you do not edit test expectations during a refactor. If a refactor needs an
expectation changed, that is a behavior change — return to step 2.
- Repeat — next behavior, next slice. One test → one implementation →
repeat, each cycle informed by what the last one taught. Never write all
the tests up front and then all the code: that tests imagined behavior.
- Commit per green cycle where sensible, following
git-discipline
(explicit git add file lists; this checkout is shared).
The assertion rule (non-negotiable)
When a test goes red during development, there are exactly two honest moves:
change the code, or consciously decide the specified behavior was wrong and
change the spec-and-test together, saying so out loud. Loosening a tolerance,
deleting an assert, widening an expected range, or marking the case "flaky"
just to get green is fabricating evidence — the same act, in miniature, as
inflating a benchmark number, and this repo's no-overclaim culture treats it
that way. A weakened assertion is worse than no test: it certifies the bug.
If an assertion is genuinely flaky, diagnose the nondeterminism (seed it, pin
it, isolate it) rather than sanding the assertion down.
Do not
- Do not write production code before a failing test exists for it.
- Do not skip watching the test fail — an unfailed test may be testing nothing.
- Do not write the test with pytest-only shapes (fixtures, bare
assert-only
files without a runner); CI runs it as a script — ci-script-test-bootstrap.
- Do not weaken, delete, or tolerance-inflate an assertion to reach green.
- Do not refactor and change behavior in the same step; the tests can only
protect one of those at a time.
- Do not claim the feature "works" from green tests alone — green means the
tested behaviors held at the tested seam; say exactly that, and let
verification-recipe-runner do the end-to-end pass at PR time.
Method adapted from mattpocock/skills skills/engineering/tdd/SKILL.md (MIT) — see THIRD_PARTY_NOTICES.md.