# ADR-072: AIOps 閉環 Bug 修復清單

> **狀態**: ✅ 全部修復完成 (BUG-001~008)
> **建立**: 2026-04-11 (台北時間)
> **更新**: 2026-04-11 深夜 — P0 (BUG-001/002/003) + P1 (BUG-004/005/006) 全修復並推送 Gitea
> **背景**: Session 6 對 Redis DB10 + 程式碼全面審計發現 8 個系統性問題

---

## 背景

ADR-070/071 實作完成後，Session 6 對 62 個 DecisionToken、112 個 Incidents 及 Telegram 告警進行全面審計，
發現 MCP 雖已實作但閉環仍未真正生效。本 ADR 記錄所有發現與修復方案。

---

## Bug 清單

### P0 — 立即修復（閉環核心失效）

#### ✅ BUG-001: drift_interpreter 永久失敗 (已修復 commit 88e3197)
- **位置**: `apps/api/src/services/drift_interpreter.py:112`
- **症狀**: 所有 K8s 漂移解析失敗，`too many values to unpack (expected 4)`
- **根因**: `response_text, success, _tokens, _cost = await nvidia.chat(...)` — nvidia_provider 已重構為返回 `NvidiaProviderResult` 物件，非 4-tuple
- **修復方案**: 改用 `drift_narrator_service`（ADR-067 Phase 30，qwen2.5:7b-instruct，已存在）做漂移摘要，完全繞過 nvidia_provider
- **影響**: 所有 K8s config drift 告警無法生成可讀摘要

#### ✅ BUG-002: 自動修復 deployment name 全為 "unknown" (已修復 commit 88e3197)
- **位置**: `apps/api/src/services/decision_manager.py:_auto_execute()` + `apps/api/src/services/incident_service.py:extract_affected_services()`
- **症狀**: 24/62 DecisionToken = error，safety guard 攔截（`"unknown" in action`）
- **根因**: HostHighCpuLoad / DockerContainerUnhealthy 等主機層告警只有 `{alertname, host}` label，無 `component`/`job`/`pod` → `extract_affected_services()` 返回 `[]` → target = "unknown"
- **修復方案**: 在 `_auto_execute()` 執行前，呼叫 K8s MCP `k8s_describe_pod` / `kubectl get pods` 根據 alert 類型動態查詢受影響 Pod，填入 target
- **影響**: 所有非 K8s 源頭告警的自動修復全部失敗

#### ✅ BUG-003: nemotron_validation 接受無效 deployment name (已修復 commit 88e3197)
- **位置**: `apps/api/src/services/decision_manager.py:_nemoclaw_second_opinion()`
- **症狀**: `<deployment_name>`、`HostHighCpuLoad`、`unknown` 等無效值通過 schema 驗證
- **根因**: 只做 JSON schema 結構驗證，不驗證 deployment name 是否真實存在於 K8s
- **修復方案**: 加入 K8s MCP `k8s_describe_pod` 存在性檢查，若 deployment 不存在則拒絕並重新分析
- **影響**: 無效的修復指令被誤判為合法

---

### P1 — 本 Sprint 修復（功能性問題）

#### ✅ BUG-004: KM vectorization 108/112 = False (已修復 commit 5aa0244)
- **位置**: `apps/api/src/services/km_conversion_service.py`
- **症狀**: RAG 知識庫未累積，飛輪自癒閉環（ADR-068）學習效果為零
- **根因**: KM 轉換服務呼叫後未確保 embedding 寫入 pgvector（Session 6 確認 `vectorized=False`，`persisted_to_pg=False`）
- **修復方案**: 修復 `convert()` 後的 embedding 寫入流程，確保 `nomic-embed-text` 768d 向量化並持久化

#### ✅ BUG-005: 15 ready decisions 無人審核 (已修復 commit 5aa0244)
- **位置**: incident_id → approval UUID 對應流程
- **症狀**: 15 個 `status=ready` 的 DecisionToken 長期無審核，Telegram 未發出審核卡片
- **根因**: approval flow 的 `incident_id` UUID 對應問題
- **修復方案**: 診斷 `incident_id` 查詢鏈，修復 Telegram 審核卡片未發送的問題

