3bf4ae7f

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.

Suite timing

Time to Start Worker time Duration Time to finish Idle
Config 0s 37s 37s 38s 0s
Eval 38s 38s 38s 1m16s 0s
Build 1m19s 4m55s 4m24s 5m44s 2s
Suite 0s 6m11s 5m43s 5m44s 3s

Timeline

0s1m2m3m4m5m