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
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:
@@ -22,6 +22,7 @@ from sqlalchemy import (
|
||||
Date,
|
||||
DateTime,
|
||||
Float,
|
||||
ForeignKey,
|
||||
Index,
|
||||
Integer,
|
||||
String,
|
||||
@@ -1398,6 +1399,137 @@ class AiGovernanceEvent(Base):
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# GovernanceRemediationDispatch — Wave 2 D 治理修復派遣表
|
||||
# 2026-05-03 ogt + Claude Sonnet 4.6(亞太): db-expert spec 實作
|
||||
#
|
||||
# 設計原則:
|
||||
# - 失敗重試 → INSERT 新 row(attempt_count+1),不改舊 row(審計痕跡)
|
||||
# - partial unique index(同 event_id 不可同時有 2 筆活躍)→ migration SQL 宣告
|
||||
# - 狀態機合法轉換由 Repository 層強制驗證
|
||||
# =============================================================================
|
||||
|
||||
class GovernanceRemediationDispatch(Base):
|
||||
"""
|
||||
治理事件修復派遣記錄
|
||||
|
||||
將 5 種治理事件(trust_drift / knowledge_degradation / llm_hallucination /
|
||||
execution_blast_radius / governance_slo_data_gap)接到修復執行器。
|
||||
|
||||
狀態機:
|
||||
pending → dispatched | skipped | cancelled
|
||||
dispatched → executing | failed | cancelled
|
||||
executing → succeeded | failed | cancelled
|
||||
failed → pending(僅當 attempt < max_attempts,且 INSERT 新 row,舊 row 留 failed)
|
||||
succeeded / cancelled / skipped:terminal
|
||||
|
||||
重試策略:INSERT 新 row(audit trail),舊 row 保留 failed 狀態不可更改。
|
||||
"""
|
||||
__tablename__ = "governance_remediation_dispatch"
|
||||
|
||||
id: Mapped[str] = mapped_column(
|
||||
String(36), primary_key=True, default=generate_uuid,
|
||||
comment="主鍵(UUID)"
|
||||
)
|
||||
governance_event_id: Mapped[str] = mapped_column(
|
||||
String(36),
|
||||
ForeignKey("ai_governance_events.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
comment="關聯的治理事件 ID(RESTRICT 禁止孤兒事件)"
|
||||
)
|
||||
event_type: Mapped[str] = mapped_column(
|
||||
PgEnum(
|
||||
"trust_drift", "knowledge_degradation", "llm_hallucination",
|
||||
"execution_blast_radius", "governance_slo_data_gap",
|
||||
name="governance_event_type", create_type=False,
|
||||
),
|
||||
nullable=False,
|
||||
comment="治理事件類型(來自 ai_governance_events)"
|
||||
)
|
||||
dispatch_status: Mapped[str] = mapped_column(
|
||||
PgEnum(
|
||||
"pending", "dispatched", "executing",
|
||||
"succeeded", "failed", "skipped", "cancelled",
|
||||
name="governance_dispatch_status", create_type=False,
|
||||
),
|
||||
nullable=False,
|
||||
default="pending",
|
||||
comment="派遣狀態機(pending 為初始)"
|
||||
)
|
||||
playbook_id: Mapped[str | None] = mapped_column(
|
||||
String(36),
|
||||
ForeignKey("playbooks.playbook_id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
comment="關聯 Playbook(可選,未匹配時 NULL)"
|
||||
)
|
||||
incident_id: Mapped[str | None] = mapped_column(
|
||||
String(30),
|
||||
ForeignKey("incidents.incident_id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
comment="關聯 Incident(可選,治理事件觸發的修復可無 incident)"
|
||||
)
|
||||
approval_id: Mapped[str | None] = mapped_column(
|
||||
String(36),
|
||||
ForeignKey("approval_records.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
comment="關聯授權記錄(需人工審核時填入)"
|
||||
)
|
||||
decision_context: Mapped[dict] = mapped_column(
|
||||
JSON, nullable=False, default=dict,
|
||||
comment="派遣決策上下文 JSONB(DecisionContextV1 schema 驗證後寫入)"
|
||||
)
|
||||
executor_type: Mapped[str] = mapped_column(
|
||||
String(80), nullable=False,
|
||||
comment="執行器類型(如 playbook_executor / manual / slo_repair)"
|
||||
)
|
||||
attempt_count: Mapped[int] = mapped_column(
|
||||
Integer, nullable=False, default=0,
|
||||
comment="本 row 的嘗試次數(失敗重試時新 row attempt_count = 上筆 +1)"
|
||||
)
|
||||
max_attempts: Mapped[int] = mapped_column(
|
||||
Integer, nullable=False, default=3,
|
||||
comment="最大重試次數上限(含首次)"
|
||||
)
|
||||
last_error: Mapped[str | None] = mapped_column(
|
||||
Text, nullable=True,
|
||||
comment="最後一次失敗的錯誤訊息"
|
||||
)
|
||||
dispatched_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=taipei_now, nullable=False,
|
||||
comment="派遣時間(台北時區)"
|
||||
)
|
||||
started_at: Mapped[datetime | None] = mapped_column(
|
||||
DateTime(timezone=True), nullable=True,
|
||||
comment="執行開始時間(executing 狀態時填入)"
|
||||
)
|
||||
completed_at: Mapped[datetime | None] = mapped_column(
|
||||
DateTime(timezone=True), nullable=True,
|
||||
comment="執行完成時間(terminal 狀態時填入)"
|
||||
)
|
||||
created_by: Mapped[str | None] = mapped_column(
|
||||
String(100), nullable=True, default="governance_dispatcher",
|
||||
comment="建立者(系統自動派遣時為 governance_dispatcher)"
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_grd_status_dispatched", "dispatch_status", "dispatched_at"),
|
||||
Index("ix_grd_event_status", "governance_event_id", "dispatch_status"),
|
||||
Index("ix_grd_playbook_id", "playbook_id"),
|
||||
Index("ix_grd_event_type_status", "event_type", "dispatch_status"),
|
||||
CheckConstraint(
|
||||
"attempt_count >= 0 AND attempt_count <= max_attempts",
|
||||
name="ck_grd_attempts",
|
||||
),
|
||||
CheckConstraint(
|
||||
"max_attempts > 0",
|
||||
name="ck_grd_max_attempts_positive",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# TrustRecordDB - ADR-088 TrustScore 持久化
|
||||
# =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user