先查看專案結構與既有 skill 格式，再撰寫 `skills/skill1.md`。
# Debug Mastery — Python 系統化除錯專精

> **模組代號**：`SKILL-DEBUG`  
> **檔案路徑**：`skills/skill1.md`（別名：`skills/debug-mastery.md`）  
> **Soul 相容**：Senior Python Developer v3  
> **主路徑**：`P2`（修 Bug）  
> **次路徑**：`P6`（測試失敗）、`P5`（症狀為效能/資源時疊加 `REF-PERF`）  
> **典型產出**：根因報告、最小復現腳本、精準修復 diff、回歸測試、預防措施  
> **上游依賴**：`SOUL.md`、`RULES.md`、`SKILL.md`（必載）  
> **常疊加模組**：`references/testing-strategy.md`、`references/performance-debugging.md`、`skills/async-concurrency.md`、`skills/data-layer.md`、`skills/api-design.md`

---

## 0. 模組定位與啟用紀律

### 0.1 本模組解決什麼

資深 Python 工程師面對 bug 時，**不靠猜、不靠運氣、不靠「我覺得是這裡」**。本模組提供一套可重複執行的 **科學除錯協議（Scientific Debugging Protocol）**：

1. **把症狀變成可重現的事實**
2. **把假設變成可反證的實驗**
3. **把根因收斂到單一、可驗證的因果鏈**
4. **用最小 diff 修復，用測試鎖住行為**
5. **把這次事故變成下次不會再發生的系統防線**

本模組是 **可插拔專精技能**：當任務命中除錯觸發詞或 `P2` 路徑時載入；與 `SKILL.md` 主 workflow 疊加執行，**取代** 主入口中 P2 的簡化流程，提供更深度 SOP。

### 0.2 載入後必守鐵律

| # | 鐵律 | 違反後果 |
|:---|:---|:---|
| D1 | **先復現，後修碼** | 修錯地方、引入回歸、無法驗證 |
| D2 | **一次只改一個變因** | 無法歸因、除錯地獄 |
| D3 | **根因必須可一句話陳述** | 治標不治本、同類 bug 復發 |
| D4 | **修復必須有回歸測試**（或等價最小斷言腳本） | 行為無契約、CI 無保障 |
| D5 | **自己執行調查** | 禁止「請你跑一下看看」 |
| D6 | **證據鏈完整** | traceback、變數狀態、命令輸出缺一不可 |
| D7 | **最小修復** | 禁止借 bugfix 做大重構 |
| D8 | **誠實邊界** | 不確定時繼續縮小，不捏造根因 |

### 0.3 與主 Skill 的關係

```
SKILL.md 判定 P2
    → 載入 skills/skill1.md（本模組）
    → 執行本模組 6 階段協議（取代 SKILL.md P2 簡版）
    → 交付前對照本模組 Checklist + SKILL.md 交付 Checklist
```

**本模組權威範圍**：復現策略、根因分析、Python 特有陷阱、除錯工具鏈、輸出格式。  
**不覆蓋**：通用倉庫偵察（見 `SKILL.md` Repo Recon）、通用測試金字塔（見 `references/testing-strategy.md`）。

---

## 1. 啟用觸發器（Triggers）

### 1.1 強觸發（應立即載入本模組）

| 類別 | 觸發詞 / 信號 |
|:---|:---|
| **錯誤類型** | `traceback`、`stack trace`、`Exception`、`Error`、`crash`、`掛了`、`崩潰` |
| **行為異常** | `不工作`、`壞了`、`返回錯誤`、`500`、`422`、`AssertionError` |
| **除錯意圖** | `debug`、`調試`、`查原因`、`為什麼會`、`根因`、`復現` |
| **間歇性** | `flaky`、`偶發`、`間歇`、`有時候`、`不穩定` |
| **測試失敗** | `pytest FAILED`、`測試掛了`、`test failed`、`紅了` |
| **用戶行為** | 直接貼上 Python traceback 或錯誤日誌 |
| **命令** | `--mode debug`、`/senior-python-developer` + 錯誤描述 |

### 1.2 弱觸發（結合上下文）

| 信號 | 條件 |
|:---|:---|
| 「這段代碼有問題」 | 伴隨 `.py` 檔案或錯誤訊息 |
| 「幫我看看」 | 上下文為失敗測試或異常輸出 |
| 「優化」 | 若症狀為 timeout/OOM → 疊加 `REF-PERF`，本模組仍載入若根因未明 |

### 1.3 反觸發（不應以本模組主導）

| 場景 | 正確路由 |
|:---|:---|
| 用戶只要解釋 traceback 含義、不修碼 | `P0` + 本模組「教育模式」（只解讀，不修） |
| 明確「重構這坨代碼」無阻塞 bug | `P3` + `skills/refactor-surgery.md` |
| 純概念題「什麼是 Exception」 | 不載入本模組 workflow |
| 非 Python 堆疊 | 不套用 Python 特有章節 |

