d1f20100

Created Started Cancelled

Let a failed control name the test that killed it

A failed control now names the output of the run that killed it.

## The problem

A control is a no-op mutation. It cannot legitimately be killed, so a killed
control means the suite is flaky or the harness is buggy — and the run fails
because of it. But the report said only *that* one had failed, never *which
test* killed it:

```
Failed controls (mutation testing may be unsound):
Control at src/NixCI/Leader/Worker/Notify.hs:120:46-55
```

That is the whole report. `report.json` carries only `{failed: 1, passed: 79}`.
Survivors and timeouts each get a log file; a failed control got none.

The cause is one hardcoded `Nothing`. When a child exits non-zero the driver
records a kill and discards its output:

```haskell
ExitFailure _ -> pure SuiteKilled          -- output dropped
ExitSuccess   -> do mRelFile <- copyChildLog "survivor-" …
```

which is right for the ~1000 ordinary mutations of a healthy run, whose kill is
expected and whose output says nothing. For a control the kill *is* the
finding, so this dropped the evidence at exactly the moment it mattered:

```haskell
MutationKilled record -> MutationControlFailed (ControlFailedMutation record Nothing)
```

## The fix

`SuiteKilled` carries an optional log path, and the driver keeps the log only
when the killed mutation is a control, under a `control-failure-` prefix. So
the report gains one file per control failure, not one per mutation. The
failed-control section then points at it:

```
Control at M.hs:1:1-2 #0
    - no-op
    + no-op
  Output of the run that killed it: control-failure-M-Control-1-1-2-no-op-0-suite.log
```

## How I know it works

Two tests, both end-to-end through `runMutationMode` against a stand-in suite
executable:

* a killed control keeps output that names the test
* an ordinary killed mutation keeps none, so a healthy run does not grow a log
  per mutation

I checked the first one fails without the fix rather than assuming it would:
`the killed control kept no log to name its test`.

## Why now

nix-ci's `mutation-leader-background` failed on exactly this: one control
killed at a time-based wait computation, no way to tell which test did it. I
could not reproduce it (20+ local runs, including 8 concurrent suites, all
clean — it needs the load of a real mutation run), and with no test name there
was nothing to fix but a guess. This turns that into a one-minute diagnosis the
next time it happens.

## Note on versions

`sydtest` goes 0.30 → 0.31 because `SuiteOutcome`'s `SuiteKilled` constructor
gained a field; `sydtest-mutation-driver` goes 0.1 → 0.2.
PR #142