---
name: systematic-debugging
description: "4-phase root cause debugging: understand bugs before fixing."
version: 1.1.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)

## Phase 0: Cross-Reference Verification (before starting)

**WHEN implementing recommendations from research, audits, or external sources:**

Before applying any change, verify the claim against **live authoritative sources**. Do not trust research findings, GitHub issue reports, or community recommendations at face value — implementations may be unmerged, deprecated, or nonexistent.

### Checklist

1. **Identify the claim** — what specific config key, code change, or behavior is being recommended?
2. **Check official docs** — does the Hermes docs site confirm this feature exists and the syntax is correct?
3. **Check GitHub** — is the relevant PR **merged** or still **open**? If open, the feature does not exist in your installed version.
4. **Check source code** — grep the installed Hermes source for the feature name. If it's not in the code, it's not available regardless of what docs/GitHub say.
5. **Check config.yaml** — verify the change is actually present and syntactically correct with `yaml.safe_load()`.

### Real-World Example (from this codebase)

| Claim | Cross-Reference Result |
|-------|----------------------|
| `tools.stub_mode: true` saves 46% per-turn | PR #48622 is **OPEN** (not merged). Code not in v0.16.0 source. **Cannot enable.** |
| `compression.threshold` should be 0.50 | Official Hermes docs confirm **0.50 is the default**. Correct recommendation. |
| `sessions.auto_prune` needs enabling | Already enabled with 45-day retention. Doc says default is `false` — verified config had it `true`. |
| `skill_view()` loads disabled skills | **FALSE for v0.16.0.** skill_view returns error for disabled skills. Workaround: `read_file` the SKILL.md directly. |

### Common Cross-Reference Traps

- **Open PR = not available.** GitHub shows "merged" or "closed" — anything else is aspirational. A feature request or draft PR does not exist in your installed version.
- **GitHub issue body = proposal, not fact.** Issues contain design discussions, not implementation status. Check the linked PR for merge status.
- **Docs may describe a future feature.** The Hermes docs site sometimes documents unreleased features. Always check your installed version (grep the source).
- **Subagents cite research that doesn't exist.** A subagent dispatched to research "Hermes optimization best practices" may cite unmerged PRs or hallucinated features. Cross-reference every subagent claim before acting.

### When to Skip

Phase 0 is only needed when you are about to **change the system** based on an external recommendation. If you already verified a claim earlier in the same session (e.g., you checked docs and found the config key yourself), skip Phase 0 for that specific item.

---

## 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. Multi-Failure Group Debugging

**WHEN multiple tests fail (10+ across different modules):**

**Do NOT investigate each failure individually.** Instead:

1. **Group by error status + message** — Collect all failures and find the common thread:
   - Run the full suite once (`pytest -q --tb=line`)
   - Check if ALL failures share a status code (e.g., all return 400)
   - Check if ALL failures share an error message substring

2. **Write a quick probe script** — Before diving into individual test details, probe all endpoints with a small script to confirm the pattern:

   ```python
   async def probe():
       for url in urls:
           resp = await client.get(url)
           print(f"{resp.status_code} -> {resp.text[:100]}")
   ```

   If every endpoint returns the same status and same body, you've found a shared infrastructure issue — middleware, config, or dependency wiring.

