# `Mutare.InvariantError`
[🔗](https://github.com/foxbenjaminfox/mutare/blob/v0.5.1/lib/mutare/invariant_error.ex#L1)

Raised under `verify_invariants: true` (`mix mutare --verify-invariants`) when a rendered
metamutant breaks a property the run's results depend on.

Every violation is a bug in Mutare or in a custom mutator, host, or extension enabled for the
run — never a problem with the project under test. Most would otherwise corrupt the report
silently: a recorded mutant that the metamutant cannot select runs the unmutated code, so it
survives whatever the suite checks, and one that no coverage record lists is reported as
uncovered and never tested.

`file` is the transformed file; `violations` lists what the checks found, in this order:

  * `{:unparseable_metamutant, message}` — the rendered metamutant is not valid Elixir, so
    no other readback check could run.
  * `{:missing_branch, site}` — no generated code selects the mutant.
  * `{:unreachable_branch, site, enclosing}` — the mutant's only branches sit inside the
    branches of other mutants (`enclosing`), which never run while this one is the only active
    mutant.
  * `{:stray_branch, subject}` — generated code selects an id that the transform did not
    deliver.
  * `{:missing_record, site}` — no coverage record outside every mutant branch lists the
    mutant.
  * `{:stray_record, subject}` — a coverage record lists an id that the transform did not
    deliver.
  * `{:unread_clean_region, decision}` — the transform emitted a clean region
    (`Mutare.Transform.CleanRegion`) whose uninstrumented copy `Mutare.Manifest` cannot find.
    The copy belongs to no mutant, so a compile error inside it could be blamed on nothing
    and would sink the single build.
  * `{:stray_clean_region, range}` — the metamutant holds a clean region the transform did
    not decide on.
  * `{:unchanged_mutant, site}` — the mutant renders identically to its original code, so no
    test can kill it.
  * `{:nondeterministic_render, differences}` — emitting the same source again gave a
    different result, so report-time diffs (which re-render) may describe other mutants than
    the ones that ran.

A `subject` is `{local_id, site}`: the integer the generated code selects on, and the Site the
transform recorded under it (`nil` when there is none — then the id belongs to no mutant).

# `clean_decision`

```elixir
@type clean_decision() :: %{
  :function =&gt; {atom(), arity()},
  :delivery =&gt; :lifted | :in_place,
  :range =&gt; Mutare.Manifest.clean_range(),
  optional(atom()) =&gt; term()
}
```

What the transform decided for one clean region: the `:function` it belongs to, its
`:delivery` (`:lifted` or `:in_place`), and the `:range` of ids identifying it, among
other fields.

# `subject`

```elixir
@type subject() :: {non_neg_integer(), Mutare.Site.t() | nil}
```

An id the generated code names, and the Site recorded under it, if any.

# `t`

```elixir
@type t() :: %Mutare.InvariantError{
  __exception__: true,
  file: String.t(),
  message: String.t(),
  violations: [violation()]
}
```

# `violation`

```elixir
@type violation() ::
  {:unparseable_metamutant, String.t()}
  | {:missing_branch, Mutare.Site.t()}
  | {:unreachable_branch, Mutare.Site.t(), [subject()]}
  | {:stray_branch, subject()}
  | {:missing_record, Mutare.Site.t()}
  | {:stray_record, subject()}
  | {:unread_clean_region, clean_decision()}
  | {:stray_clean_region, Mutare.Manifest.clean_range()}
  | {:unchanged_mutant, Mutare.Site.t()}
  | {:nondeterministic_render, [String.t()]}
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
