# Evidence Driven Bugfix

> 證據驅動除錯流程。Use for: (1) Bug 修復的結構化流程（重現 -> failing test -> 根因 -> 最小修復 -> 回歸防護）, (2) 防止盲目猜測修復, (3) 確保每次修復可驗證、可追溯、可累積。Use when: 收到 bug report、測試失敗需要調查、生產環境問題需要修復、任何非新功能的程式碼修正。

- Skill: `tarrragon/evidence-driven-bugfix` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tarrragon/evidence-driven-bugfix`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tarrragon/evidence-driven-bugfix/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: tarrragon (https://skillmd.com/u/tarrragon)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/tarrragon/evidence-driven-bugfix

---


# /bugfix - 證據驅動除錯流程

核心原則：**先有證據，才能動手。**

沒有最小重現、沒有 failing test、沒有根因證據之前，禁止直接改程式碼。

---

## 反模式

| 反模式 | 後果 |
|--------|------|
| 「我大概知道怎麼修，先 patch 再說」 | 修到症狀不是根因 |
| 跳過 failing test 直接改 code | 不知道到底修對沒 |
| 沒有調查紀錄 | 下次同類 bug 又得重查一次 |
| 一邊查一邊改 | 改動範圍失控，引入新問題 |
| 修改測試斷言來讓測試通過 | 測試失去防護價值，bug 仍存在 |
| 硬編碼預期值繞過測試 | 下次資料變動立即再次失敗 |
| 不查根因直接改程式碼試試看 | 表面通過但根因未解，同類 bug 反覆出現 |

---

## 流程模式

本流程有兩種執行模式，依據緊急程度選擇：

| 模式 | 適用場景 | 必做階段 | 可延後階段 |
|------|---------|---------|-----------|
| **標準模式** | 一般 bug 修復 | Stage 1-8 全部 | 無 |
| **Hotfix 模式** | 生產環境緊急問題 | Stage 1-5 | Stage 6-8（24 小時內補齊） |

---

## 與 pre-fix-eval 的銜接

`/pre-fix-eval` 是錯誤的**入口分類器**，`/bugfix` 是**修復執行流程**。

| pre-fix-eval 產出 | bugfix 入口 | 說明 |
|-------------------|------------|------|
| 有初步根因定位（Stage 4 完成） | 從 Stage 2 開始 | 根因假設已有，直接補 failing test 驗證 |
| 只有錯誤分類（Stage 1-3 完成） | 從 Stage 1 開始 | 需要完整重現和分析 |
| 直接由用戶觸發（無 pre-fix-eval） | 從 Stage 1 開始 | 完整流程 |

---

## 八階段流程

```
Stage 1: 最小重現         [執行者: 認領 Ticket 的開發者]
    |
Stage 2: 補 Failing Test  [執行者: 同上]
    |
Stage 3: 驗證根因假設      [執行者: 同上 / incident-responder]
    |
Stage 4: 實作最小修復      [執行者: 語言對應的 developer agent]
    |
Stage 5: 回歸防護         [執行者: 同 Stage 4]
    |
Stage 6: 規格合規審查      [執行者: /parallel-evaluation 派發]
    |
Stage 7: 程式碼品質審查    [執行者: /parallel-evaluation 派發]
    |
