Early previewMutation testing for Java

Green on the outside,
Red on the inside?

Find out whether your tests — or your agent's — actually check anything. mutastic plants small bugs in your code, runs your tests, and turns every bug that slips through into a clear note about the test you're missing.

$mutastic run --since=main
See how it works

Runs on the modern JDK, built atop the  java.lang.classfile API. Beautifully Open Source.

pip — happy
~/shop $mutastic run

This is Pip — official mutastic mascot! He takes his job very seriously.

The watermelon problem

Coverage says every line ran. It doesn't say anything was checked.

A test can call every function and assert nothing. Coding agents write these by the hundred, and then report a score they never measured.

mutastic flips one thing at a time — a >= to a >, a return value to zero — and asks your tests to notice. The ones that don't are your watermelons — and not the tasty kind!

How it works

Plant a bug. Run the tests. Read what got away.

01 · Mutate

Small bugs, in bytecode

Boundaries, negated conditions, swapped maths, replaced returns, removed calls. Scope a run to your branch's changes with --since, so you only hear about code you touched.

02 · Run

Your tests try to catch them

Each mutant runs against the tests that cover it, through the JUnit Platform, in worker JVMs beside your build. Hangs time out. Junk mutants from generated code are filtered out.

03 · Read

Missed mutants read like diagnostics

Not a mutator class name and a status code. A headline naming the gap, the code, the inputs your tests actually used, and the test to write.

It tells you about the missing test, not the mutation.

  • Real inputs. The coverage pass records what each line saw, so the advice is "none is 100", not "consider boundaries".
  • A "done when" check. Every missed mutant says exactly how you'll know it's fixed.
  • Ends on the score. And its change since the last run, as the last line.

Need the bytecode, the source mapping and the one input that tells them apart? mutastic explain m855c8e3c

~/mutastic — mutastic run samples/pricing
✗ MISSED no test checks discount() at total == 100
   ┌─ src/main/java/com/example/pricing/Pricing.java:17
   │
17 │     if (total >= 100) {
   │               ^^ changed to >
   │
   = note: every test calls discount() away from the boundary
           inputs seen: 50, 250 (none is 100)
   = help: test discount() with total = 100 and assert the result

   done when mutastic verify m855c8e3c samples/pricing passes
   reproduce mutastic run --only=m855c8e3c samples/pricing
   or ignore mutastic ignore m855c8e3c --reason="<why>" samples/pricing
 Mutants  ✓ 6 caught · ✗ 1 missed · 2 skipped (2 equivalent)
   Score  85.7%  threshold 80
The real output on the sample in mutastic's repository, shortened. Colour is never the only signal: every state also has a symbol.

mutastic sentinel · planned, not in a release yet

The measured score is the only score.

A warm daemon per repo. For you, it's watch mode: save a file, see the score move in a second or two. For your coding agent, it's the source of truth it has to convince before it can say "done".

1Agent editsSentinel waits for edits to settle, re-mutates the changed lines
2misses()Missed mutants on changed lines, with help and "done when"
3verify(id, test)Passes on the original, fails on the mutant, repeated
4gate()The stop hook asks "am I done?" — pass or fail, with reasons

gate fails → the agent gets the reasons and keeps working

Kills are checked, not claimed

An agent can't report a score it didn't earn. Only kills the sentinel verified count.

Test mode freezes the target

Strengthening tests? Production edits, deleted tests and removed assertions fail the gate.

Held-out mutants catch overfitting

A hidden sample shows whether new tests check behaviour or just the visible list.

Ignores go to a human

"Equivalent" is a proposal, shown as pending review, never as caught. Every decision is logged.

# connect over MCP, or hook it in so "done" has to pass the gate
$ mutastic sentinel start
$ mutastic mcp # stdio bridge to the warm daemon
$ mutastic sentinel gate # run from your agent's Stop hook

Output formats

One result model. Every tool you already use.

Each mutant gets a stable fingerprint that survives code moving around in review. Pick a renderer with --format.

fullbriefconcisegroupedagentjsonl
Mutation-testing report, version 2The open report format, read by report viewers, dashboards and badges
JSONL event streamVersioned (mutastic.events/v1), for agents and scripts
mutations.xmlThe format many build-server and code-quality plugins read.
Field guideHow to pick a watermelonSix signs of a ripe one. Fruit or test suite. Mostly test suite.Read Pip's guide →The docsRun it on your projectInstall it, run it on a sample in five minutes, and read every command and option.Read the docs →

The -tastic family

Spec it, build it, then prove the tests mean it.

spectasticSpec-driven development for AI coding agents: spec, plan and merge, with quality gates at each stage.spectastic.io →
mutasticMutation testing for Java: runs on JDK 25, tests projects on 17 and up. Checks that the tests your agent wrote actually check something.You are here
mutastic-rs laterThe same diagnostics and agent guardrails for Rust.

Slice your test suite open.

Early preview. Star the repo to follow along, or try it on one diff.

Star on GitHubRead the docs