Skip to main content

wouldcatch: your tests pass. Would they catch a bug?

CI PyPI Python 3.10+ Zero dependencies Windows, macOS, Linux MIT license

Quick start · The problem · What it mutates · CI and agents · FAQ


wouldcatch plants small bugs in only the lines you just changed, runs your tests after each one, and tells you which bugs got through. If your tests stay green when the code is wrong, you'll know the exact line.

wouldcatch finds 3 bugs the tests miss, then 0 after one more test

Quick start

pipx install wouldcatch        # Windows without pipx: py -m pip install wouldcatch
cd your-repo
wouldcatch

That's it. No config file, no plugin, zero dependencies. It reads your git diff, runs pytest, and cleans up after itself.

The problem

Two tests. Every line runs. Everything is green.

def discount(price, is_member):              def test_member():
    if is_member and price >= 100:               assert discount(200, True) == 190
        return price - 10
    return price                             def test_guest():
                                                 assert discount(50, False) == 50
$ wouldcatch
  MISSED  shop.py:2  `and` -> `or`
  MISSED  shop.py:2  `>=` -> `>`
  MISSED  shop.py:2  `100` -> `101`

4/7 caught. 3 bug(s) your tests would not notice.
Mutant The bug it stands for Why the tests missed it
>= -> > A member spending exactly 100 gets no discount No test checks 100
100 -> 101 Same boundary, shifted by one No test checks 100
and -> or Every guest over 100 gets the discount No test has a guest over 100

Add two asserts, discount(100, True) == 90 and discount(200, False) == 200, and it's 7/7 caught.

Coverage said 100%. Coverage counts which lines ran. It can't tell you whether any test would fail if those lines were wrong. That's the question wouldcatch answers.

Why only the diff

Mutation testing is the honest way to grade tests, but running it across a whole project takes long enough that almost nobody does. wouldcatch only touches the lines in your diff. A normal change gives 5 to 30 mutants, so it's done before you finish the commit message.

Line coverage Whole-project mutation testing wouldcatch
Tells you a test would fail on a bug no yes yes
Time on a typical change seconds minutes to hours seconds to minutes
Setup plugin config files none
Output a percentage a report on everything the exact lines to test

What it mutates

One change at a time, only on lines in your diff. Test files are never mutated.

Kind Example Catches tests that never check...
Comparison a < b -> a <= b, == -> !=, in -> not in boundaries and equality
Arithmetic + -> -, * -> /, += -> -= the actual computed value
Logic and -> or, not x -> x each side of a condition
Constants True -> False, 100 -> 101 exact values and limits
Returns return total -> return None the return value at all
Calls max(a, b) -> min(a, b) which of two values wins
Loops break -> continue how a loop ends early
Branches if cond: -> if not (cond): both paths of an if or while

Want another one? New mutations are small PRs: see the good first issues.

Usage

Command What it does
wouldcatch Uncommitted changes; on a clean feature branch, everything since it left main; otherwise the last commit
wouldcatch --base main Everything on this branch since main
wouldcatch --cmd "pytest tests/unit -x -q" Use your own test command
wouldcatch --json Machine-readable output for scripts and agents
wouldcatch --all Show caught mutants too
wouldcatch --github Also print GitHub annotations (automatic inside GitHub Actions)
wouldcatch --max 100 Try more mutants (default 50)

Exit codes: 0 nothing missed · 1 something missed · 2 setup problem (not a git repo, tests already failing) · 3 incomplete: --max left mutants untried (pass --allow-partial to accept that).

A mutant whose test run times out is reported as timeout, separately from caught: a hang isn't proof that a test checks that line.

Skip a line on purpose with # wouldcatch: ignore.

CI, pre-commit and AI agents

GitHub Action: missed bugs show up on the exact lines of the pull request
on: pull_request
jobs:
  wouldcatch:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0
      - uses: actions/setup-python@v6
        with:
          python-version: "3.13"
      - run: pip install -e . pytest   # your project and its test deps
      - uses: Sahil-u07/wouldcatch@v0.2.2

