| name | debug |
| model | opus |
| description | Diagnose and fix a bug methodically, and track the investigation in the owning task. Use when something is broken, a test fails, behavior is wrong, or asked to "debug X", "fix this bug", "why is this failing". Reproduces, finds root cause, fixes minimally, verifies, and records it in tasks/TASK-<ID>/debug.md. Attaches to the spec → plan → coding → review workflow whenever a defect appears. |
Debug
Goal: find the root cause of a defect and fix it with the smallest correct change — never patch symptoms. Record the investigation so it's not lost.
When to use
- A test fails, the app misbehaves, or a regression appears.
- Asked to "debug", "fix this bug", or "why is this failing".
- Can be triggered at any point in the workflow (during
coding, during review, or on a bug found later).
Shared task state
Part of the task pipeline — see ../WORKFLOW.md for the full contract. debug attaches to a task rather than being a fixed pipeline stage.
- Hydrate: resolve the owning
TASK-<ID> (see "Attach the bug to a task" below), read its task.md, and spec.md/plan.md as needed. If no task owns it, create a new one.
- Checkpoint: append the
BUG# entry to debug.md; update task.md — set debug artifact open-bugs/clear, set status: blocked while a blocker bug is open (note it in Blocked by:), bump updated:, append a Pipeline Log line carrying your agent label (format: ../WORKFLOW.md → Agent handoff). Clear back to active when fixed and return to the stage you came from. (blocked is involuntary; see ../WORKFLOW.md → Status values.)
- Lessons: read
tasks/LESSONS.md at hydrate and apply its rules; if the bug's root cause was a process mistake (e.g. a skipped verify, a stale plan), fix the process trace too and append an L# entry there (see ../WORKFLOW.md → Lessons).
Attach the bug to a task
- If the bug belongs to an existing task (it's in code that task touched, or found during its
coding/review): track it in that task's folder → tasks/TASK-<ID>/debug.md.
- If it's a standalone bug with no task: create a new
tasks/TASK-<ID>/ (sequential or from a ticket id) and record it there. A spec.md is optional for a pure bugfix, but always keep the debug record.
- If unsure which task owns it, ask the user before picking.
Method — scientific debugging
Work from evidence, one hypothesis at a time. Don't guess-and-change.
- Reproduce — no fix before a repro. Get a reliable, minimal reproduction and watch it fail for the expected reason before touching any code; a fix you can't demonstrate failing is a guess. If you can't reproduce it, that is the investigation — add logging, tighten the conditions, gather evidence — don't "fix" what you can't observe. Capture the exact error/stack, inputs, and environment, and write the failing test that triggers the bug — it becomes the regression test. When
coding hands off after its 3-attempt rule, start from its failing verify: command as the repro and read what was already tried — those disproven attempts are evidence, not a path to retread.
- Locate — narrow where it happens: read the stack trace, follow the data, add targeted logging or use the debugger, bisect if needed. Use
grep/rg to trace the code path. For a deep, noisy investigation (large logs, many files), delegate the search to the Explore / general-purpose agent and ask only for the suspected location + evidence — then do the fix and verification yourself in this thread. If your platform can't spawn subagents, do the same investigation inline in the main thread (../WORKFLOW.md → In-stage subagents — delegation is an optimization, never a precondition).
- Hypothesize — one at a time, revert what fails. State the suspected root cause explicitly before changing anything, and confirm it with evidence (a log, a value, a failing assertion), don't assume. If the evidence disproves it, revert that attempt's changes before testing the next hypothesis — stacked speculative edits pollute the diff and can mask the symptom without fixing the cause. Record each ruled-out hypothesis (and what disproved it) in the
BUG# entry so the investigation survives interruption and nobody retreads it.
- Fix — apply the minimum change that addresses the root cause. Stay surgical; don't refactor unrelated code or fix symptoms downstream.
- Verify — the failing test now passes, the full suite stays green, and the original reproduction is gone.
- Prevent regression — and sweep for siblings. Keep the test that reproduces it. Then hunt the same bug class elsewhere:
grep/rg for the pattern the root cause exposed (same misused API, same copy-pasted logic, same unchecked input). Trivially-same instances: fix in this BUG#. Bigger ones: record them in related: as explicit follow-ups — a root cause usually has siblings, and finding them now is far cheaper than a second debug session later.
If the root cause turns out to be a wrong/missing requirement, flag it back to spec.md (add an R#/edge case) rather than silently coding around it.
Output: record the investigation in the task
Append the bug to tasks/TASK-<ID>/debug.md (create it if missing). Each bug is a BUG# entry — never renumber, only append — so it reads as a chronological debug log for the task.
---
task: TASK-<ID>
title: <short title>
type: debug
created: <YYYY-MM-DD HH:MM +TZ>
updated: <YYYY-MM-DD HH:MM +TZ>
---
# Debug Log: TASK-<ID>
## BUG1 — <one-line summary>
- status: fixed # investigating | fixed | wont-fix
- date: <YYYY-MM-DD HH:MM +TZ>
- symptom: <observed wrong behavior + error/stack>
- reproduce: <steps / failing test that triggers it>
- ruled out: <hypotheses tried and disproven, with the evidence — omit if none>
- root cause: <the actual underlying cause, at `path:line`>
- fix: <what changed and why, files touched>
- regression test: <test name/path guarding it>
- related: <R#/AC#/S# affected, or other code with the same risk>
Update the entry's status as you go, and bump updated: on each change so the log tracks the fix over time.
When done
- State plainly: reproduced? root cause found? fix verified with the full suite green?
- Do not run
git add / commit / push unless the user asks.
External phase execution
If an orchestrator launched you for the debug phase only (../WORKFLOW.md → External phase execution), the rules there override the handoff below. In short: confirm the envelope with specship check TASK-<ID> --phase debug --actor <codex|claude-code> --expect-revision <n> --json (exit 0 or stop), work from the named task's artifacts and the repro alone, write debug.md plus the regression test, then checkpoint task.md last with revision +1 and stop. Don't invoke the stage you interrupted, don't call ship — skip "Next step" entirely.
Carry the resume phase in the state, not in your head. While bugs are open (debug: open-bugs), resume_phase: must name the phase debug interrupted — coding or review. When you clear the last blocker bug (debug: clear), set next_phase: back to that phase and clear resume_phase:. Both halves are checked: open bugs without a resume_phase, or a cleared debug that left one behind, fail the gate.
Next step
The BUG# entry and its regression test are the input for whatever stage resumes.
- Interrupted
coding or review — once the fix is verified, ask the user whether to resume that stage — e.g. "Bug đã fix xong, bạn có muốn tôi tiếp tục coding/review không?". If yes, immediately invoke that skill (via the Skill tool); it re-hydrates from task.md and picks up where it left off.
- Standalone bugfix (no interrupted stage) — suggest the
review skill to run the full gate and draft the commit message before handing off.
- If the user declines, stop here.
- Under
ship (autopilot), skip the question and resume the interrupted stage directly.