--- name: systematic-debugging description: "4-phase root cause debugging: understand bugs before fixing." version: 1.2.0 author: Hermes Agent (adapted from obra/superpowers) license: MIT platforms: [linux, macos, windows] metadata: hermes: tags: [debugging, troubleshooting, problem-solving, root-cause, investigation] related_skills: [test-driven-development, writing-plans, subagent-driven-development] --- # Systematic Debugging ## 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) - Someone 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:** ### 1. 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 **Action:** Use `read_file` on the relevant source files. Use `search_files` to find the error string in the codebase. ### 2. 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 **Action:** Use the `terminal` tool to run the failing test or trigger the bug: ```bash # Run specific failing test pytest tests/test_module.py::test_name -v # Run with verbose output pytest tests/test_module.py -v --tb=long ``` ### 3. Check Recent Changes - What changed that could cause this? - Git diff, recent commits - New dependencies, config changes **Action:** ```bash # Recent commits git log --oneline -10 # Uncommitted changes git diff # Changes in specific file git log -p --follow src/problematic_file.py | head -100 ``` ### 4. Gather Evidence in Multi-Component Systems **WHEN system has multiple components (API → service → database, CI → build → deploy):** **BEFORE proposing fixes, add diagnostic instrumentation:** For EACH component boundary: - Log what data enters the component - Log what data exits the component - Verify environment/config propagation - Check state at each layer Run once to gather evidence showing WHERE it breaks. THEN analyze evidence to identify the failing component. THEN investigate that specific component. ### 5. SPA Auth Redirect Loops — Full-File Sweep First **WHEN the symptom is a redirect loop between pages (login ↔ dashboard, etc.):** **CRITICAL: DO NOT rename files, swap pages, or edit HTML nav links as a first response.** This is the #1 cause of user frustration in redirect-loop debugging. The visible entries (anchor tags, button hrefs) are almost never the real redirect source. The real redirects come from auth listeners (`onAuthStateChanged`), guard functions, inline localStorage scripts, keyboard shortcut handlers, and form-submit callbacks — many of which are buried in JS files you haven't read yet. Renaming files creates a cascade of broken references that must be undone when you find the real cause, while the loop keeps running untouched. **Users correctly perceive this as you not reading the files.** **THE MOST COMMON ROOT CAUSE:** `onAuthStateChanged` listeners in both pages firing with opposite values. A stale PocketBase/Firebase/Supabase auth token in localStorage causes the listener to fire `callback(user)` on one page and `callback(null)` on the other (or fire twice — once sync with user, then async with null after a failed API call). Each page redirects to the other, creating the loop. The fix is usually ONE of: - Remove the redirect from the listener on the page that should NOT redirect (e.g., login page should show the form, not bounce to dashboard) - Use an inline `