### 1.4 關鍵詞加速疊加

| 堆疊 / 關鍵詞信號 | 追加模組 |
|:---|:---|
| `asyncio`、`Event loop`、`coroutine`、`await` | `skills/async-concurrency.md` |
| `sqlalchemy`、`psycopg`、`django.db`、`IntegrityError` | `skills/data-layer.md` |
| `FastAPI`、`Starlette`、`HTTPException`、`middleware` | `skills/api-design.md` |
| `timeout`、`OOM`、`memory`、`slow`、`CPU` | `references/performance-debugging.md` |
| `Celery`、`worker`、`queue` | `skills/async-concurrency.md` |

---

## 2. 科學除錯協議（6 階段主 Workflow）

### 總覽

```
┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐   ┌─────────┐
│ 1.觀測  │ → │ 2.復現  │ → │ 3.縮小  │ → │ 4.根因  │ → │ 5.修復  │ → │ 6.預防  │
│ Observe │   │Reproduce│   │ Narrow  │   │RootCause│   │   Fix   │   │ Prevent │
└─────────┘   └─────────┘   └─────────┘   └─────────┘   └─────────┘   └─────────┘
     ↑___________________________________________________________________|
                              假設被反證時回退
```

**Todo 建議**（`merge: false`）：

```
observe → reproduce → narrow → root-cause → fix → test → verify → prevent → deliver
```

---

### 階段 1：觀測（Observe）— 收集事實，零假設

**目標**：建立 **症狀事實表（Symptom Fact Sheet）**，區分觀察與推測。

#### 1.1 必收集欄位

| 欄位 | 內容 | 來源 |
|:---|:---|:---|
| **症狀** | 預期 vs 實際（可量化） | 用戶描述、日誌、測試斷言 |
| **環境** | Python 版本、OS、依賴版本 | `pyproject.toml`、`python --version`、容器映像 |
| **觸發條件** | 輸入、順序、並發度、時間 | 復現步驟、請求 payload |
| **頻率** | 100% / N% / 僅生產 | 用戶、監控、flaky 統計 |
| **首次出現** | 版本、部署、配置變更 | git log、changelog |
| **影響面** | 用戶數、資料損壞、金錢 | 嚴重度分級 |
| **完整錯誤** | 異常類型 + 訊息 + 完整 traceback | 用戶貼圖、日誌系統 |
| **相關上下文** | request_id、trace_id、user_id（脫敏） | 結構化日誌 |

#### 1.2 觀測紀律

- 把用戶說的「應該是 X 問題」記錄為 **未驗證假設**，不當事實
- 若資訊不足：**最多問 1–3 個高價值問題**（見 §1.3）
- 立即執行 Repo Recon（`SKILL.md` §1.1–1.4）若尚未完成

#### 1.3 高價值澄清問題模板

僅在阻礙復現時使用，禁止問卷：

1. 「錯誤是 **每次** 都發生，還是 **間歇**？間歇時有無共同條件（時間、負載、特定輸入）？」
2. 「能否提供 **完整 traceback**（含最底層異常）或失敗測試的 **完整 pytest 輸出**？」
3. 「問題出現在 **本地 / CI / 生產** 哪個環境？三處行為是否一致？」

#### 1.4 嚴重度分級（決定緊急程度）

| 級別 | 定義 | 除錯策略 |
|:---|:---|:---|
| **S0 災難** | 資料丟失/損壞、安全漏洞、全站不可用 | 先止血（rollback/feature flag）再根因 |
| **S1 阻塞** | 核心路徑不可用、CI 全紅 | 全速復現，可接受 hotfix 分支 |
| **S2 降級** | 非核心功能失敗、有 workaround | 標準 6 階段 |
| **S3 瑕疵** | 邊界 case、日誌噪音 | 可合併進正常 sprint |

---

### 階段 2：復現（Reproduce）— 讓 bug 變成按鈕

**目標**：在受控環境中 **穩定觸發** 症狀（flaky 則量化機率）。

#### 2.1 復現層級（由易到難）

| 層級 | 形式 | 適用 |
|:---|:---|:---|
| **L0** | 用戶已有失敗測試 / 腳本 | 直接跑，確認仍失敗 |
| **L1** | 單條 pytest 用例 | 從整合測失敗縮到單測 |
| **L2** | 獨立 Python 腳本（`repro_*.py`） | 無測試框架或需隔離環境 |
| **L3** | HTTP 請求序列（curl / httpx） | API 層 bug |
| **L4** | 最小 Docker compose | 環境依賴（DB、Redis） |
| **L5** | 生產流量鏡像 / shadow | 僅生產可復現（需審慎） |

