By: Tom Sydney Kerckhove <syd@cs-syd.eu>
sydtest: control-group (no-op) mutations Add control-group mutations: deliberate no-ops that are expected to survive. The plugin wraps an expression as `(\ctrl -> ifMutation cmid ctrl ctrl) e` (the same expression on both branches, so a genuine non-diff), one after every 12 real mutations. Both branches share the subtree through the lambda binder, so the no-op does not duplicate the instrumented expression. The control rides the existing operator field under the reserved name "Control"; no new record field. A control changes no behaviour, so killing it is impossible unless the mutation testing is unsound. Tests are already retried, so a killed control is a real defect (a flaky/nondeterministic suite or a harness bug), not transient noise, and it fails the run like a survivor: assert-score fails on it, the driver's fail-fast exit trips on it, and isMutationFailure is True for it. A passing control never trips anything and is excluded from the killed/survived score. MutationRunReport is split into three parts to match: a MutationTally (the normal killed/survived/timed-out/uncovered/skipped score), a ControlTally (passed/failed), and the per-mutation groups; the JSON nests to match. Record types used as formal arguments use RecordWildCards.
| Time to Start | Worker time | Duration | Time to finish | Idle | |
| Config | 0s | 2s | 2s | 2s | 0s |
| Eval | 2s | 42s | 42s | 45s | 0s |
| Build | 55m34s | 37s | 29s | 56m04s | 54m49s |
| Suite | 0s | 1m22s | 56m04s | 56m04s | 54m49s |