Flaky Test Finder

Proves whether a test is flaky instead of reasoning about it, by re-running it many times and reporting the observed failure rate, then narrowing the cause to ordering, shared state, timing or environment. Use when a test fails intermittently, passes on retry, fails only in CI, or the user suspects flakiness.

By ContexoryEngineering1 supporting file

Install this skill

Choose how to install

Adds Contexory as a plugin marketplace, then installs this skill from it. Run both in Claude Code. Later fixes arrive with /plugin marketplace update.

SKILL.md

Markdown4.7 KB

Flaky test finder

Flakiness is an empirical claim, and it is the one kind of test problem where reasoning from the source is actively misleading. A test that looks obviously order-dependent may be solid; a test that looks pure may fail one run in forty on a loaded machine.

So this skill measures first and reads second.

Procedure

1. Measure the rate

<skill-dir> is the directory this SKILL.md was loaded from — the skill installs outside your project, so its script is named by full path, never relatively.

bash <skill-dir>/scripts/rerun.sh "pnpm vitest run path/to/file.test.ts" 30

Runs the command N times, reports pass/fail per run, the observed failure rate, and a Wilson confidence interval for the true rate. The interval is the point: three failures in ten runs and three in a hundred are very different findings, and a bare percentage hides which one you have.

If the failure rate is 0 over a decent number of runs, say so plainly — "not reproduced in N runs" is a real result. Do not go on to diagnose a flake you could not observe.

2. Split ordering from isolation

The two commonest causes look identical from the failure message. Distinguish them:

  • Run the single test alone, many times. Failing alone means it is not

    order-dependent — look at timing, environment and external state.

  • Run the whole file, then the whole suite. If it only fails in the larger set, it is

    shared state or ordering — something before it leaves a mutation behind.

  • If the runner supports a random seed, vary it. A failure rate that moves with the seed is

    ordering by definition.

3. Find the shared thing

When it is ordering or isolation, the cause is almost always one of a short list, and it is worth checking them in order rather than reading the test top to bottom:

  • module-level mutable state, a cache, or a singleton that survives between tests
  • a fake clock, timezone or locale set by one test and not restored
  • database or filesystem state without a per-test transaction or temp directory
  • an unawaited promise from an earlier test landing during this one
  • a shared port, fixture file, or environment variable
4. Prove the fix by re-measuring

Apply the fix, then run step 1 again with at least as many iterations. The claim "fixed" is only supported by a second measurement with a comparable interval. A single green run proves nothing about a test that failed one time in twenty.

5. If it cannot be fixed now, quarantine deliberately

Say so explicitly, with the rate, and prefer an annotation the runner reports over a silent skip. A quarantined test that nobody can see becomes a deleted test.

Output

  1. Rate — failures over runs, with the interval, and the exact command measured
  2. Class — ordering, isolation, timing, environment, or not reproduced
  3. Evidence — which of the step-2 runs differed, and how
  4. Cause — the specific shared thing, with the line
  5. Post-fix rate — the second measurement, or an explicit "not yet re-measured"

What this skill deliberately does not do

  • It does not diagnose from the source without measuring. That is the failure mode

    this skill exists to replace.

  • It does not add a retry. Retries hide the rate and convert a known flake into an

    unknown one. If a retry is genuinely the right call, that is the author's decision to make explicitly, not a fix to apply quietly.

  • It does not delete or skip a test to make a suite green.
  • It does not claim a fix from one passing run.
  • It does not run suites that mutate shared infrastructure — a test command touching a

    shared database, a live API or a deployment is not re-run thirty times. It says why and asks for an isolated target.

  • It does not report a rate without saying how many runs produced it.

When this is the wrong tool

  • The test fails every single time. That is not flakiness, it is a bug with a

    reproduction already in hand — diagnose it from the failure instead. Measuring a 100% rate tells you nothing you did not already know.

  • The test does not exist yet and you are deciding what to cover. That is a coverage

    question, not a reliability one.

  • The failures started immediately after a dependency bump. Check the bump first: a

    changed default explains a whole class of new intermittency.

Supporting files

1 file

scripts/rerun.shShell4.1 KB
#!/usr/bin/env bash
# Measure a command's failure rate by running it repeatedly.
#
#   bash rerun.sh "<command>" [runs] [--stop-after N]
#
# Prints a pass/fail strip, the observed rate, and a Wilson score interval — the
# interval matters because 3/10 and 30/100 are the same percentage and very
# different evidence. Exits 0 when the command never failed, 1 when it failed at
# least once, and 2 on bad usage.
#
# Deliberately dumb about what it runs: it never inspects, retries or repairs the
# command, and it captures each run's output so a failing run can be read rather
# than re-guessed.

