- ADR-037 監控增強架構 - MONITORING_MASTER_PLAN 主計畫 - MASTER_EXECUTION_SCHEDULE 執行排程 - Phase D/E/Worker HPA Runbooks Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
500 lines
14 KiB
Markdown
500 lines
14 KiB
Markdown
# RunBook: 前端 UI/UX 核心痛點徹底解決方案
|
||
|
||
> **類型**: 架構設計 + 實施 RunBook
|
||
> **優先級**: 🔴 P0 (核心競爭力)
|
||
> **建立**: 2026-03-29 12:38 (台北)
|
||
> **建立者**: Antigravity
|
||
> **核心命題**: 讓 AWOOOI 的前端「讓人看一眼就驚艷,用一次就上癮」
|
||
|
||
---
|
||
|
||
## 一、Claude Code 前端弱點診斷
|
||
|
||
### 1.1 五大結構性弱點
|
||
|
||
| 弱點 | 後果 | 解法 |
|
||
|------|------|------|
|
||
| **視覺感知盲區** | 代碼語法正確,但視覺效果差 | Playwright 截圖 + 統帥視覺主控 |
|
||
| **CSS 語境失憶** | 改 A 壞 B,不知道全局 CSS 影響 | Storybook 隔離組件 + TypeCheck |
|
||
| **動畫設計無感** | 150ms 快閃 vs 300ms 遲滯無法感知 | 在 Storybook Story 中定義動畫標準 |
|
||
| **設計意圖推斷不足** | Nothing.tech 審美需要視覺參照 | 每個組件附帶規格截圖 |
|
||
| **Safari 兼容性盲點** | `backdrop-blur` 在 Safari 有 bug | Playwright multi-browser E2E |
|
||
|
||
### 1.2 技術債現況(截至 2026-03-29)
|
||
|
||
```
|
||
i18n 違規:40+ 處(TECHNICAL_DEBT_PHASE2.md)
|
||
shadcn/ui 殘留:已部分廢除,但需全面確認
|
||
GenUI Registry:只有基礎 5 張卡片,缺少監控類
|
||
Knowledge Base:頁面空白,無後端
|
||
Omni-Terminal:外殼完整,SSE 事件類型未完全對接
|
||
```
|
||
|
||
---
|
||
|
||
## 二、九大行動詳細實施計畫
|
||
|
||
### 🔴 行動 1: i18n 閃電清零(工時:4h)
|
||
|
||
**策略:一次性掃描→批量修復,非逐一處理**
|
||
|
||
```bash
|
||
# Step 1.1: 自動掃描所有硬編碼字串
|
||
cd /Users/ogt/awoooi/apps/web
|
||
|
||
# 掃描 TSX 中的英文硬編碼(排除技術識別符)
|
||
grep -rn '"[A-Z][A-Z]' src/ --include="*.tsx" --include="*.ts" | \
|
||
grep -v "//.*\"" | \
|
||
grep -v "className=" | \
|
||
grep -v "href=" | \
|
||
grep -v "id=" | \
|
||
grep -v "import " > /tmp/en_violations.txt
|
||
|
||
# 掃描中文硬編碼
|
||
grep -rn "\"[^\x00-\x7F]" src/ --include="*.tsx" | \
|
||
grep -v "//.*\"" > /tmp/zh_violations.txt
|
||
|
||
echo "英文違規:$(wc -l < /tmp/en_violations.txt) 處"
|
||
echo "中文違規:$(wc -l < /tmp/zh_violations.txt) 處"
|
||
```
|
||
|
||
**已知 P0 違規快速修復清單**(根據 TECHNICAL_DEBT_PHASE2.md):
|
||
|
||
```typescript
|
||
// ❌ agent/data-pincer.tsx:50-78(需修復)
|
||
// 現在:
|
||
const statuses = {
|
||
standby: 'STANDBY',
|
||
analyzing: 'ANALYZING',
|
||
executing: 'EXECUTING',
|
||
awaiting: 'AWAITING APPROVAL',
|
||
error: 'ERROR'
|
||
}
|
||
|
||
// ✅ 修復後:
|
||
const statuses = {
|
||
standby: t('status.standby'),
|
||
analyzing: t('status.analyzing'),
|
||
executing: t('status.executing'),
|
||
awaiting: t('status.awaitingApproval'),
|
||
error: t('status.error')
|
||
}
|
||
```
|
||
|
||
```typescript
|
||
// ❌ status-orb.tsx:16-31
|
||
// 現在:
|
||
const STATUS_TEXT = {
|
||
idle: 'Idle',
|
||
thinking: 'Thinking',
|
||
executing: 'Executing',
|
||
awaiting: 'Awaiting Approval'
|
||
}
|
||
|
||
// ✅ 修復後:
|
||
const STATUS_TEXT = {
|
||
idle: t('status.idle'),
|
||
thinking: t('status.thinking'),
|
||
executing: t('status.executing'),
|
||
awaiting: t('status.awaitingApproval')
|
||
}
|
||
```
|
||
|
||
**需要同步更新的字典檔**:
|
||
|
||
```json
|
||
// apps/web/messages/zh-TW.json(追加)
|
||
{
|
||
"status": {
|
||
"idle": "待機",
|
||
"thinking": "分析中",
|
||
"executing": "執行中",
|
||
"awaitingApproval": "等待核准",
|
||
"error": "錯誤",
|
||
"standby": "待機",
|
||
"analyzing": "分析中"
|
||
}
|
||
}
|
||
```
|
||
|
||
```json
|
||
// apps/web/messages/en.json(追加)
|
||
{
|
||
"status": {
|
||
"idle": "Idle",
|
||
"thinking": "Thinking",
|
||
"executing": "Executing",
|
||
"awaitingApproval": "Awaiting Approval",
|
||
"error": "Error",
|
||
"standby": "Standby",
|
||
"analyzing": "Analyzing"
|
||
}
|
||
}
|
||
```
|
||
|
||
**驗收指令(必須通過)**:
|
||
|
||
```bash
|
||
cd apps/web
|
||
|
||
# 確認無中文硬編碼
|
||
if grep -rn '"[^\x00-\x7F]' src/ --include="*.tsx" | grep -v "//"; then
|
||
echo "❌ 仍有中文硬編碼!"
|
||
exit 1
|
||
else
|
||
echo "✅ 中文硬編碼清零"
|
||
fi
|
||
|
||
# TypeScript 編譯驗證(workflow 硬規則)
|
||
pnpm exec tsc --noEmit
|
||
```
|
||
|
||
---
|
||
|
||
### 🔴 行動 2: Storybook 組件庫建立(工時:8h)
|
||
|
||
**這是解決 AI 視覺感知盲區的根本方案**。
|
||
|
||
```bash
|
||
# Step 2.1: 安裝 Storybook
|
||
cd apps/web
|
||
pnpm add -D storybook@latest @storybook/nextjs @storybook/addon-essentials \
|
||
@storybook/addon-interactions @storybook/test
|
||
npx storybook@latest init --builder webpack5
|
||
|
||
# Step 2.2: 配置 Nothing.tech 主題
|
||
```
|
||
|
||
**必須上架的 10 個核心組件 Story**:
|
||
|
||
| 組件 | Nothing.tech 規格 | Story 狀態 |
|
||
|------|-----------------|-----------|
|
||
| `GlassCard` | `bg-white/70 backdrop-blur-[20px] border border-black/[0.06]` | Loading / Content / Error |
|
||
| `StatusOrb` | 燈號 + `animate-ping`(critical 時)| idle / thinking / executing / critical |
|
||
| `ApprovalCard` | 1.0 ConversationalView 風格 | LOW / MEDIUM / HIGH / CRITICAL |
|
||
| `OmniTerminal` | VT323 字體 + 綠色游標閃爍 | empty / thinking / streaming / error |
|
||
| `HostCard` | CPU/Memory 橫條 + 脈搏點 | healthy / warning / critical |
|
||
| `MetricsCard` | 數字大字 + 趨勢箭頭 | up / down / stable |
|
||
| `SystemHealthCard` | 燈號矩陣 5x5 | all-healthy / some-warning / critical |
|
||
| `FinOpsCard` | 成本分解 + 可省金額 | monthly / quarterly |
|
||
| `SLOCard` | 達成率 + 趨勢 | healthy / at-risk / breached |
|
||
| `AnomalyFrequencyCard` | 頻率統計 + 升級建議 | normal / repeat / escalate |
|
||
|
||
**Storybook Story 範例**(GlassCard):
|
||
|
||
```typescript
|
||
// apps/web/src/components/ui/glass-card.stories.ts
|
||
import type { Meta, StoryObj } from '@storybook/react';
|
||
import { GlassCard } from './glass-card';
|
||
|
||
const meta: Meta<typeof GlassCard> = {
|
||
title: 'AWOOOI/UI/GlassCard',
|
||
component: GlassCard,
|
||
parameters: {
|
||
// Nothing.tech 白底背景
|
||
backgrounds: {
|
||
default: 'nothing-white',
|
||
values: [{ name: 'nothing-white', value: '#F5F5F0' }],
|
||
},
|
||
// 規格文件截圖
|
||
docs: {
|
||
description: {
|
||
component: `
|
||
Nothing.tech 白玻璃卡片。固定規格:
|
||
- bg: bg-white/70
|
||
- blur: backdrop-blur-[20px]
|
||
- border: border border-black/[0.06]
|
||
- radius: rounded-xl
|
||
`,
|
||
},
|
||
},
|
||
},
|
||
tags: ['autodocs'],
|
||
};
|
||
export default meta;
|
||
|
||
type Story = StoryObj<typeof GlassCard>;
|
||
|
||
export const Default: Story = {
|
||
args: { children: '玻璃卡片內容' }
|
||
};
|
||
|
||
export const WithCriticalBorder: Story = {
|
||
args: {
|
||
children: '緊急狀態',
|
||
className: 'border-status-critical border-2'
|
||
}
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### 🔴 行動 3: AI 視覺審查 SOP(工時:2h 建立,後續 0 工時)
|
||
|
||
**建立標準作業程序,讓 AI 自主截圖並等待統帥審核:**
|
||
|
||
```markdown
|
||
## AI 前端修改 SOP(強制執行)
|
||
|
||
### 修改前
|
||
1. 查閱 Storybook 對應組件的規格 Story
|
||
2. 確認 Nothing.tech 視覺 Token(詳見 tailwind.config.ts)
|
||
|
||
### 修改中
|
||
3. 修改代碼
|
||
4. 執行 `pnpm exec tsc --noEmit`(語法驗證)
|
||
|
||
### 修改後(🆕 新增強制步驟)
|
||
5. 啟動 Dev Server:`pnpm dev`
|
||
6. 執行截圖腳本:
|
||
```bash
|
||
cd apps/web
|
||
pnpm exec playwright screenshot \
|
||
--browser chromium \
|
||
http://localhost:3000 \
|
||
docs/screenshots/$(date +%Y%m%d-%H%M)/homepage.png
|
||
```
|
||
7. 截圖存至 `docs/screenshots/{date}/{component}.png`
|
||
8. 在 LOGBOOK 記錄:「已截圖,視覺存檔 docs/screenshots/xxx/yyy.png」
|
||
9. 等待統帥視覺審批後方可 commit
|
||
```
|
||
|
||
---
|
||
|
||
### 🟠 行動 4: Omni-Terminal 後端全接通(工時:8h)
|
||
|
||
根據 `ADR-031` 和 `AWOOOI_AGENTIC_WORKSPACE_ROADMAP.md` 的神經連接藍圖:
|
||
|
||
#### 4.1 三大 SSE 事件類型定義
|
||
|
||
```python
|
||
# apps/api/src/api/v1/terminal.py
|
||
# 擴充現有 SSE 端點
|
||
|
||
class SSEEventType(str, Enum):
|
||
THOUGHT = "thought" # Agent 思考流
|
||
TOOL_CALL = "tool_call" # 工具調用(含微動畫觸發信號)
|
||
TOOL_RESULT = "tool_result" # 工具結果
|
||
RENDER_UI = "render_ui" # 動態渲染 GenUI 組件
|
||
STREAM_END = "stream_end" # 思考流結束
|
||
|
||
|
||
# 範例事件:
|
||
async def stream_terminal_response(command: str):
|
||
# 1. 思考流
|
||
yield f"event: thought\ndata: {json.dumps({'text': '[Investigator] 分析指令...'})}\n\n"
|
||
|
||
# 2. 工具調用(觸發前端 CSS 動畫)
|
||
yield f"event: tool_call\ndata: {json.dumps({'tool': 'kubectl_get', 'args': {'resource': 'pod', 'namespace': 'awoooi-prod'}})}\n\n"
|
||
|
||
# 3. 工具結果
|
||
yield f"event: tool_result\ndata: {json.dumps({'pods': [...]})}\n\n"
|
||
|
||
# 4. 渲染 GenUI 卡片(前端動態載入組件)
|
||
yield f"event: render_ui\ndata: {json.dumps({'component': 'SystemHealthCard', 'props': {...}})}\n\n"
|
||
|
||
# 5. 結束
|
||
yield f"event: stream_end\ndata: {json.dumps({'success': True})}\n\n"
|
||
```
|
||
|
||
#### 4.2 前端 SSE 事件處理器(關鍵)
|
||
|
||
```typescript
|
||
// apps/web/src/hooks/useTerminalSSE.ts
|
||
// 修改現有 SSE hook,增加 tool_call 動畫觸發
|
||
|
||
const useTerminalSSE = (commandId: string) => {
|
||
const [state, setState] = useTerminalStore();
|
||
|
||
useEffect(() => {
|
||
const es = new EventSource(`/api/v1/terminal/stream/${commandId}`);
|
||
|
||
// 思考流
|
||
es.addEventListener('thought', (e) => {
|
||
const data = JSON.parse(e.data);
|
||
setState(s => ({ ...s, thoughts: [...s.thoughts, data.text] }));
|
||
});
|
||
|
||
// 工具調用 → 觸發微動畫
|
||
es.addEventListener('tool_call', (e) => {
|
||
const data = JSON.parse(e.data);
|
||
setState(s => ({
|
||
...s,
|
||
activeToolCall: data.tool, // UI 顯示「正在執行 kubectl_get...」
|
||
isAnimating: true
|
||
}));
|
||
});
|
||
|
||
// GenUI 動態渲染(核心功能!)
|
||
es.addEventListener('render_ui', (e) => {
|
||
const { component, props } = JSON.parse(e.data);
|
||
setState(s => ({
|
||
...s,
|
||
renderedCards: [...s.renderedCards, { component, props }]
|
||
}));
|
||
});
|
||
|
||
es.addEventListener('stream_end', () => {
|
||
setState(s => ({ ...s, isStreaming: false, isAnimating: false }));
|
||
es.close();
|
||
});
|
||
|
||
return () => es.close();
|
||
}, [commandId]);
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
### 🟠 行動 5: GenUI Registry 擴充(工時:8h)
|
||
|
||
新增 5 張監控類 GenUI 卡片(詳細規格見 `MONITORING_ARCHITECTURE_DEEP_DIVE.md`):
|
||
|
||
```typescript
|
||
// apps/web/src/components/genui/registry.ts
|
||
export const GENUI_COMPONENTS = {
|
||
// 現有:
|
||
'MetricsCard': () => import('./cards/MetricsCard'),
|
||
'K8sPodCard': () => import('./cards/K8sPodCard'),
|
||
|
||
// 🆕 監控類(Wave M-3):
|
||
'SystemHealthCard': () => import('./monitoring/SystemHealthCard'),
|
||
'ServiceDetailCard': () => import('./monitoring/ServiceDetailCard'),
|
||
'FinOpsCard': () => import('./monitoring/FinOpsCard'),
|
||
'SLODashboardCard': () => import('./monitoring/SLODashboardCard'),
|
||
'AlertChainStatusCard': () => import('./monitoring/AlertChainStatusCard'),
|
||
'AnomalyFrequencyCard': () => import('./monitoring/AnomalyFrequencyCard'),
|
||
'MTTRCard': () => import('./monitoring/MTTRCard'),
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 三、前端改善路線圖時間表
|
||
|
||
```
|
||
📅 本週(立即):
|
||
[4h] i18n 閃電清零(一次性全修)
|
||
[2h] AI 視覺審查 SOP 建立(.awoooi-agent-rules.md 追加)
|
||
|
||
📅 Week 2-3:
|
||
[8h] Storybook 10 個核心組件 Story
|
||
[8h] Omni-Terminal 後端全接通(三種 SSE 事件)
|
||
|
||
📅 Week 4-5:
|
||
[8h] 監控 GenUI 卡片擴充(7 張新卡片)
|
||
[8h] Nexus 頁面 AI 自治率 UI 組件
|
||
|
||
📅 Month 2:
|
||
[16h] Knowledge Base 後端 + 前端完整建設
|
||
[8h] Visual Regression Testing CI 整合
|
||
|
||
📅 Month 3(Phase 4 視覺靈魂注入):
|
||
[?h] 品牌 3D 資產 + Q 版 OpenClaw
|
||
[?h] 全站微動畫升級(150ms 快閃標準)
|
||
[?h] Nothing.tech 認證級別的設計審計
|
||
```
|
||
|
||
---
|
||
|
||
## 四、強制驗收標準
|
||
|
||
每次前端 PR 合併前,必須通過以下全部驗收:
|
||
|
||
```bash
|
||
# 1. TypeScript 無錯誤(前端美學 Workflow 硬規則)
|
||
cd apps/web && pnpm exec tsc --noEmit
|
||
|
||
# 2. i18n 無硬編碼(CI 攔截)
|
||
[ -z "$(grep -rn '"[^\x00-\x7F]' src/ --include='*.tsx')" ] || exit 1
|
||
|
||
# 3. Storybook 可正常 build(確保組件獨立可用)
|
||
pnpm storybook build
|
||
|
||
# 4. E2E Smoke 測試通過
|
||
pnpm exec playwright test --grep @smoke
|
||
|
||
# 5. 截圖存檔(AI 執行,統帥視覺審批)
|
||
pnpm exec playwright screenshot http://localhost:3000 docs/screenshots/pr-{NUMBER}/homepage.png
|
||
|
||
# 6. Build 成功(生產環境兼容)
|
||
pnpm run build
|
||
```
|
||
|
||
---
|
||
|
||
## ⚠️ 架構安全補丁(2026-03-29 更新,行動 1 開始前必讀)
|
||
|
||
> 來源:`ARCHITECTURAL_RISK_WAR_GAME.md` 深度沙盤推演,代碼確認級別
|
||
|
||
### 補丁:Feature Freeze 前必須建立 release/v1.x 穩定分支
|
||
|
||
**問題**:行動 1(i18n 閃電清零)需要全域替換 `src/` 底下數千行代碼,`main` 分支會進入持續 3-5 天的「混沌狀態」。若此期間生產爆發 P0 Bug(如簽核按鈕失效),**無法切出乾淨的 Hotfix 分支**,導致修復與重構互相衝突。
|
||
|
||
> 🔴 **行動 1 開始前,必須先建立 `release/v1.x` 穩定分支!**
|
||
|
||
#### 必須在行動 1 之前執行
|
||
|
||
```bash
|
||
# Step 0(行動 1 前置):建立穩定基準分支
|
||
git checkout main
|
||
git pull origin main
|
||
git checkout -b release/v1.x
|
||
git push origin release/v1.x
|
||
|
||
# 在 GitHub 設定 release/v1.x 為 Protected Branch
|
||
# Settings → Branches → Add branch protection rule → release/v1.x
|
||
# ✅ Require pull request reviews (1 approver)
|
||
# ✅ Do not allow bypassing the above settings
|
||
```
|
||
|
||
#### 正確的行動順序
|
||
|
||
```
|
||
Step 0:建立 release/v1.x(🆕 前置步驟,必須先做)
|
||
|
||
Step 1:宣佈 Frontend Feature Freeze(禁止非 i18n PR 合併前端代碼)
|
||
|
||
Step 2:i18n 閃電清零(在 fix/i18n-zero-violation 分支進行)
|
||
|
||
Step 3:PR 合併到 main → 解除 Frontend Freeze
|
||
|
||
Step 4:ESLint i18n Plugin 切換為 error 模式
|
||
|
||
Step 5:繼續行動 2-9(Storybook、Terminal 等)
|
||
```
|
||
|
||
#### Freeze 期間 P0 Hotfix 緊急流程
|
||
|
||
```bash
|
||
# 情境:簽核按鈕在生產失效,i18n 清零正在進行中
|
||
|
||
# 1. 從穩定基準切出 hotfix(不影響 i18n 重構)
|
||
git checkout release/v1.x
|
||
git checkout -b hotfix/fix-approval-button
|
||
|
||
# 2. 最小化修復(只改 Bug,不動其他代碼)
|
||
# ... 修復代碼 ...
|
||
git add . && git commit -m "fix: approval button not responding on mobile"
|
||
|
||
# 3. PR 到 release/v1.x → CD 直接部署 release 分支
|
||
git push origin hotfix/fix-approval-button
|
||
# → PR 合併到 release/v1.x
|
||
# → 觸發 CD 部署(CD 配置需支援 release/* 分支)
|
||
|
||
# 4. Cherry-pick 到 main(不中斷 i18n 重構)
|
||
git checkout main
|
||
git cherry-pick <hotfix-commit-hash>
|
||
# 若有衝突:手動解決後繼續
|
||
```
|
||
|
||
#### Hotfix 觸發條件(須加入 HARD_RULES.md)
|
||
|
||
```
|
||
P0 Hotfix 判定標準(任一條件符合即觸發):
|
||
□ 統帥無法使用核心功能(簽核按鈕、登入、Telegram 通知)
|
||
□ Sentry P0 Error 每分鐘 > 10 次
|
||
□ 服務 availability < 99%(監控頁面顯示)
|
||
□ OpenClaw 決策鏈完全中斷超過 5 分鐘
|
||
```
|