Mutation

Incremental inference

vary mutate --incremental-infer reuses prior mutant outcomes when neither the mutant's code nor its relevant tests changed, turning a repeat vary mutate run into a sequence of lookups instead of fresh bytecode patching and test dispatch.

Unlike --incremental, which short-circuits an entire file when the source and test files match the prior run, --incremental-infer makes the reuse decision per mutant. New mutants still execute; prior ones are re-used only when every safety gate passes.

For the basics, see Introduction.

Enabling

vary mutate src/foo.vary -t tests/test_foo.vary --incremental-infer

The inference artifact is written to .vary/inference/<12-char-source-path-hash>.inference relative to the working directory. The hash is derived from the absolute source path so two files with the same basename in different directories never collide.

Artifact format

Text-based, human-inspectable, schema version 1:

# Vary Mutation Inference
schema_version=1
source_hash=<sha-256 of source file>
test_hash=<sha-256 of test file>
compiler_version=<compiler version at write time>
operators=<sorted, comma-separated operator set>
timestamp=<ms since epoch>

# Entries
<mutant-id>|<outcome>|<killed-by;...>|<execution-ms>|<code-fp>|<tests-fp>

Outcomes persisted: KILLED, SURVIVED, TIMEOUT, EQUIVALENT. Non-equivalent engine ERRORs are never persisted, so a transient compilation or runtime failure cannot poison a future run.

Safety gates

Any gate that fails forces fresh execution for the affected mutant(s). The gates are conservative by design.

File-level gates (applied when the store is opened)

All must hold; otherwise the entire entry set is dropped.

GateWhat must match
Schema versionCurrent code's SCHEMA_VERSION
Source hashSHA-256 of the current source file
Test hashSHA-256 of the current test file
Compiler versionCurrent ArtifactId.COMPILER_VERSION
Operator setStored operators is a non-empty subset of the current run's operator set

The operator-set rule means widening the set (for example, adding relational) keeps prior arithmetic entries inferrable; narrowing invalidates the entire store.

Per-mutant gates

Even after the store loads, each mutant is re-checked.

GateWhat it validates
Stable idThe mutation's stable id matches a stored entry. An operator change that removes or renames the mutation simply fails to look up.
codeFingerprintEquals sha-256(symbolPath|type|original|mutated|line) for the current run's mutation. Catches a generator evolving to emit a different transform for the same id.
testsFingerprintEquals sha-256(sorted-csv of covering-test names, or "*" if no coverage). Catches changes to the covering-tests set even when source and tests are byte-identical.
Stored outcomeIs not ERROR.

Output labelling

Each mutant in the JSON report carries two redundant fields:

{
  "id": "add#ARITHMETIC#0",
  "killed": true,
  "inferred": false,
  "resolution": "executed"
}
CombinationMeaning
inferred: true / resolution: "inferred"Outcome reused from a prior compatible run
inferred: false / resolution: "executed"Freshly executed (no prior entry applied, or a gate failed)

Survivor flake rerun

The survivor-tail flake-detection rerun skips inferred mutants, because re-running them would defeat the point of inference. A mutant killed in the prior run via a flake-induced rerun is persisted as KILLED with its original killedBy, so inference preserves the final classification, not the intermediate one.

Non-goals

Non-goalDetail
Per-method or per-statement invalidation granularityAny edit to the source file invalidates all entries
Cross-file dependency trackingIf the mutated file depends on another module and that module changes, the gate on this file's source_hash still passes. Delete .vary/inference/ to force a fresh run.
Project-mode inferenceInference is wired into the single-file AST runner path used by vary mutate <file.vary>. Project-mode (vary mutate <dir>) does not pass an inference file through to the per-file runner.
Bytecode-backend inferenceThe fresh-loader, hot-swap, and redefine backends each own their own execution loop and do not yet consult the AST-level inference store.
← Fast mode
Parity gate →