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.
--- 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:
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.
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 (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.
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.
- 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.