Alpha. Vary is under active development and not ready for production use. Syntax, APIs, performance, and behaviour may change between releases.
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:
| Component | What it captures | Example |
|---|---|---|
| AST path | Structural position in the function | add->body[0]->Binary.left |
| Operator type | Which mutation was applied | ARITH_ADD_TO_SUB |
| Shape hash | Canonical AST subtree structure | a1b2c3d4 (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:
| Layer | When populated | Contents |
|---|---|---|
discovery | Every build (vary run, vary check, vary test) | All mutation sites with signatures, effects, expression IDs, and AST paths |
results | After vary mutate | Kill/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:
| Workflow | How signatures help |
|---|---|
| Cross-run comparison | Same logical mutation keeps the same signature, so you can track survivors across runs |
| Refactor safety | After renaming variables or reformatting, signatures stay stable, so results remain valid |
| CI integration | The manifest provides machine-readable data for quality gates, trend tracking, and dashboards |
| Quarantine management | Quarantine 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
| Page | Topic |
|---|---|
| Infrastructure | Caching, quarantine, policy gates, certificates, CLI reference |
| Effects | Effect tags that appear in the manifest |
| Advanced overview | How signatures fit the five-pillar model |