fix(flywheel): P2 Approval 逾期不結案 → KM 學習鏈斷鏈修復
Some checks failed
CD Pipeline / build-and-deploy (push) Failing after 12m11s

問題根因:
  PENDING approval 無人處置超過 48h 後應自動 EXPIRED,
  但 get_pending_approvals() 只在用戶開 UI 時觸發,
  若無人開 UI → Incident 永遠 PENDING → KM 永遠不寫入
  → Phase 6 SLO human_override_rate 低估,EWMA 缺少負向樣本。

修復:
  1. anomaly_counter.py: 新增 "timeout_ignored" disposition 類型,
     與 auto_repair / human_approved / manual_resolved 區分
  2. incident_service.py: resolve_incident() 新增 resolution_type 參數,
     resolution_type="timeout" 時記錄 "timeout_ignored" 而非 "manual_resolved"
  3. jobs/approval_timeout_resolver.py (新): 每小時掃描逾期 PENDING approval,
     批次標記 EXPIRED,對每筆有 incident_id 的記錄呼叫 resolve_incident("timeout")
  4. main.py: startup 掛載 approval_timeout_resolver 排程(interval=3600s)

效果:
  - 告警無人處置 48h → Incident 自動結案 → KM 寫入 → EWMA 取得樣本
  - disposition="timeout_ignored" 讓 SLO 計算正確區分「AI 建議被忽略」
  - 飛輪學習鏈對「無人處置告警」閉環

2026-04-15 ogt + Claude Sonnet 4.6(亞太)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-04-15 19:21:16 +08:00
parent 586602e7ff
commit f045506abd
4 changed files with 173 additions and 3 deletions

View File

@@ -387,7 +387,7 @@ class AnomalyCounter:
# 2026-04-07 Claude Code: Sprint 4 A2/A3 — 處置類型統計
# ==========================================================================
VALID_DISPOSITION_TYPES = {"auto_repair", "human_approved", "manual_resolved", "cold_start_trust"}
VALID_DISPOSITION_TYPES = {"auto_repair", "human_approved", "manual_resolved", "cold_start_trust", "timeout_ignored"}
async def record_disposition(
self,

View File

@@ -915,7 +915,11 @@ class IncidentService:
return incident
async def resolve_incident(self, incident_id: str) -> Incident | None:
async def resolve_incident(
self,
incident_id: str,
resolution_type: str = "manual",
) -> Incident | None:
"""
將 Incident 狀態更新為 RESOLVED
@@ -923,6 +927,12 @@ class IncidentService:
Args:
incident_id: 事件 ID
resolution_type: "manual"(預設)| "timeout"Approval 48h 逾期自動結案)
ADR-073 補丁 2026-04-15 ogt + Claude Sonnet 4.6:
新增 resolution_type="timeout" 路徑 — Approval EXPIRED 時由
approval_timeout_resolver 呼叫,記錄 "timeout_ignored" disposition
而非 "manual_resolved",確保 EWMA 採樣正確區分人工結案與逾期拋棄。
Returns:
Incident | None: 更新後的事件,失敗返回 None
@@ -998,7 +1008,8 @@ class IncidentService:
or disposition["cold_start_trust"] > 0
)
if not has_system_resolution:
await counter.record_disposition(anomaly_key, "manual_resolved")
disp = "timeout_ignored" if resolution_type == "timeout" else "manual_resolved"
await counter.record_disposition(anomaly_key, disp)
except Exception as _disp_e:
logger.warning("disposition_manual_resolve_failed", error=str(_disp_e))