Files
awoooi/docs/adr/ADR-086-telegram-ui-notification-cleanup.md
OG T ba8cf6105d docs(adr): ADR-086 Telegram UI 清洗規範 + ADR-087 AutoApprove kubectl 閘門
ADR-086: Telegram 通知卡片 UI 清洗規範
- _parse_debate_summary() 設計決定與各 TYPE 欄位清洗規則
- TYPE-3 鍵盤重構:批准/拒絕永遠第一行
- 技術債:_parse_debate_summary 提升模組層級(P1-1)

ADR-087: AutoApprove 安全強化 — kubectl 強制執行閘門
- 條件 1d 設計:_raw_action 語意 + NO_EXECUTABLE_ACTION reason
- Solver Nemo 格式 kubectl 驗證
- 降級指令改為真實 kubectl 唯讀調查
- min_trust_score=0 保留理由記錄(TrustEngine 記憶體持久化技術債)
- P0-2 風險記錄:kubectl exec 未加入 _DESTRUCTIVE_PATTERNS

2026-04-17 ogt + Claude Sonnet 4.6(亞太): Session 技術債清理

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 15:25:34 +08:00

93 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-086: Telegram 通知卡片 UI 清洗規範
**狀態**: 已接受
**日期**: 2026-04-17
**作者**: ogt + Claude Sonnet 4.6
**關聯**: ADR-071通知類型定義、ADR-075Telegram 通知標準)
---
## 背景
Phase 2 多 Agent 協作ADR-082上線後5-Agent debate 的輸出格式為:
```
診斷:{hypothesis};方案:{action};安全審查:{verdict};質疑:{critic}
```
`decision_manager.py` 路由層在準備 Telegram 卡片資料時,多個分支直接截斷 `reasoning` 字串,導致完整 debate_summary 傾倒到 UI 欄位,使用者在 Telegram 看到:
- TYPE-1純資訊`message=reasoning[:200]` → 顯示「診斷:...;方案:...;安全審查:...;質疑:...」
- TYPE-4DConfig Drift`diff_summary=description[:500]` → 顯示 LLM 輸出的 JSON 原文
- TYPE-3人工審核`root_cause=_smt(reasoning, 500)` → 顯示完整 debate_summary 前 500 字
- TYPE-5S資安`threat_behavior=reasoning[:150]` → 顯示 debate_summary 開頭
---
## 決策
### 1. `_parse_debate_summary()` 標準化清洗函數
`decision_manager.py` 函數體內定義(後續應提升為模組層級 — P1-1 技術債):
```python
def _parse_debate_summary(reasoning: str) -> dict[str, str]:
result = {"diagnosis": "", "plan": "", "review": "", "critic": ""}
for part in reasoning.split(""):
part = part.strip()
if part.startswith("診斷:"): result["diagnosis"] = part[3:]
elif part.startswith("方案:"): result["plan"] = part[3:]
elif part.startswith("安全審查:"): result["review"] = part[5:]
elif part.startswith("質疑:"): result["critic"] = part[3:]
return result
```
### 2. 各 TYPE 分支修正規則
| TYPE | 欄位 | 修正前 | 修正後 |
|------|------|--------|--------|
| TYPE-1 | `message` | `reasoning[:200]` | `_parse_debate_summary(reasoning)["diagnosis"] + _smt(..., 200)` |
| TYPE-4D | `diff_summary` | `description[:500]` | JSON Catcher`json.loads` → 格式化;失敗 → `description[:500]` |
| TYPE-3 | `root_cause` | `_smt(reasoning, 500)` | `_parse_debate_summary(reasoning)["diagnosis"] + _smt(..., 300)` |
| TYPE-5S | `threat_behavior` | `reasoning[:150]` | `_parse_debate_summary(reasoning)["diagnosis"] + _smt(..., 150)` |
### 3. TYPE-3 鍵盤按鈕重構
**原問題**動態按鈕K8s 操作類)可能蓋台 [批准][拒絕],使審核按鈕不可見。
**新規則**`telegram_gateway.py:_build_inline_keyboard()`
1. 第一行永遠是 `[✅ 批准][❌ 拒絕]`(無條件)
2. 第二行以後K8s/DB/Host 類操作按鈕(每行最多 3 個)
3. 最後一行:`[📋 詳情][🔕 忽略]`
移除 `requires_human_approval` 參數——由有無 `_dynamic_buttons` 決定佈局,不再需要額外條件。
---
## 後果
- **TYPE-1**:純資訊通知只顯示 AI 診斷摘要,不顯示完整辯論過程
- **TYPE-3**:批准/拒絕永遠可見,不會被操作按鈕覆蓋
- **TYPE-4D**Config Drift 卡片顯示結構化的「建議操作/說明/回滾方案」三段式
- **TYPE-5S**:資安告警的威脅行為欄位只顯示診斷結論
---
## 技術債
| 項目 | 優先 | 說明 |
|------|------|------|
| `_parse_debate_summary` 提升為模組層級 | P1 | 目前定義在函數體內,無法在同模組其他地方重用 |
| `import re as _re` 移出函數體 | P2 | 目前在函數體內 import每次呼叫重建閉包 |
---
## 相關 Commits
| Commit | 說明 |
|--------|------|
| `6baa2e9` | TYPE-8M 三連修(第一波) |
| `418d735` | BUG-A TYPE-1 + BUG-B TYPE-4D第二波 |
| `f421e65` | BUG-C TYPE-3 + 按鈕置頂(第三波) |
| `fb225c5` | P2-2 TYPE-5S secops 分支清洗Code Review 補完) |