Testing

Flaky Test Detector

Diagnose why a test fails intermittently -- timing, ordering, shared state, or real non-determinism -- instead of just retrying until it passes.

When to use this

  • A test fails in CI but passes locally, or passes on retry without any code change.
  • Before disabling/skipping a test -- diagnose it first; skipping just hides a real bug or a real race condition.
  • Not for a test that fails consistently -- that's a normal bug, not flakiness.

The skill file

Copy this verbatim. It's written in the SKILL.md format (YAML frontmatter + markdown instructions) that Claude Code, and increasingly other agent tools, read directly.

SKILL.md
---
name: flaky-test-detector
description: Diagnose the root cause of an intermittently-failing test (timing assumptions, shared/leaked state, test ordering, real concurrency bugs) rather than recommending retries or skips. Use when a test fails non-deterministically.
---

# Flaky Test Detector

A flaky test is either a bad test or a real bug wearing a disguise. Assume
the latter until you've ruled it out -- flakiness in a test that exercises
concurrent code is often the only signal a real race condition will ever
give you.

## Common root causes, check in this order

1. **Timing assumptions**: fixed `sleep()` calls, assuming an async
   operation finished, assuming clock/timestamp values won't collide.
2. **Shared state leakage**: global/module-level state, a shared database/
   file the test doesn't clean up, tests that depend on run order.
3. **Unseeded randomness**: random data generation without a fixed seed,
   so some runs hit an edge case others don't.
4. **Real concurrency bugs**: the code under test has an actual race
   condition, and the test's flakiness is just exposing it non-deterministically.
5. **External dependency flakiness**: network calls, real clocks, or a test
   double that isn't actually deterministic.

## Process

1. Run the test repeatedly (or review its failure history) to characterize
   the failure -- does it fail more under load, in a specific order, or
   randomly regardless?
2. Read the test and the code under test together, looking for the
   patterns above.
3. Identify the specific root cause with evidence, not a guess.

## Output

The root cause, evidence for it, and the fix -- which is almost always
"remove the timing/ordering assumption" or "fix the underlying race," and
almost never "just add a longer sleep" or "mark it flaky and retry."

Installing it elsewhere

The frontmatter/body split above is Claude Code's convention. Here's how to carry the same instructions into other tools:

Claude Code
.claude/skills/flaky-test-detector/SKILL.md

Save the file below verbatim (frontmatter included) at that path, project-local or in ~/.claude/skills/ for a user-level skill. Claude Code loads the name/description pair to decide when to pull it in, or you invoke it directly as /flaky-test-detector.

Cursor
.cursor/rules/flaky-test-detector.mdc

Convert the YAML frontmatter to Cursor's rule format (description, globs, alwaysApply: false) and keep the markdown body as the rule content. Cursor surfaces it by description match, same idea as Claude Code's auto-load.

Codex CLI / Copilot
AGENTS.md

Codex CLI (and increasingly other agentic CLIs) read AGENTS.md at the repo root as always-on instructions. Paste the markdown body under a heading like ## {title}; for GitHub Copilot's coding agent, the equivalent file is .github/copilot-instructions.md.

Windsurf
.windsurfrules

Append the markdown body to .windsurfrules at the repo root. Windsurf treats the whole file as always-on context, so keep only the instructions you want applied on every request.

Where this goes wrong
  • Recommending a longer sleep() as the fix -- it reduces flakiness rate without removing the underlying assumption, and it'll come back.
  • Marking a test `@flaky`/skip without diagnosing it -- that can hide a real production race condition indefinitely.
  • Assuming it's "just CI being slow" without checking for shared state or ordering dependencies first.