Systematic Debugging
Adapted from the superpowers plugin (MIT).
Overview
Random fixes waste time and create new bugs. Quick patches mask underlying issues.
Core principle: ALWAYS find root cause before attempting fixes. Symptom fixes are failure.
Violating the letter of this process is violating the spirit of debugging.
The Iron Law
NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
If you haven't completed Phase 1, you cannot propose fixes.
When to Use
Use for ANY technical issue:
- Test failures
- Bugs in production
- Unexpected behavior
- Performance problems
- Build failures
- Integration issues
Use this ESPECIALLY when:
- Under time pressure (emergencies make guessing tempting)
- "Just one quick fix" seems obvious
- You've already tried multiple fixes
- Previous fix didn't work
- You don't fully understand the issue
Don't skip when:
- Issue seems simple (simple bugs have root causes too)
- You're in a hurry (rushing guarantees rework)
- Manager wants it fixed NOW (systematic is faster than thrashing)
The Four Phases
You MUST complete each phase before proceeding to the next.
Phase 1: Root Cause Investigation
BEFORE attempting ANY fix:
Read Error Messages Carefully
- Don't skip past errors or warnings
- They often contain the exact solution
- Read stack traces completely
- Note line numbers, file paths, error codes
Reproduce Consistently
- Can you trigger it reliably?
- What are the exact steps?
- Does it happen every time?
- If not reproducible → gather more data, don't guess
Check Recent Changes
- What changed that could cause this?
- Git diff, recent commits
- New dependencies, config changes
- Environmental differences
Gather Evidence in Multi-Component Systems
WHEN system has multiple components (CI → build → signing, API → service → database):
BEFORE proposing fixes, add diagnostic instrumentation:
For EACH component boundary:
- Log what data enters component
- Log what data exits component
- Verify environment/config propagation
- Check state at each layer
Run once to gather evidence showing WHERE it breaks
THEN analyze evidence to identify failing component
THEN investigate that specific component
Example (multi-layer system):
# Layer 1: Workflow
echo "=== Secrets available in workflow: ==="
echo "IDENTITY: ${IDENTITY:+SET}${IDENTITY:-UNSET}"
# Layer 2: Build script
echo "=== Env vars in build script: ==="
env | grep IDENTITY || echo "IDENTITY not in environment"
# Layer 3: Signing script
echo "=== Keychain state: ==="
security list-keychains
security find-identity -v
# Layer 4: Actual signing
codesign --sign "$IDENTITY" --verbose=4 "$APP"
This reveals: Which layer fails (secrets → workflow ✓, workflow → build NO)
Trace Data Flow
WHEN error is deep in call stack:
See root-cause-tracing.md in this directory for the complete backward tracing technique.
Quick version:
- Where does bad value originate?
- What called this with bad value?
- Keep tracing up until you find the source
- Fix at source, not at symptom
Phase 2: Pattern Analysis
Find the pattern before fixing:
Offload the bounded search here to a sonnet finder subagent (locate similar working code, read
the reference implementation fully, return only the relevant slices) - it keeps large reference files
out of your context. Keep hypothesis/root-cause reasoning in the main (opus) agent - this deep reasoning is
capability-sensitive, so if the session is on a lesser tier offer switch-model-or-continue (the main
agent cannot self-switch its model), or hand the root-cause step to a pinned opus subagent. Multiple
independent failures -> bitranox:process-agents-dispatching-parallel. Tiers: "Concrete tiers" in
bitranox:process-agents-subagent-driven-development.
Find Working Examples
- Locate similar working code in same codebase
- What works that's similar to what's broken?
Compare Against References
- If implementing pattern, read reference implementation COMPLETELY
- Don't skim - read every line
- Understand the pattern fully before applying
Identify Differences
- What's different between working and broken?
- List every difference, however small
- Don't assume "that can't matter"
Understand Dependencies
- What other components does this need?
- What settings, config, environment?
- What assumptions does it make?
Phase 3: Hypothesis and Testing
Scientific method:
Form Single Hypothesis
- State clearly: "I think X is the root cause because Y"
- Write it down
- Be specific, not vague
Test Minimally
- Make the SMALLEST possible change to test hypothesis
- One variable at a time
- Don't fix multiple things at once
- Run both arms from the SAME path. Comparing an old version against a new one by copying
one of them to a scratch directory moves a SECOND variable: a program that derives anything
from
__file__ or the working directory behaves differently when relocated, so the
difference you measure is the relocation rather than the change. The artifact of a relocated
arm tends to be the more flattering result, which is what makes it survive review. Swap the
versions IN PLACE (stash, checkout, or a symlink the program resolves identically) and keep
every path the same across arms.
Verify Before Continuing
- Did it work? Yes → Phase 4
- Didn't work? Form NEW hypothesis
- DON'T add more fixes on top
When You Don't Know
- Say "I don't understand X"
- Don't pretend to know
- Ask for help
- Research more
Closed-Source Peer: Escalate to Disassembly After the Second Dead Hypothesis
WHEN the system on the other side of the bug is a closed-source binary you cannot
read (a proprietary driver, firmware, appliance, or vendor tool) and you are testing
hypotheses about its behavior black-box:
- 1st hypothesis dies against measurement -> form a new one (the normal Phase 3 loop).
- 2nd hypothesis also dies against measurement -> STOP. Do not form a third
black-box guess. Disassemble the peer instead: a disassembler (for example
Ghidra) driven by its scripting/Python bridge, plus any public PDB or symbols the
vendor ships.
- Read the disassembly to learn the PROTOCOL or format needed for interoperability -
never to copy the implementation. Reverse-engineering for interoperability is the
sanctioned use here; lifting the vendor's code is not.
- Cite an address and a symbol for the conclusion, not "it must be doing X".
- A clean refutation of your hypothesis is an equally valid result - it closes the
question as surely as a confirmation does, so don't discard the disassembly pass
because it disproved you.
Why: each black-box hypothesis costs a full build-deploy-measure cycle. The
detail that decides the bug (a parser quirk, a field only read on the first record,
a byte offset) can exist nowhere in any public spec, so no amount of further
black-box experiment reaches it - only reading the code does.
Phase 4: Implementation
Fix the root cause, not the symptom:
Create Failing Test Case
- Simplest possible reproduction
- Automated test if possible
- One-off test script if no framework
- MUST have before fixing
- Use the
bitranox:process-test-driven-development skill for writing proper failing tests
Implement Single Fix
- Address the root cause identified
- ONE change at a time
- No "while I'm here" improvements
- No bundled refactoring
Verify Fix
- Test passes now?
- No other tests broken?
- Issue actually resolved?
If Fix Doesn't Work
- STOP
- Count: How many fixes have you tried?
- If < 3: Return to Phase 1, re-analyze with new information
- If ≥ 3: STOP and question the architecture (step 5 below)
- DON'T attempt Fix #4 without architectural discussion
If 3+ Fixes Failed: Question Architecture
Pattern indicating architectural problem:
- Each fix reveals new shared state/coupling/problem in different place
- Fixes require "massive refactoring" to implement
- Each fix creates new symptoms elsewhere
STOP and question fundamentals:
- Is this pattern fundamentally sound?
- Are we "sticking with it through sheer inertia"?
- Should we refactor architecture vs. continue fixing symptoms?
Discuss with your human partner before attempting more fixes
This is NOT a failed hypothesis - this is a wrong architecture.
Red Flags - STOP and Follow Process
If you catch yourself thinking:
- "Quick fix for now, investigate later"
- "Just try changing X and see if it works"
- "Add multiple changes, run tests"
- "Skip the test, I'll manually verify"
- "It's probably X, let me fix that"
- "I don't fully understand but this might work"
- "Pattern says X but I'll adapt it differently"
- "Here are the main problems: [lists fixes without investigation]"
- Proposing solutions before tracing data flow
- "One more fix attempt" (when already tried 2+)
- Each fix reveals new problem in different place
ALL of these mean: STOP. Return to Phase 1.
If 3+ fixes failed: Question the architecture (see Phase 4.5)
your human partner's Signals You're Doing It Wrong
Watch for these redirections:
- "Is that not happening?" - You assumed without verifying
- "Will it show us...?" - You should have added evidence gathering
- "Stop guessing" - You're proposing fixes without understanding
- "Ultrathink this" - Question fundamentals, not just symptoms
- "We're stuck?" (frustrated) - Your approach isn't working
When you see these: STOP. Return to Phase 1.
Common Rationalizations
| Excuse |
Reality |
| "Issue is simple, don't need process" |
Simple issues have root causes too. Process is fast for simple bugs. |
| "Emergency, no time for process" |
Systematic debugging is FASTER than guess-and-check thrashing. |
| "Just try this first, then investigate" |
First fix sets the pattern. Do it right from the start. |
| "I'll write test after confirming fix works" |
Untested fixes don't stick. Test first proves it. |
| "Multiple fixes at once saves time" |
Can't isolate what worked. Causes new bugs. |
| "Reference too long, I'll adapt the pattern" |
Partial understanding guarantees bugs. Read it completely. |
| "I see the problem, let me fix it" |
Seeing symptoms ≠ understanding root cause. |
| "One more fix attempt" (after 2+ failures) |
3+ failures = architectural problem. Question pattern, don't fix again. |
Quick Reference
| Phase |
Key Activities |
Success Criteria |
| 1. Root Cause |
Read errors, reproduce, check changes, gather evidence |
Understand WHAT and WHY |
| 2. Pattern |
Find working examples, compare |
Identify differences |
| 3. Hypothesis |
Form theory, test minimally |
Confirmed or new hypothesis |
| 4. Implementation |
Create test, fix, verify |
Bug resolved, tests pass |
When Process Reveals "No Root Cause"
If systematic investigation reveals issue is truly environmental, timing-dependent, or external:
- You've completed the process
- Document what you investigated
- Implement appropriate handling (retry, timeout, error message)
- Add monitoring/logging for future investigation
But: most "no root cause" verdicts are incomplete investigation. Before accepting one,
name the evidence that RULED OUT a code cause - the absence of a cause you found is not
the same as a cause you excluded.
Supporting Techniques
These techniques are part of systematic debugging and available in this directory:
root-cause-tracing.md - Trace bugs backward through call stack to find original trigger
defense-in-depth.md - Add validation at multiple layers after finding root cause
condition-based-waiting.md - Replace arbitrary timeouts with condition polling
Related skills:
- bitranox:process-test-driven-development - For creating failing test case (Phase 4, Step 1)
- bitranox:process-review-verification-before-completion - Verify fix worked before claiming success
Real-World Impact
From debugging sessions. The direction is the claim; the sizes are unmeasured, so do not
quote them as figures:
- Systematic investigation reaches a fix sooner than trying candidate fixes, and reaches
the right one more often.
- A fix aimed at a confirmed root cause rarely introduces a new bug. Candidate fixes
routinely do, because each one changes code that was never shown to be at fault.
1---2name: process-debug-systematic3description: Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes4---56# Systematic Debugging78> Adapted from the superpowers plugin (MIT).910## Overview1112Random fixes waste time and create new bugs. Quick patches mask underlying issues.1314**Core principle:** ALWAYS find root cause before attempting fixes. Symptom fixes are failure.1516**Violating the letter of this process is violating the spirit of debugging.**1718## The Iron Law1920```21NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST22```2324If you haven't completed Phase 1, you cannot propose fixes.2526## When to Use2728Use for ANY technical issue:29- Test failures30- Bugs in production31- Unexpected behavior32- Performance problems33- Build failures34- Integration issues3536**Use this ESPECIALLY when:**37- Under time pressure (emergencies make guessing tempting)38- "Just one quick fix" seems obvious39- You've already tried multiple fixes40- Previous fix didn't work41- You don't fully understand the issue4243**Don't skip when:**44- Issue seems simple (simple bugs have root causes too)45- You're in a hurry (rushing guarantees rework)46- Manager wants it fixed NOW (systematic is faster than thrashing)4748## The Four Phases4950You MUST complete each phase before proceeding to the next.5152### Phase 1: Root Cause Investigation5354**BEFORE attempting ANY fix:**55561. **Read Error Messages Carefully**57 - Don't skip past errors or warnings58 - They often contain the exact solution59 - Read stack traces completely60 - Note line numbers, file paths, error codes61622. **Reproduce Consistently**63 - Can you trigger it reliably?64 - What are the exact steps?65 - Does it happen every time?66 - If not reproducible → gather more data, don't guess67683. **Check Recent Changes**69 - What changed that could cause this?70 - Git diff, recent commits71 - New dependencies, config changes72 - Environmental differences73744. **Gather Evidence in Multi-Component Systems**7576 **WHEN system has multiple components (CI → build → signing, API → service → database):**7778 **BEFORE proposing fixes, add diagnostic instrumentation:**79 ```80 For EACH component boundary:81 - Log what data enters component82 - Log what data exits component83 - Verify environment/config propagation84 - Check state at each layer8586 Run once to gather evidence showing WHERE it breaks87 THEN analyze evidence to identify failing component88 THEN investigate that specific component89 ```9091 **Example (multi-layer system):**92 ```bash93 # Layer 1: Workflow94 echo "=== Secrets available in workflow: ==="95 echo "IDENTITY: ${IDENTITY:+SET}${IDENTITY:-UNSET}"9697 # Layer 2: Build script98 echo "=== Env vars in build script: ==="99 env | grep IDENTITY || echo "IDENTITY not in environment"100101 # Layer 3: Signing script102 echo "=== Keychain state: ==="103 security list-keychains104 security find-identity -v105106 # Layer 4: Actual signing107 codesign --sign "$IDENTITY" --verbose=4 "$APP"108 ```109110 **This reveals:** Which layer fails (secrets → workflow ✓, workflow → build NO)1111125. **Trace Data Flow**113114 **WHEN error is deep in call stack:**115116 See `root-cause-tracing.md` in this directory for the complete backward tracing technique.117118 **Quick version:**119 - Where does bad value originate?120 - What called this with bad value?121 - Keep tracing up until you find the source122 - Fix at source, not at symptom123124### Phase 2: Pattern Analysis125126**Find the pattern before fixing:**127128> Offload the bounded search here to a **`sonnet`** finder subagent (locate similar working code, read129> the reference implementation fully, return only the relevant slices) - it keeps large reference files130> out of your context. Keep hypothesis/root-cause reasoning in the main (`opus`) agent - this deep reasoning is131> capability-sensitive, so if the session is on a lesser tier offer switch-model-or-continue (the main132> agent cannot self-switch its model), or hand the root-cause step to a pinned `opus` subagent. Multiple133> independent failures -> `bitranox:process-agents-dispatching-parallel`. Tiers: "Concrete tiers" in134> `bitranox:process-agents-subagent-driven-development`.1351361. **Find Working Examples**137 - Locate similar working code in same codebase138 - What works that's similar to what's broken?1391402. **Compare Against References**141 - If implementing pattern, read reference implementation COMPLETELY142 - Don't skim - read every line143 - Understand the pattern fully before applying1441453. **Identify Differences**146 - What's different between working and broken?147 - List every difference, however small148 - Don't assume "that can't matter"1491504. **Understand Dependencies**151 - What other components does this need?152 - What settings, config, environment?153 - What assumptions does it make?154155### Phase 3: Hypothesis and Testing156157**Scientific method:**1581591. **Form Single Hypothesis**160 - State clearly: "I think X is the root cause because Y"161 - Write it down162 - Be specific, not vague1631642. **Test Minimally**165 - Make the SMALLEST possible change to test hypothesis166 - One variable at a time167 - Don't fix multiple things at once168 - **Run both arms from the SAME path.** Comparing an old version against a new one by copying169 one of them to a scratch directory moves a SECOND variable: a program that derives anything170 from `__file__` or the working directory behaves differently when relocated, so the171 difference you measure is the relocation rather than the change. The artifact of a relocated172 arm tends to be the more flattering result, which is what makes it survive review. Swap the173 versions IN PLACE (stash, checkout, or a symlink the program resolves identically) and keep174 every path the same across arms.1751763. **Verify Before Continuing**177 - Did it work? Yes → Phase 4178 - Didn't work? Form NEW hypothesis179 - DON'T add more fixes on top1801814. **When You Don't Know**182 - Say "I don't understand X"183 - Don't pretend to know184 - Ask for help185 - Research more1861875. **Closed-Source Peer: Escalate to Disassembly After the Second Dead Hypothesis**188189 **WHEN the system on the other side of the bug is a closed-source binary you cannot190 read (a proprietary driver, firmware, appliance, or vendor tool) and you are testing191 hypotheses about its behavior black-box:**192193 - 1st hypothesis dies against measurement -> form a new one (the normal Phase 3 loop).194 - **2nd hypothesis also dies against measurement -> STOP. Do not form a third195 black-box guess.** Disassemble the peer instead: a disassembler (for example196 Ghidra) driven by its scripting/Python bridge, plus any public PDB or symbols the197 vendor ships.198 - Read the disassembly to learn the PROTOCOL or format needed for interoperability -199 never to copy the implementation. Reverse-engineering for interoperability is the200 sanctioned use here; lifting the vendor's code is not.201 - Cite an address and a symbol for the conclusion, not "it must be doing X".202 - A clean refutation of your hypothesis is an equally valid result - it closes the203 question as surely as a confirmation does, so don't discard the disassembly pass204 because it disproved you.205206 **Why:** each black-box hypothesis costs a full build-deploy-measure cycle. The207 detail that decides the bug (a parser quirk, a field only read on the first record,208 a byte offset) can exist nowhere in any public spec, so no amount of further209 black-box experiment reaches it - only reading the code does.210211### Phase 4: Implementation212213**Fix the root cause, not the symptom:**2142151. **Create Failing Test Case**216 - Simplest possible reproduction217 - Automated test if possible218 - One-off test script if no framework219 - MUST have before fixing220 - Use the `bitranox:process-test-driven-development` skill for writing proper failing tests2212222. **Implement Single Fix**223 - Address the root cause identified224 - ONE change at a time225 - No "while I'm here" improvements226 - No bundled refactoring2272283. **Verify Fix**229 - Test passes now?230 - No other tests broken?231 - Issue actually resolved?2322334. **If Fix Doesn't Work**234 - STOP235 - Count: How many fixes have you tried?236 - If < 3: Return to Phase 1, re-analyze with new information237 - **If ≥ 3: STOP and question the architecture (step 5 below)**238 - DON'T attempt Fix #4 without architectural discussion2392405. **If 3+ Fixes Failed: Question Architecture**241242 **Pattern indicating architectural problem:**243 - Each fix reveals new shared state/coupling/problem in different place244 - Fixes require "massive refactoring" to implement245 - Each fix creates new symptoms elsewhere246247 **STOP and question fundamentals:**248 - Is this pattern fundamentally sound?249 - Are we "sticking with it through sheer inertia"?250 - Should we refactor architecture vs. continue fixing symptoms?251252 **Discuss with your human partner before attempting more fixes**253254 This is NOT a failed hypothesis - this is a wrong architecture.255256## Red Flags - STOP and Follow Process257258If you catch yourself thinking:259- "Quick fix for now, investigate later"260- "Just try changing X and see if it works"261- "Add multiple changes, run tests"262- "Skip the test, I'll manually verify"263- "It's probably X, let me fix that"264- "I don't fully understand but this might work"265- "Pattern says X but I'll adapt it differently"266- "Here are the main problems: [lists fixes without investigation]"267- Proposing solutions before tracing data flow268- **"One more fix attempt" (when already tried 2+)**269- **Each fix reveals new problem in different place**270271**ALL of these mean: STOP. Return to Phase 1.**272273**If 3+ fixes failed:** Question the architecture (see Phase 4.5)274275## your human partner's Signals You're Doing It Wrong276277**Watch for these redirections:**278- "Is that not happening?" - You assumed without verifying279- "Will it show us...?" - You should have added evidence gathering280- "Stop guessing" - You're proposing fixes without understanding281- "Ultrathink this" - Question fundamentals, not just symptoms282- "We're stuck?" (frustrated) - Your approach isn't working283284**When you see these:** STOP. Return to Phase 1.285286## Common Rationalizations287288| Excuse | Reality |289|----------------------------------------------|-------------------------------------------------------------------------|290| "Issue is simple, don't need process" | Simple issues have root causes too. Process is fast for simple bugs. |291| "Emergency, no time for process" | Systematic debugging is FASTER than guess-and-check thrashing. |292| "Just try this first, then investigate" | First fix sets the pattern. Do it right from the start. |293| "I'll write test after confirming fix works" | Untested fixes don't stick. Test first proves it. |294| "Multiple fixes at once saves time" | Can't isolate what worked. Causes new bugs. |295| "Reference too long, I'll adapt the pattern" | Partial understanding guarantees bugs. Read it completely. |296| "I see the problem, let me fix it" | Seeing symptoms ≠ understanding root cause. |297| "One more fix attempt" (after 2+ failures) | 3+ failures = architectural problem. Question pattern, don't fix again. |298299## Quick Reference300301| Phase | Key Activities | Success Criteria |302|-----------------------|--------------------------------------------------------|-----------------------------|303| **1. Root Cause** | Read errors, reproduce, check changes, gather evidence | Understand WHAT and WHY |304| **2. Pattern** | Find working examples, compare | Identify differences |305| **3. Hypothesis** | Form theory, test minimally | Confirmed or new hypothesis |306| **4. Implementation** | Create test, fix, verify | Bug resolved, tests pass |307308## When Process Reveals "No Root Cause"309310If systematic investigation reveals issue is truly environmental, timing-dependent, or external:3113121. You've completed the process3132. Document what you investigated3143. Implement appropriate handling (retry, timeout, error message)3154. Add monitoring/logging for future investigation316317**But:** most "no root cause" verdicts are incomplete investigation. Before accepting one,318name the evidence that RULED OUT a code cause - the absence of a cause you found is not319the same as a cause you excluded.320321## Supporting Techniques322323These techniques are part of systematic debugging and available in this directory:324325- **`root-cause-tracing.md`** - Trace bugs backward through call stack to find original trigger326- **`defense-in-depth.md`** - Add validation at multiple layers after finding root cause327- **`condition-based-waiting.md`** - Replace arbitrary timeouts with condition polling328329**Related skills:**330- **bitranox:process-test-driven-development** - For creating failing test case (Phase 4, Step 1)331- **bitranox:process-review-verification-before-completion** - Verify fix worked before claiming success332333## Real-World Impact334335From debugging sessions. The direction is the claim; the sizes are unmeasured, so do not336quote them as figures:337- Systematic investigation reaches a fix sooner than trying candidate fixes, and reaches338 the right one more often.339- A fix aimed at a confirmed root cause rarely introduces a new bug. Candidate fixes340 routinely do, because each one changes code that was never shown to be at fault.