By Contexory

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.

1 supporting file

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