Runtime selection of the active mutant.
The active mutant is constant for an entire suite run, so it is read once from
the environment in the sandbox mix.exs prefix, before target project code, and
stashed in :persistent_term (O(1) reads, built for write-once/read-many). Every
selector site in the metamutant reads this key, normally hoisted to one read per
function activation.
Eligible functions run their uninstrumented source when the active mutation
belongs elsewhere: a lifted function through a copy of its original clauses, a
function that stays in place through a second body chosen after its one read.
Pure direct self-recursion may stay in a lifted function's clean clauses without
rereading the selector; it relies on selection remaining stable during the
computation. Ordinary entries and effectful recursion still read afresh, so
put/1 can select another mutant between in-process calls.
A schema mutant is stored as {root_relative_file, local_id} in that single
slot, the file as an atom (namespace_key/1): every instrumented function
compares it on entry, and an atom compares in one instruction where a path string
of equal length is read byte by byte. Mutare.Metamutant projects the slot to the
local integer for the active file, 0 at baseline, or :inactive for other files.
Thus only one file can be active and inactive files still short-circuit coverage
before its tracking-flag read. put/1 and active/0 speak the string form.
Standalone transforms continue to select by plain integer, including :start_id.
The bootstrap combines MUTARE_ACTIVE_MUTANT (the local integer) with
MUTARE_MUTANT_NAMESPACE (the file); the runner translates report ids before
setting these. An absent namespace retains standalone integer selection.
This module defines both sides of that contract: Mutare.Metamutant uses its key
and baseline when building selectors, while Mutare.Sandbox renders
bootstrap_ast/0 into the target project's dependency-free project prefix, with
an idempotent fallback in the test bootstrap.
Self-hosting: a private key for the suite-under-test
When the target is Mutare, the suite-under-test contains Mutare's own tests,
many of which drive selection directly (put/1, building fixtures via
Mutare.Transform) to exercise the machinery. If those tests wrote the same
:persistent_term slot the harness uses to hold the mutant-under-test, they
would clobber it mid-run — the active mutant would silently revert to baseline
and every mutant whose killing test ran afterwards would register a false
survivor (see NOTES.md, "Self-hosting").
So the runtime key is configurable: key/0 returns default_key/0
(:mutare_active, the harness key) unless override_env/0 names another, in
which case the suite plays in that private slot. Mutare.Sandbox.Command sets
that env var (to suite_key/0) on every sandbox mix it spawns, so the
suite-under-test reads/writes :mutare_active__suite while the real
metamutant — whose sites and bootstrap bake default_key/0 as literals at
transform time, in the harness process where the override is unset — keeps
reading :mutare_active. The two never collide. On a normal target there is no
Mutare.Selector compiled in, so the env is inert.
Summary
Functions
The active mutant id for in-process execution (0 if unset).
The baseline id (no mutant active).
Dependency-free code that reads the selector environment variables and stores the active runtime identity. Initialization precedes target project code; repeated project prefixes and umbrella helpers preserve the first value, even if target code later changes the environment.
The harness selection key (:mutare_active) — the default key/0, env-independent.
The environment variable a runner sets to pick the active mutant.
Selection environment, explicitly clearing an inherited namespace for integer ids.
The :persistent_term key the metamutant reads at runtime.
Environment variable carrying a schema mutant's root-relative file namespace.
The form a file namespace takes in the stored selection and in the projection generated
code matches it against (Mutare.Metamutant.subject_ast/1): an atom, or the string itself
when it is too long to be one. bootstrap_ast/0 applies the same rule inside the target.
Env var a sandbox run sets to give the suite-under-test a private selection key.
Set the active mutant id directly for in-process execution.
The private key (override_env/0's value) the suite-under-test selects on under dogfooding.
Functions
@spec active() :: Mutare.RuntimeId.t()
The active mutant id for in-process execution (0 if unset).
@spec baseline() :: non_neg_integer()
The baseline id (no mutant active).
@spec bootstrap_ast() :: Macro.t()
Dependency-free code that reads the selector environment variables and stores the active runtime identity. Initialization precedes target project code; repeated project prefixes and umbrella helpers preserve the first value, even if target code later changes the environment.
Mutare.Sandbox renders this AST directly into project prefixes and the test
bootstrap fallback, so the target does not need Mutare as a dependency.
@spec default_key() :: atom()
The harness selection key (:mutare_active) — the default key/0, env-independent.
@spec env_var() :: String.t()
The environment variable a runner sets to pick the active mutant.
@spec environment(Mutare.RuntimeId.t()) :: [{String.t(), String.t() | nil}]
Selection environment, explicitly clearing an inherited namespace for integer ids.
@spec key() :: atom()
The :persistent_term key the metamutant reads at runtime.
default_key/0 (:mutare_active) unless override_env/0 names another — the
one knob self-hosting needs so the suite-under-test does not clobber the
harness's active-mutant slot (see the moduledoc).
@spec namespace_env() :: String.t()
Environment variable carrying a schema mutant's root-relative file namespace.
The form a file namespace takes in the stored selection and in the projection generated
code matches it against (Mutare.Metamutant.subject_ast/1): an atom, or the string itself
when it is too long to be one. bootstrap_ast/0 applies the same rule inside the target.
@spec override_env() :: String.t()
Env var a sandbox run sets to give the suite-under-test a private selection key.
@spec put(Mutare.RuntimeId.t()) :: :ok
Set the active mutant id directly for in-process execution.
@spec suite_key() :: String.t()
The private key (override_env/0's value) the suite-under-test selects on under dogfooding.