Sep 1, 2026 · v0.4.5

The Branch No Test Took

The coverage rule was statement-level, so a changed if counted as covered the moment its header executed. A conditional whose one branch no test entered earned SAFE with the changed behaviour untested. SAFE now requires that changed conditionals were fully branched, and --mutate arrives to ask whether anything asserted on the change at all.

The coverage rule was statement-level: a changed if or elif counted as covered the moment its header executed. So a change to a conditional whose one branch no test entered earned SAFE with the changed behaviour untested. A changed conditional is one of the most common regression shapes, and it is exactly where statement coverage is weakest.

SAFE now also requires that changed conditionals were fully branched. With coverage.py --branch, a changed line reported as a partially-taken branch floors the verdict at UNPROVEN, and the reason names it.

text[UNPROVEN] Tests pass, but a changed conditional has a branch no test took
(calc.py:3). Add a test that enters the other branch.

The decision was made on measurement rather than assertion (ADR-14). --branch added no runtime cost on a real 600-test suite. The flip is surgical: it bites only a changed conditional with an untaken arc, never straight-line code, which is 0% affected, and never a fully-exercised conditional. It cannot make SAFE unreachable for ordinary changes.

--mutate, an opt-in check for whether the change was asserted

Coverage proves a changed statement ran. It does not prove that any test would fail if its behaviour changed. A line a test executes but never asserts on earns SAFE.

refactron verify-diff --mutate perturbs the operators in the changed statements, boundary, arithmetic and boolean, reruns the suite against each, and floors the verdict at UNPROVEN if any mutant survives, naming the survivor.

text[UNPROVEN] Tests pass, but a mutant of a changed statement survived
(calc.py:2, + to -): no test failed when its behaviour changed.

Off by default and slower, since it reruns the suite once per mutant, so it is a deep check rather than the fast gate. Downgrade-only: a survivor can move SAFE to UNPROVEN and never the reverse, a clean run never lifts a verdict, and an inconclusive mutant that times out is skipped. Bounded to the changed statements and Python-only (ADR-15).