Flaky Test Diagnoser
Runs competing-hypothesis experiments to isolate the true root cause of a flaky test. Produces a diagnosis with reproducible evidence — or an honest inconclusive label. Never promotes a guess to "root cause."
This is a tracing skill, not a fixer. The goal is to explain why the test is intermittent with falsifiable evidence, not to jump into patching code.
Execution contracts
Subagent watchdog: any time this skill spawns a test-running subagent via run_in_background=true (multi-run protocols, ordering bisection, distinguishing experiments), it MUST be armed with a staleness monitor per _shared/subagent-watchdog.md. Use Flavor A (Monitor tail per spawn) with thresholds STALE=15 min, HUNG=45 min — test suites can legitimately take a long time, especially with N≥10 reruns, but a 45-min silent window is pathological. TaskOutput status reports PID liveness only; output-file mtime is the ground truth for progress. Contract inheritance: timed_out_heartbeat joins this skill's termination vocabulary as a peer of blocked_by_environment (watchdog kill is a different failure class — the test-runner subagent hung, not the test environment). A watchdog-killed experiment contributes zero runs to the N≥10 count; the skill must retry or escalate.
Workflow
- Bootstrap run state — create
flaky-diag-{run_id}/ in CWD with state.json, hypotheses/, runs/, experiments/. See STATE.md.
- Detect test runner — identify the project's test framework and runner command from config files. See RUNNERS.md.
- Confirm flakiness — run the target test N>=10 times in isolation, record pass/fail per run, compute fail rate. Record each run under
runs/. See EXPERIMENTS.md.
- Generate competing hypotheses — produce >=3 deliberately different hypotheses from the 6 categories (ORDERING, TIMING, SHARED_STATE, EXTERNAL_DEPENDENCY, RESOURCE_LEAK, NON_DETERMINISM). Each hypothesis gets its own file in
hypotheses/ following the schema in FORMAT.md. See ANALYSIS.md for category signatures.
- Gather evidence for AND against each hypothesis — run the structured experiments (isolation, ordering bisection, timing, environment, code reads) and record findings as
evidence_for / evidence_against entries per hypothesis. See EXPERIMENTS.md.
- Design a distinguishing experiment per surviving hypothesis — an experiment whose outcome uniquely discriminates that hypothesis from its rivals. An experiment that "merely supports" all hypotheses does NOT count. See FORMAT.md.
- Run distinguishing experiments — record outcomes under
experiments/. Down-rank hypotheses whose distinctive prediction failed. See GOLDEN-RULES.md for the falsifiability gate.
- Rebuttal round — let the second-ranked hypothesis present its best rebuttal against the leader. The leader must answer with evidence, not assertion.
- Attempt red-green-red demonstration — if a leader has been selected, describe the minimal fix and have the user apply it (or apply it in an experimental branch). Rerun the multi-run protocol (N>=10). Only label
root_cause_isolated_with_repro if the red-green-red cycle succeeds.
- Emit structured output and diagnosis report — each hypothesis gets a
STRUCTURED_OUTPUT_START / STRUCTURED_OUTPUT_END block. Final report follows REPORT.md.
Honest termination labels
Pick exactly one. Never invent synonyms.
| Label |
Meaning |
Requires |
root_cause_isolated_with_repro |
Single hypothesis confirmed; fix demonstrated with red-green-red across N>=10 runs |
Distinguishing experiment passed; all rivals empirically falsified; fix verified |
narrowed_to_N_hypotheses |
Could not discriminate between N surviving hypotheses; additional probes recommended |
Each surviving hypothesis has evidence_for; no distinguishing experiment available within budget |
inconclusive_after_N_runs |
Could not reproduce flakiness or no hypothesis gathered sufficient evidence |
N>=20 runs executed; no consistent signal |
blocked_by_environment |
Cannot execute experiments (runner broken, missing deps, no shell access, tests require secrets) |
Specific blocker identified with the exact error output |
Never use: "diagnosed", "likely root cause", "probably", "should be", "root cause found" as a terminal label without a red-green-red demo. See GOLDEN-RULES.md.
Self-review checklist
Before delivering the report, verify ALL:
Golden rules
Hard rules. See GOLDEN-RULES.md for the full text, anti-rationalization counter-table, and the falsifiability gate.
- Never promote a hypothesis to root cause without a distinguishing experiment that empirically rejects all rivals.
- "Diagnosed" requires red-green-red. Test fails → fix applied → test passes deterministically across N>=10 runs. Without it, the label is
narrowed_to_N_hypotheses.
- Always run isolation before ordering. If the test fails in isolation, ordering analysis is irrelevant.
- Bisect, never brute-force when searching for an interfering test.
- Capture exact commands verbatim. Every experiment logs its shell command so the user can reproduce it.
- Minimum 10 runs for any statistical claim. Flaky tests can have fail rates under 10%.
- Never modify test code during diagnosis beyond temporary instrumentation that is reverted before the report is emitted.
- Evidence for AND against every hypothesis. A hypothesis with only supporting evidence is unfalsifiable and must be flagged.
- Coordinator does not self-approve. The hypothesis ranking is based on experiment outcomes recorded in files, not coordinator opinion.
- Structured output is the contract. Per-hypothesis evaluations live between
STRUCTURED_OUTPUT_START/STRUCTURED_OUTPUT_END markers; free-text commentary is ignored when computing the verdict.
Cancellation / resume
If interrupted mid-run: on next invocation read flaky-diag-{run_id}/state.json, identify the highest-generation completed stage, and replay from the next stage. Never recompute completed experiments — trust the recorded run artifacts. See STATE.md for the replay protocol.
Reference files
| File |
Contents |
| RUNNERS.md |
Test runner detection, command templates for pytest/jest/junit/go test/cargo test, and how to target a single test |
| EXPERIMENTS.md |
Multi-run protocol, isolation protocol, ordering bisection algorithm, timing instrumentation, red-green-red demo protocol |
| ANALYSIS.md |
Root cause categories, code pattern matching for flakiness signals, environment factor checklist, decision matrix |
| FORMAT.md |
Per-hypothesis evaluation schema, STRUCTURED_OUTPUT_START markers, distinguishing-experiment template |
| STATE.md |
state.json schema, per-stage state layout, resume protocol |
| GOLDEN-RULES.md |
Full golden rules text, falsifiability gate, anti-rationalization counter-table |
| REPORT.md |
Diagnosis report output template with honest termination labels |
Execution routing (sagaflow-first — Contract 6)
Sagaflow is the default execution path. The in-session workflow above is the FALLBACK, used only when the sagaflow worker is confirmed unavailable. See _shared/execution-model-contracts.md Contract 6.
Routing sequence (mandatory before any in-session work):
- Run
sagaflow doctor
- If healthy → launch via sagaflow below. Stop. Do not run in-session.
- If unhealthy → log
SAGAFLOW_UNAVAILABLE, proceed with in-session fallback.
Launch command:
Bash(
run_in_background=true,
command="sagaflow launch flaky-test-diagnoser --arg test='<TEST_ID>' --arg command='<RUN_CMD>' --arg n_runs=<N> --await"
)
Both test and command are REQUIRED. Substitute <TEST_ID> with the test identifier (e.g. tests/test_foo.py::test_bar), <RUN_CMD> with the exact shell command that runs that test, and <N> with the stability-run count (default 10). The workflow writes ~/.sagaflow/runs/<run_id>/report.md with pass/fail table, isolation+ordering results, timing analysis, top hypothesis with evidence, and proposed fix or mitigation.
1---2name: flaky-test-diagnoser3description: Systematically diagnoses why a test is flaky by running multi-run experiments, isolation tests, ordering permutations, and timing analysis. Use when the user says a test is flaky, intermittent, non-deterministic, randomly failing, passes sometimes, or asks to debug test flakiness.4---56# Flaky Test Diagnoser78Runs competing-hypothesis experiments to isolate the true root cause of a flaky test. Produces a diagnosis with reproducible evidence — or an honest inconclusive label. Never promotes a guess to "root cause."910This is a tracing skill, not a fixer. The goal is to explain **why** the test is intermittent with falsifiable evidence, not to jump into patching code.1112## Execution contracts1314**Subagent watchdog:** any time this skill spawns a test-running subagent via `run_in_background=true` (multi-run protocols, ordering bisection, distinguishing experiments), it MUST be armed with a staleness monitor per [`_shared/subagent-watchdog.md`](../_shared/subagent-watchdog.md). Use Flavor A (Monitor tail per spawn) with thresholds `STALE=15 min`, `HUNG=45 min` — test suites can legitimately take a long time, especially with N≥10 reruns, but a 45-min silent window is pathological. `TaskOutput` status reports PID liveness only; output-file mtime is the ground truth for progress. Contract inheritance: `timed_out_heartbeat` joins this skill's termination vocabulary as a peer of `blocked_by_environment` (watchdog kill is a different failure class — the test-runner subagent hung, not the test environment). A watchdog-killed experiment contributes zero runs to the N≥10 count; the skill must retry or escalate.1516## Workflow17181. **Bootstrap run state** — create `flaky-diag-{run_id}/` in CWD with `state.json`, `hypotheses/`, `runs/`, `experiments/`. See [STATE.md](STATE.md).192. **Detect test runner** — identify the project's test framework and runner command from config files. See [RUNNERS.md](RUNNERS.md).203. **Confirm flakiness** — run the target test N>=10 times in isolation, record pass/fail per run, compute fail rate. Record each run under `runs/`. See [EXPERIMENTS.md](EXPERIMENTS.md).214. **Generate competing hypotheses** — produce >=3 deliberately different hypotheses from the 6 categories (ORDERING, TIMING, SHARED_STATE, EXTERNAL_DEPENDENCY, RESOURCE_LEAK, NON_DETERMINISM). Each hypothesis gets its own file in `hypotheses/` following the schema in [FORMAT.md](FORMAT.md). See [ANALYSIS.md](ANALYSIS.md) for category signatures.225. **Gather evidence for AND against each hypothesis** — run the structured experiments (isolation, ordering bisection, timing, environment, code reads) and record findings as `evidence_for` / `evidence_against` entries per hypothesis. See [EXPERIMENTS.md](EXPERIMENTS.md).236. **Design a distinguishing experiment per surviving hypothesis** — an experiment whose outcome uniquely discriminates that hypothesis from its rivals. An experiment that "merely supports" all hypotheses does NOT count. See [FORMAT.md](FORMAT.md).247. **Run distinguishing experiments** — record outcomes under `experiments/`. Down-rank hypotheses whose distinctive prediction failed. See [GOLDEN-RULES.md](GOLDEN-RULES.md) for the falsifiability gate.258. **Rebuttal round** — let the second-ranked hypothesis present its best rebuttal against the leader. The leader must answer with evidence, not assertion.269. **Attempt red-green-red demonstration** — if a leader has been selected, describe the minimal fix and have the user apply it (or apply it in an experimental branch). Rerun the multi-run protocol (N>=10). Only label `root_cause_isolated_with_repro` if the red-green-red cycle succeeds.2710. **Emit structured output and diagnosis report** — each hypothesis gets a `STRUCTURED_OUTPUT_START` / `STRUCTURED_OUTPUT_END` block. Final report follows [REPORT.md](REPORT.md).2829## Honest termination labels3031Pick exactly one. Never invent synonyms.3233| Label | Meaning | Requires |34|---|---|---|35| `root_cause_isolated_with_repro` | Single hypothesis confirmed; fix demonstrated with red-green-red across N>=10 runs | Distinguishing experiment passed; all rivals empirically falsified; fix verified |36| `narrowed_to_N_hypotheses` | Could not discriminate between N surviving hypotheses; additional probes recommended | Each surviving hypothesis has evidence_for; no distinguishing experiment available within budget |37| `inconclusive_after_N_runs` | Could not reproduce flakiness or no hypothesis gathered sufficient evidence | N>=20 runs executed; no consistent signal |38| `blocked_by_environment` | Cannot execute experiments (runner broken, missing deps, no shell access, tests require secrets) | Specific blocker identified with the exact error output |3940Never use: "diagnosed", "likely root cause", "probably", "should be", "root cause found" as a terminal label without a red-green-red demo. See [GOLDEN-RULES.md](GOLDEN-RULES.md).4142## Self-review checklist4344Before delivering the report, verify ALL:4546- [ ] State file exists at `flaky-diag-{run_id}/state.json` with `generation` >= 147- [ ] >=3 competing hypotheses were generated, each with its own file in `hypotheses/`48- [ ] Every hypothesis has both `evidence_for` AND `evidence_against` entries (never empty)49- [ ] Every hypothesis has an explicit `uncertainty_level` in {high, medium, low}50- [ ] Every hypothesis has a `distinguishing_experiment` field (or is explicitly marked "no distinguisher available within budget")51- [ ] Flakiness was confirmed: at least one fail AND at least one pass in N>=10 runs52- [ ] Isolation vs in-suite comparison completed (both were run)53- [ ] If a hypothesis is promoted to `root_cause_isolated_with_repro`: a red-green-red demonstration exists with N>=10 post-fix runs (all green)54- [ ] Report uses one of the 4 honest termination labels — never "diagnosed" without red-green-red55- [ ] Each hypothesis evaluation has a `STRUCTURED_OUTPUT_START` / `STRUCTURED_OUTPUT_END` block per [FORMAT.md](FORMAT.md)56- [ ] Anti-rationalization counter-table was consulted before finalizing the label ([GOLDEN-RULES.md](GOLDEN-RULES.md))5758## Golden rules5960Hard rules. See [GOLDEN-RULES.md](GOLDEN-RULES.md) for the full text, anti-rationalization counter-table, and the falsifiability gate.61621. **Never promote a hypothesis to root cause without a distinguishing experiment** that empirically rejects all rivals.632. **"Diagnosed" requires red-green-red.** Test fails → fix applied → test passes deterministically across N>=10 runs. Without it, the label is `narrowed_to_N_hypotheses`.643. **Always run isolation before ordering.** If the test fails in isolation, ordering analysis is irrelevant.654. **Bisect, never brute-force** when searching for an interfering test.665. **Capture exact commands verbatim.** Every experiment logs its shell command so the user can reproduce it.676. **Minimum 10 runs for any statistical claim.** Flaky tests can have fail rates under 10%.687. **Never modify test code during diagnosis** beyond temporary instrumentation that is reverted before the report is emitted.698. **Evidence for AND against every hypothesis.** A hypothesis with only supporting evidence is unfalsifiable and must be flagged.709. **Coordinator does not self-approve.** The hypothesis ranking is based on experiment outcomes recorded in files, not coordinator opinion.7110. **Structured output is the contract.** Per-hypothesis evaluations live between `STRUCTURED_OUTPUT_START`/`STRUCTURED_OUTPUT_END` markers; free-text commentary is ignored when computing the verdict.7273## Cancellation / resume7475If interrupted mid-run: on next invocation read `flaky-diag-{run_id}/state.json`, identify the highest-`generation` completed stage, and replay from the next stage. Never recompute completed experiments — trust the recorded run artifacts. See [STATE.md](STATE.md) for the replay protocol.7677## Reference files7879| File | Contents |80|------|----------|81| [RUNNERS.md](RUNNERS.md) | Test runner detection, command templates for pytest/jest/junit/go test/cargo test, and how to target a single test |82| [EXPERIMENTS.md](EXPERIMENTS.md) | Multi-run protocol, isolation protocol, ordering bisection algorithm, timing instrumentation, red-green-red demo protocol |83| [ANALYSIS.md](ANALYSIS.md) | Root cause categories, code pattern matching for flakiness signals, environment factor checklist, decision matrix |84| [FORMAT.md](FORMAT.md) | Per-hypothesis evaluation schema, `STRUCTURED_OUTPUT_START` markers, distinguishing-experiment template |85| [STATE.md](STATE.md) | `state.json` schema, per-stage state layout, resume protocol |86| [GOLDEN-RULES.md](GOLDEN-RULES.md) | Full golden rules text, falsifiability gate, anti-rationalization counter-table |87| [REPORT.md](REPORT.md) | Diagnosis report output template with honest termination labels |8889---9091## Execution routing (sagaflow-first — Contract 6)9293**Sagaflow is the default execution path.** The in-session workflow above is the FALLBACK, used only when the sagaflow worker is confirmed unavailable. See [`_shared/execution-model-contracts.md`](../_shared/execution-model-contracts.md) Contract 6.9495**Routing sequence (mandatory before any in-session work):**961. Run `sagaflow doctor`972. If healthy → launch via sagaflow below. Stop. Do not run in-session.983. If unhealthy → log `SAGAFLOW_UNAVAILABLE`, proceed with in-session fallback.99100**Launch command:**101```102Bash(103 run_in_background=true,104 command="sagaflow launch flaky-test-diagnoser --arg test='<TEST_ID>' --arg command='<RUN_CMD>' --arg n_runs=<N> --await"105)106```107108Both `test` and `command` are REQUIRED. Substitute `<TEST_ID>` with the test identifier (e.g. `tests/test_foo.py::test_bar`), `<RUN_CMD>` with the exact shell command that runs that test, and `<N>` with the stability-run count (default 10). The workflow writes `~/.sagaflow/runs/<run_id>/report.md` with pass/fail table, isolation+ordering results, timing analysis, top hypothesis with evidence, and proposed fix or mitigation.