# ADR-075：Telegram 告警通知格式標準化與完整流程修復

> **狀態**: 🟡 批准實作中
> **建立日期**: 2026-04-12（台北時間）
> **決策者**: 統帥 ogt + Claude Sonnet 4.6
> **相關 ADR**: ADR-071（四種通知類型）、ADR-073（飛輪修復）、ADR-074（監控補全）

---

## 背景

ADR-071（2026-04-11）設計了 TYPE-1/2/3/4/4D 五種通知類型，並實作了 `classify_alert_early()`、`_build_inline_keyboard()` 等函數。但在 2026-04-12 的全面盤點中，發現整條資料流存在 **4 個斷點**，導致動態分類按鈕從未真正生效，所有 TYPE-3 告警一律 fallback 到通用的 [批准][拒絕][靜默] 三鍵。

同時，在與 Gemini 的架構討論中，確認了 8 種通知類型矩陣（TYPE-1 到 TYPE-8M）以及雙頻道發送策略。

---

## 問題診斷：4 個斷點

```
斷點 A：decision_manager._push_decision_to_telegram()
         呼叫 send_approval_card() 時沒有傳入 alert_category

斷點 B：telegram_gateway.send_approval_card()
         呼叫 _build_inline_keyboard() 時沒有傳入 alert_category/notification_type

斷點 C：telegram_gateway._send_approval_card_to_group()
         含 Callback Button 的 TYPE-3 卡片也發到 SRE 群組 → nonce 洩漏安全漏洞

斷點 D：classify_alert_early() 輸出 "kubernetes"
         但 _build_inline_keyboard 期待 "k8s_workload" → 永遠不命中
         另有 10 個告警分類錯誤（host_resource/devops_tool/ssl_cert 等全落入 general）
```

---

## 決策

### D1：統一 Category 命名

- `k8s_workload` → 改為 `kubernetes`（語義更廣，涵蓋 Node/Service/Ingress）
- `Host*` 告警從 `infrastructure` 分離為 `host_resource`
- `Docker*` 告警保留 `infrastructure`

### D2：新增 7 個 Category

| Category | 告警 | 說明 |
|---------|------|------|
| `alertchain_health` | AlertChainBroken_* / NoAlertsReceived2Hours / AlertChainUnhealthy | meta-monitoring，優先度最高 |
| `flywheel_health` | AutoRepairLowSuccessRate / PermanentFixRequired / Flywheel* | 飛輪自身健康 |
| `storage` | MinIODown | 物件儲存 |
| `devops_tool` | GiteaDown / HarborDown / SignOzDown / OpenClawDown / SentryDown / AlertmanagerDown | DevOps 工具鏈 |
| `external_site` | MoWoooWorkDown / TsenyangWebsiteDown / StockWoooWorkDown / BitanWoooWorkDown | 外部業務網站 |
| `ssl_cert` | ExternalSiteSSL* / TLSCert* | SSL 憑證 |
| `security_tool` | KaliScannerDown | 資安掃描工具 |

### D3：8 種通知類型矩陣（完整版）

| 類型 | 適用場景 | 按鈕 | 發送目標 |
|-----|---------|------|---------|
| TYPE-1 | 純資訊、備份成功、心跳 | 無 | SRE 群組 |
| TYPE-2 | AI 自動修復完成 | 無（結果通知）| SRE 群組 |
| TYPE-3 | 需人工審核（預設）| 依 category 動態 ≤4 個 | SRE 群組 |
| TYPE-4 | AI 無法判斷 | [手動記錄][查面板][忽略] | SRE 群組 |
| TYPE-4D | Config Drift | [查Diff][採納][回滾][忽略] | SRE 群組 |
| TYPE-5S | 資安防禦 | [隔離][封鎖IP][驅逐Pod][確認授權]；危險動作先記授權/多簽 | SRE 群組 |
| TYPE-6B | 業務/FinOps（未來）| [暫停][查SignOz][忽略] | SRE 群組 |
| TYPE-7E | 重大事故升級 / auto-repair unavailable | 無 ghost callback；人工/AI 接手先靠卡片與 timeline/AOL 留痕，按鈕需有 dispatcher 後才可開 | SRE 群組 |
| TYPE-8M | 飛輪/告警鏈路健康 | [觸發診斷][查看面板][靜默] | SRE 群組 |

### D4：雙頻道路由規則

```
2026-04-30 起，正式告警收件通道統一為 AwoooI SRE 戰情室群組
  SRE_GROUP_CHAT_ID=-1003711974679

OPENCLAW_TG_CHAT_ID 僅作 SRE_GROUP_CHAT_ID 缺失時的 fail-soft fallback。
```

### D5：Telegram 卡片格式防腐規則

