Fix: ignore filter does not suppress RemoveAction on ignored do-block statements
## Summary of the bug
The mutation plugin's `ignore` config (e.g. `ignore: [logDebug, logInfo]`) is
supposed to leave certain calls — and their argument subtrees — completely
untouched. But mutations were still generated for ignored calls used as
`do`-block statements. In `~/src/nix-ci` these surfaced as `RemoveAction`
mutations dropping statements like:
- `forPrometheus prometheusEnvCounterBytesPut $ liftIO . Counter.add (SB.length contents)`
- `logDebug $ Text.pack $ unwords [...]`
- `logDebug "Unauthenticated request"`
## The regression test
`sydtest-mutation-example/src/Example/IgnoreLib.hs` gains `markM` (a no-op
`Writer` action, the monadic analogue of the existing `mark` marker) and a
`markAction` `do`-block that uses it as a non-binding statement:
```haskell
markAction = execWriter $ do
markM "tag"
tell "body"
```
`markM` is added to the `ignore:` list in `nix/mutation.yaml`. The golden
manifest `Example.IgnoreLib.json` asserts no mutations (`[]`). Before the fix,
the golden manifest check (`checks.x86_64-linux.mutation-manifest-example`)
fails, producing a `RemoveAction` mutation that removes the ignored
`markM "tag"` statement.
## Root cause
The `ignore` filter in `instrumentLExpr` only checks the syntactic head of the
*current* expression. GHC 9.10 expands `do { logDebug "x"; rest }` into
`(>>) (logDebug "x") rest`, whose head is `(>>)` — not `logDebug` — so the
filter does not fire. The `RemoveAction` operator then matches the `(>>)` chain
and removes its LHS action (`logDebug "x"`), which is exactly the noise the
ignore list is meant to silence.
## The fix
`RemoveAction` now consults `instrumentEnvIgnore` and declines to remove an
action whose syntactic head is on the ignore list, in both code paths:
- `mutateChain` — the expanded `(>>) lhs rest` form (the common GHC 9.10 case).
- `rawDoAction` — the raw `HsDo` fallback (list/monad comprehensions).
To keep this silent, `applyOperator` in `Instrument.hs` now treats an operator
action that returns no alternatives as a deliberate non-candidate rather than a
validation failure, so it no longer prints the "all replacements dropped"
warning in this case.
Non-ignored removals are unaffected: `Example.DoLib`'s `RemoveAction` mutations
are unchanged.
## Things to manually check
- In `~/src/nix-ci`, add the relevant names (e.g. `logDebug`, `logInfo`,
`forPrometheus`) to the `ignore:` list in `mutation.yaml` — the plugin still
only ignores names that are explicitly configured; there is no built-in
default ignore list. The three reported sites are all `do`-block statements
that this fix now suppresses once their head is listed.
- Confirm the mutation report for nix-ci no longer lists the `RemoveAction`
survivors on those statements after listing their heads under `ignore:`.