Stage 8: 結案報告         [執行者: 認領 Ticket 的開發者]
```

### 與既有工具的銜接

| 階段 | 對應的專案工具 |
|------|--------------|
| 進入本流程前 | `/pre-fix-eval`（錯誤分類 + Ticket 開設） |
| Stage 3 深度分析 | `incident-responder`（複雜根因需要時派發） |
| Stage 4 實作 | 語言對應的 developer agent |
| Stage 6-7 審查 | `/parallel-evaluation`（派發後以其檢查清單為準） |
| Stage 8 結案 | `/doc-flow`（工作日誌）、`/error-pattern add`（錯誤模式） |

---

## Stage 1：最小重現

**目標**：把「使用者描述的症狀」轉成「可判斷的重現條件」。

**執行者**：認領 Ticket 的開發者。

**產出**：重現步驟或腳本 + 環境條件記錄。

### 標準路徑（確定性 bug）

| 檢查項 | 說明 |
|--------|------|
| 重現步驟是否穩定？ | 連續執行 3 次都能觸發 |
| 環境條件是否記錄？ | 瀏覽器版本、OS、資料狀態 |
| 是否為最小條件？ | 移除任何一步就無法重現 |

### 替代路徑（非確定性 bug）

適用場景：race condition、時序問題、Chrome Service Worker 生命週期問題、特定網站結構下觸發。

| 替代條件 | 說明 |
|---------|------|
| 有觸發條件描述 | 能說明在什麼狀態/時序下容易觸發 |
| 有日誌或錯誤截圖證據 | 至少有一次觸發的完整記錄 |
| 有機率估計 | 如「十次操作約觸發三次」 |

滿足以上三項即可通過閘門，進入 Stage 2 時使用防禦性測試策略（測試邊界條件而非精確重現）。

**閘門**：標準路徑或替代路徑任一滿足即可進入 Stage 2。兩者皆不滿足 → 回頭補充資訊。

---

## Stage 2：補 Failing Test

**目標**：把重現步驟轉成穩定失敗的測試或結構化驗證計畫。

**執行者**：認領 Ticket 的開發者。

### 標準產出（可自動化）

至少一個 failing test，精確描述「預期行為」與「實際行為」的差異。

| 要求 | 說明 |
|------|------|
| 測試命名 | 描述 bug 行為，如 `test_should_not_crash_when_empty_input` |
| 斷言精確 | 斷言預期的正確行為，不是斷言「不 crash」 |
| 獨立可執行 | 不依賴其他測試的副作用 |

### 替代產出（無法自動化）

適用場景：Chrome 跨 context 通訊、popup/background 互動、UI 渲染問題等 Jest mock 無法真實覆蓋的場景。

產出一份**結構化手動驗證計畫**：

```markdown
## 手動驗證計畫

### 前置條件
[環境設定、資料準備]

### 驗證步驟
1. [具體操作步驟]
2. [預期結果]
3. [實際結果欄位（執行時填寫）]

