Files
awoooi/docs/adr/ADR-046-incident-type-unification.md
OG T 22de22c989 refactor(phase-s): Phase S 技術債清理 - 五項架構改善
S-01: generate_alert_fingerprint() 移至 alert_analyzer_service (Router→Service)
S-02: 移除廢棄 USE_NEW_ENGINE config (Phase R 已完成歷史使命)
S-03: github_webhook.py linter 清理 (Field unused + delivery_id noqa)
S-04: Pydantic v2 遷移 - approval/incident models (class Config → ConfigDict)
S-05: Skill 09 v1.1 更新 (USE_NEW_ENGINE 廢棄說明)

測試: 393 passed, 零失敗

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 13:12:02 +08:00

4.6 KiB
Raw Blame History

ADR-046: 跨套件 Incident 型別統一治理

項目 內容
狀態 實作完成 (Option B + #123 全部完成 2026-04-01)
日期 2026-04-01
決策者 首席架構師 + 統帥
觸發 Phase R-R2.1 架構審查 P2-01

背景

Phase R-R2 完成後,系統中同時存在兩種 Incident 型別:

型別 來源 欄位
src.models.incident.Incident 本地 (apps/api) decision_chain, outcome, ttl_days, vectorized, persisted_to_pg
lewooogo_brain.interfaces.incident_processor.Incident (BrainIncident) lewooogo-brain 套件 精簡欄位,無 decision_chain 等

問題根源

IncidentDbAdapter._record_to_incident() 返回 BrainIncident,但靜態型別宣告為本地 IncidentIncidentMemoryAdapterload_incident() 通過 brain.DualIncidentMemory 返回 BrainIncident。 部分 Service 層(如 proposal_service.pyincident_service.py)期望本地 Incident 型別, 導致 AttributeError 風險或靜態分析誤報。

三種方案

Option A本地 Incident 繼承 BrainIncident

# src/models/incident.py
from lewooogo_brain.interfaces.incident_processor import Incident as BrainIncident

class Incident(BrainIncident):
    """AWOOOI 本地擴展 Incident"""
    decision_chain: AIDecisionChain | None = None
    ttl_days: int = 30
    vectorized: bool = False
    persisted_to_pg: bool = False
  • 型別統一,靜態分析通過
  • ⚠️ 強依賴 lewooogo-brain 套件版本
  • ⚠️ BrainIncident 變更會影響本地

Option B建立 IncidentConverter 轉換層

# src/utils/incident_converter.py
def brain_to_local(brain_incident: BrainIncident) -> Incident: ...
def local_to_brain(local_incident: Incident) -> BrainIncident: ...
  • 兩者保持獨立,變更不互相影響
  • 轉換點明確,可審計
  • ⚠️ 每次跨邊界需顯式轉換,增加樣板代碼

Option C逐步廢棄本地 Incident,全面切換至 BrainIncident

  • 最終架構最簡潔
  • 🔴 需要修改所有使用本地 Incident 的程式碼30+ 處)
  • 🔴 proposal_service、incident_service 等需要大規模重構

建議

推薦 Option BIncidentConverter 轉換層),理由:

  1. 套件邊界清晰,符合 leWOOOgo 積木化原則
  2. 遷移風險最低,可漸進式採用
  3. 若未來棄用 lewooogo-brain只需修改 Converter

短期緩解(已實施):

  • _record_to_incident 返回型別標注為 AnyP0-01 修復)
  • 不阻擋現有業務邏輯

決策結果 (2026-04-01 首席架構師審查)

採用 Option BIncidentConverter 轉換層

首席架構師審查評分 72/100 (條件通過),確認 Option B 為最佳方案。

緊急修復已完成 (P2-01)

  • signal_worker.py:402persisted_to_pg 改為 getattr(incident, "persisted_to_pg", False)

已實作 (Phase R-R3 Sprint - 2026-04-01)

  • 建立 src/utils/incident_converter.py
    • brain_to_local(BrainIncident) -> Incident
    • local_to_brain(Incident) -> BrainIncident
  • IncidentDbAdapter._record_to_incident 返回型別說明更新(仍返回 BrainIncident供 brain 內部使用)
  • IncidentEngineAdapter 包裝層建立(incident_engine.py
    • process_signal() → brain_to_local 轉換
    • get_incident() → brain_to_local 轉換
    • update_status() → 直接委派
  • get_incident_engine() 返回 IncidentEngineAdapter(輸出為 LocalIncident
  • #123 proposal_service.py 清理 commit 44840f5 (2026-04-01 ogt)
    • _load_incident() 委派 IncidentEngineAdapter.get_incident()
    • _persist_incident() Redis 委派 brain.DualIncidentMemory.save_incident()
    • 移除: 直接 Redis 存取、錯誤 key prefix "incident:"、重複 _record_to_incident()
  • USE_NEW_ENGINE config 項 已標記失效 (P2-03);回滾路徑更新為 git revert

四個跨邊界轉換點

位置 方向 狀態
IncidentDbAdapter._record_to_incident() DB→BrainIncident (brain 內部) 邊界清晰
IncidentEngineAdapter.process_signal() BrainIncident→LocalIncident 已實作
IncidentEngineAdapter.get_incident() BrainIncident→LocalIncident 已實作
proposal_service._persist_incident() LocalIncident→BrainIncident (save) commit 44840f5

相關文件

  • ADR-024: API 分層架構 (Phase 16 絞殺者模式)
  • ADR-003: leWOOOgo Module Architecture
  • feedback_lewooogo_modular_enforcement.md
  • Phase R-R2.1 架構審查報告 (commit d17b67c)