| name | debug |
| description | Use when code behaves differently from expectations โ 'why does this happen', 'find the bug', 'follow the callstack', symptom reports even without the word debugging. Blackbox methodology: AS-IS/TO-BE confirmation, callstack tracing recorded in a CWD-relative trace file, red-green fix. |
stv:debug: 'Blackbox Debugging'
A debugging methodology that records everything like an airplane black box while hunting for the root cause.
Core constraint: An unexplored branch is an unexamined branch.
1. Problem Definition โ AS-IS / TO-BE Confirmation
Before starting, always confirm with the user:
- Forward: "Did X, got Y โ doing X should produce Z"
- Reverse: "Got Y โ should have been Z"
The gap between AS-IS and TO-BE is the bug. Do not start debugging without confirmation.
2. Tracing โ Follow the Callstack While Recording in trace.md
Create a debugging log file under the current working directory (NOT the home directory):
./.claude/stv/debugging/{issueID}-{YYYYMMDDhhmm}/trace.md
The ./ prefix is intentional โ every stv artifact (spec.md, trace.md, debugging trace) lives under the active CWD so multi-tenant / multi-session agents stay isolated. The tradeoff is that the same {issueID} started in different working trees yields separate trace dirs; that is the intended isolation model, not a bug.
Path guards (all three are hard rules):
- NEVER write to the home-level
.claude. The trace dir is always the CWD-relative ./.claude/stv/โฆ; a trace path that resolves to the HOME-level .claude (~/.claude/โฆ, $HOME/.claude/โฆ, /Users/<user>/.claude/โฆ, /home/<user>/.claude/โฆ) is a bug in the run, not a fallback โ older versions of this skill polluted ~/.claude/stv/debugging/ exactly this way. (A checkout that happens to live under /Users/<user>/project/ is fine โ the guard is about the home .claude, not the home prefix.) If the CWD is somehow unwritable, stop and surface that instead of falling back to home.
- Keep trace dirs out of commits (gitignore). When the CWD is a git checkout,
./.claude/stv/ must be git-ignored before the first trace write: if ! git check-ignore -q .claude/stv; then echo '.claude/stv/' >> .git/info/exclude; fi. Default to .git/info/exclude (local scratch policy, no tracked-file mutation); append to the repo's .gitignore instead only when the project explicitly wants the ignore rule shared. A debugging trace that leaks into a commit is scratch entering the tree.
- Trace dirs are disposable evidence โ cleanup is part of closing. When debugging concludes, fold the durable findings (root cause, the redโgreen test, the fix rationale) into the issue/PR/spec that owns the bug; the trace dir itself has served its purpose. Leave it in place for session-scoped CWDs (the sandbox is thrown away), and delete or trash it in long-lived checkouts once its content has been folded in. The lifecycle rule: durable knowledge moves out, the scratch dir never outlives the investigation.
Follow the callstack one step at a time from the entry point, recording in this file.
Recording Rules
- At each step, specify the actual filename:line number.
- At conditional branches, record which condition leads where.
- API calls to other services, DB queries, and message queue publications are part of the callstack. When crossing service boundaries, follow into that service's code and continue tracing.
- Do not assume. Only write what you have directly read and verified in the code.
Exploration Strategy: Heuristic โ Exhaustive
- Phase 1 โ Heuristic: Quickly check the top-3 most likely hypotheses first.
- Phase 2 โ Exhaustive: If top-3 yields nothing, draw the callstack graph in trace.md and exhaustively explore every branch by actually writing each one down.
3. Verification โ Red-Green Cycle
Once a hypothesis is identified:
- Write a test that reproduces the bug first (Red โ confirm the test fails)
- Apply the fix
- Confirm the test passes (Green)
- Confirm existing tests are not broken (regression prevention)
- Feed the root cause back to the vertical trace โ if the bug lives in a feature that has
docs/{feature}/trace.md, update that trace via the Delta Protocol (a wrong transformation rule โ MODIFIED; a missing error path โ ADDED) so trace and code stay synchronized. The debugging trace dir is scratch; the vertical trace is the contract.
4. Systematic Debugging Principles
The above blackbox method covers how to trace. These principles cover how to think while debugging.
4a. Reproduce First
- ์ฌํ ์์ด ์์ ํ์ง ๋ง๋ผ. ์ฌํํ ์ ์์ผ๋ฉด ๊ณ์ธก(instrumentation)๋ถํฐ ์ถ๊ฐํ๋ผ.
- ์ฌํ ๊ฐ๋ฅํ๋ฉด โ ๊ฐ์ค ์๋ฆฝ. ์ฌํ ๋ถ๊ฐ๋ฉด โ ๋ก๊ทธ/๊ณ์ธก ์ถ๊ฐ ํ ์ฌ์คํ.
4b. Single Hypothesis per Iteration
- ํ ๋ฒ์ ํ๋์ ๊ฐ์ค๋ง ๊ฒ์ฆํ๋ค.
- ์ฌ๋ฌ ์์ ์ ๋์์ ํ๋ฉด ๋ญ๊ฐ ํจ๊ณผ์ธ์ง ๋ชจ๋ฅธ๋ค.
- ๊ฐ์ค์ด ํ๋ฆฌ๋ฉด ์๋ณตํ๊ณ ์ ๊ฐ์ค์ ์ธ์๋ผ.
4c. Root-Cause Tracing
- ์ฆ์์ด ๋ํ๋ ๊ณณ์ด ์๋๋ผ ์์ธ์ด ์์๋ ๊ณณ์ ์ฐพ์๋ผ.
- ์ฝ์คํ์ ์ญ๋ฐฉํฅ์ผ๋ก ์ถ์ : "์ด ๊ฐ์ ์ด๋์ ์๋?" โ "๊ทธ๊ฑธ ๋๊ฐ ๋๊ฒผ๋?" โ ์์ ๊น์ง.
- ์ฆ์ ์ง์ ์์ ์์ ํ๋ฉด ๋ค๋ฅธ ๊ฒฝ๋ก๋ก ๊ฐ์ ๋ฒ๊ทธ๊ฐ ์ฌ๋ฐํ๋ค.
4d. Condition-Based Waiting (Flaky Test ๋์)
setTimeout/sleep ๋์ ์กฐ๊ฑด ํด๋ง์ ์จ๋ผ.
- ํจํด:
waitFor(() => condition, timeout) โ 10ms ๊ฐ๊ฒฉ ํด๋ง + ํ์์์ ํ์.
- ํ์ด๋ฐ ํ
์คํธ๊ฐ ์๋๋ฐ ์์ ๋๋ ์ด๋ฅผ ์ฐ๋ฉด flaky์ ์ํ.
4e. Defense-in-Depth (๋ฒ๊ทธ ์์ ํ)
- ๋ฒ๊ทธ๋ฅผ ๊ณ ์น ๋ค, ๋ฐ์ดํฐ๊ฐ ์ง๋๋ ๋ชจ๋ ๋ ์ด์ด์ ๊ฒ์ฆ์ ์ถ๊ฐํ๋ผ.
- 4๊ฐ ๋ ์ด์ด: Entry Point โ Business Logic โ Environment Guard โ Debug Instrumentation.
- ํ ๊ณณ๋ง ๋ง์ผ๋ฉด ๋ค๋ฅธ ์ฝ๋ ๊ฒฝ๋ก๋ก ์ฐํ๋๋ค. ๊ตฌ์กฐ์ ์ผ๋ก ๋ถ๊ฐ๋ฅํ๊ฒ ๋ง๋ค์ด๋ผ.
4f. 3ํ ์คํจ ์ ์ํคํ
์ฒ ์์ฌ
- 3๋ฒ ์ฐ์ ์์ ์ด ์คํจํ๋ฉด ๋ฉ์ถ๊ณ ๊ตฌ์กฐ๋ฅผ ์์ฌํ๋ผ.
- ๊ฐ ์์ ์ด ๋ค๋ฅธ ๊ณณ์์ ์ ๋ฌธ์ ๋ฅผ ๋ง๋ค๋ฉด, ๊ทธ๊ฑด ๋ฒ๊ทธ๊ฐ ์๋๋ผ ์ค๊ณ ๋ฌธ์ ๋ค.
- ๋ ๊ณ ์น์ง ๋ง๊ณ ์ ์ /Oracle๊ณผ ์์ํ๋ผ.
trace.md Example
# Bug Trace: ISSUE-1234 โ Soccer team names appear in results
## AS-IS: Movie search results include soccer team names mixed in
## TO-BE: Movie search results should only contain movies
## Phase 1: Heuristic Top-3
### Hypothesis 1: Search query runs without category filter
- `SearchController.cs:45` โ calls `SearchService.Query()`
- `SearchService.cs:112` โ check categoryFilter parameter โ **passed as null** โ
Likely
### Hypothesis 2: DB table join is incorrect
- `SearchRepository.cs:78` โ check SQL โ join is correct โ Ruled out
### Hypothesis 3: Response mapping mixes in other entities
- `SearchMapper.cs:34` โ check mapping logic โ correct โ Ruled out
## Conclusion: Hypothesis 1 confirmed โ categoryFilter passed as null to SearchService.Query()