#### 2.2 復現腳本紀律

```python
#!/usr/bin/env python3
"""Minimal reproduction for issue #XXX — <one-line symptom>."""
from __future__ import annotations

# 1. 固定隨機種子（若涉隨機）
# 2. 最小依賴 — 能 mock 就不起真實服務
# 3. 斷言必須表達「錯誤行為」，不是「程式沒 crash」

def main() -> None:
    ...

if __name__ == "__main__":
    main()
```

#### 2.3 復現命令清單（按專案實際調整）

```bash
# 單一失敗測試（pytest 最佳實踐）
pytest path/to/test_file.py::test_specific_case -xvs --tb=long

# 重現 flaky（多次運行）
pytest path/to/test.py -x --count=50   # 需 pytest-repeat 或 shell 迴圈

# 帶環境變數復現
APP_ENV=test DEBUG=1 pytest ...

# API 復現
curl -sS -X POST http://localhost:8000/api/... -H 'Content-Type: application/json' -d '{...}'
```

#### 2.4 復現失敗時的分支

| 情況 | 行動 |
|:---|:---|
| 本地無法復現、生產可復現 | 對比環境變數、依賴版本、資料樣本、時區、locale |
| 完全無法復現 | 加強觀測（日誌、斷言、採樣）；請求更多上下文；**禁止瞎改** |
| 復現需要敏感資料 | 脫敏 fixture；合成等價資料 |
| flaky < 5% | 進入 §8 Flaky 專章；先量化機率 |

#### 2.5 復現成功標準

- [ ] 同一命令連續執行 **≥3 次** 結果一致（確定性 bug）
- [ ] 或 flaky bug 已量化失敗率（如 12/100）
- [ ] 復現路徑已記錄（命令 + 環境 + 輸入）
- [ ] 復現 **不依賴** 生產秘密或不可分享資料

---

### 階段 3：縮小（Narrow）— 二分定位，削掉無關變因

**目標**：把故障域從「整個系統」縮到「單一模組 / 函式 / 行」。

#### 3.1 縮小維度

| 維度 | 手段 |
|:---|:---|
| **代碼** | 註解掉半段、git bisect、臨時 monkeypatch |
| **輸入** | 最小 payload、空集合、邊界值 |
| **依賴** | 關閉 cache、mock 外部 I/O、換 in-memory DB |
| **時間** | 固定 `freezegun` / mock `datetime.now` |
| **並發** | 降為單線程、單 worker |
| **配置** | 預設配置 vs 生產配置 diff |

#### 3.2 Git Bisect 協議

```bash
git bisect start
git bisect bad                    # 當前版本可復現
git bisect good <last-known-good> # 已知正常版本
# 每次 checkout 後跑復現命令
git bisect run pytest path::test_case -x
git bisect reset
```

**紀律**：bisect 腳本必須 **快速**（<30s）且 **確定**（無 flaky）。

#### 3.3 二分除錯心智模型

```
全整合測試失敗
  → 能否在無 DB 的單元測失敗？ ─否→ 問題在整合邊界（連線、遷移、fixture）
  → 能否在純函式層失敗？       ─否→ 問題在 I/O / mock 設置
  → 能否用更小輸入失敗？       ─否→ 問題在特定資料形狀或狀態累積
```

#### 3.4 日誌插樁策略（無法二分代碼時）

```python
import logging
log = logging.getLogger(__name__)

# 結構化、可關聯
log.debug("checkpoint", extra={"user_id": uid, "state": state, "step": "before_query"})
```

**紀律**：

- 插樁用 `DEBUG`，交付前移除或改為永久可觀測點
- 不記錄密碼、token、PII 全文
- 每個 checkpoint 有 **唯一 step 名**

#### 3.5 縮小成功標準

- [ ] 已定位到 **≤1 個檔案** 為主戰場（允許跨檔案調用鏈）
- [ ] 已定位到 **具體函式或行號範圍**
- [ ] 已排除：環境、依賴版本、無關模組（有證據）

---

### 階段 4：根因（Root Cause）— 5 Whys + 可反證假設

**目標**：回答 **「為什麼會發生」**，而非 **「哪行拋了異常」**。

#### 4.1 症狀 vs 根因 vs 觸發

| 概念 | 定義 | 範例 |
|:---|:---|:---|
| **症狀** | 觀察到的異常表現 | `AttributeError: 'NoneType' has no attribute 'id'` |
| **直接原因** | 立即導致異常的代碼事實 | `user` 為 `None` 時存取 `.id` |
| **根因** | 為何 `user` 會是 `None` | 上游查詢在 soft-delete 用戶時返回 None 但未處理；API 契約未文檔化 |
| **觸發條件** | 讓根因暴露的輸入/狀態 | 請求已刪除用戶的 profile |

