Mergestro

Get started

Mergestro Gate mutates the lines a pull request changed and reruns your tests. A mutation nothing catches is a line your tests don't really check. This page adds it to a GitHub workflow, reads what it reports, and makes it a required check.

Add it to a pull-request workflow

One file. It needs the full history (the gate diffs against the pull request's base) and a Rust toolchain, because every mutant is compiled and tested. Start it advisory: it reports and comments, and never fails the check.

.github/workflows/mergestro-gate.yml

on: pull_request
permissions: { contents: read, pull-requests: write }
jobs:
  gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - uses: lucheeseng827/mergestro-gate@v0.6.1
        with:
          advisory: "true"   # report, never block — start here

The Action installs cargo-mutants and a prebuilt, checksummed slop-gate binary, works out the merge-base, and runs the gate on your own runner. There is no account, no API key and no service behind it.

Pin the release tag (@v0.6.1), not @main. A gate that can block merges should change only when you change it.

Read what it reports

The job log opens with a summary. This is the gate on a small crate whose tests catch every mutation of the changed line (an excerpt of a real 0.6.1 run):

preflight:  green & stable across 1 run(s)
mutants:    5 candidate(s), 5 tested, 0 capped out
outcomes:   5 caught · 0 timed out · 0 unviable · 0 SURVIVED
verdict:    PASS

Each survivor is listed with its file, line, severity and the exact mutation — for example replace is_test_scaffold -> bool with false: the function could always return false and no test would notice. The same list goes into the pull-request comment, which is updated in place on every push and says what is new, still open and resolved since the last run.

It also flags tests that contain no assertion at all, and — as advisory lanes — slop, security, convention and weakened-test patterns in the diff.

Exit codes

CodeMeaning
0Passed, or ran advisory.
2Blocked: survivors over max-survivors, or another gate you turned on tripped. This is what fails a required check.
1Operational: no merge-base, cargo-mutants missing, the engine died. Never a verdict about your code.

Make it a required check

Watch a dozen pull requests first. When the survivors it reports are ones you would have wanted to know about, turn blocking on:

      - uses: lucheeseng827/mergestro-gate@v0.6.1
        with:
          max-survivors: "0"              # any survivor blocks
          block-on-zero-assertion: "true"  # so does a test that asserts nothing
          budget: "10m"                  # stay inside your CI time limit

Then add the job to the branch-protection rule as a required status check. To start softer, max-survivors: "3" tolerates a few survivors while a codebase catches up; add block-on-severity: high so a high or critical one still blocks whatever the count.

Before turning it on for a large change, slop-gate estimate predicts how many mutants a diff produces without building anything — see Run it anywhere.

Next: Run it anywhere →