Mutation

Signatures & manifest

Every mutant receives a stable identity that survives variable renames, comment changes, and formatting. The mutation manifest provides a machine-readable record of all mutation sites and their results for CI integration and cross-run tracking.

For an overview of all advanced features, see Advanced overview.

What it does

Stable signatures

Every mutant receives a signature based on three components:

ComponentWhat it capturesExample
AST pathStructural position in the functionadd->body[0]->Binary.left
Operator typeWhich mutation was appliedARITH_ADD_TO_SUB
Shape hashCanonical AST subtree structurea1b2c3d4 (first 8 chars of SHA-256)

The full signature looks like: add->body[0]->Binary.left:ARITH_ADD_TO_SUB:a1b2c3d4

Signatures survive variable renames, comment changes, and formatting because the shape hash encodes node types and operators but not identifiers or literal values. This makes mutation results comparable across refactors.

Expression IDs

Each mutation site receives a content-addressed expression ID derived from its AST position and structural shape. The ExpressionId combines the source file, line/column span, node kind, and a canonical shape hash into a deterministic identifier.

Expression IDs provide stable cross-run mutation tracking: even if surrounding code changes, the same logical expression keeps the same ID as long as its structure is unchanged.

Mutation manifest

Every compilation emits a mutation manifest at .vary/mutation-manifest.json. The manifest has two layers:

LayerWhen populatedContents
discoveryEvery build (vary run, vary check, vary test)All mutation sites with signatures, effects, expression IDs, and AST paths
resultsAfter vary mutateKill/survive/error status per mutant, killing test names

When you run vary mutate, it upgrades an existing discovery-only manifest with test results rather than rebuilding from scratch.

What it outputs

The manifest at .vary/mutation-manifest.json contains entries like:

{
  "discovery": [
    {
      "expressionId": "math.vary:15:3-15:8:Binary:a1b2c3d4",
      "signature": "add->body[0]->Binary.left:ARITH_ADD_TO_SUB:a1b2c3d4",
      "operator": "ARITH_ADD_TO_SUB",
      "effects": ["PURE"],
      "astPath": "add->body[0]->Binary.left"
    }
  ],
  "results": [
    {
      "signature": "add->body[0]->Binary.left:ARITH_ADD_TO_SUB:a1b2c3d4",
      "status": "KILLED",
      "killedBy": ["test_add_positive"]
    }
  ]
}

How to use it

Signatures and the manifest work automatically. No flags needed for basic usage.

Disable manifest emission with --no-manifest (on vary run) or in vary.toml:

[mutation]
emit_manifest = false

Use signatures in CLI commands:

vary mutate source.vary --why m-0042          # Explain a specific mutant
vary mutate source.vary --replay m-0042       # Re-run a specific mutant
vary mutate source.vary --quarantine m-0042   # Quarantine a mutant by ID

How to interpret the results

Stable signatures enable several workflows:

WorkflowHow signatures help
Cross-run comparisonSame logical mutation keeps the same signature, so you can track survivors across runs
Refactor safetyAfter renaming variables or reformatting, signatures stay stable, so results remain valid
CI integrationThe manifest provides machine-readable data for quality gates, trend tracking, and dashboards
Quarantine managementQuarantine equivalent mutants by signature; they stay quarantined across code changes

Expression IDs appear in the manifest's discovery entries as the expressionId field. Use them to correlate mutation sites across files and runs.

What to do next

PageTopic
InfrastructureCaching, quarantine, policy gates, certificates, CLI reference
EffectsEffect tags that appear in the manifest
Advanced overviewHow signatures fit the five-pillar model
← Effects & integrity
Infrastructure →