Systematic Debugging
Iron Law: NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST.
Phase 1: Reproduce & Isolate
- Reproduce the failure reliably before anything else
- Isolate the smallest failing case
- If you can't reproduce it, you can't fix it
- Record the exact command, input, and output that demonstrates the failure
Phase 2: Gather Evidence
Before forming hypotheses, collect facts:
- Read the actual code path from input to failure point. Don't guess from memory.
- Check recent changes:
git log --oneline -10 -- <file>
- Add temporary logging or assertions at key points to trace data flow
- Collect the full error message, stack trace, and relevant log output
- If a stack trace exists, trace backward through the call chain to the origin
Known bug patterns — check these first:
| Pattern |
Signature |
| Race condition |
Intermittent failure, timing-dependent, works in debugger |
| Nil/null propagation |
undefined is not a function, NoMethodError, NoneType |
| State corruption |
Works first time, fails on second; stale data after mutation |
| Integration failure |
Works in isolation, fails with real dependency (DB, API, queue) |
| Configuration drift |
Works locally, fails in CI/staging; env var mismatch |
| Stale cache |
Old behavior persists after code change; hard refresh fixes it |
| Off-by-one / boundary |
Fails at 0, 1, max, or empty input; pagination edge cases |
If the bug matches a known pattern, state which one and focus investigation there.
Phase 3: Hypothesize & Test
- Form a specific hypothesis: "The root cause is X because evidence Y"
- Predict the outcome BEFORE running each test
- Test one variable at a time
- If your prediction is wrong, update your mental model — don't just try the next thing
"5 Whys" drill-down — when the cause isn't obvious, ask why iteratively:
- Why did the request fail? → The handler returned null
- Why did it return null? → The query returned no rows
- Why did the query return no rows? → The ID was from a different tenant
- Why was the ID from a different tenant? → The session wasn't scoped to tenant
- Why wasn't the session scoped? → Root cause: middleware ordering
Keep asking why until you reach something you can fix directly.
Phase 4: Fix & Verify
- Write a regression test that fails without the fix and passes with it. This is not optional.
- Fix the root cause, not the symptom
- Keep the diff minimal — only change what's needed to fix the bug
- Run the original failing test to confirm the fix
- Run the full test suite to check for regressions
Three-Strikes Rule
If 3 hypotheses have failed, STOP. You are likely wrong about the root cause.
- Go back to Phase 2 and re-read the code path from scratch
- Question your assumptions about the architecture
- Use AskUserQuestion to escalate: describe what you've tried, what you've ruled out, and where you're stuck
- Optionally WebSearch for the error message or pattern — someone may have hit this before
Escalation is not failure. Bad work is worse than no work. Stop guessing.
Red Flags
If you catch yourself thinking any of these, STOP:
- "Let me just try..." — You're guessing, not tracing.
- "Maybe if I..." — Form a hypothesis and predict the outcome first.
- "This might work..." — Why would it work? What's your evidence?
- "It's probably this..." — Probably based on what? State the evidence.
Anti-Rationalization Table
| Excuse |
Reality |
| "Let me try a quick fix first" |
Quick fixes without root cause analysis create new bugs. |
| "I know what the problem is" |
Then state your hypothesis and predict the test outcome. |
| "It works now after my change" |
Correlation is not causation. Verify your fix addresses the root cause. |
| "The test is flaky, not my code" |
Reproduce it 3 times. If it fails 2/3, it's your code. |
| "I'll add better error handling" |
Error handling hides bugs. Fix the cause, not the symptom. |
Debug Report
After resolution, output a structured report:
═══════════════════════════════════════════
DEBUG REPORT
═══════════════════════════════════════════
Status: DONE / DONE_WITH_CONCERNS / BLOCKED
Symptom: [what was observed]
Root Cause: [what was actually wrong]
Pattern: [known pattern match, if any]
Fix: [what was changed and why]
Regression Test: [test name and what it verifies]
Evidence: [how you confirmed the fix addresses the root cause]
Hypotheses Tested: N (list failed ones if >1)
═══════════════════════════════════════════
- DONE — Root cause identified, fix applied, regression test passes, full suite green
- DONE_WITH_CONCERNS — Fixed, but related issues discovered. Note them for follow-up.
- BLOCKED — Cannot resolve. Document what was tried and what's needed to proceed.
If the root cause was non-obvious, save debugging insights to auto-memory.
Evolved from obra/superpowers systematic-debugging skill with patterns from garrytan/gstack and community best practices.
1---2name: debug3description: Use when facing a bug, test failure, or unexpected behavior that isn't immediately obvious4---56# Systematic Debugging78**Iron Law**: NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST.910## Phase 1: Reproduce & Isolate1112- Reproduce the failure reliably before anything else13- Isolate the smallest failing case14- If you can't reproduce it, you can't fix it15- Record the exact command, input, and output that demonstrates the failure1617## Phase 2: Gather Evidence1819Before forming hypotheses, collect facts:2021- Read the actual code path from input to failure point. Don't guess from memory.22- Check recent changes: `git log --oneline -10 -- <file>`23- Add temporary logging or assertions at key points to trace data flow24- Collect the full error message, stack trace, and relevant log output25- If a stack trace exists, trace backward through the call chain to the origin2627**Known bug patterns — check these first:**2829| Pattern | Signature |30|---------|-----------|31| Race condition | Intermittent failure, timing-dependent, works in debugger |32| Nil/null propagation | `undefined is not a function`, `NoMethodError`, `NoneType` |33| State corruption | Works first time, fails on second; stale data after mutation |34| Integration failure | Works in isolation, fails with real dependency (DB, API, queue) |35| Configuration drift | Works locally, fails in CI/staging; env var mismatch |36| Stale cache | Old behavior persists after code change; hard refresh fixes it |37| Off-by-one / boundary | Fails at 0, 1, max, or empty input; pagination edge cases |3839If the bug matches a known pattern, state which one and focus investigation there.4041## Phase 3: Hypothesize & Test4243- Form a specific hypothesis: "The root cause is X because evidence Y"44- Predict the outcome BEFORE running each test45- Test one variable at a time46- If your prediction is wrong, update your mental model — don't just try the next thing4748**"5 Whys" drill-down** — when the cause isn't obvious, ask why iteratively:491. Why did the request fail? → The handler returned null502. Why did it return null? → The query returned no rows513. Why did the query return no rows? → The ID was from a different tenant524. Why was the ID from a different tenant? → The session wasn't scoped to tenant535. Why wasn't the session scoped? → **Root cause: middleware ordering**5455Keep asking why until you reach something you can fix directly.5657## Phase 4: Fix & Verify58591. **Write a regression test** that fails without the fix and passes with it. This is not optional.602. Fix the root cause, not the symptom613. Keep the diff minimal — only change what's needed to fix the bug624. Run the original failing test to confirm the fix635. Run the full test suite to check for regressions6465## Three-Strikes Rule6667If 3 hypotheses have failed, STOP. You are likely wrong about the root cause.6869- Go back to Phase 2 and re-read the code path from scratch70- Question your assumptions about the architecture71- Use AskUserQuestion to escalate: describe what you've tried, what you've ruled out, and where you're stuck72- Optionally WebSearch for the error message or pattern — someone may have hit this before7374**Escalation is not failure.** Bad work is worse than no work. Stop guessing.7576## Red Flags7778If you catch yourself thinking any of these, STOP:7980- "Let me just try..." — You're guessing, not tracing.81- "Maybe if I..." — Form a hypothesis and predict the outcome first.82- "This might work..." — Why would it work? What's your evidence?83- "It's probably this..." — Probably based on what? State the evidence.8485## Anti-Rationalization Table8687| Excuse | Reality |88|--------|---------|89| "Let me try a quick fix first" | Quick fixes without root cause analysis create new bugs. |90| "I know what the problem is" | Then state your hypothesis and predict the test outcome. |91| "It works now after my change" | Correlation is not causation. Verify your fix addresses the root cause. |92| "The test is flaky, not my code" | Reproduce it 3 times. If it fails 2/3, it's your code. |93| "I'll add better error handling" | Error handling hides bugs. Fix the cause, not the symptom. |9495## Debug Report9697After resolution, output a structured report:9899```100═══════════════════════════════════════════101DEBUG REPORT102═══════════════════════════════════════════103Status: DONE / DONE_WITH_CONCERNS / BLOCKED104Symptom: [what was observed]105Root Cause: [what was actually wrong]106Pattern: [known pattern match, if any]107Fix: [what was changed and why]108Regression Test: [test name and what it verifies]109Evidence: [how you confirmed the fix addresses the root cause]110Hypotheses Tested: N (list failed ones if >1)111═══════════════════════════════════════════112```113114- **DONE** — Root cause identified, fix applied, regression test passes, full suite green115- **DONE_WITH_CONCERNS** — Fixed, but related issues discovered. Note them for follow-up.116- **BLOCKED** — Cannot resolve. Document what was tried and what's needed to proceed.117118If the root cause was non-obvious, save debugging insights to auto-memory.119120*Evolved from obra/superpowers systematic-debugging skill with patterns from garrytan/gstack and community best practices.*