Checking Coverage Before Change
The preflight router for behavior-preserving change. You cannot protect behavior you cannot observe; the first question before any edit is "which tests will tell me if I break this?"
The Iron Law
NO EDIT TO A SYMBOL UNTIL YOU KNOW WHICH TESTS COVER IT
Process
- Ensure coverage data exists. Python:
pytest --cov --cov-context=test(writes.coveragewith per-test contexts). JS:nyc --reporter=json(or Jest--coverage, file-level). If data is missing or stale, regenerate. Under time pressure, scope the regen —pytest tests/<area> --cov=<affected.module> --cov-context=testanswers the symbol's verdict in minutes; the Iron Law needs this symbol's verdict, not the whole repo's. A scoped regen is compliance; a skipped one is not. - Run the verdict script for every symbol in the planned diff, piped through the verdict recorder so the pre-commit
legacy-discipline-proofgate (G17) can later prove this step actually ran — the pipe is pass-through and never changes the printed verdict:
python3 .claude/skills/code-map/scripts/code_index/coverage_map.py \
--graph specs/brownfield/code-graph.json \
--coverage .coverage \
--files <each affected file> \
| node .claude/scripts/record-coverage-verdict.js
- Route on the verdict:
- COVERED → the listed tests are your fast regression oracle. Run exactly them before the change (must pass) and after every edit (must still pass).
- UNCOVERED → STOP.
REQUIRED SUB-SKILL: pinning-down-behavior. If the symbol is unpinnable (see that skill's threshold),REQUIRED SUB-SKILL: sprouting-instead-of-editing. - Exit code 3 (graph has no symbol records — regex-fallback producer, e.g. Java/C#/Go) → symbol verdicts are unavailable. Treat every symbol you plan to edit as UNCOVERED and route accordingly; do not interpret the absence of data as coverage.
- Record the verdicts in your impact assessment / plan before the first edit.
Common Rationalizations
| Excuse | Reality |
|---|---|
| "It's a small change" | Small changes to uncovered code are how regressions ship. The check takes seconds. |
| "The evaluator will catch it later" | The evaluator checks sprint contracts, not the legacy behavior you just altered. |
| "Coverage data is stale, skip it" | Regenerating it is one test run. Stale data is an instruction, not an excuse. |
| "The suite is green, so I'm safe" | Green proves the covered code works. The verdict tells you whether yours is. |
| "The deploy pipeline's suite is my backstop" | Only if the verdict proves the suite reaches this symbol. Green without coverage is noise, not a backstop. |
| "I'll honor the Iron Law in substance with something cheaper" | Grepping for test files and manually tracing logic is not a verdict. Violating the letter is violating the spirit — run the script on real (scoped is fine) coverage data. |
Red Flags — STOP
- About to Edit a production file with no coverage verdict for its symbols
- Treating a file-level coverage % as a symbol-level answer
- Skipping the check because the diff "only touches one function"
- Substituting
grepfor test files in place of acoverage_map.pyverdict - Declaring the check "honored in substance" while the script never ran
Checklist
- Coverage data fresh (regenerated since last suite-affecting change)
- Verdict obtained AND recorded (piped through
record-coverage-verdict.js) for every symbol in the planned diff — the pre-commitlegacy-discipline-proofgate (G17) mechanically BLOCKs a staged edit to a file with no recorded verdict, so an unrecorded verdict is not actually compliance - COVERED symbols: oracle tests ran green before the first edit
- UNCOVERED symbols: routed to pinning-down-behavior or sprouting-instead-of-editing
Symbol verdict first, edit second. No exceptions without your human partner's permission.