--- name: diagnose description: > Use when the user says "diagnose this" or "debug this", reports something broken, throwing, or failing, or says something got slow. Not filing or triaging a reported bug -> `triage`. Not test-first feature work -> `tdd`. metadata: version: "1.0.1" --- # Diagnose A discipline for hard bugs. Skip phases only when explicitly justified. When exploring the codebase, use the domain glossary for a clear mental model of the relevant modules, and check ADRs in the area. ## Phase 1 — Build a feedback loop **This is the skill.** Everything else is mechanical. If you have a fast, deterministic, agent-runnable pass/fail signal for the bug, you will find the cause — bisection, hypothesis-testing, and instrumentation all just consume that signal. If you don't have one, no amount of staring at code will save you. Spend disproportionate effort here. **Be aggressive. Be creative. Refuse to give up.** **If you do not yet have such a signal, read `references/feedback-loops.md`** — ten ways to build one ordered by cost, and what to ask the user for when the bug resists reproduction entirely. **If you do have one, it is probably not sharp enough yet.** Make it faster and more deterministic, and make it assert on the exact symptom rather than "didn't crash" — a 30-second flaky loop is barely better than no loop. If it stays slow or intermittent after that, read that file's "Iterate on the loop itself" and "Intermittent bugs" sections. Do not proceed to Phase 2 until you have a loop you believe in. If you cannot build one, stop and say so explicitly, listing what you tried — never hypothesise without a signal. ## Phase 2 — Reproduce Run the loop. Watch the bug appear. Confirm: - [ ] The loop produces the failure mode the **user** described — not a different failure that happens to be nearby. Wrong bug = wrong fix. - [ ] The failure is reproducible across multiple runs. If it is intermittent, `references/feedback-loops.md` defines the rate high enough to debug against — go back to Phase 1 and raise it. - [ ] You have captured the exact symptom (error message, wrong output, slow timing) so later phases can verify the fix actually addresses it. Do not proceed until you reproduce the bug. ## Phase 3 — Hypothesise Generate **3–5 ranked hypotheses** before testing any of them. Single-hypothesis generation anchors on the first plausible idea. Each hypothesis must be **falsifiable**: state the prediction it makes. > Format: "If is the cause, then will make the bug disappear / will make it worse." If you cannot state the prediction, the hypothesis is a vibe — discard or sharpen it. **Show the ranked list to the user before testing.** They often have domain knowledge that re-ranks instantly ("we just deployed a change to #3"), or know hypotheses they've already ruled out. Cheap checkpoint, big time saver. Don't block on it — proceed with your ranking if the user is AFK. ## Phase 4 — Instrument Each probe must map to a specific prediction from Phase 3. **Change one variable at a time.** Tool preference: 1. **Debugger / REPL inspection** if the env supports it. One breakpoint beats ten logs. 2. **Targeted logs** at the boundaries that distinguish hypotheses. 3. Never "log everything and grep". **Tag every debug log** with a unique prefix, e.g. `[DEBUG-a4f2]`. Cleanup at the end becomes a single grep. Untagged logs survive; tagged logs die. **Perf branch.** For performance regressions, logs are usually wrong. Instead: establish a baseline measurement (timing harness, `performance.now()`, profiler, query plan), then bisect. Measure first, fix second. ## Phase 5 — Fix + regression test Write the regression test **before the fix** — but only at a **correct seam**: one where the test exercises the real bug pattern as it occurs at the call site. If the available seam looks too shallow, or you cannot tell whether it is, read `references/regression-seams.md`. **If no correct seam exists, that itself is the finding.** Note it and carry it into Phase 6 — the architecture is preventing the bug from being locked down. At a correct seam: 1. Turn the Phase 1 loop into a failing test at that seam, narrowed to the symptom captured in Phase 2. 2. Watch it fail. 3. Apply the fix. 4. Watch it pass. 5. Re-run the Phase 1 feedback loop against the original, un-narrowed scenario. ## Phase 6 — Cleanup + post-mortem Required before declaring done: - [ ] Original repro no longer reproduces (re-run the Phase 1 loop) - [ ] Regression test passes (or absence of seam is documented) - [ ] All `[DEBUG-...]` instrumentation removed (`grep` the prefix) - [ ] Throwaway prototypes deleted (or moved to a clearly-marked debug location) - [ ] The hypothesis that turned out correct is stated in the commit / PR message — so the next debugger learns **Then ask: what would have prevented this bug?** If the answer involves architectural change (no good test seam, tangled callers, hidden coupling) hand off to the `/improve-codebase-architecture` skill with the specifics. Make the recommendation **after** the fix is in, not before — you have more information now than when you started.