Generated banner for Mutation Testing
Generated logo for Mutation Testing

Mutation Testing

Trail of BitsOfficialPublished by Trail of Bits

  • Skill
  • Tool integration
  • Software development
  • CC-BY-SA-4.0
  • No issues found

From the author

Configures mewt or muton campaigns, analyzes surviving mutants, and investigates bugs exposed by testing gaps. Use when setting up mutation testing, reviewing campaign results, identifying equivalent mutants, or finding bugs from surviving mutations.
— the description by Trail of Bits

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a structured approach to mutation testing using the mewt or muton tools. It guides users through the entire lifecycle of a mutation testing campaign, from initial configuration and execution to the analysis of surviving mutants. By systematically injecting faults into source code and observing test behavior, it helps developers distinguish between equivalent mutants and genuine testing gaps, ultimately revealing bugs that standard coverage metrics might miss.

What it does

  • Configures and executes mutation testing campaigns via mewt or muton.
  • Provides commands to filter, inspect, and re-test specific mutants.
  • Categorizes mutation results into caught, uncaught, timeout, or skipped outcomes.
  • Offers a framework for identifying equivalent mutants to reduce false positives.
  • Prioritizes investigation based on mutation severity and language-specific patterns.
  • Supports advanced analysis for blockchain-specific constructs like Solidity and Move.

When to use it

  • When you need to validate the effectiveness of your existing test suite.
  • When you want to identify untested code paths or weak assertions.
  • When you need to analyze why specific mutants survived your test execution.
  • When you are working on a project that requires high-assurance testing, such as blockchain smart contracts.

Advantages

  • Uncovers subtle bugs that traditional line coverage metrics fail to detect.
  • Provides a clear, actionable workflow for triaging and resolving surviving mutants.
  • Reduces noise by helping developers identify and ignore semantically equivalent mutants.
  • Enables targeted re-testing of specific code segments to verify fixes.

Usage examples

“Run a mutation testing campaign on the src/auth directory.”
The agent will execute the mutation campaign specifically targeting the auth module and provide a summary of the results.
“Show me all uncaught mutants in the current project.”
The agent will list the surviving mutants, allowing you to investigate potential testing gaps.
“How do I investigate a high-severity surviving mutant?”
The agent will guide you through inspecting the mutated code and suggest how to write a test to catch it.
“Is this mutant equivalent to the original code?”
The agent will apply the verification procedure to help you determine if the mutation is semantically identical or a genuine testing gap.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/trailofbits/mutation-testing/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip mutation-testing.zip -d .agents/skills

Documentation by Trail of Bits

Mutation Testing (mewt/muton)

Routes to the right mutation testing workflow and loads the references that workflow needs.

Note: muton and mewt share identical interfaces. Examples use mewt; substitute muton and its file names (muton.toml, muton.sqlite) for muton projects.

mewt --help and mewt <subcommand> --help are the source of truth for command-line behavior. Examples below reflect the mewt 4.x API; run --help when a flag looks unfamiliar or a command fails.

When to Use

Use this skill when the user:

  • Mentions "mewt", "muton", or "mutation testing"

  • Wants to configure, scope, or speed up a mutation testing campaign

  • Wants to analyze mutation results — surviving/uncaught mutants, equivalent mutants, kill rate

  • Wants to use mutation results to find bugs in the source code

When NOT to Use

Do not use this skill when the user asks about tests or line coverage without any mutation testing context.


Routing

Pick the workflow, then load it together with the references listed for it. Workflows and references do not load each other — that decision belongs here.

Setting up, scoping, or speeding up a campaign → workflows/configuration.md → Also load references/optimization-strategies.md when the campaign estimate is long enough to need trimming, or the user asks to make it faster.

Campaign finished, hunting for bugs in untested code → workflows/bug-hunter.md

Turning results into a formal analysis report → workflows/analyzing-results.md, plus:

Anything else → run mewt --help or mewt <subcommand> --help, then assist directly.


Essential Commands

# Set up and run
mewt init                    # Create config and database
mewt mutate [paths]          # Generate mutants without testing them
mewt run [paths]             # Generate mutants and run the campaign

# Read results
mewt status                  # Overview with per-file breakdown
mewt results                 # Uncaught mutants (default view)
mewt results --all           # Every outcome, not just uncaught
mewt results --format json   # json | sarif | ids | table

