feat(governance): AI 治理事件處理鏈四軌交付(C/D/B/A)
Some checks failed
Code Review / ai-code-review (push) Successful in 48s
run-migration / migrate (push) Failing after 45s
CD Pipeline / tests (push) Successful in 3m46s
Type Sync Check / check-type-sync (push) Successful in 2m8s
CD Pipeline / build-and-deploy (push) Failing after 31m14s
CD Pipeline / post-deploy-checks (push) Has been skipped

【十二人專家團隊全景掃描 + 並行四軌實施】

統帥質疑「有讓 12-agent 一起協作嗎」後,依照團隊規則完成全鏈路交付:
onboarder + critic + db-expert + debugger + frontend-designer 並行掃描,
找到 6 大 Gap,再由 fullstack-engineer × 4、refactor-specialist 協作落地。

【Track C — trust_drift 雙寫整併】

兩條獨立寫 event_type=trust_drift 路徑互不呼叫,下游 consumer 拿到雙份資料
無法判定 source-of-truth。整併保留 governance_agent.check_trust_drift(功能
更全:auto-deprecate + Telegram + PG),TrustDriftDetector 降為純統計 lib,
W-6 watchdog 改呼叫 governance_agent。新增 TestSinglePgWritePerDriftScenario
驗證同一 drift 場景只觸發一次 PG 寫入。

  變更:
    - apps/api/src/services/trust_drift_detector.py(lib only,不再寫 PG)
    - apps/api/tests/test_trust_drift_watchdog.py(W-6 改 mock governance_agent)

【Track D — governance_remediation_dispatch 派遣表】

ai_governance_events 是不可變 Event Sourcing,不能塞執行狀態。新建派遣表
作為投影層:1 event → 0..N dispatches,狀態可變、可重試、可審計。

  - PgEnum 5 種 event_type + 7 階段狀態機(pending → dispatched → executing →
    succeeded/failed/cancelled/skipped)
  - 失敗重試 INSERT 新 row(不改舊 row 的 status,保留審計痕跡)
  - Partial unique index ux_grd_one_active_per_event 強制「同事件唯一活躍」
  - 4 個複合 index 支援 worker poll、去重查詢、觀測面板
  - FK 對應 ai_governance_events / playbooks / incidents / approval_records
    全部 SET NULL(avoid cascade lock,但 governance_event 用 RESTRICT)

  變更:
    - apps/api/src/db/models.py(GovernanceRemediationDispatch ORM class)
    - apps/api/migrations/governance_remediation_dispatch_2026-05-03.sql
    - apps/api/src/repositories/governance_remediation_dispatch_repo.py
      (6 個 async 函式 + 3 個自訂例外:DispatchAlreadyActive /
       InvalidStatusTransition / DispatchNotFound)
    - apps/api/src/models/governance_dispatch.py(DecisionContextV1 等 4 schema)
    - apps/api/tests/test_governance_remediation_dispatch.py(29 tests)

【Track B — /governance 頁面】

後端 PR1 三個 endpoint + 前端 PR2-5 完整三 Tab。

PR1 後端:
  - GET /api/v1/ai/governance/events(events_tab,含 event_type/severity/
    狀態/時間範圍篩選 + 分頁)
  - GET /api/v1/ai/governance/queue(queue_tab,含 graceful fallback:
    dispatch 表不存在時回 table_pending=True 不拋 500)
  - GET /api/v1/ai/governance/summary(slo_tab 30d 違反時序圖)
  - severity 映射規則寫死(critic 建議未來移 settings)

