22 KiB
Extracted from implement/SKILL.md (Mode: bugfix) — moved verbatim 2026-08-25, ticket [org-internal #3381].
Mode: bugfix
Reproduce, isolate, and fix a bug with a regression test to prevent recurrence. For small, localized bugs, use standalone mode — the existing system behavior is the specification. For large, complex bugs, route through the full quality pipeline.
Process Overview
Every diamond below is a gate Developers rationalize skipping — especially under "the bug is obvious" pressure.
digraph bugfix {
rankdir=TB;
node [shape=box, fontname="Helvetica"];
repro [shape=diamond, label="Bug reproduces?"];
norepro [label="STOP: report cannot-reproduce\n(do not guess-patch)"];
rc [shape=diamond, label="Root cause found\n(not just symptom)?"];
symptom [label="Go deeper — do NOT\npatch the symptom"];
route [shape=diamond, label="Routing:\nstandalone vs pipeline?"];
escalate [label="Uncertain → escalate\nto pipeline mode"];
rtest [shape=diamond, label="Regression test\nFAILS before fix?"];
notest [label="Test does not cover\nthe bug — rewrite it"];
fix [label="Phase 4: Fix\n(one change, root cause only)"];
green [shape=diamond, label="Regression test PASS\n+ full suite green?"];
done [shape=doublecircle, label="Phase 5/6:\nSelf-Check + Report"];
repro -> norepro [label="no"];
repro -> rc [label="yes"];
rc -> symptom [label="no"];
rc -> route [label="yes"];
route -> escalate [label="uncertain"];
route -> rtest [label="standalone"];
rtest -> notest [label="passes already"];
rtest -> fix [label="fails (confirmed)"];
fix -> green;
green -> fix [label="no: fix + re-run"];
green -> done [label="yes"];
}
Execution Modes
| Mode | Entry Point | Scope Source | Review Gate | Verify Gate |
|---|---|---|---|---|
| Standalone | User says "fix this bug" | Bug report + code | Optional (>20 lines or ≥3 files) | None |
| Pipeline | User requests full-process bugfix, or auto-escalation | Bug report → requirements → design → plan → implement | Mandatory | Mandatory |
Routing override (ticket-seeded): the Optional/None gate declarations above apply to user-initiated standalone mode. When the ticket carries a
Kind/*route whosekeep_gatesincludesreview-code/verify(e.g.Kind/Bug,Kind/Testing— see<instance-root>/workflow-routing.yaml), those gates are MANDATORY regardless of size. Percore/rules/workflow-routing.md, a gate is mandatory if EITHER the route OR the skill requires it; skipping is valid only when BOTH agree it is skippable.
In pipeline mode, the bug report becomes a bugfix work item that flows through the full requirements → design → plan → implement → review-code → verify pipeline.
Role & Responsibilities
The bugfix is owned and executed by the Developer (Worker). The Developer owns both implementation and bugfix — same role, same skill set.
The Developer is responsible for:
- Reproducing the bug from the description.
- Identifying the root cause (not just patching the symptom).
- Writing a regression test that fails before the fix and passes after.
- Applying the minimal surgical fix — one change, one purpose.
- Running the full test suite to confirm no regressions.
The Builder's role is to present the bugfix report and route it to code review if the change is non-trivial (> 20 lines or touches ≥ 3 files).
Tester focus for bugfix
The Tester role in bugfix writes regression tests and, uniquely, intervenes BEFORE the fix (a regression test must fail before the fix to prove the bug exists):
- Failing regression test — read
repro-notes.md(the Developer's reproduction + root-cause analysis from Phases 1–2), write a test that exercises the exact bug path and FAILS with the bug's symptom. This MUST happen before Phase 4 (Fix), not after — it is Phase 3. - Passing confirmation — after the Developer's fix, the same test MUST pass (the Developer's green run in Phase 4 verifies this).
- Boundary regression tests — inputs adjacent to the bug trigger, similar conditions that must NOT trigger the fix (guards against over-fixing), and error paths near the root cause.
Bugfix-specific orchestration (overrides the standard role-split flow):
Because a regression test must fail BEFORE the fix, the bugfix role split inverts the standard orchestration — the Tester dispatches between Phase 2 and Phase 4, not after the fix:
[Worker: developer] Phase 1 reproduce + Phase 2 root cause
→ write repro-notes (reproduction steps, root cause, bug path,
expected behavior)
↓ persist: wiki page `{slug}/repro-notes` (gitea_wiki__create_page)
[Worker: tester] read repro-notes → Phase 3 write failing regression test
(confirms FAIL before fix)
↓ persist: wiki page `{slug}/test-report` (gitea_wiki__create_page, failing test confirmed)
[Worker: developer] Phase 4 fix → run test:changed to green
→ write impl-notes (post-fix behavior contract)
↓ persist: wiki page `{slug}/impl-notes` (gitea_wiki__create_page, post-fix behavior contract)
[Worker: tester] supplement boundary regression tests → run test:changed
↓ persist: update wiki page `{slug}/test-report` (gitea_wiki__update_page, final)
— consumed by the human stakeholder / next iteration planning for DoD regression-test evidence
For small, single-file bugfixes with an obvious fix, a single Developer Worker may write the failing test, fix, and confirm green in one invocation — the split is optional for trivial fixes (Phase 3 + Phase 4 in one session). Force the split when the fix touches ≥ 2 files or the root cause spans ≥ 2 levels of indirection.
Pipeline-mode bugfixes route through the implement pipeline (see ### Mode: implement (default)), with the bugfix-specific Tester focus above layered on top of the standard role-split orchestration.
Preconditions (standalone)
Before starting the bugfix, confirm:
- Bug description exists (user's message, issue tracker link, or error log).
- Existing codebase is accessible.
core/checklists/bugfix.mdis accessible.
If the user describes a symptom without specifics, ask for:
To fix this bug, I need:
1. What is the expected behavior? (what should happen)
2. What is the actual behavior? (what happens instead)
3. Steps to reproduce.
4. Any error messages, logs, or stack traces.
Routing Decision
After Phase 1 (reproduce) and Phase 2 (root cause), the Developer evaluates whether the fix qualifies for standalone or pipeline mode:
Escalate to pipeline mode when ANY of:
| Condition | Reason |
|---|---|
| Fix touches ≥ 5 files | Cross-file changes need design review and integration tests |
| Fix spans ≥ 2 modules / components | Multi-module fixes need architectural validation |
| Root cause is in a design-level decision (protocol, schema, architecture) | Design change needs requirements + design review |
| Fix requires data migration or schema change | Schema changes need data design review and migration plan |
| Fix changes a public API or interface contract | API changes need interface design review and compatibility check |
| Fix introduces a new dependency or changes an existing one | Dependency changes need review (DGN dimension, code review) |
| Estimated lines changed > 50 | Large change carries high regression risk |
| User explicitly requests full-process bugfix | User wants quality gates |
Stay in standalone mode when ALL of:
| Condition |
|---|
| Fix is ≤ 4 files |
| Fix is ≤ 1 module / component |
| Fix is a logic error, not a design error |
| No data migration or schema change |
| No API or interface contract change |
| No dependency change |
| Estimated lines changed ≤ 50 |
If the Developer is uncertain, escalate. A false pipeline escalation costs a few extra review rounds. A false standalone decision risks missing a quality gate on a complex change.
Big-bug relabel rule ([org-internal #3061]) — before the generic escalation below, split the triggers by kind:
- Design-level triggers (root cause is a design decision — protocol /
schema / architecture; shared-contract or public-API change; data
migration): do NOT push through bugfix and do NOT run the legacy pipeline
escalation — relabel the ticket
Kind/Featureand reroute via Step 0 (DAG route; a 1–3 node small DAG is the expected shape for a single design-level fix). The fix work already done (repro notes, root cause) becomes node input, not wasted work. - Mechanical size triggers only (many files / many lines, same design): stay in bugfix — batch the change into iterations and keep the review-code + verify gates. Scale alone never justifies a relabel.
When escalating, the Developer pauses after Phase 2, reports the routing decision, and asks the user to confirm pipeline escalation:
This bugfix qualifies for pipeline mode:
- {N} files across {M} modules
- Root cause: {design-level issue}
- Estimated lines: {N}
→ Route through requirements → design → plan → implement → review → verify?
(yes / no — proceed with standalone)
Phase 1 — Understand & Reproduce
-
Read relevant code — find the module/component likely responsible for the bug. Use
grepfor error messages,globfor related files. -
Check existing tests — do existing tests cover this code path? If a test exists but passes, the bug is in the test or in an uncovered branch.
-
Reproduce — run the relevant test(s) or manually trigger the bug. Confirm the actual behavior matches the bug report. Document the reproduction:
## Reproduction **Steps**: 1. {step} 2. {step} **Expected**: {what should happen} **Actual**: {what happens} -
If the bug CANNOT be reproduced, stop and report:
Cannot reproduce the bug. Here's what I tried: - {step 1} - {step 2} → Is the environment different? Are there missing steps? Does a specific data state trigger it?
Phase 2 — Isolate Root Cause
Trace from the symptom to the root cause:
- Symptom: surface-level error (e.g. "500 on login").
- Proximate cause: the code that throws or returns wrong (e.g. "password hash comparison returns false for valid password").
- Root cause: the underlying defect (e.g. "password hashing config changed in commit abc123 but the stored hashes were not re-hashed").
## Root Cause Analysis
**Symptom**: {error message or wrong behavior}
**Proximate cause**: {file}:{line} — {what the code does wrong}
**Root cause**: {underlying defect — config, data, logic, or assumption}
**Introduced in**: {commit hash or version if known}
Rules:
- If you're fixing a symptom (e.g. adding a null check where the real bug is that null should never reach that line), stop and go deeper.
- If you can't find the root cause after examining 3 levels of indirection, pause and report findings. Do NOT apply a surface-level patch.
- After Phase 2, evaluate the routing decision (see Routing Decision table above). If the fix qualifies for pipeline mode, pause and present the escalation prompt before proceeding to Phase 3.
Phase 3 — Write a Regression Test
Before fixing, write a test that proves the bug exists:
- Write a test that exercises the bug path with the failing inputs.
- Run the test — it MUST fail with the bug's symptom.
- The test must be specific: test the exact condition that was broken, not a general "endpoint returns 200" test.
## Regression Test
- **File**: {path to test file}
- **Test name**: {test function name}
- **What it verifies**: {the expected behavior that was broken}
- **Fails before fix**: ✅ (confirmed)
Rules:
- If you cannot write a test that fails (bug is non-deterministic, environment-
specific), write the most targeted test you can and mark it
[flaky]. - The test must fail NOW, before you apply the fix. If it passes already, the test does not cover the bug.
Phase 4 — Fix
Apply the minimum change that resolves the root cause:
- One conceptual change per fix — do not bundle a bugfix with refactoring, style changes, or "while I'm here" improvements.
- Fix the root cause, not the symptom. If the root cause is in a different file than the symptom, fix it there.
- Update only what's necessary — if fixing a null-safety bug requires adding a null check in one place, add one null check, not a comprehensive null-safety overhaul of the entire module.
- Run the regression test — it MUST pass.
- Run the relevant unit tests — all existing tests must still pass.
Phase 5 — Self-Check & Report
Self-Check
- Typecheck:
bun typecheck— zero errors. - Lint:
bun oxlint --deny-warnings— zero errors. - Full test suite:
bun run test:parallel— all tests pass (new + existing). - Checklist: verify every item in
core/checklists/bugfix.md. - Publish bugfix report: write the bugfix report as a wiki page via
wiki 读写 API(见 TERMINOLOGY)with page_name{slug}/bugfix-report(_shared/gitea-write-patterns.mdPattern 1).
Report
## Bugfix Report
**Bug**: {one-line description}
**Root cause**: {file}:{line} — {explanation}
**Fix**: {file} — {single-sentence description of change}
**Lines changed**: {N}
**Regression test**: {test file}:{test name}
### Verification
- Regression test: {PASS | FAIL}
- Full test suite: {N} passed, 0 failed
- Typecheck: ✅
- Lint: ✅
### Files Changed
| File | Lines | Purpose |
| ------------------- | ------ | ------------------------------------------------------ |
| `src/auth/login.ts` | +3, -1 | Fix password hash comparison when salt version changes |
---
**Handoff**: {if changes > 20 lines or ≥ 3 files → run `core/skills/review-code/SKILL.md`
| otherwise → fix complete, no review needed}
> **Routing override**: when the ticket carries a `Kind/*` route whose
> `keep_gates` includes `review-code` / `verify` (e.g. `Kind/Bug`,
> `Kind/Testing`), those gates are MANDATORY even for small fixes — the
> "no review needed" branch above does not apply (see the Execution Modes
> routing-override note above).
Phase 5.5 — Issue Checklist Sync (standalone bugfix)
In standalone-bugfix mode there are no skill-exit boundaries between commit,
PR, review, and CI — without explicit sync points the issue goes stale. Per
the issue-checklist-sync L1 rule, sync at each externally visible
transition (skip any step if no source issue exists):
| When | Sync action |
|---|---|
| After the fix commit | Check off fix-delivered ACs with _(commit {sha}: file)_ |
| After PR creation | Ensure the ## 当前状态 section exists (the PR row is auto-written by the status-sync poller — see issue-checklist-sync.md § Automated sync) |
| After review convergence | Review-related ACs get _(reviewed: round N PASS)_ (done by review-code Phase E 2.7) |
| On CI state transitions | Update process-AC progress (e.g. "N consecutive green") with run number |
| At verify PASS / close | Final sweep per verify Phase 5.6 |
Bugfix mode delegates the "after commit" step to the same mechanics as Phase
4.6 above (fetch issue body → map - [ ] items → 工单 API(见 TERMINOLOGY)update), and
the PR-creation step to Phase 4.7.
Phase 6 — Approval
Present the report:
Bug fixed: {one-line description}
- Root cause: {file}:{line}
- {N} lines changed in {M} files
- Regression test added: {test name}
- Full test suite: ✅
→ {if review needed: "Run code review?" | else: "Fix complete. Approve?"}
Common Rationalizations (bugfix)
Bugfixes fail from pressure and false confidence far more than from ignorance — "the bug is obvious" is the thought that precedes a symptom patch, a bundled diff, or a regression that surfaces weeks later. These are the excuses that precede every reopened bug. If you catch yourself thinking any row's "Excuse", stop: the "Reality" column is the exact rule you are about to break.
| Excuse | Reality (the rule being broken) |
|---|---|
| "Just add a null check where it crashes" | Symptom-patching. Phase 2: if you are fixing a symptom, stop and go deeper — the real defect is whatever let null reach that line. |
| "Can't reproduce, but I'm sure it's X" | Phase 1: if the bug cannot be reproduced, stop and report. Guess-patching a non-reproduced bug fixes nothing verifiable. |
| "3 levels deep, can't find it, patch the symptom" | Phase 2: after 3 levels of indirection with no root cause, pause and report — do NOT apply a surface patch. |
| "Bug's obvious, I'll fix then add the test" | Phase 3: the regression test MUST fail before the fix. Fix-first means you test your fix, not the bug. |
| "Test passed immediately, ship it" | Phase 3 Rules: a test that passes before the fix does not cover the bug — rewrite it until it fails. |
| "While I'm in this file, also clean up…" | Phase 4 rule 1: one conceptual change per fix. Bundling refactors/style/other-fixes pollutes the regression signal. |
| "Make the whole module null-safe while I'm here" | Phase 4 rule 3: update only what is necessary. Over-fixing turns a 3-line surgical fix into a high-risk diff. |
| "Fix is isolated, skip the full suite" | Phase 4 rule 5 + Phase 5: the full suite catches regressions your isolated view cannot. |
| "4 files but one module, standalone's fine" | Routing Decision: escalate when uncertain. A false-standalone call skips quality gates on a complex change. |
Incident Triage Carve-Out
When the bugfix occurs under active production incident pressure (user-facing outage, on-call escalation), the Phase 2→3 ordering can be temporarily relaxed — but never skipped:
- A stop-gap (symptom patch) MAY ship first to restore service.
- BUT the full root-cause trace + failing regression test + proper root-cause fix MUST land in the same incident window — never deferred to "tomorrow" or "a follow-up ticket".
- If you defer, you have not fixed the bug — you have shipped a symptom patch with a promise. Promises are not regression tests.
This carve-out exists because the rationalization table above cannot resolve a legitimate priority conflict (service down vs process discipline). It resolves it by permitting triage but forbidding deferral.
Pipeline Mode (bugfix)
Legacy path retired ([org-internal #3072] phase 3, 2026-08-21): the full requirements-elicitation → design → review-artifact(design-space) → plan-iterations → review-artifact(plan) front-end was archived (
<instance-root>/archive/skills/). A big bug that needs a design-level decision now relabelsKind/Featureand enters the DAG route (see the big-bug relabel rule above) — repro + root-cause notes carry over as node input. The abort criteria below still apply to any multi-stage bug run before code is written.
When a bugfix escalates beyond standalone scope, the bug report becomes a pipeline input; the original bugfix phases (reproduce, root cause, regression test, fix) are embedded within the implement stage, and review-code + verify remain mandatory gates.
Pipeline Abort Criteria
Before any code is written in pipeline mode, abort the pipeline if ANY of:
| # | Condition | Action |
|---|---|---|
| 1 | Bug no longer reproduces after environment change (strace re-isolation returns 0 reproductions, user confirms symptom resolved) | Write ABORT to wiki page {slug}/ABORT (wiki 读写 API(见 TERMINOLOGY)), preserve all completed artifacts, run retrospective |
| 2 | Root cause hypothesis is falsified during re-isolation (e.g., strace shows suspected git spawn is NOT hanging) | Write ABORT.md, escalate to Architect for design revision OR abort pipeline |
| 3 | Bug is resolved by external change (new binary build, dependency update, OS/kernel patch) | Write ABORT.md with resolution evidence, close without code changes |
| 4 | Reproduction confidence < 3/5 after re-isolation attempt | Write ABORT.md if confidence cannot be improved within 1 re-isolation iteration |
Abort procedure:
- Write ABORT to wiki page
{slug}/ABORT(wiki 读写 API(见 TERMINOLOGY)) documenting the reason, evidence, and which artifacts are preserved. - Do NOT commit or merge the bugfix branch (no code was written).
- Run retrospective to extract process improvements.
- Archive artifacts to wiki page
_archive/{slug}/(wiki 读写 API(见 TERMINOLOGY)) after retrospective.
Scope: these criteria apply before the implement stage. Once code is written, the pipeline proceeds through review-code → verify — abort is no longer valid.
Stage: Implement → Code Review → Verify
On the DAG route a bug-fix node's spec (ACs tracing to the repro + root cause)
lives in {epic-slug}/dag; the Developer follows the bugfix Phases 1–6 (from
standalone mode above) as the implementation method, then produces the
standard implementation report (see ### Mode: implement (default), Phase 5).
Code review runs all 10 dimensions against the bugfix changes. Verify runs
the full DoD matrix including regression tests, integration tests, and NFR
validation. Output pages: code review → {slug}/reviews/code/final/report;
verification → {slug}/05-verify-iteration-1.