Google found 1 in 7 tests eventually becomes flaky

Find flaky tests.
Name the cause.

A CLI that diagnoses flaky pytest tests through differential diagnosis. Change one hidden variable at a time, observe which change flips a passing test to failing. That variable is the cause.

See how it works View on GitHub
$ pip install flake-exorcist

Differential diagnosis for your test suite

Stop re-running failures and hoping they pass. This tool changes one hidden variable at a time and watches which change flips a green test red. The variable that flipped the outcome is the root cause.

01

Baseline

Run the suite once in default order. Record every test outcome. This is the control group that all other runs get compared against.

02

Broad Scan

Re-run the suite once per dimension (order, environment, parallelism, seed). Only one variable changes per run. Tests whose outcome flipped become candidates.

03

Deep Dive

For each candidate, repeat the flipping dimension multiple times and score confidence. If the cause is order-dependence, binary search identifies the exact polluting test.

04

Report

Ranked findings with cause, confidence percentage, reproduction command, and a fix hint. Exit code 1 gates your CI. JSON output feeds automation.


What gets perturbed

Each dimension isolates one hidden variable. Only one changes per run, so when a test flips, you know exactly what caused it.

Order

Shuffles execution order with different seeds. Catches tests that leak shared state to tests running after them.

Environment

Varies timezone and locale. Catches tests that assume the local clock or depend on string collation rules.

Parallel

Runs serial vs. multi-worker via pytest-xdist. Catches race conditions, port conflicts, and file locks that only surface under concurrency.

Seed

Varies PYTHONHASHSEED with order held fixed. Catches tests that depend on set/dict iteration order or unseeded randomness.


What you actually see

Run one command against a repo with planted flaky tests. The tool finds the cause, tells you why, and suggests how to fix it.

zsh
$ exorcist hunt tests/ --fast --seed 42 Estimated runs: 22 (budget: 50) Exorcist Report | Runs: 37 | Seed: 42 | Flaky: 1 | Env-dependent: 1 | Failing: 1 Flaky Tests MEDIUM tests/test_hash_seed.py::test_set_iteration_order cause: seed confidence: 50% Fix: Seed the RNG in a fixture; sort collections before asserting. Non-portable (not flaky) tests/test_timezone.py::test_utc_date_format Genuinely failing (not flaky) tests/test_real_failure.py::test_always_fails $ echo $? 1

Honest classification, not guessing

Every test gets one of five labels. The tool says "inconclusive" when it does not have enough evidence, instead of making something up.

deterministic_pass Always passes. Stable.
deterministic_fail Always fails. A real bug, not flaky.
flaky Outcome depends on a named dimension.
environment_dependent Non-portable. Fails on some machines.
inconclusive Not enough evidence. Reported honestly.
Exit 0: no flaky tests
Exit 1: flaky found
Exit 2: internal error
Exit 3: bad arguments

Built with Kiro, spec-first

75 EARS requirements written before any code. 7 architecture decision records. 26 ordered TDD tasks. Property-based tests (Hypothesis) proving the classifier is deterministic. A self-diagnose hook that catches any flakiness the tool introduces into its own suite.

Spec (75 requirements)
Design (7 ADRs)
Tasks (26 ordered)
Property tests
Self-diagnose hook
.kiro/ directory
$ tree .kiro/ .kiro/ steering/ product.md tech.md structure.md flakiness-taxonomy.md specs/flake-exorcist/ requirements.md # 75 functional + 8 non-functional design.md # architecture, algorithms, JSON schema tasks.md # 26 TDD tasks, ordered by dependency hooks/ self-diagnose.kiro.hook
75
EARS Requirements
7
ADRs
26
TDD Tasks
4
Dimensions tested

Prior art credited: NonDex (2016), detect-test-pollution, pytest-randomly, Shaker (2020).
What is different here: One command, zero CI history needed, works on a cold repo, cross-dimension attribution.