PR2-5 前端:
  - /governance 路由 + AppLayout + Compliance Badge 橫幅 + PageTabs
  - SLO Tab:3 KPI 卡片(Syne 28px + StatusOrb + 7d sparkline)+
    30d 違反 stacked BarChart
  - Events Tab:篩選列 + 表格 + inline 展開行(JSON / 修復建議 / 派遣記錄)
  - Queue Tab:HITL 待辦卡片 + 信任度進度條 + 批准/拒絕按鈕(本 PR console.log)
  - Sidebar 加入「AI 治理」入口(ShieldCheck icon)
  - i18n 雙語完整(governance namespace + nav.governance)
  - 7 個新元件:slo-kpi-card / slo-violation-chart / events-table /
    events-filter-bar / event-detail-drawer / queue-item-card / queue-history-tabs

  變更:
    - apps/api/src/api/v1/ai_governance.py(router)
    - apps/api/src/services/governance_query_service.py
    - apps/api/src/models/governance.py(Pydantic V2 schemas)
    - apps/api/tests/test_ai_governance_endpoints.py(21 tests)
    - apps/web/src/app/[locale]/governance/(page + 3 tabs)
    - apps/web/src/components/governance/(7 元件)
    - apps/web/messages/{zh-TW,en}.json(governance namespace)
    - apps/web/src/components/layout/sidebar.tsx(+1 行)
    - apps/api/src/main.py(router include)

【Track A — GovernanceDispatcher 決策融合】

把治理事件接到 remediation 執行器,走北極星方向決策融合(LLM × Playbook trust
× MCP),符合「禁寫死規則」鐵律。

  - 設計鐵律:DecisionFusionAdapter 是新增 wrapper,**不修改任何 Tier 3 檔**
    (decision_manager / learning_service / trust_engine),只 consume 既有 API
  - 三維融合公式:confidence = 0.4×llm + 0.3×playbook_trust + 0.3×mcp_consistency
    (權重加 TODO 標明未來由 AI 自學調整)
  - 三分支決策路徑:
    confidence ≥ 0.85 → auto_dispatch(status=dispatched)
    0.65 ≤ confidence < 0.85 → pending_approval(HITL)
    confidence < 0.65 → skip + log
  - decision_context JSONB 完整記錄三維輸入快照(給未來 fine-tune 用)
  - poll 30s 掃 unresolved 事件,仿 governance loop 模式
  - 重複事件擋去重(呼叫 get_active_for_event)

  變更:
    - apps/api/src/services/governance_dispatcher.py
    - apps/api/src/services/decision_fusion_adapter.py
    - apps/api/tests/test_governance_dispatcher.py(14 tests)
    - apps/api/src/main.py(lifespan task 接 run_governance_dispatcher_loop)

【驗證】

1836 個 unit test 全過(29 skipped 為既有 PG integration env 問題)

【調度教訓 — 已記入 memory】

- vuln-verifier 應在 fullstack-engineer **之前**跑(避免並行讀到已修代碼誤判)
- critic 雙輪審查不可省(第二輪抓到 NaN sentinel + Prom rule 連鎖)
- 北極星「禁寫死規則」搭配 decision-fusion 確實實施

【未動 Tier 3 — 已驗證】

git diff 確認本 commit 完全沒改 decision_manager.py / learning_service.py /
trust_engine.py,只新增 wrapper service consume 既有 API。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-05-03 12:42:40 +08:00
parent 577250a678
commit e45b055e0e
29 changed files with 6510 additions and 92 deletions

View File

