| name | debug |
| description | Use when diagnosing a failure — a failing or flaky test, a stack trace, a crash, a CI or lint error — "why is this failing", "find the bug", "debug this", "it crashes when", "works on my machine". Not for writing new features or fixing a bug whose cause is already known (build).
|
Debug — Hypothesis Before Fix
Diagnose methodically. Each speculative edit moves the system from a known state to an
unknown one; the harder the bug, the stricter the loop.
The loop
- Reproduce. Make the failure happen on demand — ideally as a failing test. Can't
reproduce → don't ship a speculative fix: add instrumentation to capture it next time,
or ask for environment/input/steps, and say plainly that it isn't reproduced.
- Isolate. Shrink to the smallest input that triggers it — binary-search the input;
git bisect the history if it used to work.
- Hypothesize testably. First read the error message word by word — it usually names
the file, line, and cause. Then write 2–3 hypotheses in the form "the bug is at
file:line because implies "; test the cheapest first.
- Probe. One smallest experiment per hypothesis — a log line, an assertion, a
narrowed test. One change at a time. Probes are temporary: remove them when done.
- Fix at the root cause and verify. The stack trace points at the symptom; trace the
wrong value upstream to where it originated and fix there. The regression test fails
before the fix and passes after; re-run the full suite and the original reproduction.
Intermittent bug → loop the test (e.g. 100×) before declaring victory.
Error-handling rule
When the user says "make it not crash" / "stop it taking down the server", they are asking
you to make the failure survivable and detectable — never to suppress it. A fix where
the operation silently looks like it succeeded (order unmarked, null nobody checks) is a
worse bug than the crash. If the fix catches an error, the failure must stay detectable
AND the affected state must stay consistent: log it, mark the affected record/operation as failed (not left half-done),
and either raise a domain error a caller is expected to handle or return a result the
caller checks. Test the failure path too — a test that only covers the happy path can't
tell a fix from a swallow. A silent catch {} / return null / pass trades a loud
crash for silent corruption — that is not a fix. The shape of a real catch:
catch (e) { log.error(e); markFailed(record); return { ok: false, error: e }; } — and
the caller checks it. Throwing a domain error (OperationFailedError) instead is right
only when a caller is set up to catch it. If you find yourself typing catch {} or
catch (e) { return null; }, delete it; that is the bug, not the fix.
When stuck (~1 hour, or the user says "still failing")
Never repeat a suggestion that was already tried — each next step must produce NEW
information. For environment-specific failures (CI-only, prod-only, "works on my
machine"): capture artifacts from where it fails (logs, recordings, core state), reproduce
that environment locally, isolate the difference, or bisect. More sleeps and bigger
timeouts are not diagnostics. If genuinely out of moves, stop: re-read the original report
— are you debugging what was actually reported? — and hand off the note below filled in as
far as you verifiably got, ending with the one question that would branch the search.
Right-sizing
An obvious one-line bug with an obvious cause doesn't need the full loop. But after two
speculative edits with no traction, you are in a hard bug: return to step 1.
Delegated mode (running as a subagent)
Run the loop with the tools you have; return the note in one response, filled to wherever
you verifiably got. A confirmed root cause without a fix is a valid result — say so.
Output — debugging note
## Bug: <one line>
Reproduction: <command or test that triggers it, or "NOT REPRODUCED: <why>">
Isolated to: <smallest input / commit range>
Hypotheses tested: <each → confirmed / rejected, with evidence>
Root cause: <file:line + why>
Fix: <the minimal change, at the cause not the symptom>
Regression test: <name; failed before fix, passes after>
Suite: <result verbatim>
Next: build (fix is nontrivial) | plan (it's a design flaw) | done
Checks
| If you are about to… | Instead |
|---|
| Make a speculative edit "to see if it helps" | That's guess-and-check. Reproduce and hypothesize first. |
| Fix at the exact line the stack trace names | That's the symptom location; trace the bad value upstream. |
| Wrap the failing call in try/catch to make the error go away | You're hiding the bug. Diagnose first. |
| Declare an intermittent bug fixed after one green run | Loop the test before declaring victory. |