**合格根因陳述模板**：

> 因為 **[系統設計/遺漏/錯誤假設]**，當 **[觸發條件]** 時，**[機制]** 導致 **[症狀]**。

#### 4.2 5 Whys 範例

```
1. 為什麼 500？ → 未捕獲的 AttributeError
2. 為什麼 AttributeError？ → user 是 None
3. 為什麼 user 是 None？ → repository.get 對已刪除用戶返回 None
4. 為什麼沒處理 None？ → service 假設用戶必存在
5. 為什麼有這假設？ → API 契約與 ORM 行為不一致，無測試覆蓋已刪除用戶路徑
→ 根因：缺失的邊界測試 + 不一致的 None 語意
```

#### 4.3 假設驅動除錯循環

```
形成假設 H1 → 設計實驗 E1（預測結果 P1）→ 執行 → 比對
  ├─ 符合 → H1 升級為「暫定根因」→ 嘗試反證
  └─ 不符 → 否定 H1 → 新假設 H2（記錄學習）
```

**實驗紀錄表**（內化或複雜時輸出）：

| ID | 假設 | 實驗 | 預測 | 實際 | 結論 |
|:---|:---|:---|:---|:---|:---|
| H1 | 競態導致 | 單線程復現 | 仍失敗 | 仍失敗 | 否定 |
| H2 | 時區 | freezegun UTC | 通過 | 通過 | 支持 |

#### 4.4 Traceback 解剖協議（Python 專用）

**閱讀順序**（由下往上，但 **先讀最底**）：

1. **最底層異常**：類型 + 訊息（真實錯誤，非包裝）
2. **`raise ... from` 鏈**：`__cause__` / `__context__` 區分主因與次因
3. **你的代碼幀**：跳過 `site-packages`，除非懷疑庫 bug
4. **幀間資料流**：每幀的 locals 合理嗎？（pytest `--tb=long`、pdb）
5. **頂層入口**：CLI、HTTP handler、Celery task 的輸入從何而來

**常見包裝陷阱**：

| 表面異常 | 常見真因 |
|:---|:---|
| `ExceptionGroup` (3.11+) | 讀取子異常每一項（`except*` 語法） |
| `requests.HTTPError` | 讀 `response.status_code` 與 body |
| `pydantic.ValidationError` | 讀 `errors()` JSON，定位具體欄位 |
| `sqlalchemy.exc.IntegrityError` | 讀 `orig.diag`（PostgreSQL）約束名 |
| `asyncio.TimeoutError` | 找被超時的 coroutine，非僅怪 timeout 值 |
| `JSONDecodeError` | 檢查 response Content-Type 與實際 body 前 200 字 |

#### 4.5 根因分類法（便於預防）

| 類別 | 代碼 | 典型修復方向 |
|:---|:---|:---|
| **邏輯錯誤** | `RC-LOGIC` | 修正分支、守衛、不變量 |
| **邊界遺漏** | `RC-BOUNDARY` | None、空集合、極值、unicode |
| **狀態/生命週期** | `RC-STATE` | 未初始化、快取過期、連線未關 |
| **並發/競態** | `RC-RACE` | 鎖、原子操作、隔離 |
| **契約不一致** | `RC-CONTRACT` | 型別、API schema、ORM 語意 |
| **環境/配置** | `RC-ENV` | 環境變數、路徑、權限 |
| **依賴/版本** | `RC-DEPS` | pin、升級、breaking change |
| **資源耗盡** | `RC-RESOURCE` | 連線池、記憶體、fd |
| **觀測不足** | `RC-OBS` | 非根因，是放大器——仍須修 |

#### 4.6 根因成功標準

- [ ] 一句話根因 + 觸發條件
- [ ] 分類標籤（上表）
- [ ] 能解釋 **為何現有測試沒攔住**
- [ ] 至少一個 **反證實驗** 已執行（「若不是 X 則應 Y」）

---

### 階段 5：修復（Fix）— 最小、精準、可審查

**目標**：消除根因，不掩蓋症狀。

#### 5.1 修復策略選型

| 根因類別 | 優先策略 | 避免 |
|:---|:---|:---|
| `RC-BOUNDARY` | 顯式守衛 + 早期返回 + 清晰錯誤 | 吞掉異常 |
| `RC-CONTRACT` | 統一語意 + 更新 schema/型別 | 到處 `if x is not None` 補丁 |
| `RC-LOGIC` | 修正算法 + 測試鎖定 | 魔術常數偏移 |
| `RC-RACE` | 鎖/隊列/冪等 | 加大 sleep |
| `RC-ENV` | 啟動校驗 + 文檔 | 硬編碼路徑 |
| `RC-RESOURCE` | 上下文管理器 + 限流 | 增大超時掩蓋 |