@@ -0,0 +1,175 @@
"""
GovernanceRemediationDispatch Pydantic Schemas
================================================
Wave 2 D: 治理事件修復派遣的輸入/輸出驗證 schema。
用途:
- DecisionContextV1: JSONB 欄位結構驗證service 層寫入前必須通過)
- DispatchCreate: 建立 dispatch 的輸入驗證
- DispatchResponse: 單筆 dispatch API 回應
- DispatchListItem: 列表頁輕量回應(含 /governance Queue tab
設計原則:
- Pydantic V2對齊 models/ 目錄)
- DecisionContextV1 版本化version 欄位方便 fine-tune pipeline 過濾相容版本
- 所有時間欄位使用 datetime含 timezone台北時區
2026-05-03 ogt + Claude Sonnet 4.6(亞太): Wave 2 D db-expert spec 實作
"""
from __future__ import annotations
from datetime import datetime
from typing import Any, Literal
from pydantic import BaseModel, Field
# =============================================================================
# DecisionContextV1 — JSONB 欄位驗證service 層寫入 DB 前強制驗證)
# =============================================================================
class DecisionContextV1(BaseModel):
"""治理派遣決策上下文 v1.
service 層必須用此 model 驗證後序列化為 dict 再寫入 decision_context 欄位。
版本欄位方便未來 schema 演進時過濾不相容記錄。
欄位均 optional允許部分感官缺失MCP 呼叫失敗時為 None
"""
version: Literal["v1"] = Field(
default="v1",
description="schema 版本,固定為 v1",
)
trigger_source: str | None = Field(
default=None,
description="觸發來源(如 governance_agent / trust_drift_watchdog",
)
triggered_metric: str | None = Field(
default=None,
description="觸發指標名稱(如 avg_trust_score / hallucination_rate",
)
metric_value: float | None = Field(
default=None,
description="觸發時的指標數值",
)
threshold: float | None = Field(
default=None,
description="觸發閾值",
)
affected_resources: list[str] = Field(
default_factory=list,
description="受影響的資源列表(如 playbook_id / km_entry_id",
)
suggested_action: str | None = Field(
default=None,
description="AI 建議的修復動作摘要≤200 字)",
)
extra: dict[str, Any] = Field(
default_factory=dict,
description="其他擴充欄位(預留 forward compatibility",
)
# =============================================================================
# DispatchCreate — 建立 dispatch 的輸入驗證
# =============================================================================
class DispatchCreate(BaseModel):
"""建立新 dispatch 的輸入 schema.
service 層呼叫 create_dispatch() 前先用此 schema 驗證輸入。
decision_context 在此層以 DecisionContextV1 驗證,序列化後寫入 DB。
"""
governance_event_id: str = Field(
description="關聯的 ai_governance_events.idUUID"
)
event_type: Literal[
"trust_drift",
"knowledge_degradation",
"llm_hallucination",
"execution_blast_radius",
"governance_slo_data_gap",
] = Field(description="治理事件類型")
executor_type: str = Field(
max_length=80,
description="執行器類型(如 playbook_executor / manual / slo_repair",
)
playbook_id: str | None = Field(
default=None,
description="可選,關聯 playbooks.playbook_id",
)
incident_id: str | None = Field(
default=None,
description="可選,關聯 incidents.incident_id",
)
approval_id: str | None = Field(
default=None,
description="可選,關聯 approval_records.id",
)
decision_context: DecisionContextV1 = Field(
default_factory=DecisionContextV1,
description="決策上下文DecisionContextV1 強制驗證)",
)
max_attempts: int = Field(
default=3,
ge=1,
description="最大重試次數(含首次,必須 >= 1",
)
created_by: str | None = Field(
default="governance_dispatcher",
description="建立者(系統自動派遣時為 governance_dispatcher",
)
# =============================================================================
# DispatchResponse — 單筆 dispatch 完整回應
# =============================================================================
class DispatchResponse(BaseModel):
"""單筆 GovernanceRemediationDispatch API 回應."""
id: str
governance_event_id: str
event_type: str
dispatch_status: str
playbook_id: str | None = None
incident_id: str | None = None
approval_id: str | None = None
decision_context: dict[str, Any]
executor_type: str
attempt_count: int
max_attempts: int
last_error: str | None = None
dispatched_at: datetime
started_at: datetime | None = None
completed_at: datetime | None = None
created_by: str | None = None
model_config = {"from_attributes": True}
# =============================================================================
# DispatchListItem — 列表頁輕量回應(/governance Queue tab 用)
# =============================================================================
class DispatchListItem(BaseModel):
"""列表頁輕量 dispatch 回應.
僅回傳 Queue / Events tab 所需欄位,避免傳輸完整 decision_context。
"""
id: str
governance_event_id: str
event_type: str
dispatch_status: str
executor_type: str
playbook_id: str | None = None
attempt_count: int
max_attempts: int
dispatched_at: datetime
completed_at: datetime | None = None
model_config = {"from_attributes": True}