e1d73bdf

Redundant-test detection for mutation testing

## What

Adds redundant-test detection to sydtest mutation testing, produced **by the mutation `run` itself** (no separate command). After a full run, alongside `report.json`/`report.txt`, the driver writes `redundancy.json`/`redundancy.txt` describing which tests are redundant w.r.t. the mutations they *kill*.

The analysis (`analyzeRedundancy`, pure, in `sydtest-mutation-runtime`) reports three lenses plus extras:
- **Equivalence classes** — tests with identical killsets (keep one).
- **Dominated tests** — every mutation a test kills is also killed by another test.
- **Suggested minimal suite** — greedy min-set-cover + the removable remainder.
- **Load-bearing (sole-killer)** tests, and the dual **redundant-mutant** grouping.

## How (kill basis, no extra processes)

The mutation child already runs each mutation's covering set. When `--mutation-kill-row-output` is given it runs that set with within-set fail-fast **off** and writes a `TestKillRow` (`Map TestId Bool`) derived from the **structured `ResultForest`** (not text). One pass yields both the killed/survived verdict and the kill-matrix row. The parent aggregates the rows and runs `analyzeRedundancy`.

Per-test ids are aligned by canonical source order (full-forest flatten <-> result leaves; randomisation disabled), and `buildKillRow` returns `Left` rather than crashing on a mismatch — so the optional analysis can never flip a mutation's verdict.

## Toggle

Opt-out via `--redundancy`/`--no-redundancy` (default on) and `mutationCheck { redundancy = false; }`. Cost: each *killed* mutant runs its whole covering set instead of stopping at the first killer (no extra processes; survived/uncovered mutants are unaffected).

## Notable fix included

Found and fixed a latent bug: the mutation child writes its kill row *after* `printOutputSpecForest`, which in a C/POSIX-locale build sandbox **crashed encoding the checkmark marker** and made the child exit non-zero — silently mis-scoring **every survivor as killed**. Forcing UTF-8 on the child's stdout/stderr fixes it, and unmasked 9 genuine `sydtest-mutation-example` survivors, now killed by exhaustive `majorityOf5` tests.

Also: all on-disk JSON in the mutation pipeline (and the rerun cache) now uses `aeson-pretty` for diff/debug-friendliness.

## Tests / CI

- `sydtest-mutation-driver-gen`: `analyzeRedundancy`/`buildKillRelations`/`buildKillRow` unit tests, `genValidSpec` + `jsonSpec` roundtrips, golden renders (76 pass).
- Nix checks green: `release`, `mutation-sydtest-mutation-example` (redundancy populates; 35 sole-killers), `mutation-manifest-example`, `pre-commit`.

Suite timing

Time to Start Worker time Duration Time to finish Idle
Config 3h31m41s 4s 4s 3h31m45s 3h31m41s
Eval 3h55m22s 34s 34s 3h55m56s 23m37s
Build 4h27m50s 33s 2m30s 4h30m21s 33m51s
Suite 3h31m41s 1m11s 58m39s 4h30m21s 4h29m10s

Timeline

0s3h55m3h55m3h55m4h29m4h30m