```python
# telegram_gateway.py 頂部常數（ADR-075 鎖定）
NOTIFICATION_TYPE_RULES = {
    "TYPE-1":  "無按鈕，可有 URL Button，嚴禁 Callback Button",
    "TYPE-2":  "無按鈕，可有 URL Button，嚴禁 Callback Button",
    "TYPE-3":  "最多 4 個 Callback Button，依 alert_category 動態選擇",
    "TYPE-4":  "固定 3 個按鈕：[手動記錄][查看面板][忽略]",
    "TYPE-4D": "固定 4 個按鈕：[查看Diff][採納][回滾][忽略]",
    "TYPE-5S": "固定 4 個按鈕：[隔離][封鎖IP][驅逐Pod][確認授權]，危險動作只記授權/多簽",
    "TYPE-6B": "最多 3 個按鈕：[暫停][查看SignOz][忽略]",
    "TYPE-7E": "無 ghost callback；未落地 dispatcher 前不顯示 callback button",
    "TYPE-8M": "固定 3 個按鈕：[觸發診斷][飛輪面板][靜默]",
}
```

2026-05-01 補充：TYPE-7E 已用於 `auto_repair_unavailable` 與
`drift_auto_adopt_blocked` 緊急通道。Telegram 卡片本身不是閉環；每次升級
必須寫入 `alert_operation_log` 與 `timeline_events`，讓 WarRoom、KM 與
learning loop 能反查。TYPE-5S 的 `record_authorization` 也必須寫 Redis TTL
和 AOL/timeline；不得只回 Telegram toast。

---

## 實施計畫

完整計畫書：`docs/superpowers/plans/2026-04-12-adr075-telegram-alert-notification.md`

**Phase 1（斷點修復，同一 PR）**：
- Task 1-2：重寫 `classify_alert_early()`（斷點 D）
- Task 3：`_build_inline_keyboard` 字典對齊（斷點 D）
- Task 4：`send_approval_card()` 接收並傳遞 `alert_category`（斷點 B）
- Task 5：雙頻道路由修正（斷點 C）
- Task 6：`decision_manager` 傳入 `alert_category`（斷點 A）

**Phase 2（TYPE-8M 擴充）**：
- Task 7-8：新增 `send_meta_alert()` + decision_manager 路由

**Phase 3（Prometheus 規則，獨立）**：
- Task 9：新增 6 個欠缺告警規則

---

## 驗收標準

| 驗收項目 | 驗收方式 |
|---------|---------|
| HostHighCpuLoad 出現 [查程序][重啟服務][清Log] | 送測試告警驗收 |
| KubePodCrashLooping 出現 [重啟][擴容][縮容][回滾] | 送測試告警驗收 |
| AlertChainBroken_* 出現 [啟動緊急診斷] | 送測試告警驗收 |
| TYPE-3 卡片不發 SRE 群組 | 確認群組無帶按鈕訊息 |
| TYPE-1 卡片只發 SRE 群組 | 確認個人 DM 無純資訊通知 |
| 全部單元測試通過 | pytest >620 passed |

---

## 範疇外（本 ADR 不包含）

- TYPE-5S SecOps 卡片（待 SecOps Prometheus 規則完備後）
- TYPE-6B Business 卡片（待 MomoScraper metrics 接入後）
- TYPE-7E Escalation Monitor Service
- FlywheelPlaybookZero 告警（依賴 ADR-074 Exporter）


---

## 實作記錄（2026-04-12）

| Phase | Commit | 內容 |
|-------|--------|------|
| Phase 1 | 2cef209 | 4 斷點修復 + classify_alert_early 13 規則 + _CATEGORY_BUTTONS 更新 |
| Phase 2 | 561c1d8 | send_meta_alert() + decision_manager TYPE-8M elif |
| CR 修補 | 1cb654c | TYPE_8M 加入 enum + 死碼清理 + docstring 更新 |

**測試覆蓋**：52 classify_alert_early tests + 664 total passed

**CR 評分**：7/10（首席架構師審查通過，P0/P1 全修補）

**Phase 3（Prometheus 規則）**：尚未實作，不阻擋 Phase 1/2 上線

---

## 更新引用（2026-04-24）

| ADR | 影響 | 說明 |
|-----|------|------|
| [ADR-093](ADR-093-telegram-group-migration.md) | 🔄 更新 D4 雙頻道路由 | 加「群組 Callback Button 授權模式」子條款：TYPE-3/4/4D/8M 解除 `_interactive_types` 黑名單，改以 callback_data + user_id binding + Approvers 白名單保護（P0-1 修）|
| [ADR-094](ADR-094-hermes-nl-interface.md) | ➕ 新增 NL 對話入口 | Hermes 透過 @mention 在群組處理自然語言對話，輸出仍遵守 ADR-075 D5 格式規則 |
| [ADR-095](ADR-095-12agent-sdk-integration.md) | ➕ 12-Agent 視覺分派 | TG 訊息加 `@hermes-*` 前綴 + emoji + hashtag 三件套，符合 ADR-086 UI 清洗後的視覺規範 |