### 通過標準
[明確的 pass/fail 判斷條件]
```

手動驗證計畫視為技術債，在 Stage 8 記錄，後續版本補自動化測試。

**閘門**：標準產出或替代產出任一完成即可進入 Stage 3。

---

## Stage 3：驗證根因假設

**目標**：帶著 failing test 回頭讀 code，找出真正失敗的原因。

**執行者**：認領 Ticket 的開發者。複雜問題可派發 `incident-responder`。

**流程**：

1. 列出所有合理的根因假設（不限數量；若超過 5 個，先依影響範圍排序，取前 3 個驗證）
2. **執行「假設粒度自檢」**（見下方清單，反模式 3 防護）
3. 對每個假設設計驗證方式（加 log、讀 code、LSP 追蹤引用）
4. 逐一排除，直到定位唯一根因
5. 記錄排除過程（供日後參考）

### 假設粒度自檢清單（W17-104 反模式 3 防護）

**Why**：W17-102 Test G 設計時用「PM 是否已 Edit 過該檔」（檔案層級）作變因，否證後直接跳到「不是 PM 接觸史」結論。但真正的變因可能在不同粒度——「PM 是否已 Edit 過 prefix（`.claude/`）內任一檔」（前綴層級）。同一個變因在不同粒度可能得到相反結論。

**Consequence**：粒度錯誤會讓對照實驗的否證跳到錯誤結論。否證「該檔層級的 PM 接觸史」≠ 否證「整個 PM 接觸史變因」。

**Action**：列假設後、設計驗證實驗前，對每個假設執行粒度自檢：

| 粒度層 | 範例（以「檔案修改史」為變因）| 範例（以「session 狀態」為變因） |
|------|----|----|
| 檔案層 | 該特定檔是否被改過 | 該檔在當前 session 是否被觸碰 |
| 前綴 / 目錄層 | `.claude/` 下任一檔是否被改 | session 內是否曾操作該前綴 |
| 模組 / 命名空間層 | 同模組任一檔是否被改 | session 內是否載入該模組 |
| Session 層 | 任一 session 內檔案修改紀錄 | 當前 session 是否處於某狀態（已 commit / 已 pytest） |
| 跨 session 層 | 跨 session 累積修改 | 跨 session 累積觸發狀態 |
| 時間層 | 修改時間段（過去 N 分鐘 / N 小時 / N 天）| session 啟動時間距今 |
| 全域層 | 系統全域狀態（CC 版本 / 環境變數 / OS） | runtime 全域狀態 |

**自查問句**（每個假設都要問）：

- 我選的粒度是「檔案層」「前綴層」「模組層」還是更高？
- 是否有更細粒度（檔案內的特定行）或更粗粒度（整個目錄/session）的同類變因？
- 否證該粒度後，是否需要驗證其他粒度才能完整否證該假設族？

**強制動作**：每個假設至少考慮 2 個相鄰粒度層；若只考慮單一粒度即進入驗證實驗，標記為「粒度未充分」並補實驗或在結論中明示。

粒度修正的教訓：先以大粒度否證後，若未驗證相鄰粒度即下結論，假設族否證不完整（需補相鄰粒度實驗）。

**工具選擇**：

| 需求 | 工具 |
|------|------|
| 追蹤函式呼叫鏈 | Serena `find_referencing_symbols` |
| 搜尋特定模式 | Grep / `search_for_pattern` |
| 理解符號結構 | Serena `get_symbols_overview` |

### 外部因素退出點

若 Stage 3 確認根因不在本專案（如外部平台 API 行為變更、目標站點改版、上游套件行為變更）：

1. 記錄證據（API 文件、changelog、網站截圖）
2. 開 workaround Ticket（類型 IMP，標題含「workaround」）
3. 退出本流程，在 Stage 8 產出簡化報告

**閘門**：根因已確認（含外部因素確認） → 進入 Stage 4。禁止「看起來像是這裡的問題」就動手。

**迭代上限**：Stage 3↔4 來回不超過 3 次（Hotfix 模式下為 2 次）。超過上限仍無法定位 → 升級為 incident，派發 `incident-responder` 深度分析。Stage 6/7 回退到 Stage 4 後，迭代計數器重置（因根因已確認，問題性質不同）。

---

## Stage 4：實作最小修復

**目標**：只改讓 failing test 通過的最小範圍。

**執行者**：語言對應的 developer agent。

| 原則 | 說明 |
|------|------|
| 最小變更 | 修改檔案數 <= 3，diff 行數建議 < 50 行 |
| 超過需說明 | 超過上述範圍須在 Ticket log 記錄理由 |
| 不擴大範圍 | 發現周圍有問題 → 開新 Ticket，不在此修 |
| 確認 test 變綠 | 修完立即執行 Stage 2 的 failing test |
| 修產品程式碼，不修測試 | 見下方「測試完整性保護」 |

### 測試完整性保護

修復的目標是讓**產品程式碼**符合**測試描述的正確行為**，而非反過來。

| 禁止行為 | 範例 | 為什麼禁止 |
|---------|------|-----------|
| 修改斷言值 | `expect(result).toBe(3)` 改成 `toBe(5)` | 測試描述的是正確行為，改斷言 = 改需求 |
| 硬編碼通過 | 函式直接 `return 3` 讓測試過 | 沒有解決邏輯問題，只騙過斷言 |
| 刪除失敗測試 | 刪掉「不方便」的測試案例 | 測試覆蓋率下降，bug 不被偵測 |
| 放寬驗證條件 | `toEqual` 改成 `toBeTruthy` | 降低測試精度，喪失防護力 |

**唯一允許修改測試的場景**：Stage 3 根因分析確認**測試本身的預期值有誤**（如需求變更但測試未同步）。此時必須：

1. 在 Ticket log 記錄「測試預期值修正」及修正理由
2. 引用需求文件或 use case 作為新預期值的依據
3. 修正後的測試仍必須能驗證正確行為

**閘門**：Stage 2 的 failing test 變綠 → 進入 Stage 5。未變綠 → 回到 Stage 3（受迭代上限約束）。

---

## Stage 5：回歸防護

**目標**：避免同一個 bug 之後偷偷回來。

**執行者**：同 Stage 4。

| 動作 | 說明 |
|------|------|
| 確認 failing test 已納入 CI | 不是臨時腳本，是正式測試 |
| 補邊界測試 | 根因相同但輸入不同的場景，至少補 1 個測試 |
| 執行完整測試套件 | `npm test` 確認無回歸 |

### 測試失敗分流

完整測試套件出現失敗時：

| 情況 | 判斷方式 | 處理 |
|------|---------|------|
| 本次修復導致 | `git stash` 後失敗消失 | 回到 Stage 4 修正 |
| 既有 flaky test | `git stash` 後仍失敗 | 開獨立 Ticket 追蹤，不阻擋本流程 |

**閘門**：完整測試套件通過（排除已記錄的 flaky test） → 進入 Stage 6。未通過 → 依分流表處理。

---

## Stage 6：規格合規審查

**目標**：確認修復符合原始需求與驗收條件。

**執行者**：透過 `/parallel-evaluation` 派發，以其檢查清單為準。

以下為最低檢查項（若 `/parallel-evaluation` 已涵蓋則不重複）：

| 檢查項 | 說明 |
|--------|------|
| 修復是否符合 use case 描述？ | 對照 `docs/use-cases.md` |
| 是否引入行為變更？ | 對使用者可見的行為改變需記錄 |
| 驗收條件是否滿足？ | 對照 Ticket 的驗收條件 |

**閘門**：審查通過 → 進入 Stage 7。不通過 → 回到 Stage 4 修正（不重置 Stage 3 的根因結論）。

---

## Stage 7：程式碼品質審查

**目標**：確認修法乾淨、可維護、無副作用。

**執行者**：同 Stage 6（通常與 Stage 6 在同一次 `/parallel-evaluation` 中完成）。

以下為最低檢查項（若 `/parallel-evaluation` 已涵蓋則不重複）：

| 檢查項 | 說明 |
|--------|------|
| 符合專案品質基線？ | 對照 `.claude/references/quality-common.md` |
| 無硬編碼？ | 常數提取、訊息外部化 |
| 可觀測性？ | 錯誤路徑有日誌 |
| 無過度修改？ | diff 只含必要變更 |

**閘門**：審查通過 → 進入 Stage 8。不通過 → 回到 Stage 4 修正。

---

## Stage 8：結案報告

**目標**：整理修復紀錄，讓經驗可累積。

**執行者**：認領 Ticket 的開發者。

**最小產出**：

```markdown
## Bug Fix Report

