# ADR-UI-02: Contract Governance UI

**狀態**：Accepted
**日期**：2026-05-03（台北）
**決策者**：統帥
**範圍**：M3 Contract Dashboard + M4 Contract Editor 的詳細設計
**關聯**：ADR-UI-01（Operator Console 架構）、ADR-112（contract governance）、ADR-113（outbox）

---

## 背景

Contract Dashboard（M3）和 Contract Editor（M4）是 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 revision，lifecycle_status = active
- `⚠️ None`：無 active revision（需要初始設定）
- `🔄 Pending Activation`：有 published revision 等待 activate（approval 路徑）
- `🔴 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` 三類 contract（ADR-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 indicator（ADR-113）：若 relay worker 卡住，在 UI 立即可見
- Diff 視圖：activate 前可以比對版本差異，減少誤操作

### Costs
- JSON diff 顯示需要第三方套件或自製（約 100 行）
- Activation approval flow 需要與 M7/M8 的 Approval 模組配合

---

## 驗收標準

- [ ] M3：六合約全部顯示，狀態 badge 正確（Phase 5 整合測試）
- [ ] M4：draft → publish → activate 完整流程可操作（Phase 5）
- [ ] M4：路徑 A contract activate → Approval Queue 有對應請求（整合測試）
- [ ] M4：JSON diff 視圖可比對版本差異（Phase 5）

## 關聯

- ADR-UI-01（Operator Console 整體路由）
- ADR-112（contract governance API）
- ADR-113（outbox，M3 的 outbox lag indicator）
