/ Mutation testing for pull requests
Kill the mutants your tests miss.
Reapr runs mutation testing on every pull request, with your own StrykerJS or cargo-mutants config, on the lines your branch changed.
- - run: npx stryker run
+ - uses: reapr-corp/run@v1curl -LsSf https://github.com/reapr-corp/reapr/releases/latest/download/rpr-installer.sh | shrpr run --diff origin/mainrpr 0.3.0 for Linux (x86_64, aarch64). Needs git and your engine: StrykerJS (Node.js 20.10 or later) or cargo-mutants 26.2 to 27.x. macOS and Windows are planned.
/ Reference measurement
Reference measurement
- faster Stryker run after one ignore pattern: 38.4 s → 12.5 s, same results
- 3x
- of a small diff run happens before the first test runs
- 85%
- score of the diff, against 87.0% package-wide in Stryker's own report
- 100%
Measured on one package of a pnpm + Turborepo monorepo (~55k files incl. build caches), StrykerJS 9.6.1 and Vitest 4, medians of 5 runs on a loaded 22-thread Linux machine. The 3x comes from adding .turbo to Stryker's ignorePatterns (Turborepo's cache was 97% of the files Stryker copied into its sandbox), not from Reapr. The 87.0% includes results that incremental: true carried over from outside the diff; the diff's own 3 mutants were all killed. Read the full bench report, or the guide How to speed up a slow Stryker run.
/ Why
Coverage measures execution, not verification.
Teams ship a lot of AI-generated code, and agents write tests that pass. The mutation score tells you whether those tests protect behavior. Time keeps it out of pull requests: a full run takes 40 to 90 minutes. Reapr scopes it to the diff.
More in the guides Mutation testing for AI-generated code and Mutation testing on pull requests.
- Line coverage asks
- Did a test run this line?yes, 100%
- The mutation score asks
- If this line changed, would a test fail?no, it survived
/ How it works
Your engine, your config, only your diff.
Read the diff
git diff <base>...HEADgives the changed line ranges.src/a.ts:8-12
src/b.ts:5-6
docs/usage.md:3-4Run Stryker on it
Your config's
mutateglobs pick the files. A wrapper imports your config and sets onlymutateand ajsonreporter.src/a.ts:8-12
src/b.ts:5-6docs/usage.md:3-4Score the diff
Reapr keeps the mutants inside the changed ranges, computes the score and writes the run record.
score 80.00%
2 survived
File names are an example. Your test runner, thresholds, timeouts, checkers and plugins stay as you wrote them. What Reapr changes in your config. Why the score covers only the diff: Stryker incremental mode and package-wide scores.
/ Output
A summary for people. JSON for agents.
Each survivor has the file, line, column, mutator and replacement: enough for an agent to write the missing test and run Reapr again. Reapr reports; your agent writes the missing test. The mutation-audit agent skill does that with fixed rules.
rpr run --diff origin/mainrpr · stryker · diff vs origin/main · 3 ranges in 2 files
score 80.00% (8 killed, 0 timeout, 2 survived, 0 no coverage) 1m 23s
survivors:
src/a.ts:10:3 ConditionalExpression → false
src/b.ts:6:2 ConditionalExpression → false
run: /home/me/repo/.git/reapr/runs/1793640000-4242rpr run --diff origin/main --json (survivors)"survivors": [
{ "file": "src/a.ts", "line": 10, "column": 3,
"mutator": "ConditionalExpression",
"replacement": "false", "status": "Survived" },
{ "file": "src/b.ts", "line": 6, "column": 2,
"mutator": "ConditionalExpression",
"replacement": "false", "status": "Survived" }
]Samples from the output section of the README. Exit codes: 0 passed, 1 below --min-score or the engine's threshold failed, 2 error.
/ GitHub Action
Run it on every pull request.
reapr-corp/run@v1 writes a job summary with the score, counts, survivors and warnings, and exits with Reapr's exit code. Linux runners. No token needed.
on: pull_request
jobs:
rpr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0 # full history, so origin/<base>...HEAD has a merge base
- uses: actions/setup-node@v5
with:
node-version: 22
cache: npm
- run: npm ci # rpr runs your engine (StrykerJS) with your installed deps
- name: rpr
uses: reapr-corp/run@v1
id: rpr
with:
min-score: 80 - name: rpr
uses: reapr-corp/run@v1
id: rpr
with:
configs: |
# one per package; blank lines and # comments are ignored
stryker.api.config.mjs
stryker.web.config.mjs
stryker.worker.config.mjs/ Principles
What Reapr does and does not do.
- We invent nothing
- Reapr runs your engine config as written and adds only the diff scope and a JSON reporter. It forces no flags. When a setting can miscount results, it warns and changes nothing.
- Agents are first-class users
--jsonprints one document with every surviving mutant, and the mutation-audit agent skill turns it into tests. An MCP server is planned.- Every run leaves raw data
- Run facts, both reports and the wrapper config go to
.git/reapr/runs/<run_id>/on your machine. Reapr never uploads or deletes them. - Anonymous, opt-out telemetry
- One event per run: outcome, counts, durations, bucketed diff size. Never code, paths or names.
DO_NOT_TRACK=1turns it off. See every telemetry property.
/ Engines
StrykerJS and cargo-mutants today. More engines planned.
Reapr runs mutation engines; it does not replace them. Each engine plugs in through one adapter. Add an adapter.
| Engine | Language | Status |
|---|---|---|
| StrykerJS | JavaScript, TypeScript | Supported |
| cargo-mutants | Rust | Supported: 26.2 to 27.x |
| PIT | Java, JVM | Planned |
| Infection | PHP | Planned |
| mutmut | Python | Planned |
| Stryker.NET | C# | Planned |
/ Open core
Free to use, modify and self-host.
The CLI, the Action and the engine adapters are open source and work on their own. A hosted layer is planned as the paid product.
Available today, free
rprCLI andreapr-core: FSL-1.1-ALv2, each release becomes Apache 2.0 after two years- Engine adapters: MIT
- GitHub Action: MIT
- Agent skill (
mutation-audit): MIT
Planned, paid, not built: Reapr Cloud
- Hosted compute with warm workers
- A global cache of mutant results
- History and metrics across runs
/ Reapr Cloud
Reapr Cloud is planned. Join the waitlist.
A hosted layer on top of the open source tool: faster runs on warm workers, a dashboard of mutation scores across pull requests, and a bot that proposes tests for surviving mutants. None of it is built yet. The CLI and the Action stay free and work without it.
/ FAQ
Frequently asked questions
- What is mutation testing?
- A mutation engine changes your code on purpose, one small change at a time (for example > becomes >=), and runs your tests against each change. If a test fails, the mutant is killed. If every test still passes, the mutant survived: a change in behavior that no test catches. The mutation score is the share of mutants your tests detect.
- How is mutation testing different from code coverage?
- Coverage records which lines ran during the tests. The mutation score records whether a test fails when those lines change. A line can be 100% covered by a test that asserts nothing, and a mutant on that line survives.
- How do I run Stryker only on the lines I changed?
- StrykerJS accepts line ranges in mutate, such as src/app.ts:5-7. Reapr builds those ranges from git diff against the merge base, keeps the files your own mutate globs select, and runs Stryker with your config: rpr run --diff origin/main.
- Does Reapr change my Stryker config?
- No. Reapr never edits your config file. For each run it writes a wrapper config that imports yours and overrides three keys: mutate (the changed ranges), reporters (adds json) and jsonReporter.fileName. Test runner, thresholds, timeouts, checkers and plugins stay as you wrote them.
- Does it work with Jest, Vitest and monorepos?
- Reapr runs whatever test runner your StrykerJS config uses, including Jest and Vitest, and it runs cargo-mutants for Rust projects. In a monorepo, run rpr with --project and --config per package, or list one config per package in the GitHub Action's configs input. Reapr runs on Linux (x86_64 and aarch64) today; macOS and Windows are planned.
- Is Reapr free?
- Yes. The rpr CLI, the GitHub Action, the engine adapters and the agent skill are free and open source: FSL-1.1-ALv2 for the core and CLI (each release becomes Apache 2.0 after two years) and MIT for the adapters, the Action and the skill. A hosted layer, Reapr Cloud, is planned as a paid product and is not built yet.
Scoped to the diff. Your config, your engine.
Install rpr, or add one step to your pull request workflow.