Mutation testing: diff-runner fixes, per-operator plugin config, SB rename
Diff-scoped mutation testing fixes, a per-operator plugin config, and a
qualifier cleanup. Split into four reviewable commits; base is the current
`development` tip.
## Commits
1. **Use `SB` as the qualifier for `Data.ByteString`** — mechanical rename
(was `B` in the driver/runtime, `BS` in `Mutate`), leaving lazy
`Data.ByteString.Lazy` as `LB`.
2. **Mutation driver: diff-runner accuracy, reporting, concurrency cap, debug**
- **CWD fix (the real bug):** the diff-scoped runner now runs each mutation
child in its suite's resource directory (per-child `setWorkingDir`,
race-safe under `mapConcurrently`), matching the coverage phase and the
full run. Previously the recorded covering `TestId`s didn't match the
forest the child enumerated → false survivors. Also fixes the full run,
which ran every suite's children from only the *first* suite's resource dir.
- **Surviving-mutations report** repeated at the very end of stdout, so they
aren't scrolled off in long CI logs.
- **Lenient UTF-8 decoding** of the diff (`obtainDiff`), matching the git
path, so a non-ASCII diff doesn't crash under a sandbox C locale.
- **Concise per-mutation progress** by default; `--debug` adds the full diff.
- **`--mutation-jobs`** caps mutation-child concurrency (mirrors
`--coverage-jobs`; defaults to `coverageJobs`). DB-backed suites flake
under full core-count contention, producing non-reproducible false kills.
3. **Mutation plugin: per-operator config** — a generic `operators` config
object:
```yaml
operators:
ConstEmptyList:
skip-strings: true
Arith:
enable: false
```
`settingOperators :: Map Text OperatorConfig`; each `OperatorConfig` has an
`enable` toggle (`enable: false` ≡ listing it in `disabled-mutations`) and an
opaque `operatorConfigExtra :: Map Text Value` that each operator interprets
itself. `ConstEmptyList` reads `skip-strings` (skip `[Char]`/`String`
expressions, keeping genuine `[a]` list mutations) and `skip-literal-strings`.
Defaults keep every operator enabled and the options off, so existing
behaviour is unchanged.
4. **Bump `sydtest` to 0.26.0.0** for the breaking
`renderMutationProgressEvent` signature change. (`sydtest-mutation-plugin`
→ 0.3.0.0 is in commit 3; driver/runtime stay `0.0.0.0`.)
## Validation
- `nix flake check` passes (release, weeder, `mutation-sydtest-mutation-example`,
pre-commit).
- The downstream nix-ci `.#mutation` gate passes against this branch:
`killed=2519, survived=0, uncovered=0`.
- New regression tests: each mutation child runs in its suite's resource dir
(`MutateSpec`); the survivors report block (`DiffRunSpec` golden); per-operator
`enable`/`extra` parsing (`OptParseSpec`).
## Note on commit granularity
The driver commit (2) bundles five improvements because they're coupled through
`runMutationMode`'s parameter list (it gained `debug`, `mutationJobs`, and the
`suiteConfigs` CWD change together) — splitting them further would mean
re-threading that signature across several non-compiling intermediate commits.
Happy to do that if you'd prefer it for review.