### 症狀
[使用者觀察到的問題]

### 根因
[Stage 3 確認的根本原因]

### 修復方式
[Stage 4 的修改摘要]

### 影響範圍
[修改的檔案清單 + 影響的功能模組]

### 排除的假設
[Stage 3 中排除的其他假設及排除理由]

### 回歸防護
[Stage 5 新增的測試]

### 殘留風險
[已知但未處理的相關問題，已開 Ticket 追蹤]

### 技術債
[Stage 2 的手動驗證計畫待自動化、其他待處理項目]

### 關聯資訊
- Ticket ID: [ID]
- 修復耗時: [時間]
- 關聯 Ticket: [相關 Ticket ID]
```

**額外動作**：
- 若為常見錯誤模式 → `/error-pattern add` 記錄
- 更新工作日誌 → `/doc-flow`
- 標記 Ticket 完成 → `/ticket track complete`

---

## 複合 bug 處理

一個 bug report 實際上包含多個獨立問題時：

| 時機 | 判斷標準 | 處理 |
|------|---------|------|
| Stage 1 發現 | 重現步驟觸發多個不同症狀 | 拆分為獨立 Ticket，各自走完整流程 |
| Stage 3 發現 | 根因分析指向多個獨立原因 | 當前 Ticket 只修第一個根因，其餘開新 Ticket |

---

## 閘門總覽

| 階段 | 閘門條件 | 不通過時 |
|------|---------|---------|
| Stage 1 → 2 | 標準路徑或替代路徑任一滿足 | 回頭補充資訊 |
| Stage 2 → 3 | 有 failing test 或手動驗證計畫 | 不准開始分析 |
| Stage 3 → 4 | 根因已確認（含外部因素） | 不准開始修改 |
| Stage 3 → 8 | 外部因素確認（捷徑） | 產出簡化報告，開 workaround Ticket |
| Stage 4 → 5 | failing test 變綠 | 回到 Stage 3（上限 3 次 / Hotfix 2 次） |
| Stage 5 → 6 | 完整測試通過（排除已知 flaky） | 依分流表處理 |
| Stage 6 → 7 | 規格合規審查通過 | 回到 Stage 4 修正（迭代計數器重置） |
| Stage 7 → 8 | 品質審查通過 | 回到 Stage 4 修正（迭代計數器重置） |

---

## 使用方式

### `/bugfix`

顯示本流程概覽和閘門總覽。

### `/bugfix start [ticket-id]`

從 Stage 1 開始完整流程。行為：
1. 若提供 ticket-id，載入 Ticket 資訊作為 bug 描述
2. 若無 ticket-id，提示用戶描述症狀
3. 引導進入 Stage 1

### `/bugfix stage N [ticket-id]`

跳到指定階段。行為：
1. 檢查前置閘門是否已通過（讀取 Ticket log）
2. 閘門未通過 → 拒絕跳轉，提示需先完成哪個階段
3. 閘門已通過 → 進入指定階段

### `/bugfix hotfix [ticket-id]`

啟動 Hotfix 模式。行為：
1. 執行 Stage 1-5（與標準模式相同）
2. Stage 5 完成後直接產出簡化結案報告
3. 自動建立延後 Ticket：「補齊 Stage 6-8 審查」（priority: P0，due: 24 小時內，who: PM 在下一個 session 檢查）

---

版本紀錄在同目錄的 `CHANGELOG.md`。