# Narrow down (these filters work on both `results` and `print mutants`)
mewt results --target 'src/auth/**'   # Quote globs so the shell does not expand them
mewt results --severity high,medium
mewt results --mutation-types ER,CR
mewt results --status Uncaught        # Uncaught | TestFail | Skipped | Timeout
mewt results --line 42

# Investigate and re-test
mewt print mutant --id [id]              # View the mutated code
mewt test --ids [ids]                    # Re-test specific mutants
mewt test --ids-file uncaught_ids.txt    # Re-test IDs from a file, or '-' for stdin

# Inspect configuration
mewt print config                        # Effective config
mewt print targets                       # Files actually mutated
mewt print mutations --language [lang]   # Mutations and severities for a language

Language labels are canonical family or family/dialect values in mewt 4.x — for example rust, javascript/ts, move/sui, move/iota.


What Results Mean

  • Caught/TestFail: tests detected the mutation (good)

  • Uncaught: tests did not detect the change. Inspect the code to distinguish a testing gap from an equivalent mutation.

  • Timeout: tests took too long — inconclusive, not evidence of coverage

  • Skipped: a less severe mutant was skipped because a more severe mutant on the same line was uncaught


Interpreting Mutation Types

mewt print mutations --language [lang] lists every mutation slug, description, and severity for a language, and is authoritative — the operator set grows with each release. What that output does not tell you is what a survivor means, which is where prioritization comes from:

SeverityRepresentative slugsWhat an uncaught mutant tells you
HighER (Error Replacement)Tests tolerate the injected error. Investigate whether the path executes, whether error handling masks the change, and whether assertions check the outcome.
MediumCR (Comment Replacement)Removing the statement does not fail the tests. Check whether its effects matter and whether assertions observe them.
MediumIF/IT (If False/True), NR (Negation Removal)Tests do not distinguish the changed condition. Both constant replacements surviving can indicate an unexecuted condition or weak assertions on the branch outcomes.
LowOperator shuffles (AOS, COS, LOS, BOS, shift/assignment variants), BL, AS, LC, WFCheck boundary inputs, arithmetic assertions, and semantic equivalence. The mutation result alone does not establish whether the code executed.

Severity ranks the mutation, not the risk. A low-severity survivor in a fee calculation matters more than a high-severity survivor in a log line — weigh what the mutated code does. Filter with --severity to work through the results in priority order.

Files

references/equivalent-mutants.md

Equivalent Mutant Catalog

Equivalent mutants are mutations that produce semantically identical behavior to the original code. They are false positives in mutation testing: no test can kill them because no observable difference exists. Correctly identifying equivalent mutants prevents inflating the count of real testing gaps.

This file covers general equivalence checks and patterns. Blockchain-specific equivalence patterns are loaded separately by SKILL.md for blockchain projects.

Verification Procedure

Before classifying a mutant as equivalent, complete all five checks:

  1. Type context: Determine the types of all variables involved in the mutation. Unsigned integers, booleans, and enums each have specific equivalence patterns

  2. Reachability: Confirm that the mutated code is actually reachable during execution. Mutations in dead code are trivially equivalent

  3. Downstream consumption: Trace whether the mutated value is read or used by any subsequent logic. If the value is never consumed, the mutation has no observable effect

  4. Semantic comparison: Compare the behavior of original and mutated code across all possible inputs within the type constraints. If no input produces a different result, the mutant is equivalent

  5. Distinguishing test attempt: Try to construct a concrete input that produces different behavior between the original and mutated code. If such an input exists, the mutant is not equivalent — describe the input as a recommended test case. If no distinguishing input can be found after checking boundary values, type extremes, and domain-specific edge cases, this strengthens the equivalence classification

A mutant is equivalent only when the analysis establishes identical observable behavior. If a check is uncertain, retain it as unresolved and explain what evidence is missing. Uncertainty alone does not establish a testing gap.


Semantic Equivalence Patterns

Unsigned Comparison Equivalence

Pattern: x > 0 mutated to x != 0 (or vice versa) Why equivalent: For unsigned integers, the only value that is not greater than zero is zero itself. Therefore x > 0 and x != 0 produce identical results for all possible unsigned values. Applies to: Any unsigned integer type (Rust u8-u128/usize, C/C++ unsigned, Solidity uint, FunC/Tolk integers when used as unsigned)