#### 5.2 修復紀律

1. **先寫失敗測試**（紅）→ 修碼（綠）→ 重構（可選）
2. **一個 commit 邏輯**：修復 + 測試；不夾帶格式化全專案
3. **錯誤訊息可行動**：告訴調用方 **什麼錯了、怎麼辦**
4. **不破壞公共 API**；必須破壞時走 deprecation
5. **S0/S1** 可先 hotfix 再 follow-up 結構修復（須在交付中說明）

#### 5.3 修復反模式（禁止）

| 反模式 | 為何有害 |
|:---|:---|
| 裸 `except:` / `except Exception: pass` | 吞掉根因、難以除錯 |
| 無限 `retry` 無退避 | 放大故障、資源耗盡 |
| `time.sleep` 祈禱 | 掩蓋競態 |
| 大範圍 `type: ignore` | 型別債務 |
| 回滾到「能跑」的錯誤邏輯 | 技術債 |
| 僅加 log 不修 | 除非明確為觀測階段且已計劃跟進 |

#### 5.4 Hotfix vs 結構修復決策

```
生產 S0/S1？
  ├─ 是 → 最小 hotfix 止血 → 部署 → 開 ticket 結構修復
  └─ 否 → 直接正確修復（含測試）
```

---

### 階段 6：預防（Prevent）— 讓 bug 類型絕種

**目標**：同類問題 **更難再發生、更容易被攔截**。

#### 6.1 預防措施層級

| 層級 | 措施 | 成本 |
|:---|:---|:---|
| **L1 回歸測試** | 鎖定本次觸發條件 | 低 — **必做** |
| **L2 鄰近測試** | 同族邊界（空、None、權限） | 低 |
| **L3 型別加強** | `Optional` → 必填、Literal、NewType | 中 |
| **L4 靜態檢查** | mypy strict、ruff 規則、自定義 lint | 中 |
| **L5 契約測試** | OpenAPI contract、consumer-driven | 中 |
| **L6 運行時斷言** | `assert invariant`（非 -O 依賴） | 低-中 |
| **L7 可觀測** | 結構化日誌、metric、告警規則 | 中 |
| **L8 流程** | CODEOWNERS、必填 review 清單 | 組織 |

#### 6.2 預防產出清單

- [ ] 至少 **1 個** 回歸測試，測試名描述 **行為** 非實作
- [ ] 若 `RC-CONTRACT`：更新 docstring / OpenAPI / README
- [ ] 若 `RC-RACE`：註釋說明並發假設或加鎖文檔
- [ ] 若生產事故：簡短 **postmortem 要點**（時間線、根因、行動項）

---

## 3. Python Traceback 深度解讀手冊

### 3.1 幀結構

```
Traceback (most recent call last):
  File "app/service.py", line 42, in get_profile    ← 你的代碼
    return user.id
  File "app/repo.py", line 18, in get_user
    return session.get(User, uid)
TypeError: ...                                        ← 異常類型與訊息
```

| 元素 | 解讀 |
|:---|:---|
| `most recent call last` | 底部為 **拋出點**；頂部為入口 |
| `line N, in func` | 精確到行；注意 `.pyc` 與源碼同步 |
| 多段 traceback | 多線程/重試/包裝；找 **最初** 你的代碼幀 |

### 3.2 高頻異常決策樹

#### `TypeError: 'NoneType' object is not subscriptable / has no attribute X`

```
None 從哪來？
  → 函式未 return（隱式 None）
  → .get() / dict[] 缺 key
  → ORM .first() 無結果
  → optional 鏈中斷未處理
修復：守衛 + 明確錯誤類型（UserNotFoundError）
```

#### `KeyError: 'foo'`

```
合法缺失 vs 程式錯誤？
  → 用戶輸入 → .get() + 校驗
  → 內部約定 key → 應 KeyError，修發送方
```

#### `ValueError: too many values to unpack` / `not enough values`

```
解包與資料形狀不一致 → 打印實際值與型別 → 對齊契約
```

#### `ImportError` / `ModuleNotFoundError`

```
模組名錯 vs 未安裝 vs 循環 import vs 相對/絕對 import 混用
  → python -c "import pkg"
  → 檢查 PYTHONPATH、editable install、src layout
```

#### `RecursionError: maximum recursion depth exceeded`

```
真遞迴 vs __repr__ 循環 vs Pydantic model 循環引用
  → sys.setrecursionlimit 不是修復
```

#### `pickle` / `json` 相關

```
JSON: 常為 datetime/Decimal/UUID 未序列化
Pickle: 版本跨域、類路徑變更 — 生產除錯慎用
```

### 3.3 異常鏈（PEP 3134）

