# `mix mut`
[🔗](https://github.com/lukaszsamson/mutalisk/blob/v0.1.0/lib/mix/tasks/mut.ex#L1)

Runs mutation testing.

## Options

  - `--files "PATTERN"` — Only mutate files matching glob pattern. Quote
    globs so your shell does not expand them first. Repeat the flag, pass
    multiple path tokens after it, or use comma-separated patterns to mutate
    several paths.
  - `--test-paths "PATH"` — Restrict test discovery to these paths (default:
    `test/` for a single app, every child app's `<apps_path>/<child>/test/` for an
    umbrella). Repeat the flag, pass multiple path tokens after it, or use
    comma-separated paths, exactly like `--files`. Paths must be
    project-relative.
  - `--mutators NAMES` — Comma-separated mutator name list
  - `--enable TARGETS` — Comma-separated enabled targets. Defaults:
    `dispatch`, `guard`, `env_walker` (only
    AtomLiteral active; pass it explicitly to run
    all env-walker literals), `pattern_shape`.
    Opt-in: `module_attribute`, `body_literal`,
    `pattern_literal`, `variable`, `conditional`,
    `statement_delete`, `clause_delete`,
    `guard_boolean`, `pipeline_drop`,
    `map_update_drop`, `receive_timeout`. Passing
    --enable replaces the default enabled targets and selects the default selectable mutator
    set gated by the listed targets. Note: an opt-in
    mutator selected via `--mutators` ALSO needs its
    target enabled, and a few experimental mutators
    require `--mutators` explicitly (see docs/MUTATORS.md).
  - `--fail-at SCORE` — Mutation score threshold; exit 1 below
    (default: 80)
  - `--reporters NAMES` — Comma-separated: `terminal`, `stryker-json`
    (default: both), `html`, `github-actions`
    (opt-in)
  - `--output-path PATH` — Report output path (default:
    `stryker.report.json`). Absolute paths are honored;
    relative paths are under the project root. The
    `html` reporter writes the same path with a
    `.html` extension.
  - `--concurrency N` — Worker pool size for parallel mutant execution.
    Default: min(schedulers_online, 4). Use
    `--concurrency 1` for sequential execution. Explicit
    values are bounded to avoid pathological sandbox
    startup/teardown costs.
  - `--max-mutants N` — Cap total mutants (stable-id sorted sample if
    exceeded). Limits planned mutant executions before coverage collection. Oracle
    construction and planning still run in full; coverage still collects
    the selected test files.
  - `--debug-plan` — Dump plan JSON to plan.debug.json and exit before
    any mutant runs. It still builds the oracle and baseline plan; it is not
    a zero-cost parser-only mode.
  - `--selection MODE` — Test selection mode: `static`, `coverage`,
    `coverage_with_static_fallback` (default). `static` is the fully-portable
    escape hatch — also the fast choice on
    macro-heavy/generated apps (e.g. Phoenix) where
    coverage collection is slow before it falls back.
  - `--keep-work-copy` — Skip cleanup of the run's temporary work copies on
    exit and print their retained paths (debug aid; default: false)
  - `--test-timeout-ms N` — Per-test ExUnit timeout in milliseconds.
    Default 10000. Range 1000..600000.
  - `--suite-timeout-ms N` — Whole-suite host budget, in milliseconds, for one
    mutant's selected tests (range 1000..3600000). The host kills the mutant's
    test port after `max(test_timeout_ms, suite_timeout_ms)` plus a 10000 ms
    drain buffer. Unset (the default) it is derived from the measured baseline run:
    `max(test_timeout_ms, min(baseline_wall_ms * 2, 600000)) + 10000`. Deriving it matters
    because several individually valid slow tests can otherwise exceed a budget
    sized for a single test, and the resulting host timeout counts as a
    detection and inflates the score.
  - `--priv-fingerprint stat|hash` — How each sandbox's copied `priv/` tree is
    fingerprinted for the between-mutant reset. `stat` (the default) compares
    size + mtime: cheap, but it misses a rewrite that keeps the byte size AND
    lands in the same mtime second. `hash` compares content: exact, at the
    cost of reading every `priv/` file on every reset (per mutant, per
    worker). Use `hash` when tests rewrite fixtures under `priv/` in place.
  - `--incremental` — Reuse verdicts from a prior run's history for
    unchanged mutants instead of re-executing them
    (opt-in; see `history_path` config). Materially
    changes execution and report interpretation.
  - `--since REF` — With `--incremental`, force execution of mutants
    whose file changed since git `REF` (e.g. `HEAD~1`,
    `main`). Other mutants remain eligible only if all digest checks pass.
    Outside a git repo or with an unknown ref,
    reuse falls back to digest checks with a warning.

## Configuration

Settings can come from four layers, lowest to highest precedence:

    .mutalisk.exs project file  <  legacy `config :mut`  <  config :mutalisk  <  CLI flags

A CLI flag always wins; `config :mutalisk` overrides both `config :mut` (a
deprecated, still-accepted namespace kept for compatibility) and the file;
the file is the base. Keys (same names in all layers): `files`, `test_paths`, `mutators`,
`enabled_targets`, `selection`, `fail_at`, `concurrency`, `test_timeout_ms`,
`suite_timeout_ms`, `priv_fingerprint`, `reporters`, `output_path`, `exclude`, `max_mutants`,
`since`, `incremental`, `history_path`, and
`coverage_timeout_ms`. `exclude`, `history_path`, and
`coverage_timeout_ms` are config-only (no CLI flag); the rest accept a CLI
flag that overrides config. `history_path` controls where every run writes
reusable verdict history for future `--incremental` runs. `coverage_timeout_ms`
is a positive integer in milliseconds for each per-test-file coverage
collection attempt. Timed-out or failed files have unknown coverage and are
included conservatively in every mutant's selection. Run-scoped switches
`debug_plan` and `keep_work_copy` are CLI-only.

When `test_paths` is unset it defaults to `test/` for a single app and to
every child app's `<apps_path>/<child>/test/` for an umbrella. Set it explicitly only
to override that — note a bare `["test"]` finds no tests in an umbrella.

`.mutalisk.exs` (in the project root, loaded if present) is a plain
keyword-list term — no `Config` runtime needed:

    # .mutalisk.exs
    [
      selection: :coverage_with_static_fallback,
      fail_at: 75.0,
      concurrency: 8,
      exclude: [~r"lib/my_app_web/router.ex"]
    ]

Or via application config:

    config :mutalisk,
      exclude: [~r/lib\/my_app_web\/router.ex/],
      fail_at: 80.0,
      selection: :coverage_with_static_fallback,
      concurrency: System.schedulers_online()

## Source-level ignores

Add `@mutalisk_ignore true` to a module to exclude every mutant in that
module (generated/DSL modules, intentionally-untested code):

    defmodule MyApp.Generated do
      @mutalisk_ignore true
      # ... no mutants are produced for this module ...
    end

Run as `MIX_ENV=test mix mut`.

---

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