#### ✅ BUG-006: outcome/verification_result 全 null (已修復 commit 5aa0244)
- **位置**: `apps/api/src/services/decision_manager.py:_push_auto_repair_result()`
- **症狀**: 所有已完成修復無 outcome 記錄
- **根因**: post-repair 驗證邏輯未寫入 DB outcome 欄位
- **修復方案**: 修復 `_push_auto_repair_result()` 確保 outcome + verification_result 寫入

---

### P2 — 下 Sprint 修復（數據品質問題）

#### ✅ BUG-007: 所有告警 severity = P3/High（label 缺失）— 確認不需修 (2026-04-11)
- **位置**: `k8s/monitoring/alerts-unified.yml`
- **症狀**: 所有 62 個 DecisionToken 對應告警都是 P3（mapping: no label → P3）
- **根因**: Prometheus 告警規則大部分未設 `severity` label
- **修復方案**: 按告警類型補充 `severity: critical|high|warning|info` label

#### ✅ BUG-008: 69/112 incidents type = "custom" (已修復 commit f34fe19)
- **位置**: `apps/api/src/api/v1/webhooks.py:1103`（`alertname_to_type` 只有 9 筆）
- **症狀**: 大部分告警無法觸發對應 Playbook
- **根因**: `alertname_to_type` mapping 只覆蓋 9 種 K8s 告警，其他全部 → "custom"
- **修復方案**: 整合 ADR-064 Rule Engine，從 YAML 規則動態推斷告警類型，取代靜態 9 筆 mapping

---

## 修復順序

```
BUG-001 (drift_interpreter) → BUG-002 (deployment_name) → BUG-003 (nemotron_validation)
→ BUG-004 (KM vectorization) → BUG-005 (approval flow) → BUG-006 (outcome)
→ BUG-007 (severity labels) → BUG-008 (alertname_to_type)
```

---

## 驗收標準

| Bug | 驗收 |
|-----|------|
| BUG-001 | K8s config drift 告警 → Telegram 出現可讀摘要（非錯誤訊息）|
| BUG-002 | HostHighCpuLoad 告警 → DecisionToken status = completed（非 error）|
| BUG-003 | 無效 deployment name → nemotron 拒絕並重新分析 |
| BUG-004 | 新 Incident 完成後 Redis + DB 兩處 vectorized = True（C2 修復：同步 Redis）|
| BUG-005 | ready decisions → Telegram 發出審核卡片 |
| BUG-006 | 修復完成後 outcome + verification_result 不為 null |
| BUG-007 | 確認不需修：alerts-unified.yml 全 42 規則均有 severity label |
| BUG-008 | HostHighCpuLoad → incident_type = "host_cpu"（非 "custom"）|

---

## Code Review 發現（2026-04-11 首席架構師審查）

### 已修復（C1/C2/I2/M2）

| 項目 | 問題 | 修復 |
|------|------|------|
| C1 | `drift_interpreter.py` 寫死內網 IP `192.168.0.111` | 改從 `settings.OLLAMA_URL` 讀取 |
| C2 | BUG-004 只更新 DB，Redis Working Memory `vectorized` 未同步 | 補 Redis JSON patch 同步 |
| I2 | `_ALERTNAME_KEYWORDS` 用 `HostHighDiskUsage`（與 alerts-unified.yml 不符） | 改為 `HostOutOfDiskSpace` + 補 `DockerContainerExited` + fallback log |
| M2 | `import json as _json` 在 for 迴圈體內 | 移至方法頂部 |

### 已記錄技術債（不阻塞合併）

| 項目 | 說明 | 後續 |
|------|------|------|
| I1 | BUG-008 規格說整合 ADR-064 Rule Engine，實際改用靜態 dict 擴充 | 下 Sprint 整合 ADR-064 YAML 規則動態推斷；靜態 dict 為可接受中間狀態 |
| I3 | `resend_stale_ready_tokens()` 直接 Redis SCAN + DB 存取，輕微違反積木化 | Phase R 清理；decision_manager 全域已有類似模式，不單獨修 |
| I4 | BUG-006 outcome 寫入非 atomic | 已足夠改善（從「永遠 null」到「大部分有值」），完整 atomic 待 Phase R |
| M3 | `alertname_to_type` dict 應抽至 constants 模組 | 下 Sprint 與 ADR-064 整合時一起重構 |