Inputs (all optional): cmd (test command), base (defaults to the PR's base branch), max (default 50), allow-partial (default false), fail-on-missed (default true).

pre-commit: check before every commit

With wouldcatch installed in your project environment:

- repo: https://github.com/Sahil-u07/wouldcatch
  rev: v0.2.2
  hooks:
    - id: wouldcatch
AI coding agents: make them write tests that check something

Agents are good at writing tests that pass and bad at writing tests that would fail on a bug. The MISSED list gives them a concrete target.

Claude Code: copy skills/wouldcatch into .claude/skills/ in your project.

Any other agent: add this to AGENTS.md:

After changing Python code, run `wouldcatch --json`. For every mutant with
"status": "missed", add a test that fails for that change. Re-run until none are missed.
Never change the source code to make a mutant go away.

How it works

git diff ──> changed lines ──> mutants ──> run tests per mutant ──> restore file ──> report
  1. git diff gives the Python lines you changed.
  2. Each mutant is a tiny text edit at an exact position, so your formatting and comments stay untouched.
  3. Your test command runs once per mutant. A fresh bytecode cache each run means a stale .pyc can never hide a mutant.
  4. The file is restored byte for byte (CRLF included) after every run, even on Ctrl+C. If the process is killed, the next run restores it from a backup kept inside .git first.

FAQ

Isn't this what mutmut or pytestmut already do?

Partly, and they're good tools. mutmut and cosmic-ray mutate the whole project, which is a thorough audit. pytestmut also has a diff mode (--diff, --staged) and parallel workers, so if you need speed on a big suite, try it. Diff-based mutation testing isn't new in other languages either: Infection (PHP) and Mull (C/C++) both do it.

wouldcatch aims at one workflow: zero config, run it on the change in front of you, get exact lines back. That includes warnings on the pull request diff through the GitHub Action, and a --json list an AI agent can turn into tests.

Will it mess up my files?

It edits one file at a time and restores it right after, byte for byte. If the process dies mid-run, the next run restores the file from a backup kept inside .git before doing anything else.

My test suite is slow.

Each mutant is one run of your test command. Point --cmd at the relevant tests and add -x so pytest stops at the first failure.

A mutant can't be caught because the code really doesn't care.

That happens (a log interval, a cache size). Mark the line # wouldcatch: ignore.

JavaScript, TypeScript, Go?

Python today. The git side is language-neutral and JS/TS is next. Weigh in on issue #4.

Contributing

PRs welcome, especially new mutations. Start with a good first issue and read CONTRIBUTING.md.


If wouldcatch found a bug your tests missed, a star helps other people find it.

MIT · made by Sahil Lenka

Metadata

Release files for wouldcatch 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wouldcatch 0.2.2
File Size Uploaded Provenance Actions
wouldcatch-0.2.2.tar.gz 21.5 kB With provenance Details for wouldcatch-0.2.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for wouldcatch 0.2.2
File Interpreter ABI Platform Actions
wouldcatch-0.2.2-py3-none-any.whl Python 3 none any Details for wouldcatch-0.2.2-py3-none-any.whl

Total release size: 36.5 kB

Release files / wouldcatch-0.2.2.tar.gz

Download URL wouldcatch-0.2.2.tar.gz
Size 21.5 kB
Tags Source
SHA-256 checksum
How to use checksums
b59f39eed0bec1f5c3946687cd09c183e16708fece950d44bf61e1a02b148d85
BLAKE2b-256 checksum
How to use checksums
b15184809fdf83e300308111b08959f34ac89492675be9f5bdfd0cf33e5eae02
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Release files / wouldcatch-0.2.2-py3-none-any.whl

Download URL wouldcatch-0.2.2-py3-none-any.whl
Size 14.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cfb576799aa89766f941cee6e0c1ae0b3cd42a987178373e2df71589459a4ba8
BLAKE2b-256 checksum
How to use checksums
29976a50b5934c94d2bec10b98280bf0c145dd96d8de26257e238166f631aea3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

Security for wouldcatch 0.2.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for wouldcatch-0.2.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page