set -uo pipefail

CMD="${1:-}"
RUNS="${2:-20}"
STOP_AFTER=0

if [ "${3:-}" = "--stop-after" ]; then
  STOP_AFTER="${4:-0}"
fi

if [ -z "$CMD" ]; then
  echo "usage: rerun.sh \"<command>\" [runs] [--stop-after N]" >&2
  exit 2
fi
# Digits-only, then non-zero. The digit test alone accepts `0`, which the message
# already calls invalid: zero runs skips the loop and then divides by `runs` in
# the Wilson block, so the script died on a ZeroDivisionError *and still exited
# 0* — a caller reads that as a successful measurement with no interval.
case "$RUNS" in
  ''|*[!0-9]*) echo "runs must be a positive integer" >&2; exit 2 ;;
esac
if [ "$RUNS" -lt 1 ]; then
  echo "runs must be a positive integer" >&2; exit 2
fi
# Same guard for --stop-after: it is compared with `-gt` on every iteration, so a
# non-numeric value prints a bash error per run while the measurement continues.
case "$STOP_AFTER" in
  ''|*[!0-9]*) echo "--stop-after takes a non-negative integer" >&2; exit 2 ;;
esac

LOG_DIR="$(mktemp -d "${TMPDIR:-/tmp}/rerun.XXXXXX")"
trap 'echo; echo "run logs: $LOG_DIR"' EXIT

echo "command: $CMD"
echo "runs:    $RUNS"
echo

failures=0
strip=""
first_failure=""

for i in $(seq 1 "$RUNS"); do
  log="$LOG_DIR/run-$i.log"
  if bash -c "$CMD" >"$log" 2>&1; then
    strip="${strip}."
  else
    strip="${strip}X"
    failures=$((failures + 1))
    [ -z "$first_failure" ] && first_failure="$log"
  fi
  # Redraw in place only for a human at a terminal. Piped — which is how an agent
  # runs this — `\r` is not a cursor move, so every iteration lands as another
  # copy of the strip and a 40-run measurement becomes 40 lines of near-identical
  # noise in the reader's context.
  if [ -t 1 ]; then
    printf "\r  [%-${RUNS}s] %d/%d  failures: %d" "$strip" "$i" "$RUNS" "$failures"
  fi
  if [ "$STOP_AFTER" -gt 0 ] && [ "$failures" -ge "$STOP_AFTER" ]; then
    echo
    echo "  stopping early: reached $STOP_AFTER failure(s)"
    RUNS="$i"
    break
  fi
done

echo
echo "  runs: [$strip]   . = pass   X = fail"
echo

# Wilson score interval at 95%. Preferred to the normal approximation because the
# rates that matter here are near zero, where the normal interval goes negative
# and stops meaning anything.
python3 - "$failures" "$RUNS" <<'PY'
import math, sys

failures, runs = int(sys.argv[1]), int(sys.argv[2])
p = failures / runs if runs else 0.0
z = 1.96
denom = 1 + z * z / runs
centre = (p + z * z / (2 * runs)) / denom
margin = (z * math.sqrt(p * (1 - p) / runs + z * z / (4 * runs * runs))) / denom
low, high = max(0.0, centre - margin), min(1.0, centre + margin)

print(f"observed failure rate: {failures}/{runs} = {p * 100:.1f}%")
print(f"95% interval:          {low * 100:.1f}% – {high * 100:.1f}%")
if failures == 0:
    print(
        f"\nNot reproduced in {runs} runs. The true rate could still be as high as "
        f"{high * 100:.1f}% — report this as 'not reproduced', not as 'not flaky'."
    )
elif failures == runs:
    print(
        "\nFailed every run. This is a broken test, not a flaky one — diagnose it from "
        "the failure itself rather than measuring a rate that is already 100%."
    )
else:
    print(
        "\nReproduced. Next: run the test alone many times, then inside its file, then "
        "inside the whole suite. Only-fails-in-the-larger-set means ordering or shared "
        "state; fails-alone means timing or environment."
    )
PY

if [ -n "$first_failure" ]; then
  echo
  echo "first failing run: $first_failure"
  echo "── tail ──"
  tail -n 25 "$first_failure"
fi

[ "$failures" -eq 0 ] && exit 0 || exit 1