Mutation run: a kill is confirmed, and one copy per worker
Merged
DO
3d ago+501+501+012345678901234567890123456789-31-31-01234567890123456789
Why. Two problems with the mutation runner:
- False kills: a mutant counted as killed when any test failed, even one failing for its own reasons under load. On #145 four mutants looked killed locally and survived in CI.
- Disk churn: every mutant copied the whole repo into a fresh directory. On 2026-10-17, local runs across several sessions wrote about 550 GB in 12 hours. The file-event service grew to 42.6 GB, swap ran out, and the Mac had a kernel panic.
What changes
- A kill is confirmed. The first failing test is run again alone on the mutant, and alone on the unbroken code in the same tree.
- It must fail on the mutant and pass on the unbroken code.
- If not, the whole suite runs once more on the mutant. A pass is
SURVIVED; a second unconfirmed failure isFLAKY, which fails the run (a working ruling). - A timeout or crash stays
ERROR.
- Timing-sensitive tests: six tests are marked
@wall_clockand skipped during the mutation run; they still run inmake verify. A test holds that the marked set is exactly the set of tests that read the clock, by file, class and method, includingfrom time importandimport time as. - One copy per worker (#152, folded in at the Maintainer's call):
- Each worker copies the tree once, 2.8 MB and 227 files, with no
.gitand no caches. It runs mutants one at a time in that copy. - A mutant writes only the file it breaks. However the run ends, crash or hang included, the original bytes are written back and read again before the next mutant. A copy that doesn't read back is an
ERROR, and the copy is never reused. - Each mutated file gets its own timestamp, so no bytecode is reused across mutants.
- At most 4 workers by default.
- Each worker copies the tree once, 2.8 MB and 227 files, with no
- Test ids: tests are discovered from the repo top (
-s tests -t .), so a failing test's id can be run alone. This matchestools/verify.py. - Records:
- Decision 0027 records it,
tools/AGENTS.mdnames its test, and lesson L-60 is added. - The shard-time comment in
verify.ymland its two copies is updated. The figure is an estimate until CI shows it.
- Decision 0027 records it,
Verified:
make verifypasses (524 tests) on a plaingit archiveexport withPATH=/usr/bin:/bin; the record was renumbered in the export only, for the 0027 gap.- The runner's 18 tests pass, including a test that a mutant leaves no trace in the next mutant's tree, and one where a test that fails without
.gitcomes outFLAKY. - Five named mutants with 2 workers: 5 of 5 killed in 101 s, including the one that skips the restore.
- No full local mutation run: it is banned on this Mac until this merges. CI's six shards are the check.
Reviewed by: a fresh-context Claude review, a security pass, and AGY (Gemini 4 Argon) on 9abeffd. All their findings are fixed in 4f31038:
- the unbroken-side rerun ran in the checkout rather than an export, so a change could fake its own kills;
- a multi-test
onlycase was untested; - three rules had no mutant;
- the clock-mark test checked a subset;
- a stale
AGENTS.mdrow and the time comment.
The copy-once rework in d163dba was read again by a focused Claude review and by AGY (Gemini 4 Argon), and AGY approved with no findings. The review found no high or medium bugs. Its three low findings all fail safe:
- Only the mutated file is checked: a file a test leaves elsewhere in the copy is not detected. That gives FLAKY or ERROR, never a false kill, and the suite looks hermetic today.
- A second Ctrl-C can delete the worker directory while suites still run.
- The restored timestamp is not read back; the bytes are.
š¤ Generated with Claude Code
Summary by CodeRabbit
- Testing
- Mutation checks now verify that failures are caused by the change being tested, reducing false positives from flaky or load-sensitive tests.
- Timing-sensitive tests are skipped during mutation checks. Unconfirmed failures and test errors fail the run.
- Documentation
- Updated mutation-check timing estimates to about 450 mutants per shard, with a six-minute shard estimate. Six shards are still expected to handle about 800 mutants in under 15 minutes.