3. **Trace up the middleware stack** — When every request returns the same error:
   - Check middleware first (it's the outermost layer)
   - `TrustedHostMiddleware` is a common culprit — it returns 400 "Invalid host header" for any host not in `allowed_hosts`
   - Check config: test clients use `host=test` or `host=localhost` which may not match production `ALLOWED_HOSTS`
   - Check CORS middleware (blocking preflight)
   - Check auth dependencies blocking anonymous endpoints

4. **Apply the single fix** — One middleware/config fix will resolve ALL failures at once. This is substantially faster than fixing 73 tests one at a time.

**Real-world example from this codebase (73 failures, 1 root cause):**
- Every endpoint returned 400 "Invalid host header" → not an auth issue, not a routing issue, not 73 separate bugs
- Root cause: `ALLOWED_HOSTS=127.0.0.1,localhost` in `.env` blocked `host=test` used by `httpx.AsyncClient` base_url
- Fix: middleware logic for non-production environments now uses `["*"]` always
- Result: 73/73 failures resolved with one 3-line change

**Counter-indication:** If failures have DIFFERENT status codes and different error messages, they're likely real individual bugs — proceed with the standard Phase 1 approach on each group.

#### Escalation After Root Cause Identification

Once the root cause is identified and a fix is applied, route model escalation by failure type:

| Failure Type | Escalate To | Why |
|-------------|-------------|-----|
| **Configuration/infrastructure** (middleware, env, wiring) | Qwen3.6 Plus | Validate production safety and edge cases |
| **Async/performance** (race conditions, N+1, blocking calls) | DeepSeek V4 Pro | Async correctness and perf review |
| **Architecture/coupling** (module boundaries, layering, DI) | Kimi K2.6 | Architecture pattern review |

**Protocol:** Present the grouped findings, root cause, and fix diff. Ask the model to validate correctness, security, and suggest alternatives. Apply any improvements found, then re-run the full test suite.

### 6. Trace Data Flow

**WHEN error is deep in the call stack:**

- Where does the bad value originate?
- What called this function with the bad value?
- Keep tracing upstream until you find the source
- Fix at the source, not at the symptom

**Action:** Use `search_files` to trace references:

```python
# Find where the function is called
search_files("function_name(", path="src/", file_glob="*.py")

# Find where the variable is set
search_files("variable_name\\s*=", path="src/", file_glob="*.py")
```

### 7. Browser Media Playback Debugging

**WHEN a browser `<video>`/HLS player reports `MEDIA_ELEMENT_ERROR`, `Format error`, or stays at `readyState=0`:**

Do not assume the media codec/container is the root cause until you verify what bytes the browser actually received.

1. **Inspect the actual video element source and state** in the browser:
   ```js
   (() => {
     const v = document.querySelector('video');
     return {
       src: v?.currentSrc || v?.src,
       error: v?.error && { code: v.error.code, message: v.error.message },
       networkState: v?.networkState,
       readyState: v?.readyState,
       currentTime: v?.currentTime,
       duration: v?.duration,
     };
   })()
   ```
2. **Fetch the exact `src` with the same auth context** (or from browser console if auth is in localStorage/cookies). A video "format error" can be a JSON/HTML error body (401/403/500) delivered to the media element.
3. **For backend media proxies**, verify the browser can authenticate. Native `<video>` requests cannot attach arbitrary `Authorization` headers. If the backend requires auth, use one of these deliberate patterns:
   - signed short-lived media URL;
   - cookie/session auth acceptable for media requests;
   - query-token fallback on the media proxy, with token redacted from logs/UI.
4. **Verify range semantics** after auth succeeds. Browser media playback often needs `Range` passthrough and `206 Partial Content`/`Content-Range` from the proxy. Test with:
   ```bash
   curl -D - -H 'Range: bytes=0-1023' 'http://localhost:5173/api/stream-proxy?...' -o /tmp/probe.bin
   file /tmp/probe.bin
   ```
5. **Only then investigate codec/container support** (e.g., MKV/HEVC/HDR/Atmos in Chromium) or transcoding fallback.

**Common root cause pattern:** resolver returns a valid CDN URL, but the frontend wraps it in `/streams/proxy?url=...` without browser-compatible auth. The media element receives `401 {"detail":"Not authenticated"}` and surfaces only `MEDIA_ELEMENT_ERROR: Format error`. Fix the auth path first; then retest range and bytes.

### 8. Tool Availability Diagnosis

**WHEN you believe a tool is unavailable, missing, or broken:**

Do NOT claim a tool doesn't exist before thoroughly checking — the user will correct you. Tool availability can often be fixed without a new session.

**Diagnosis procedure:**

1. **Check if the toolset is configured but not loaded** — tools are baked at session start from `platform_toolsets.cli` in config.yaml.

2. **Try enabling mid-session**:
   ```bash
   hermes tools list                    # see current enabled toolsets
   hermes tools enable web browser      # enable web + browser toolsets
   hermes tools enable --platform cli web
   ```

3. **Check config for the platform_toolsets list** — if tools were removed (e.g., during a slimming audit), re-add them.

4. **Check agent.disabled_toolsets** — some full categories are disabled at the agent level.

5. **Distinguish tool categories from tool functions** — web/browser provides `browser_navigate`/`browser_console`, not a `web_search` function. Navigate to a search engine with the browser tool.

6. **Terminal workaround for this session** — use `curl` in terminal for web lookups if tools aren't available mid-session.

**Pitfall:** Do not claim "this tool does not exist" before running `hermes tools list` and checking config. Negative claims persist in memory and harden into self-imposed constraints. Report what you found and the workaround.

### 9. Debrid/Provider Resolution Debugging

**WHEN a debrid stream resolver says “no streams,” “valid debrid account,” or the UI blames account linking even though an account is active:**

Do not assume credential failure. Split the pipeline into distinct provider facts:

1. **Validate account credentials independently** — call a harmless account endpoint (e.g. Real-Debrid `/user`) with the stored decrypted token. If this returns 200, the API key/account is active.
2. **Verify aggregator candidates independently** — query the aggregator by the exact IMDb/TMDB-derived ID and count candidate hashes. If candidates exist, the metadata/search layer is working.
3. **Probe one provider operation per candidate class** — for RD, `addMagnet` can return provider-specific errors even when the account is valid. Capture the provider error code/message, sanitized.
4. **Distinguish “no account” from “provider rejected content” in user-facing errors.** A valid active RD account plus `451 {"error":"infringing_file","error_code":35}` means the title/candidate hashes are rejected/unavailable by RD, not that the user needs to relink the account.
5. **Do not provider-cooldown content-specific failures.** Cooldowns should be reserved for provider/rate/infra failures (`429`, `5xx`, timeouts). Content-specific rejections like RD `infringing_file` should not cause the next candidate hashes to be skipped as if the whole provider is down.
6. **Beware stale/deprecated provider shortcuts.** If a preflight endpoint such as RD `instantAvailability` returns `403 disabled_endpoint`, treat that as an unavailable optimization and continue with the authoritative resolve path instead of concluding that nothing is cached.

**Implementation pattern:** preserve sanitized provider reasons through the resolver/use-case boundary, aggregate them during TMDB→torrent→debrid orchestration, then return a precise final message:
- Account invalid/missing → “No linked debrid accounts available.”
- Aggregator empty → “No torrent streams found for IMDb ID.”
- Provider content rejection for all candidates → “Account is active, but provider rejected all candidate torrents for this title as unavailable/infringing.”
- Mixed provider failures → “Account is active, but no candidate torrent could be resolved by the provider.”

### 9. Cross-Component API Contract Debugging

WHEN the bug spans frontend and backend (wrong API response, wrong request shape, missing data):

The frontend sends some data to the backend, the backend processes it and returns a response. When one side makes an assumption the other doesn't satisfy, you need to compare them field by field.

**Procedure:**

1. **Read the frontend API call** — what data shape does it send in the request body? What shape does it expect in the response?
2. **Read the backend endpoint** — read the route handler, the Pydantic request schema, and the response model
3. **Compare EVERY field and parameter** — check types, formats, optionality, nullability
4. **Check validation schemas** — the backend may have stricter validation than the frontend expects (Pydantic `Field(ge=1)`, `pattern=r"^[a-z]"`, `PositiveInt`)
5. **Check response wrapping** — some endpoints wrap data in `{item: {...}}` (DetailResponse pattern) while others return flat. The frontend type annotation may not match the actual JSON shape.

**Key question:** Is there a **pipeline gap** — a missing transformation step between what the frontend sends and what the backend ultimately needs?

**Real-world example from this codebase (playback flow — 422 "Invalid source"):**

| Side | What was sent/received | Problem |
|------|----------------------|---------|
| Frontend | `resolveStream({ source: "tt:550", provider: "real_debrid", account_id: 1 })` | Frontend sent a TMDB ID prefixed with `tt:` |
| Backend schema | `ResolveRequest.source: str` — passes string validation | String accepted, looks valid |
| Backend parser | `ResolveSource.from_string("tt:550")` rejects with `ValueError` | Only magnets/URLs/hashes accepted |

Root cause: `tt:550` is not a valid source format — the pipeline needed TMDB ID → IMDb ID (from TMDB metadata) → Torrentio search (info hashes) → debrid resolve, but no endpoint chained these steps.

**Action:** When the frontend sends a value that seems obviously correct but the backend rejects it:
1. Check what formats the backend's value object/parser actually accepts (read the `from_string`, `__post_init__`, or `validate` method)
2. Check what format the frontend is sending (trace the API call to the actual value building)
3. Check if there's a missing data transformation step between the two
4. Check if the frontend is calling the wrong endpoint entirely

### Phase 1 Completion Checklist

- [ ] Error messages fully read and understood
- [ ] Issue reproduced consistently
- [ ] Recent changes identified and reviewed
- [ ] Evidence gathered (logs, state, data flow)
- [ ] Problem isolated to specific component/code
- [ ] Root cause hypothesis formed

**STOP:** Do not proceed to Phase 2 until you understand WHY it's happening.

---

## Phase 2: Pattern Analysis

**Find the pattern before fixing:**

### 1. Find Working Examples

- Locate similar working code in the same codebase
- What works that's similar to what's broken?

**Action:** Use `search_files` to find comparable patterns:

```python
search_files("similar_pattern", path="src/", file_glob="*.py")
```

### 2. Frontend API Response Shape Mismatch

**WHEN a frontend page shows empty/missing data (no title, no poster, no details) but the API call succeeds (200):**

The backend may wrap the response in a container object that the frontend's TypeScript type doesn't reflect.

**The pattern:**

- Backend Pydantic schema: `class DetailResponse(BaseModel): item: MediaItemResponse` — wraps the data in `{item: {...}}`
- Frontend type: `interface MediaItem { tmdb_id: number, title: string, ... }` — expects flat data
- Frontend API call: `const data = await api<MediaItem>('/metadata/movie/550')`
- Actual response: `{ "item": { "tmdb_id": 550, "title": "Fight Club", ... } }`
- Result: `data.tmdb_id` is `undefined` because the actual structure is `data.item.tmdb_id`
- TypeScript doesn't catch this because the `api<MediaItem>()` generic coerces the type without runtime validation

**Detection checklist:**

1. Open browser dev tools / network tab → inspect the actual API response JSON
2. Compare the JSON keys to the TypeScript type
3. Does a wrapping key like `item`, `data`, `result`, or `response` exist?
4. Is the frontend type flat but the response nested? Or vice versa?

**Fix pattern:**

```typescript
// Before (broken — trusts type annotation over actual JSON shape):
const data = await api<MediaItem>('/metadata/movie/550');

// After (fixed — unwraps the container):
const raw = await api<{ item: MediaItem }>('/metadata/movie/550');
return raw.item;
```

### 3. Compare Against References

- If implementing a pattern, read the reference implementation COMPLETELY
- Don't skim — read every line
- Understand the pattern fully before applying

### 4. Identify Differences

- What's different between working and broken?
- List every difference, however small
- Don't assume "that can't matter"

### 5. Understand Dependencies

- What other components does this need?
- What settings, config, environment?
- What assumptions does it make?

---

## Phase 3: Hypothesis and Testing

**Scientific method:**

### 1. Form a Single Hypothesis

- State clearly: "I think X is the root cause because Y"
- Write it down
- Be specific, not vague

### 2. Test Minimally

- Make the SMALLEST possible change to test the hypothesis
- One variable at a time
- Don't fix multiple things at once

### 3. Verify Before Continuing

- Did it work? → Phase 4
- Didn't work? → Form NEW hypothesis
- DON'T add more fixes on top

### 4. When You Don't Know

- Say "I don't understand X"
- Don't pretend to know
- Ask the user for help
- Research more

---

## Phase 4: Implementation

**Fix the root cause, not the symptom:**

### 1. Create Failing Test Case

- Simplest possible reproduction
- Automated test if possible
- MUST have before fixing
- Use the `test-driven-development` skill

### 2. Implement Single Fix

- Address the root cause identified
- ONE change at a time
- No "while I'm here" improvements
- No bundled refactoring

### 3. Verify Fix

```bash
# Run the specific regression test
pytest tests/test_module.py::test_regression -v

# Run full suite — no regressions
pytest tests/ -q
```

### 4. Post-Fix Validation Pass

**After the fix compiles and tests pass, delegate a review for safety-critical fixes:**

When the fix touches:
- **Middleware or auth components** (CORS, TrustedHost, security headers, rate limiting)
- **Configuration or environment validation**
- **Any code that runs on every request** (middleware, dependencies, singletons)

→ Delegate to **Qwen3.6 Plus** for correctness, edge cases, and security implications.

When the fix involves:
- **Async race conditions, concurrent state, or lock patterns**
- **Performance-critical paths** (N+1 queries, blocking calls in async paths)

→ Delegate to **DeepSeek V4 Pro** for async-safety and performance review.

When the fix involves:
- **Architecture patterns, module boundaries, or layering decisions**
- **Cross-module coupling, dependency injection choices**

→ Delegate to **Kimi K2.6** for architecture review.

**Validation prompt template:**

```
Review this fix for correctness, security, and production safety:
[describe the fix in 2-3 sentences]
[link to files changed]
[describe what was wrong and why this fix is intended to resolve it]
Answer: (1) Is this fix safe? (2) Any edge cases missed? (3) Better approaches?
```

The review should happen against the committed diff, not before applying the fix. This catches security regressions, config gaps, and cross-cutting concerns that the original fixer didn't consider.

### 5. Orchestrating Endpoint Pattern (Pipeline Gap Fix)

**WHEN the root cause is a MISSING pipeline step — not a wrong value, but no endpoint that chains the necessary transformations:**

The system needs A → B → C → D, but only endpoints for A→B and C→D exist. Don't modify existing endpoints (risks regression). Instead:

1. **Create a NEW orchestrating endpoint** that calls all the existing services in sequence
2. The new endpoint handles the missing transformation step(s)
3. Existing endpoints remain unchanged
4. The new endpoint is independently testable

**Real-world example (TMDB → debrid pipeline gap):**

| Already existed | Missing |
|----------------|---------|
| `POST /streams/resolve` — accepts magnets/URLs/hashes → returns debrid URLs | No endpoint converts TMDB ID → debrid playback |
| `GET /metadata/{type}/{id}` — returns TMDB details (incl. external_ids.imdb_id) | |
| Torrentio `stream/{type}/{id}.json` — returns info hashes from IMDb ID | |

**Fix:** New `POST /streams/resolve-tmdb` endpoint:
```
1. Call TMDB external_ids API → get IMDb ID from TMDB ID
2. Call Torrentio aggregator → get info hashes from IMDb ID
3. For each info hash, call existing resolve use case → get playable URLs
4. Return combined stream list
```

**When to use this pattern:**
- The pipeline has multiple independent services that work individually
- The gap is in coordination, not in any single component
- Modifying existing components would be invasive or risk regressions
- The new endpoint clearly owns the orchestration concern

### 5. If Fix Doesn't Work — The Rule of Three

- **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

### 5. If 3+ Fixes Failed: Question Architecture

**Pattern indicating an architectural problem:**
- Each fix reveals new shared state/coupling in a 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 the architecture vs. continue fixing symptoms?

**Discuss with the user 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 a new problem in a different place**

**ALL of these mean: STOP. Return to Phase 1.**

**If 3+ fixes failed:** Question the architecture (Phase 4 step 5).

## 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 the pattern, don't fix again. |

## Quick Reference

| Phase | Key Activities | Success Criteria |
|-------|---------------|------------------|
| **1. Root Cause** | Read errors, reproduce, check changes, gather evidence, trace data flow | Understand WHAT and WHY |
| **2. Pattern** | Find working examples, compare, identify differences | Know what's different |
| **3. Hypothesis** | Form theory, test minimally, one variable at a time | Confirmed or new hypothesis |
| **4. Implementation** | Create regression test, fix root cause, verify | Bug resolved, all tests pass |

## Hermes Agent Integration

### Investigation Tools

Use these Hermes tools during Phase 1:

- **`search_files`** — Find error strings, trace function calls, locate patterns
- **`read_file`** — Read source code with line numbers for precise analysis
- **`terminal`** — Run tests, check git history, reproduce bugs
- **`web_search`/`web_extract`** — Research error messages, library docs

### With delegate_task

For complex multi-component debugging, dispatch investigation subagents:

```python
delegate_task(
    goal="Investigate why [specific test/behavior] fails",
    context="""
    Follow systematic-debugging skill:
    1. Read the error message carefully
    2. Reproduce the issue
    3. Trace the data flow to find root cause
    4. Report findings — do NOT fix yet

    Error: [paste full error]
    File: [path to failing code]
    Test command: [exact command]
    """,
    toolsets=['terminal', 'file']
)
```

### With test-driven-development

When fixing bugs:
1. Write a test that reproduces the bug (RED)
2. Debug systematically to find root cause
3. Fix the root cause (GREEN)
4. The test proves the fix and prevents regression

## Real-World Impact

From debugging sessions:
- Systematic approach: 15-30 minutes to fix
- Random fixes approach: 2-3 hours of thrashing
- First-time fix rate: 95% vs 40%
- New bugs introduced: Near zero vs common

**No shortcuts. No guessing. Systematic always wins.**