```python
try:
    ...
except DatabaseError as exc:
    raise ServiceError("failed to load user") from exc  # 保留 cause
```

除錯時：

```python
exc = ...  # 捕獲的 ServiceError
print(exc.__cause__)   # 原始 DatabaseError
print(exc.__context__) # 處理時另一異常（若未 from）
```

---

## 4. 除錯工具鏈與命令手冊

### 4.1 互動式除錯

#### pdb / breakpoint()

```python
# Python 3.7+
breakpoint()  # 等同 import pdb; pdb.pdb.set_trace()

# 常用命令
# n(next) s(step) c(continue) l(list) p expr pp expr
# w(here) u(up) d(down) bt(backtrace)
# !stmt  執行語句
```

**pytest 整合**：

```bash
pytest --pdb                          # 失敗時進 pdb
pytest --trace                      # 每行進 pdb（極慢）
pytest -x --pdb path::test_case
```

#### ipdb / pudb（若專案已依賴）

```bash
pip install ipdb  # 或專案 dev 依賴
# 在代碼中：import ipdb; ipdb.set_trace()
```

### 4.2 日誌驅動除錯

```python
import logging
logging.basicConfig(level=logging.DEBUG)

# 第三方庫降噪
logging.getLogger("urllib3").setLevel(logging.WARNING)
```

**structlog**（若專案使用）：

```python
import structlog
log = structlog.get_logger()
log.info("user_fetched", user_id=uid, found=user is not None)
```

### 4.3 靜態追蹤

```bash
# 找符號定義與引用
rg "def get_user" -n
rg "UserNotFoundError" -n

# 找呼叫鏈
rg "get_profile\(" -n
```

### 4.4 運行時自省

```python
import inspect
inspect.getsource(func)
func.__annotations__
import dis; dis.dis(func)  # 僅小函式
```

### 4.5 記憶體除錯（症狀：OOM / 記憶體漲）

```bash
# tracemalloc（標準庫）
python -X tracemalloc=25 script.py

# memray（若已安裝）
memray run -o out.bin python script.py
memray flamegraph out.bin
```

疊加 `references/performance-debugging.md`。

### 4.6 效能除錯（症狀：慢）

```bash
python -m cProfile -s cumtime script.py
python -m cProfile -o out.prof script.py && snakeviz out.prof

# 生產採樣（需安裝）
py-spy record -o profile.svg -- python app.py
```

### 4.7 非同步除錯

```python
import asyncio
asyncio.get_running_loop().slow_callback_duration  # 3.12+

# 啟用 debug mode（開發環境）
asyncio.run(main(), debug=True)
```

```bash
# 堆疊 dump（asyncio 卡死）
# 向進程發 SIGUSR1 / 使用 py-spy dump
```

疊加 `skills/async-concurrency.md`。

### 4.8 網路 / API 除錯

```bash
# httpx CLI 等價
curl -v http://...

# mitmproxy / httpie（若可用）
http POST localhost:8000/api/users name=test
```

### 4.9 資料庫除錯

```python
# SQLAlchemy 2.x — 開 echo 或事件監聽
engine = create_engine(url, echo=True)

# Django
# settings LOGGING + django.db.backends
```

```bash
# 解釋查詢計劃（PostgreSQL）
psql -c "EXPLAIN ANALYZE SELECT ..."
```

疊加 `skills/data-layer.md`。

### 4.10 依賴與環境

```bash
python --version
pip freeze | rg package-name
pip show package-name
python -c "import pkg; print(pkg.__file__, pkg.__version__)"
```

---

## 5. 常見 Bug 模式百科（Python 生產環境）

### 5.1 可變預設參數

```python
# BUG
def append_item(item, bucket=[]):
    bucket.append(item)
    return bucket

# FIX
def append_item(item, bucket=None):
    if bucket is None:
        bucket = []
    bucket.append(item)
    return bucket
```

### 5.2 閉包遲綁定

```python
# BUG
funcs = [lambda: i for i in range(3)]  # 全返回 2

# FIX
funcs = [lambda i=i: i for i in range(3)]
```

### 5.3 例外吞沒

```python
# BUG
try:
    risky()
except Exception:
    pass

# FIX
try:
    risky()
except SpecificError as exc:
    log.warning("risky_failed", exc_info=exc)
    raise
```

### 5.4 資源未關閉

```python
# FIX — 上下文管理器
with open(path) as f:
    data = f.read()

# async
async with session.post(...) as resp:
    ...
```

### 5.5 時區 naive/aware 混用

```python
from datetime import datetime, timezone

# FIX — 統一 UTC aware
now = datetime.now(timezone.utc)
```

### 5.6 浮點金額

```python
from decimal import Decimal
# 金額用 Decimal，不用 float
```

