Retry a mutation control that only ran out of time, and never store its verdict
## The bug
A mutation check failed with nothing wrong with the code, and could not be retried without deleting things out of the nix store by hand.
The run that prompted this was nix-ci's `mutation-leader-background`:
```
Killed: 1132
(of which timed out: 10)
Survived: 0
Controls failed: 2
Failed controls (mutation testing may be unsound)
```
Nothing survived. Both "failed" controls were timeouts, at 30s and 50s, on a 64-core machine at load average 42.
Two separate things went wrong.
**One overrun decided the control.** A control is a no-op, so a suite that *kills* one is unsound however often you repeat it — that conclusion is sound on the first run. A suite that merely *overruns its budget* has shown nothing of the sort, because the budget is `max defaultTimeoutMicros (10 * baseline)` and the baseline is measured on an idle machine. The driver drew the unsoundness conclusion from a single overrun with no second attempt.
**The verdict was then stored where it would be read forever.** `run` exits zero whatever the controls did, so the expensive mutation derivation *succeeded* while recording a failing verdict. Nix caches successes, so every later attempt re-read the stored report rather than running anything, and the cheap `assert-score` gate failed identically each time. Retrying meant `nix store delete` on the report path — which was itself pinned by a GC root, so it could not be deleted at all.
## The test
`MutateSpec`'s "retries a control that timed out instead of calling the suite unsound" drives `runMutationMode` with a stand-in suite executable that overruns its timeout on its first run and is prompt afterwards, against a 200ms budget. The control passes only if a timed-out control is given another go. It failed before the fix, recording the control as failed on the first overrun.
`AugmentedManifestSpec` covers `runSoundness` directly: a failed control is `RunUnsound`, survivors are `RunSound`.
## The fix
A control now gets three goes, and **only an overrun is retried** — a kill still decides a control at once, because that conclusion was always sound. The last attempt's outcome stands, so a control that overruns every time still fails, as a suite nobody can measure should.
An unsound run now exits non-zero even without `--fail-fast`, so nothing is stored and the next attempt runs the mutations again. No nix change was needed: a failing derivation is not cached.
Survivors keep the old behaviour deliberately. They are reproducible, so doing the expensive run once and letting the cheap gate re-read it is exactly right. `runSoundness :: MutationRunReport -> RunSoundness` names that distinction, as a two-constructor type rather than a `Bool`, and is testable where the inline condition was not.
## Things to check manually
- **Three attempts** is a guess. A genuinely hanging suite now costs three budgets before it is called unsound. If that is too slow, or too few, it is one constant in `Mutate.hs`.
- **`assertAllKilled = false` (report-only) mode** now fails when a control fails, where before it produced a report. That follows from the rule — an unsound report is not worth keeping — but it is a behaviour change for anyone using that mode.
- **A timed-out *mutant* is still counted as killed.** Under load that inflates the score in the opposite direction: a mutation the tests never actually caught is scored as caught. Left alone here because it does not fail a build, but it has the same root cause and may deserve the same treatment.
## CI
`nix flake check` does not pass on this branch, and I do not believe it is this change.
`sydtest-test` fails on two tests, neither of which this branch can reach — it only adds a type and a function to `AugmentedManifest`:
- `long diff timing is bounded` took **205s** against its own 120s bound. Its comment says the bound is "generous to account for slow CI sandboxes".
- `seeds the global pseudorandom generator so a FixedSeed run is reproducible`, which sydtest itself marked `!!! FLAKY !!!` and which passed on a retry.
The machine was at load average 33 throughout, with seven `nix-ci-leader-gen` test processes at ~300% CPU each. `sydtest` depends on `sydtest-mutation-runtime`, so this change invalidated `sydtest-test` and forced it to actually run there — which is the same caching behaviour this PR is about. Worth re-running on a quiet machine before merging.