Files
awoooi/docs/adr/ADR-UI-02-contract-governance-ui.md
Your Name 13e51802fe feat(awooop): Phase 0 全 ADR + Phase 1 control plane schema(含 critic 四項修正)
## Phase 0(文件層,全部 Accepted)
- ADR-106/107:AwoooP 平台架構 + 儲存策略
- ADR-111~118:Bootstrap → RLS 七項核心 ADR
- ADR-119~124:SAGA → Singleton Decomposition 六項 ADR
- ADR-UI-01~04:Operator Console 四個 UI ADR

## Phase 1(DB schema + migration)
- awooop_phase1_control_plane_2026-05-04.sql:7 張新表 + trigger + RLS
  - Step 1:三角色(platform_admin/migration BYPASSRLS,awooop_app 受 RLS)
  - Step 13:GRANT awooop_app 最小權限(7 條)
  - Step 14:RLS fail-closed,移除 __platform__ 後門
- awooop_phase1_batch1_rls_2026-05-04.sql:高流量四表三步式 ADD COLUMN
- awooop_phase1_batch1_backfill.py:SKIP LOCKED 分批回填腳本
- awooop_models.py:7 個 SQLAlchemy 2.x models

## Critic 修正(4 Critical + 3 Major)
- C-1:ADD CONSTRAINT IF NOT EXISTS → DO 塊 + pg_constraint 查詢
- C-2:__mapper_args__ 字串 list → primary_key=True on mapped_column
- C-3:__platform__ RLS 後門 → 全移除,改用 BYPASSRLS role
- C-4:awooop_app role 從未建立 → Step 1 + 7 條 GRANT
- M-1:active_pointer_guard SECURITY DEFINER(FORCE RLS 跨租戶保護)
- M-2:pg_partman create_parent 加冪等防護
- M-3:immutability trigger 新增身份欄位保護(project_id/family/contract_id)

## Task 1.2 修補
- agent_loader.py:硬編碼 Mac 路徑 → AGENTS_DIR 環境變數
- Dockerfile:補 COPY .claude/agents/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 13:37:11 +08:00