### 5.7 生成器/迭代器一次性消費

```python
items = get_items()  # generator
list(items)  # 第一次
list(items)  # 空！→ 必要時 materialize 或 tee
```

### 5.8 `is` vs `==`

```python
# BUG: if x is 256  # 小整數快取，不可靠
# FIX: if x == 256
# 例外：if x is None 正確
```

### 5.9 Dataclass 可變預設

```python
from dataclasses import dataclass, field

@dataclass
class Config:
    tags: list[str] = field(default_factory=list)
```

### 5.10 Pydantic v2 常見

| 問題 | 原因 | 處理 |
|:---|:---|:---|
| `ValidationError` 欄位難讀 | 嵌套 model | `e.errors(include_url=False)` |
| 循環引用 | model 互相引用 | `model_rebuild()`、延遲 annotation |
| `model_dump` 與 ORM 混用 | 型別不一致 | 明確 `from_attributes=True` |

### 5.11 pytest 常見

| 問題 | 原因 | 處理 |
|:---|:---|:---|
| `fixture not found` | conftest 不在路徑 | 調整 `pytest.ini` pythonpath |
| `async def` 測試不跑 | 缺 pytest-asyncio | `asyncio_mode = auto` |
| 順序依賴 | 共享 mutable fixture | 隔離 scope、autouse 審慎 |
| `caplog` 抓不到 | logger 層級 | 設 `caplog.set_level` |

### 5.12 FastAPI / Starlette

| 問題 | 檢查點 |
|:---|:---|
| 422 | Pydantic schema vs 實際 body；讀 `detail` |
| 307 無限重定向 | trailing slash 不一致 |
| Depends 未注入 | 參數默認值、`Annotated` |
| BackgroundTasks 未執行 | 響應前異常、worker 關閉 |

### 5.13 SQLAlchemy

| 問題 | 檢查點 |
|:---|:---|
| `DetachedInstanceError` | session 關閉後懶加載 |
| N+1 | 開 echo 數查詢；`selectinload` |
| 隱式 commit | autoflush、異常時 rollback |

### 5.14 Celery / 任務隊列

| 問題 | 檢查點 |
|:---|:---|
| 任務未執行 | broker 連線、queue 名、routing |
| 重複執行 | 冪等鍵、ETA、visibility timeout |
| 序列化失敗 | JSON 不支援的型別 |

---

## 6. Flaky 測試專章

### 6.1 Flaky 根因族

| 族 | 機制 | 檢測 |
|:---|:---|:---|
| **順序污染** | 全域狀態、單例、env var | 單獨跑過、隨機順序 `pytest-randomly` |
| **時間** | `datetime.now()`、TTL | freezegun |
| **隨機** | 未固定 seed | `random.seed(0)` / pytest fixture |
| **競態** | 並發 assert | 降並發、重複跑 |
| **外部依賴** | 真網路、真時鐘 | mock / vcr |
| **資源耗盡** | 埠佔用、fd | 隔離 fixture |

### 6.2 Flaky 量化協議

```bash
for i in $(seq 1 100); do
  pytest path::test_case -q || echo "FAIL run $i"
done
```

記錄：失敗率、失敗 run 編號、是否有關聯。

### 6.3 修復優先級

1. **消除共享 mutable 狀態**（最高 ROI）
2. **mock 外部 I/O**
3. **固定時間與隨機**
4. **重試僅作最後手段**，且必須記錄技術債 ticket

---

## 7. 生產環境除錯 SOP

### 7.1 原則

- **只讀優先**：先 log/metric/trace，後改碼
- **最小影響**：不在生產開 pdb
- **可回滾**：變更必可逆
- **關聯 ID**：request_id / trace_id 貫穿

### 7.2 生產資訊收集清單

| 來源 | 查什麼 |
|:---|:---|
| **日誌** | 錯誤時間窗、相同 trace_id、堆疊 |
| **Metrics** | 錯誤率、p99、連線池、queue depth |
| **Traces** | 慢 span、失敗 span 屬性 |
| **部署** | 最近 release、配置 diff |
| **依賴** | DB 慢查詢、Redis、第三方 status |

### 7.3 止血手段（S0/S1）

| 手段 | 適用 |
|:---|:---|
| **Rollback** | 明確由 release 引入 |
| **Feature flag off** | 有開關的新功能 |
| **Scale up** | 資源耗盡但無邏輯錯 |
| **限流** | 下游故障保護 |
| **熔斷** | 依賴持續失敗 |

### 7.4 從生產到本地

1. 脫敏復現 payload → fixture
2. 對齊依賴版本（`pip freeze` diff）
3. 對齊配置（非秘密部分）
4. 對齊資料形狀（合成等價）

---

## 8. 教育模式（只解讀，不修碼）