Not equivalent when: The variable is a signed type. For signed integers, x > 0 excludes negative values while x != 0 includes them.

Boolean Double-Negation

Pattern: !(!condition) mutated to condition Why equivalent: Double negation of a boolean produces the original value. These expressions are logically identical. Applies to: All languages

Tautological Bound Checks

Pattern: x >= 0 for an unsigned integer, mutated to true Why equivalent: Both expressions are true for every value of the unsigned type, provided evaluating x has no observable side effects. Applies to: Any unsigned integer type (Rust unsigned types, C/C++ unsigned, Solidity uint)

Not equivalent when: The replacement is x > 0. At x == 0, the original returns true and the replacement returns false. Zero is reachable for an unsigned type unless the surrounding code excludes it. Signed types and comparisons such as x >= 1 also require a separate analysis.

Dead Code Mutations

Pattern: Any mutation inside an unreachable branch Why equivalent: If the code path is never executed, no mutation within it can produce an observable difference. How to verify: Check whether the branch condition can ever be true. Common cases: if (false), branches after unconditional return/revert, code guarded by compile-time constants that always evaluate one way.

Caution: A branch that is not exercised by current tests is not necessarily unreachable. Distinguish between "no test covers this" (real finding) and "no input can reach this" (equivalent).

Redundant Assignment Mutations

Pattern: Mutation to a variable that is never read after the assignment Why equivalent: If the variable's value is not consumed by any subsequent operation, changing the assigned value has no effect on program behavior. How to verify: Trace all uses of the variable after the mutated assignment. If no read occurs before the variable is reassigned or goes out of scope, the mutation is equivalent.

Commutative Operation Reordering

Pattern: a + b mutated to b + a, or a * b mutated to b * a Why equivalent: Addition and multiplication are commutative. The operand order does not affect the result. Applies to: Built-in integer operations with pure operands and the same types. Inspect overloaded operators and floating-point semantics separately, including observable NaN representations and floating-point exceptions.

Not equivalent when: The operation has side effects (e.g., function calls as operands with observable side effects), or the operation is non-commutative (subtraction, division, modulo).


Common Mistakes in Equivalence Classification

These patterns look equivalent but are NOT. Do not classify them as false positives:

Boundary Value Changes

Pattern: > mutated to >= (or vice versa) Why NOT equivalent: The boundary value (where x == threshold) produces a different result. This is a real finding unless the boundary value is provably unreachable. Common error: Assuming that because current tests do not exercise the boundary, it is unreachable. Unreachability must be proven from the type system or control flow, not from test coverage.

Off-by-One in Loop Bounds

Pattern: i < length mutated to i <= length in a loop Why NOT equivalent: The loop executes one additional iteration, which may access out-of-bounds memory or process an extra element. This is a real finding.

Removed Event Emission

Pattern: emit Transfer(from, to, amount) commented out Why NOT equivalent in all cases: While event removal does not change execution logic, it may break downstream monitoring, indexing services, or integration tests that rely on events. Classify as Tier 4 (Low), not as equivalent.

Swapped Non-Commutative Operations

Pattern: a - b mutated to b - a, or a / b mutated to b / a Why NOT equivalent: Subtraction and division are not commutative. The results differ unless a == b.

Undefined Behavior Introduction (C/C++)

Pattern: Mutation changes a bounds check, pointer operation, or integer arithmetic in a way that introduces undefined behavior (e.g., signed overflow, buffer overrun, null dereference) Why NOT equivalent: The mutation may produce identical test output today, but undefined behavior can manifest differently under other compilers, optimization levels, or platforms. UB-introducing mutations represent real safety bugs regardless of current test results. Common error: Observing that all tests still pass and concluding the mutation is equivalent. In C/C++, UB can silently corrupt memory, be optimized away, or cause crashes only under specific conditions.

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Declares allowed tools

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
Trail of Bits
Publisher
Trail of Bits (Official)
Source
trailofbits/skills · plugins/mutation-testing/skills/mutation-testing · commit e4dd0bb
Licence
Creative Commons Attribution-ShareAlike 4.0 International (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Trail of Bits or Trail of Bits.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download mutation-testing.zip