# `Mutare.Run.Context`
[🔗](https://github.com/foxbenjaminfox/mutare/blob/v0.2.1/lib/mutare/run/context.ex#L1)

The **runtime wiring** for a mutation run: the validated `Mutare.Options`
(configuration) bundled with the things that are *not* configuration — the
resolved `Mutare.Project` (copy-root + mutate-scope, derived from the target
path and `--app`/`--workspace`) and the four optional live-progress hooks
(`reporter`, `on_phase`, `on_start`, `on_scan`).

`on_phase` receives both the phase-transition atoms (`:compiling` → `:baseline`
→ `:coverage_probe` → `{:running, total}`) and structured **detail** events fired
alongside them — `{:compiled, ms}`, `{:baseline_done, ms}`, `{:coverage_done,
summary}`, `{:run_config, cfg}` (from `Mutare.Runner`), and `{:seed_app_build,
summary}` and `{:inference_override_declined, info}` (what `Mutare.Sandbox` reports
back from materialising, relayed by the runner during `:compiling`: what the app-build
`_build` seed did and each `mix.exs` whose inference override did not land) — carrying
the behind-the-scenes detail `--verbose` renders. A custom hook should ignore unrecognised events (the in-tree consumer,
`Mutare.Report.Live`, has a catch-all).

Splitting these off `Mutare.Options` keeps that struct pure configuration. The
pipeline (`Mutare.Schema`, `Mutare.Sandbox`, `Mutare.Runner`) threads a
`Run.Context`, reading config from `context.options` and wiring from the
context's own fields.

## Wrap-at-boundary

`new/1` is the normalization boundary, mirroring `Mutare.Options.new/1`: it
accepts an existing `Run.Context` (idempotent), a bare `Mutare.Options` (no
wiring), or a keyword list. For a keyword list it **splits** the wiring keys
(`:project` + the hooks) from the configuration keys, validating the former here
and routing the latter through `Mutare.Options.new/1`. So an existing
keyword-list call site — `Schema.build(root, project: p, mutators: m)` — keeps
working unchanged: the wiring rides into the context, the rest into options.

`new/2` is the convenience the Mix task uses to attach wiring to an already
resolved `Options` (`Run.Context.new(options, project: project)`).

# `hook`

```elixir
@type hook() :: (term() -&gt; any()) | nil
```

# `t`

```elixir
@type t() :: %Mutare.Run.Context{
  defer_site_code: boolean(),
  on_phase: hook(),
  on_scan: hook(),
  on_start: hook(),
  options: Mutare.Options.t(),
  project: Mutare.Project.t() | nil,
  reporter: hook(),
  summarize_sites: boolean()
}
```

# `ensure_project`

```elixir
@spec ensure_project(t(), Path.t()) :: t()
```

Ensure the context carries a `Mutare.Project`, resolving one from `root` if it is
unset. The entry points (`Mutare.Runner.run/2`, the Mix task) resolve the project
from the target path + scope flags; the direct `Mutare.run/2` API leaves it `nil`,
so it is resolved here as a single-app project at `root`.

# `hook`

```elixir
@spec hook(t(), atom()) :: (term() -&gt; any())
```

The 1-arity hook bound to `field` (`:reporter`/`:on_phase`/`:on_start`/`:on_scan`),
or a no-op when unset — so `Mutare.Runner` and `Mutare.Schema` invoke it
unconditionally without each re-stating the `|| fn _ -> :ok end` default. The
single home for that default.

# `new`

```elixir
@spec new(t() | Mutare.Options.t() | keyword()) :: t()
```

Normalize an input into a `Run.Context`.

Accepts an existing `Run.Context` (returned as-is), a `Mutare.Options` (wrapped
with no wiring), or a keyword list (wiring keys split out and validated, the
rest validated through `Mutare.Options.new/1`).

    iex> ctx = Mutare.Run.Context.new(mutators: [:arithmetic], on_scan: fn _ -> :ok end)
    iex> {Enum.map(ctx.options.mutators, & &1.name), is_function(ctx.on_scan, 1)}
    {[:arithmetic], true}

    iex> ctx = Mutare.Run.Context.new(workers: 2)
    iex> Mutare.Run.Context.new(ctx) == ctx
    true

# `new`

```elixir
@spec new(
  Mutare.Options.t() | keyword(),
  keyword()
) :: t()
```

Build a `Run.Context` from already-resolved `options` (an `Options` or a keyword
list) plus a keyword list of wiring (`:project` and/or the hooks).

---

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