c694896f
build: an empty diff is not an invitation to mutate the tree
a73x 2026-08-22 16:18
Commit message
Makefile
| Old | New | ||
|---|---|---|---|
| @@ -201,8 +201,20 @@ mutate-diff: gremlins-tool | |||
| 201 | # first: the tree has equivalent mutants that CANNOT be killed (internal/ | 201 | # first: the tree has equivalent mutants that CANNOT be killed (internal/ |
| 202 | # cloudinit's `i+1 < len(root.Content)` is the same loop for every even-length | 202 | # cloudinit's `i+1 < len(root.Content)` is the same loop for every even-length |
| 203 | # YAML mapping either way), so a 100% floor would fail honest work. | 203 | # YAML mapping either way), so a 100% floor would fail honest work. |
| 204 | # The empty-diff guard is not an optimisation. Gremlins treats an EMPTY parsed | ||
| 205 | # diff as "everything changed" (internal/diff: IsChanged returns true when the | ||
| 206 | # diff has no entries), so -D against a base that equals HEAD silently becomes a | ||
| 207 | # whole-tree run — 22 minutes, and it reaches internal/agent/cloudhv, whose | ||
| 208 | # `if pid != 0` mutant turns into kill(0) and SIGKILLs this make process. Which | ||
| 209 | # is exactly what a push to main does: it moves origin/main to HEAD, and the | ||
| 210 | # NEXT ci run has nothing to diff. | ||
| 211 | # | ||
| 212 | # setsid for the same reason: a mutant that signals its own process group must | ||
| 213 | # not be able to reach the build that spawned it. | ||
| 204 | mutate-report: gremlins-tool | 214 | mutate-report: gremlins-tool |
| 205 | -@$(GREMLINS) unleash --workers $(GREMLINS_WORKERS) \ | 215 | @git diff --quiet --merge-base $(MUTATE_BASE) -- '*.go' 2>/dev/null && \ |
| 216 | { echo "mutate-report: no Go changes against $(MUTATE_BASE) — skipped"; exit 0; } || \ | ||
| 217 | setsid $(GREMLINS) unleash --workers $(GREMLINS_WORKERS) \ | ||
| 206 | --timeout-coefficient $(GREMLINS_TIMEOUT) \ | 218 | --timeout-coefficient $(GREMLINS_TIMEOUT) \ |
| 207 | -D $(MUTATE_BASE) . 2>&1 | tail -6 || true | 219 | -D $(MUTATE_BASE) . 2>&1 | tail -6 || true |
| 208 | 220 | ||
RETRO.md
| Old | New | ||
|---|---|---|---|
| @@ -9,3 +9,7 @@ One line per push to `main`: what slowed the work down. Enforced by | |||
| 9 | filesystem, not the tool, was producing the numbers — ext4 reported 207 | 9 | filesystem, not the tool, was producing the numbers — ext4 reported 207 |
| 10 | timed-out mutants and a flattering 100% efficacy where tmpfs reported 199 | 10 | timed-out mutants and a flattering 100% efficacy where tmpfs reported 199 |
| 11 | killed and an honest 93%. | 11 | killed and an honest 93%. |
| 12 | - The diff-scoped mutation check was wired without asking what an empty diff | ||
| 13 | means. Pushing main answered it: origin/main became HEAD, gremlins read a | ||
| 14 | diff with no entries as "everything changed", and the whole-tree run it | ||
| 15 | started was killed by the one mutant that signals its own process group. | ||