167 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-UI-02: Contract Governance UI
**狀態**Accepted
**日期**2026-05-03台北
**決策者**:統帥
**範圍**M3 Contract Dashboard + M4 Contract Editor 的詳細設計
**關聯**ADR-UI-01Operator Console 架構、ADR-112contract governance、ADR-113outbox
---
## 背景
Contract DashboardM3和 Contract EditorM4是 Operator Console 最複雜的模組,需要展示六合約的全貌,並支援 draft → publish → activate 的工作流程。
---
## M3 — Contract Dashboard
### 頁面設計
**路由**`/awooop/contracts`
**主要資訊**
```
┌─────────────────────────────────────────────────────────────┐
│ Contract Dashboard [+ New Draft] │
├──────────┬──────────────────────┬──────────┬───────────────┤
│ Project │ Contract Family │ Active │ Last Updated │
├──────────┼──────────────────────┼──────────┼───────────────┤
│ awoooi │ policy_routing │ v2.1 ✅ │ 2026-05-03 │
│ awoooi │ mcp_gateway │ v1.3 ✅ │ 2026-04-20 │
│ awoooi │ agent │ v1.0 ✅ │ 2026-03-15 │
│ awoooi │ project_tenant │ v1.0 ✅ │ 2026-01-10 │
│ awoooi │ runtime_run_state │ v1.1 ✅ │ 2026-04-28 │
│ awoooi │ channel_event │ v1.2 ✅ │ 2026-04-15 │
│ ewoooc │ policy_routing │ ⚠️ None │ — │
└──────────┴──────────────────────┴──────────┴───────────────┘
```
**狀態 badge**
- `✅ v2.1`:有 active revisionlifecycle_status = active
- `⚠️ None`:無 active revision需要初始設定
- `🔄 Pending Activation`:有 published revision 等待 activateapproval 路徑)
- `🔴 Outbox Lag`outbox 有未送出事件超過 60 秒ADR-113 告警)
### API 呼叫
```typescript
// GET /v1/platform/projects/{project_id}/contracts
// Response: ContractSummary[]
interface ContractSummary {
project_id: string
contract_family: ContractFamily
contract_id: string
active_revision_id: string | null
active_version: string | null
lifecycle_status: "active" | "no_active" | "pending_activation"
last_updated_at: string
has_pending_outbox: boolean // ADR-113 outbox lag indicator
}
```
---
## M4 — Contract Editor
### 頁面設計
**路由**`/awooop/contracts/[contract_id]/revisions`
**頁面佈局**
```
左側Revision List 右側Revision Detail
┌─────────────────────┐ ┌──────────────────────────────────┐
│ policy_routing/ │ │ Revision: v2.1 (Active) │
│ awoooi-sre │ │ │
│ ─────────────────── │ │ [Body JSON] [Signature] [History]│
│ v2.1 ● Active │ →選擇→ │ │
│ v2.0 Published │ │ { │
│ v1.9 Published │ │ "routing_rules": [...], │
│ v1.8 Published │ │ "model_priority": [...] │
│ v0.1 Draft │ │ } │
│ ─────────────────── │ │ │
│ [+ New Draft] │ │ Signature: ✅ Valid │
└─────────────────────┘ │ Published by: admin@awoooi │
│ Published at: 2026-05-03 14:23 │
│ │
│ [Activate] [Revoke] [Diff vs v2.0]│
└──────────────────────────────────┘
```
### 操作按鈕邏輯
| 按鈕 | 可見條件 | 行為 |
|------|---------|------|
| `[+ New Draft]` | 任何時候 | 建立新 draft revision |
| `[Publish]` | status=draft | 呼叫 publish API + 簽章驗證 |
| `[Activate]` | status=published | 路徑 A送 approval路徑 B直接 activate |
| `[Revoke]` | status=active | 確認對話框 → revoke回到 no_active 狀態)|
| `[Diff vs v2.0]` | status=published or active | 顯示與前一版本的 JSON diff |
### Activation Approval Flow路徑 A
對於 `policy_routing``mcp_gateway``runtime_run_state` 三類 contractADR-112 D3 路徑 A
```
點擊 [Activate]
顯示對話框:
「此 contract 需要 approval。
請說明變更原因:[ textarea ]
[提交 Approval 請求] [取消]」
POST /v1/platform/projects/{project_id}/contracts/{contract_id}/activate
Body: { "revision_id": "...", "change_reason": "..." }
API 返回:{ "approval_run_id": "...", "status": "WAITING_APPROVAL" }
頁面顯示「Approval 請求已送出 → 前往 Approval Queue」
M7 Approval Queue 顯示待審核項目
```
### 版本 Diff 視圖
```typescript
// 使用 react-diff-viewer 或自製 JSON diff
import { DiffViewer } from "@/components/awooop/diff-viewer"
// 對比兩個 revision 的 body_json
<DiffViewer
oldValue={JSON.stringify(oldRevision.body_json, null, 2)}
newValue={JSON.stringify(newRevision.body_json, null, 2)}
splitView={true}
/>
```
---
## 後果
### Benefits
- Contract Dashboard 一眼看清六合約的 active 狀態
- Outbox lag indicatorADR-113若 relay worker 卡住,在 UI 立即可見
- Diff 視圖activate 前可以比對版本差異,減少誤操作
### Costs
- JSON diff 顯示需要第三方套件或自製(約 100 行)
- Activation approval flow 需要與 M7/M8 的 Approval 模組配合
---
## 驗收標準
- [ ] M3六合約全部顯示狀態 badge 正確Phase 5 整合測試)
- [ ] M4draft → publish → activate 完整流程可操作Phase 5
- [ ] M4路徑 A contract activate → Approval Queue 有對應請求(整合測試)
- [ ] M4JSON diff 視圖可比對版本差異Phase 5
## 關聯
- ADR-UI-01Operator Console 整體路由)
- ADR-112contract governance API
- ADR-113outboxM3 的 outbox lag indicator