當用戶明確只要理解 traceback：

1. 逐幀解釋調用鏈
2. 指出 **最可能** 的問題行（標註置信度）
3. 給 **建議調查方向**，不直接改倉庫
4. 可給 **示例修復思路**，標註「未驗證」

---

## 9. 輸出格式模板

### 9.1 標準除錯交付（P2）

```markdown
## 根因
[一句話根因陳述]（分類：`RC-XXX`）

## 證據
- **症狀**：[預期 vs 實際]
- **復現**：[命令] → [結果摘要]
- **關鍵幀**：[file:line — 說明]
- **實驗**：[假設 → 結果]

## 修復
- `path/to/file.py` — [說明]
[代碼引用 startLine:endLine:filepath]

## 測試
- 新增 `test_xxx` — [覆蓋的觸發條件]
- 命令：`pytest path -q` → [N passed]

## 預防
- [回歸測 / 型別 / 文檔 / 可觀測]

## 風險
- [已知限制、需跟進項]
```

### 9.2 進行中（根因未明）

```markdown
## 現狀
已復現 / 未復現；當前縮小到 [範圍]

## 已排除
- [假設 H1] — [反證實驗]

## 待驗證
- [假設 H2] — [計劃實驗]

## 需要協助（若有）
- [單一具體資訊請求]
```

### 9.3 Postmortem 要點（S0/S1）

```markdown
## 事故摘要
[影響、時長]

## 時間線
- HH:MM — [事件]

## 根因
[5 Whys 結論]

## 修復
[hotfix + 長期]

## 行動項
| 項 | 負責 | 截止 |
|:---|:---|:---|
| 回歸測 | ... | ... |
```

---

## 10. 與 Grok 工具協作矩陣

| 階段 | 工具 | 用法 |
|:---|:---|:---|
| 觀測 | Read | 讀錯誤相關源碼、測試、配置 |
| 觀測 | Grep | 追符號、異常類、日誌關鍵字 |
| 復現 | Shell | 跑 pytest、復現腳本 |
| 縮小 | Grep, Read | 呼叫鏈、git bisect |
| 根因 | Read, Shell | pdb、日誌實驗 |
| 修復 | StrReplace, Write | 最小 diff |
| 驗證 | Shell | pytest、ruff、mypy |
| 預防 | Write | 新增測試檔 |

**紀律**：聲稱「已復現」「已修復」「測試通過」前必須有 **Shell 輸出**。

---

## 11. 交付前 Checklist（本模組專用）

### 復現

- [ ] 復現命令已記錄且可重複執行
- [ ] 非 flaky 或已量化 flaky 率

### 根因

- [ ] 一句話根因 + 觸發條件
- [ ] 區分症狀 / 直接原因 / 根因
- [ ] 至少一個假設已被反證

### 修復

- [ ] 最小 diff；無 drive-by 重構
- [ ] 無裸 except；無 sleep 祈禱
- [ ] 錯誤訊息可行動

### 測試

- [ ] 修復前有失敗測（或等價斷言）
- [ ] 修復後 pytest 通過（已實際運行）
- [ ] 相鄰測試未回歸

### 預防

- [ ] 回歸測試已加入
- [ ] 若契約變更已更新文檔/schema

### 交付

- [ ] 輸出符合 §9 模板
- [ ] 含驗證命令與結果
- [ ] 不讓用戶代跑調查命令

---

## 12. 快速參考卡（30 秒）

| 用戶說 / 看到 | 第一步 |
|:---|:---|
| 貼 traceback | 讀最底異常 → 找你的第一幀 → 列事實表 |
| pytest FAILED | `pytest path::name -xvs --tb=long` |
| 偶發失敗 | 單獨跑 50 次 + 查全域狀態 |
| 500 無堆疊 | 查服務日誌 + request_id |
| 本地過 CI 掛 | diff 版本、env、並發 |
| 升級後壞了 | git bisect + changelog |
| async 卡死 | py-spy / asyncio debug |
| 記憶體漲 | tracemalloc / memray |
| DB 錯 | SQL echo + EXPLAIN |

**口訣**：`事實 → 復現 → 縮小 → 根因 → 紅測 → 綠測 → 預防`

---

## 13. 版本與維護

| 欄位 | 值 |
|:---|:---|
| **模組版本** | 1.0.0 |
| **別名路徑** | `skills/debug-mastery.md`（與 `SKILLS-MANIFEST` SKILL-DEBUG 對齊） |
| **更新原則** | 新增 Bug 模式或工具時，同步更新 §5 百科與 §12 快速卡 |

---

**本模組是 Senior Python Developer 的第一可插拔專精技能。命中除錯觸發詞或 P2 路徑時：嚴格執行 6 階段科學除錯協議，用證據與測試交付，不靠猜測。**