Mutation Testing
Trail of BitsOfficialPublished by Trail of Bits
- Skill
- Tool integration
- Software development
- CC-BY-SA-4.0
- No issues found
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
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/trailofbits/mutation-testing/downloadUnzip 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; substitutemutonand 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:
references/equivalent-mutants.md — equivalence catalog and verification procedure
references/severity-classification.md — severity tier criteria
references/report-template.md — report structure
references/blockchain-patterns.md — only for Solidity, Move, FunC/Tolk, Cairo, or Solana Rust targets
references/input-formats.md — unless the results came from mewt or muton. Foreign tool output may not be self-describing; this covers the parsing anchors for slither-mutate, mull, and dextool-mutate
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 languageLanguage 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:
| Severity | Representative slugs | What an uncaught mutant tells you |
|---|---|---|
| High | ER (Error Replacement) | Tests tolerate the injected error. Investigate whether the path executes, whether error handling masks the change, and whether assertions check the outcome. |
| Medium | CR (Comment Replacement) | Removing the statement does not fail the tests. Check whether its effects matter and whether assertions observe them. |
| Medium | IF/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. |
| Low | Operator shuffles (AOS, COS, LOS, BOS, shift/assignment variants), BL, AS, LC, WF | Check 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
- ATTRIBUTION.mdAdded by AutoGineer942 B
- LICENSELicence file added by AutoGineer19.7 KiB
- SKILL.md6.1 KiB
- agents/openai.yaml243 B
- assets/trail-of-bits-mark.svg3.0 KiB
- references/blockchain-patterns.md4.8 KiB
- references/equivalent-mutants.md7.0 KiB
- references/input-formats.md2.4 KiB
- references/optimization-strategies.md8.7 KiB
- references/report-template.md4.3 KiB
- references/severity-classification.md5.9 KiB
- workflows/analyzing-results.md6.7 KiB
- workflows/bug-hunter.md7.4 KiB
- workflows/configuration.md11.3 KiB
references/optimization-strategies.md
Optimization Strategies
Apply these strategies before running a campaign when Phase 3 of the configuration workflow requires optimization (estimated >16 hours or user requests).
Priority 1: Verify Target Selection
Most common issue: Mutating non-source code.
Diagnostic:
mewt print config # Check [targets] include/ignore
mewt print targets # Check what was actually mutatedLook for unintended files:
Mocks:
src/mocks/,__mocks__/Tests:
*_test.rs,*.test.js,tests/Dependencies:
vendor/,node_modules/Generated:
proto/,generated/
Fix: Update [targets] in mewt.toml to be more specific:
# Before (too broad)
[targets]
include = ["**/*.rs"]
# After (specific)
[targets]
include = ["src/**/*.rs", "lib/**/*.rs"]
ignore = ["test", "mock", "generated"]Re-run mewt mutate and check new count.
Priority 2: Analyze Project Structure
Goal: Understand mutant distribution and test organization to choose the right optimization.
1. Get mutant counts per component:
# Quote globs to prevent shell expansion. IDs output contains one mutant per line.
mewt print mutants --target 'src/auth/**/*.rs' --format ids | wc -l
mewt print mutants --target 'src/core/**/*.rs' --format ids | wc -l
mewt print mutants --target 'src/utils/**/*.rs' --format ids | wc -lPresent breakdown to user:
Component breakdown:
- src/auth/: 200 mutants × 5s = ~17 min
- src/core/: 800 mutants × 8s = ~1.8 hrs
- src/utils/: 150 mutants × 3s = ~8 min
Total: 1150 mutants, ~2.3 hrs worst-case2. Count mutations by severity:
# Check enabled mutation types
mewt print config | grep mutations
# Count by severity level
mewt print mutants --severity high --format ids | wc -l
mewt print mutants --severity medium --format ids | wc -l
mewt print mutants --severity low --format ids | wc -l
# Or count specific mutation types
mewt print mutants --mutation-types ER --format ids | wc -l
mewt print mutants --mutation-types CR --format ids | wc -l
# Compare to total
mewt print mutants | wc -lExample output:
High/Medium severity: 450 mutants
Total mutants: 1200
Percentage: 37.5%Note: The percentage varies drastically between codebases (15% to 50+ % is common).
Priority 3: Choose Optimization Approach
Based on project structure analysis, present options to user with concrete time estimates:
Option A: Run Full Campaign
"Estimated ~X hours worst-case (likely faster in practice)"
"Recommend starting Friday evening for weekend completion"
When to suggest: Duration acceptable, comprehensive coverage desired
Option B: Target Critical Components
"Focus on specific components: src/auth/ (~17 min), src/crypto/ (~45 min)"
"Start with one component and expand scope after review?"
When to suggest: Clear component boundaries, user wants rapid iteration
Implementation:
[targets]
# Start with critical component
include = ["src/auth/**/*.rs"]
# After review, expand scope
# include = ["src/auth/**/*.rs", "src/core/**/*.rs"]After editing mewt.toml, purge removed targets then mutate any newly included files:
Preserve any results that must be retained and confirm that discarding the affected campaign data is authorized before purging.
mewt purge # removes targets no longer matching [targets].include/ignore
mewt mutate src/ # adds mutants for any newly included files
mewt status # confirm reduced mutant countOption C: High/Medium Severity Only
"Limit to high/medium severity mutations (X mutants, ~Y hours)"
"Low severity (operator shuffles) tests edge cases, less critical"
When to suggest: Time-constrained, need actionable findings quickly
Implementation (by severity level):
[run]
mutations = ["ER", "CR", "IF", "IT"] # Specific types (high/medium)After editing mewt.toml, full regeneration is required since existing mutants may no longer be valid under the new filter:
mewt purge --all # clear all existing mutants
mewt mutate src/ # regenerate with restricted mutation types
mewt status # confirm reduced mutant countOr use severity filtering during analysis instead (no database changes needed):
# Run all mutants but filter results by severity
mewt results --severity high,medium
mewt print mutants --severity highTrade-offs to explain:
High/med severity: ~30-40% of mutants (varies by codebase)
Low severity: ~60-70% of mutants (operator shuffles, edge cases)
Low severity still provides value, just lower priority
Using severity filters during analysis allows flexibility without re-running campaign
Option D: Two-Phase Campaign (Integration-Heavy Only)
"Phase 1: Targeted tests (estimable upfront), Phase 2: Re-test uncaught with full suite (duration depends on Phase 1 survivor count)"
"Total: Phase 1 estimate + (survivors × full-suite time) vs naive total"
When to suggest: Integration tests dominate, unit tests don't map cleanly to files
See Two-Phase Campaigns section below for detailed setup.
Two-Phase Campaigns
Use ONLY for integration-heavy test suites. Not recommended for well-organized unit tests.
When to Use
Good fit:
Integration tests dominate runtime
Unit tests provide broad coverage but don't map cleanly to specific files
Targeted test commands significantly faster than full suite
Not recommended:
Well-organized unit tests with clear file mappings
Tests already fast and targeted
Setup
Phase 1 config (targeted tests):
# TWO-PHASE CAMPAIGN
# Phase 1: Targeted tests (duration estimable upfront)
# Phase 2: Re-test uncaught mutants (duration depends on Phase 1 survivor count)
[test]
# PHASE 2: Uncomment after phase 1 completes
# cmd = "cargo test"
# timeout = 60
# PHASE 1: Targeted tests
[[per_target]]
glob = "src/auth/*.rs"
test.cmd = "cargo test auth::unit"
test.timeout = 10
[[per_target]]
glob = "src/core/*.rs"
test.cmd = "cargo test core::unit"
test.timeout = 15
# Catch-all: full suite for any file not matched above.
# Required unless [targets] is scoped to exactly the globs listed above.
[[per_target]]
glob = "**/*.rs"
test.cmd = "cargo test"
test.timeout = 60Rationale: Phase 1 uses fast targeted tests. Phase 2 re-tests only the survivors with the comprehensive suite.
Execution
Phase 1:
mewt runWait for completion.
Phase 2 (after phase 1 completes):
Extract uncaught mutants:
mewt results --status Uncaught --format ids > uncaught_ids.txtUpdate mewt.toml:
Comment out all
[[per_target]]sections (including the catch-all)Uncomment Phase 2
[test]section
Re-test with full suite:
mewt test --ids-file uncaught_ids.txtReview final results:
mewt results # Remaining uncaught are true coverage gaps
Example speedup:
Naive approach:
2,000 mutants × 45s = 25 hours
Two-phase approach:
Phase 1: 2,000 mutants × 8s = 4.4 hours → 450 uncaught (example outcome)
Phase 2: 450 uncaught × 45s = 5.6 hours → 180 truly uncaught
Total: ~10 hours (2.5× speedup)
Note: Phase 2 duration is unknowable before Phase 1 completes — it depends entirely
on how many mutants survive. The figures above illustrate one possible outcome.
Present Phase 1 as a firm estimate; present Phase 2 as (survivors × full-suite time)
once Phase 1 results are available.Per-Target Test Configuration
Use when: Tests are well-organized by module/file, and running targeted tests is significantly faster than the full suite.
Setup Pattern
# Test full suite for every mutant (slow but comprehensive)
[test]
cmd = "go test ./..."
timeout = 45
# ALTERNATIVE: Targeted tests per file (fast, may miss cross-module failures)
[[per_target]]
glob = "auth/*.go"
test.cmd = "go test ./auth"
test.timeout = 10
[[per_target]]
glob = "core/*.go"
test.cmd = "go test ./core"
test.timeout = 15
[[per_target]]
glob = "utils/*.go"
test.cmd = "go test ./utils"
test.timeout = 8
# Catch-all for unmatched files
[[per_target]]
glob = "*.go"
test.cmd = "go test ./..."
test.timeout = 45Ordering matters: First match wins. Place most specific patterns first, catch-all last.
Verify Speedup
time go test ./... # Full suite: 45s
time go test ./auth # Targeted: 8sIf targeted tests aren't significantly faster, this optimization won't help.
Trade-offs
Benefits:
Faster campaign execution
Scales linearly with codebase size
Risks:
May miss cross-module integration bugs
Requires correct glob-to-test mapping
Mitigation:
Use this for initial passes
Consider two-phase approach for comprehensive validation
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.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download mutation-testing.zip