Files
awoooi/docs/adr/ADR-UI-01-operator-console-architecture.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

6.3 KiB
Raw Permalink Blame History

ADR-UI-01: Operator Console Architecture

狀態Accepted 日期2026-05-03台北 決策者:統帥 範圍AwoooP Operator Console 前端整體架構、與現有 apps/web/ 的整合方式 關聯ADR-106六合約、ADR-112contract governance、ADR-115principal mapping


背景

AwoooP 需要一個 Operator Console管理員後台讓平台管理員可以

  1. 管理 tenantproject和 principal 角色
  2. 查看和管理 contract revisions六合約的 draft/publish/activate
  3. 監控 runsrun state, saga steps, budget usage
  4. 執行 approval decisionshuman approval for WAITING_APPROVAL runs

現有 apps/web/ 是 Next.js 14 前端,已有 AI 監控 dashboard。Operator Console 需要在不破壞現有功能的情況下整合。


決策

D1 — 整合方式:子路由擴展(不建新應用)

Operator Console 作為 apps/web/ 的新路由段:

apps/web/src/app/
├── (existing)/           ← 現有頁面(不動)
│   ├── dashboard/
│   ├── incidents/
│   └── ...
└── awooop/               ← 新增 Operator Console
    ├── layout.tsx        ← Console layoutsidebar + auth gate
    ├── tenants/          ← Tenant 管理
    │   ├── page.tsx
    │   └── [project_id]/
    │       ├── page.tsx
    │       └── principals/
    ├── contracts/        ← Contract governance
    │   ├── page.tsx
    │   └── [contract_id]/
    │       ├── page.tsx
    │       └── revisions/
    ├── runs/             ← Run 監控
    │   ├── page.tsx
    │   └── [run_id]/
    │       └── page.tsx
    └── approvals/        ← Approval decisions
        ├── page.tsx
        └── [run_id]/
            └── page.tsx

D2 — Auth GateOperator Console 專屬)

Operator Console 需要 platform_subject.roles 中包含 adminapprover

// apps/web/src/app/awooop/layout.tsx
import { redirect } from "next/navigation"
import { getServerSession } from "@/lib/auth"
import { hasOperatorRole } from "@/lib/awooop-auth"

export default async function AwoooPLayout({ children }) {
  const session = await getServerSession()
  if (!session || !hasOperatorRole(session.user)) {
    redirect("/unauthorized")
  }
  return (
    <div className="awooop-operator-layout">
      <AwoooPSidebar />
      <main>{children}</main>
    </div>
  )
}

D3 — API Client 分層

apps/web/src/lib/
├── awooop/
│   ├── client.ts         ← AwoooP Platform API client/v1/platform/...
│   ├── types.ts          ← TypeScript types從 API spec 生成)
│   ├── runs.ts           ← Run 相關 API calls
│   ├── contracts.ts      ← Contract 相關 API calls
│   ├── tenants.ts        ← Tenant 相關 API calls
│   └── approvals.ts      ← Approval 相關 API calls

API base URL

// 從環境變數讀取(禁止 hardcode 內網 IPfeedback_frontend_internal_ip_ban
const AWOOOP_API_BASE = process.env.NEXT_PUBLIC_AWOOOP_API_URL
// K8s ConfigMap 設置NEXT_PUBLIC_AWOOOP_API_URL=https://api.awoooi.com

D4 — 8 個 UI 模組Phase 5 實作)

模組 路由 功能 ADR 關聯
M1 Tenant List /awooop/tenants 列出所有 project建立/停用 ADR-115
M2 Principal Manager /awooop/tenants/[id]/principals 角色管理auto-provision 確認 ADR-115
M3 Contract Dashboard /awooop/contracts 六合約總覽active revision 狀態 ADR-106
M4 Contract Editor /awooop/contracts/[id]/revisions draft/publish/activate 操作 ADR-112
M5 Run Monitor /awooop/runs 所有 run 的即時狀態 ADR-114
M6 Run Detail /awooop/runs/[run_id] saga_steps, budget, trace_id ADR-119
M7 Approval Queue /awooop/approvals WAITING_APPROVAL run 列表 ADR-114
M8 Approval Decision /awooop/approvals/[run_id] approve/reject + token 生成 ADR-116

各模組的詳細設計在 ADR-UI-02 / ADR-UI-03 / ADR-UI-04。

D5 — 設計原則

  1. 禁止 emoji使用 Lucide iconfeedback_no_emoji_use_icons
  2. 全站統一字體/顏色feedback_design_system_consistency
  3. 100% next-intl i18nfeedback_i18n_zero_hardcode— 所有文字走翻譯 key
  4. 禁止內網 IPfeedback_frontend_internal_ip_ban— NEXT_PUBLIC_* 只用公網域名
  5. Logo SVG 與正式環境一致feedback_brand_logo_consistency

D6 — 即時更新WebSocket / SSE

Run MonitorM5和 Approval QueueM7需要即時更新

// Server-Sent Events比 WebSocket 更簡單,適合單向推送)
// GET /v1/platform/runs/stream?project_id=awoooi&status=RUNNING,WAITING_APPROVAL

const eventSource = new EventSource(
  `/v1/platform/runs/stream?project_id=${projectId}`
)
eventSource.onmessage = (event) => {
  const update = JSON.parse(event.data) as RunStateUpdate
  updateRunInCache(update)
}

後果

Benefits

  • 複用現有 apps/web/ 的 auth、i18n、設計系統不重造輪子
  • 子路由設計:現有功能不受影響,可獨立部署 Operator Console 路由
  • Phase 5 前,/awooop/* 路由可以用 feature flag 隱藏9 個 feature flag 中的 ENABLE_OPERATOR_CONSOLE

Costs

  • 需要在 apps/web/ 建立 awooop/ 目錄結構(~15 個新檔案)
  • API client 需要 TypeScript type generation從 OpenAPI spec

Risks

  • Operator Console 的 auth gate 若有漏洞 → 非管理員可存取敏感操作
  • 緩解auth gate 在 layout.tsx 實作server-side不依賴 client-side 隱藏

驗收標準

  • /awooop 路由在 apps/web/src/app/ 建立Phase 5
  • auth gate非 admin/approver → redirect /unauthorized(整合測試)
  • NEXT_PUBLIC_AWOOOP_API_URL 不含內網 IPCI lint check
  • 8 個模組全部有對應路由Phase 5 完成)

關聯

  • ADR-106六合約Contract Dashboard M3/M4 的資料來源)
  • ADR-112contract governance APIM4 的操作路徑)
  • ADR-114run stateM5 M6 的資料來源)
  • ADR-115principal mappingM1 M2
  • ADR-116approval tokenM8 核心功能)