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>
4.6 KiB
4.6 KiB
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,但靜態型別宣告為本地 Incident。
IncidentMemoryAdapter 的 load_incident() 通過 brain.DualIncidentMemory 返回 BrainIncident。
部分 Service 層(如 proposal_service.py、incident_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 B(IncidentConverter 轉換層),理由:
- 套件邊界清晰,符合 leWOOOgo 積木化原則
- 遷移風險最低,可漸進式採用
- 若未來棄用 lewooogo-brain,只需修改 Converter
短期緩解(已實施):
_record_to_incident返回型別標注為Any(P0-01 修復)- 不阻擋現有業務邏輯
決策結果 (2026-04-01 首席架構師審查)
採用 Option B:IncidentConverter 轉換層
首席架構師審查評分 72/100 (條件通過),確認 Option B 為最佳方案。
緊急修復已完成 (P2-01)
signal_worker.py:402—persisted_to_pg改為getattr(incident, "persisted_to_pg", False)
✅ 已實作 (Phase R-R3 Sprint - 2026-04-01)
- 建立
src/utils/incident_converter.pybrain_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清理 ✅ commit44840f5(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_ENGINEconfig 項 ✅ 已標記失效 (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)