Body 1126 -> 808 words, clearing the FAIL tier, and description 290 -> 220 chars. Phase 1's depth moves to references/feedback-loops.md; the six-phase spine stays in the body, since a linear procedure is not a dispatch case. The description rewrite was not originally in scope, which was an error: adding a mandatory boundary clause to a 290-char description cannot land under 400. The dropped capability chain was also inaccurate -- it named 'minimise' as a phase that does not exist while omitting the one phase the body calls 'This is the skill'. A clean-context audit found no text lost but three reachability defects, all fixed: content stranded behind an inverted trigger, Phase 2's reproduction-rate threshold defined only in a file that path never loaded, and a script path that did not resolve from the file carrying it. The two reference files are merged into one, since the split is what created the first two. Refs #99
90 lines
5.0 KiB
Markdown
90 lines
5.0 KiB
Markdown
---
|
||
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`.
|
||
---
|
||
|
||
# Diagnose
|
||
|
||
A discipline for hard bugs. Skip phases only when explicitly justified.
|
||
|
||
When exploring the codebase, use the project's domain glossary to get a clear mental model of the relevant modules, and check ADRs in the area you're touching.
|
||
|
||
## 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.**
|
||
|
||
Read `references/feedback-loops.md` — even if you already have a signal. Ten ways to build a loop ordered by cost, how to sharpen the one you have, and what to do when the bug resists reproduction. An unsharpened loop is usually not good enough yet.
|
||
|
||
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 <X> is the cause, then <changing Y> will make the bug disappear / <changing Z> 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 if there is a **correct seam** for it.
|
||
|
||
A correct seam is one where the test exercises the **real bug pattern** as it occurs at the call site. If the only available seam is too shallow (single-caller test when the bug needs multiple callers, unit test that can't replicate the chain that triggered the bug), a regression test there gives false confidence.
|
||
|
||
**If no correct seam exists, that itself is the finding.** Note it. The codebase architecture is preventing the bug from being locked down. Flag this for the next phase.
|
||
|
||
If a correct seam exists:
|
||
|
||
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.
|