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. ss-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>/ (from a ticket id, or generated per ../WORKFLOW.md → "Choosing TASK-<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
ss-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 ss-explorer agent if your platform can spawn it (it ships with specship for Claude Code), else Explore / general-purpose, 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` (`symbol`) — not a line number; they drift>
- 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 ss-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
ss-review skill to run the full gate and draft the commit message before handing off.
- If the user declines, stop here.
- Under
ss-ship (autopilot), skip the question and resume the interrupted stage directly.
1---2name: ss-debug3description: 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.4---56# Debug78Goal: 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.910## When to use11- A test fails, the app misbehaves, or a regression appears.12- Asked to "debug", "fix this bug", or "why is this failing".13- Can be triggered at any point in the workflow (during `coding`, during `review`, or on a bug found later).1415## Shared task state16Part of the task pipeline — see `../WORKFLOW.md` for the full contract. `ss-debug` attaches to a task rather than being a fixed pipeline stage.17- **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.18- **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.)19- **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).2021## Attach the bug to a task22- **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`.23- **If it's a standalone bug with no task**: create a new `tasks/TASK-<ID>/` (from a ticket id, or generated per `../WORKFLOW.md` → "Choosing `TASK-<ID>`") and record it there. A `spec.md` is optional for a pure bugfix, but always keep the debug record.24- If unsure which task owns it, ask the user before picking.2526## Method — scientific debugging27Work from evidence, one hypothesis at a time. Don't guess-and-change.28291. **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 `ss-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.302. **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 **`ss-explorer`** agent if your platform can spawn it (it ships with specship for Claude Code), else **Explore** / `general-purpose`, 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).313. **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.324. **Fix** — apply the **minimum** change that addresses the root cause. Stay surgical; don't refactor unrelated code or fix symptoms downstream.335. **Verify** — the failing test now passes, the full suite stays green, and the original reproduction is gone.346. **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.3536If 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.3738## Output: record the investigation in the task39Append 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.4041```markdown42---43task: TASK-<ID>44title: <short title>45type: debug46created: <YYYY-MM-DD HH:MM +TZ>47updated: <YYYY-MM-DD HH:MM +TZ>48---4950# Debug Log: TASK-<ID>5152## BUG1 — <one-line summary>53- status: fixed # investigating | fixed | wont-fix54- date: <YYYY-MM-DD HH:MM +TZ>55- symptom: <observed wrong behavior + error/stack>56- reproduce: <steps / failing test that triggers it>57- ruled out: <hypotheses tried and disproven, with the evidence — omit if none>58- root cause: <the actual underlying cause, at `path` (`symbol`) — not a line number; they drift>59- fix: <what changed and why, files touched>60- regression test: <test name/path guarding it>61- related: <R#/AC#/S# affected, or other code with the same risk>62```6364Update the entry's `status` as you go, and bump `updated:` on each change so the log tracks the fix over time.6566## When done67- State plainly: reproduced? root cause found? fix verified with the full suite green?68- **Do not run `git add` / `commit` / `push`** unless the user asks.6970## External phase execution71If 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 `ss-ship` — skip "Next step" entirely.7273**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.7475## Next step76The `BUG#` entry and its regression test are the input for whatever stage resumes.7778- **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.79- **Standalone bugfix** (no interrupted stage) — suggest the `ss-review` skill to run the full gate and draft the commit message before handing off.80- If the user declines, stop here.81- Under `ss-ship` (autopilot), skip the question and resume the interrupted stage directly.