Debugging Wizard
Load Order
Read shared-kernel/SKILL.md first.
Core Principle
Do not guess. Reproduce, observe, hypothesize, falsify, fix, verify.
Debugging is applied epistemology. Every shortcut is a debt the next bug collects.
Debugging Protocol
Phase 1 — Reproduce
You do not have a bug until you can trigger it on demand.
- Capture the exact input that produced the failure
- Capture the environment: OS, runtime version, library versions, env vars
- Capture the observable: error message (full, not summary), stack trace, logs, screenshot
- Build the smallest script or test that reliably reproduces
If the bug is intermittent:
- Run the reproduction 100 times and measure the failure rate
- Capture all artifacts from every run, not just failures
- Look for shared state: caches, connection pools, file handles, environment
Phase 2 — Observe
Read the evidence you have. Do not theorize past it.
- Full stack trace, top to bottom — the framework frames matter as much as your frames
- Full error message, including nested
cause chains
- Logs from ±5 seconds around the failure
- Metrics from the same window — CPU, memory, GC, connection count
Phase 3 — Hypothesize
Form the cheapest falsifiable hypothesis first.
- "It fails because X" → what would prove X wrong in under 5 minutes?
- Run that test before anything bigger
- If X is falsified, move to the next hypothesis — do not try to salvage X
Phase 4 — Bisect
When a known-good version exists:
git bisect start
git bisect bad HEAD
git bisect good v1.2.3
git bisect run ./reproduce.sh # script exits 0 if good, non-zero if bad
Bisect collapses "somewhere in 400 commits" to "this one commit" in log₂(n) steps.
Phase 5 — Instrument
If observation is insufficient, add signal:
- Structured logs at every decision point in the code path
- Tracepoints at function entry/exit with arguments and return values
perf / dtrace / eBPF / py-spy for sampling profiles
strace / dtruss for syscall-level observation
- Wireshark / tcpdump for network-level observation
Phase 6 — Fix Root Cause
Not the symptom. The cause.
- If a null pointer crashed the service, do not wrap it in a null check and move on — find why it was null.
- If a test is flaky, do not add
retry(3) — find why it is flaky.
- If a deploy failed, do not
kubectl delete pod — find why it failed.
Phase 7 — Verify
- Add a regression test that fails before the fix and passes after
- Deploy the fix to staging, reproduce the original trigger, confirm the failure does not recur
- Monitor production metrics for 24 hours after the fix lands
Diagnostic Tool Matrix
| Symptom |
First Tool |
Second Tool |
| High CPU |
Sampling profiler (perf, Instruments, py-spy, async-profiler) |
Flame graph |
| Memory leak |
Heap profiler (Instruments, pprof, valgrind, Chrome DevTools) |
Heap diff between two snapshots |
| Deadlock |
Thread dump (jstack, py-spy dump, lldb thread backtrace all) |
Lock graph analysis |
| Slow DB query |
EXPLAIN ANALYZE |
pg_stat_statements / slow query log |
| Flaky test |
Run 100×, capture artifacts |
Check shared state, test isolation, race conditions |
| Network issue |
tcpdump / Wireshark |
mtr / traceroute + timestamp correlation |
| Crash / segfault |
Core dump + debugger (gdb, lldb) |
ASAN / UBSAN / valgrind |
| OOM kill |
Container memory limit vs actual usage |
Heap profile at time of kill |
| Race condition |
ThreadSanitizer (TSAN) |
Deterministic replay (rr, Pernosco) |
| Intermittent 500s |
Log correlation by request ID |
Distributed trace by trace ID |
Common Failure Signatures
"It works on my machine"
- Environment variable present locally, missing in CI
- File in local checkout, missing from container image
- Different OS path separator (
\ vs /)
- Different locale / timezone / encoding
- Different library version
"It works the first time"
- Caching masking subsequent failures
- Connection pool exhausted after N requests
- File handle leak reaching ulimit
- Token expiration
"It works in isolation but fails under load"
- Connection pool too small
- Lock contention
- GC pressure under allocation rate
- Thread pool starvation
"It used to work"
- Dependency auto-upgraded (check lockfile)
- Data changed shape (schema migration, new edge case)
- Infrastructure changed (certificate rotation, DNS change, IAM change)
git bisect is the answer
Non-Negotiables
- Do not fix what you cannot reproduce
- Do not ship the fix without a regression test
- Do not close the ticket until root cause is documented
- State the root cause in the postmortem, not the symptom that went away
- If the fix is "I restarted the service," the investigation is not done
Anti-Patterns to Refuse
| Anti-Pattern |
Why It Fails |
except: pass |
Swallows the signal you need |
retry(100) on flaky test |
Hides the race, does not fix it |
sleep(5) to "let things settle" |
Masks timing bug that will return |
| Commenting out failing assertion |
The assertion was correct; the code is wrong |
// TODO: figure out why merged to main |
Investigation debt compounds |
| "Works now, must have been a fluke" |
Heisenbugs do not self-heal |
Postmortem Template (for production incidents)
INCIDENT: [short name]
DATE: [UTC]
DURATION: [detection to mitigation]
IMPACT: [users affected, revenue, SLO burn]
TIMELINE:
HH:MM — event
HH:MM — event
ROOT CAUSE:
[the actual cause, not the trigger]
CONTRIBUTING FACTORS:
- [what made it worse or delayed detection]
RESOLUTION:
[what was done to stop the bleeding]
ACTION ITEMS:
- [owner] [action] [due date]
LESSONS LEARNED:
[what the team now knows]
Reference Links to Verify
1---2name: debugging-wizard3description: Use for diagnosing bugs, crashes, memory leaks, race conditions, deadlocks, performance regressions, production incidents, and flaky tests — stack trace analysis, core dump inspection, heap profiling, flame graphs, distributed tracing, log correlation, reproducing intermittent failures, bisecting regressions via git bisect, and root-cause analysis. Triggers on mentions of bug, crash, leak, slow, flaky, hang, deadlock, segfault, OOM, stack trace, panic, exception, "why is this broken", "what's wrong with", or any diagnostic framing.4---56# Debugging Wizard78## Load Order9Read `shared-kernel/SKILL.md` first.1011## Core Principle12**Do not guess. Reproduce, observe, hypothesize, falsify, fix, verify.**1314Debugging is applied epistemology. Every shortcut is a debt the next bug collects.1516## Debugging Protocol1718### Phase 1 — Reproduce19You do not have a bug until you can trigger it on demand.201. Capture the exact input that produced the failure212. Capture the environment: OS, runtime version, library versions, env vars223. Capture the observable: error message (full, not summary), stack trace, logs, screenshot234. Build the smallest script or test that reliably reproduces2425If the bug is intermittent:26- Run the reproduction 100 times and measure the failure rate27- Capture all artifacts from every run, not just failures28- Look for shared state: caches, connection pools, file handles, environment2930### Phase 2 — Observe31Read the evidence you have. Do not theorize past it.32- Full stack trace, top to bottom — the framework frames matter as much as your frames33- Full error message, including nested `cause` chains34- Logs from ±5 seconds around the failure35- Metrics from the same window — CPU, memory, GC, connection count3637### Phase 3 — Hypothesize38Form the **cheapest falsifiable hypothesis** first.39- "It fails because X" → what would prove X wrong in under 5 minutes?40- Run that test before anything bigger41- If X is falsified, move to the next hypothesis — do not try to salvage X4243### Phase 4 — Bisect44When a known-good version exists:4546```bash47git bisect start48git bisect bad HEAD49git bisect good v1.2.350git bisect run ./reproduce.sh # script exits 0 if good, non-zero if bad51```5253Bisect collapses "somewhere in 400 commits" to "this one commit" in log₂(n) steps.5455### Phase 5 — Instrument56If observation is insufficient, add signal:57- Structured logs at every decision point in the code path58- Tracepoints at function entry/exit with arguments and return values59- `perf` / `dtrace` / `eBPF` / `py-spy` for sampling profiles60- `strace` / `dtruss` for syscall-level observation61- Wireshark / tcpdump for network-level observation6263### Phase 6 — Fix Root Cause64Not the symptom. The cause.65- If a null pointer crashed the service, do not wrap it in a null check and move on — find why it was null.66- If a test is flaky, do not add `retry(3)` — find why it is flaky.67- If a deploy failed, do not `kubectl delete pod` — find why it failed.6869### Phase 7 — Verify70- Add a regression test that fails before the fix and passes after71- Deploy the fix to staging, reproduce the original trigger, confirm the failure does not recur72- Monitor production metrics for 24 hours after the fix lands7374## Diagnostic Tool Matrix7576| Symptom | First Tool | Second Tool |77|---|---|---|78| High CPU | Sampling profiler (`perf`, Instruments, `py-spy`, `async-profiler`) | Flame graph |79| Memory leak | Heap profiler (Instruments, `pprof`, `valgrind`, Chrome DevTools) | Heap diff between two snapshots |80| Deadlock | Thread dump (`jstack`, `py-spy dump`, lldb `thread backtrace all`) | Lock graph analysis |81| Slow DB query | `EXPLAIN ANALYZE` | `pg_stat_statements` / slow query log |82| Flaky test | Run 100×, capture artifacts | Check shared state, test isolation, race conditions |83| Network issue | `tcpdump` / Wireshark | mtr / traceroute + timestamp correlation |84| Crash / segfault | Core dump + debugger (`gdb`, `lldb`) | ASAN / UBSAN / valgrind |85| OOM kill | Container memory limit vs actual usage | Heap profile at time of kill |86| Race condition | ThreadSanitizer (TSAN) | Deterministic replay (rr, Pernosco) |87| Intermittent 500s | Log correlation by request ID | Distributed trace by trace ID |8889## Common Failure Signatures9091### "It works on my machine"92- Environment variable present locally, missing in CI93- File in local checkout, missing from container image94- Different OS path separator (`\` vs `/`)95- Different locale / timezone / encoding96- Different library version9798### "It works the first time"99- Caching masking subsequent failures100- Connection pool exhausted after N requests101- File handle leak reaching ulimit102- Token expiration103104### "It works in isolation but fails under load"105- Connection pool too small106- Lock contention107- GC pressure under allocation rate108- Thread pool starvation109110### "It used to work"111- Dependency auto-upgraded (check lockfile)112- Data changed shape (schema migration, new edge case)113- Infrastructure changed (certificate rotation, DNS change, IAM change)114- `git bisect` is the answer115116## Non-Negotiables117- Do not fix what you cannot reproduce118- Do not ship the fix without a regression test119- Do not close the ticket until root cause is documented120- State the root cause in the postmortem, not the symptom that went away121- If the fix is "I restarted the service," the investigation is not done122123## Anti-Patterns to Refuse124125| Anti-Pattern | Why It Fails |126|---|---|127| `except: pass` | Swallows the signal you need |128| `retry(100)` on flaky test | Hides the race, does not fix it |129| `sleep(5)` to "let things settle" | Masks timing bug that will return |130| Commenting out failing assertion | The assertion was correct; the code is wrong |131| `// TODO: figure out why` merged to main | Investigation debt compounds |132| "Works now, must have been a fluke" | Heisenbugs do not self-heal |133134## Postmortem Template (for production incidents)135136```137INCIDENT: [short name]138DATE: [UTC]139DURATION: [detection to mitigation]140IMPACT: [users affected, revenue, SLO burn]141142TIMELINE:143 HH:MM — event144 HH:MM — event145146ROOT CAUSE:147 [the actual cause, not the trigger]148149CONTRIBUTING FACTORS:150 - [what made it worse or delayed detection]151152RESOLUTION:153 [what was done to stop the bleeding]154155ACTION ITEMS:156 - [owner] [action] [due date]157158LESSONS LEARNED:159 [what the team now knows]160```161162## Reference Links to Verify163- https://brendangregg.com/ (performance and systems debugging)164- https://sre.google/workbook/postmortem-culture/ (blameless postmortem doctrine)165- Platform-specific: Apple Instruments docs, Linux perf wiki, Chrome DevTools docs