Files
ewoooc/docs/guides/observability_ui_governance.md
OoO dc7fe371bd
All checks were successful
CD Pipeline / deploy (push) Successful in 1m0s
test(observability): add deploy gate self-test
2026-05-06 13:44:20 +08:00

129 lines
5.4 KiB
Markdown
Raw Permalink 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.
# AI 觀測台 UI Governance
> Scope: `/observability/*` pages using `ewoooc_base.html` and `.momo-observability-mode`.
## 目標
AI 觀測台是「AI 中樞控制室」,不是 Bootstrap 報表頁。任何新增或修改都必須維持暖色焦糖系、Noto Sans TC、點陣方格、卡片化資料面、可降級空狀態。
## 必守規範
- 使用 `static/css/observability-system.css` 的 token 與 utility class。
- 標題使用 `--obs-title-size`KPI 數字使用 `--obs-value-size`
- 圖表容器使用 `.obs-chart-frame``.obs-chart-frame-tall``.obs-chart-frame-slim`
- 頁面資料缺表或 migration 未完成時,顯示安全空狀態,不得把 SQL exception 顯示給使用者。
- 側欄必須維持暖深咖啡背景,第二/三層文字要有足夠對比。
- 表格必須有暖色表頭、row hover、長文字換行保護。
- 手機與中螢幕要能自動從多欄收成單欄。
## 禁止事項
- 不要在 template 裡新增 Times / Georgia / serif 作為主標題字型。
- 不要新增紫色系、純黑 sidebar、純白卡片 hover。
- 不要把整頁做成寬表格;資料密集內容優先用卡片、矩陣、可橫向 overflow 的表格。
- 不要硬寫超大 `font-size: clamp(...)`;先調 token。
- 不要新增 `style="height:..."` 圖表容器;使用 chart frame class。
- 不要用 `查詢失敗: ProgrammingError...` 顯示給使用者。
## 新頁 checklist
1. Template 設定正確 `active_page`,讓 `observability-system.css` 載入。
2. Hero 有清楚頁面任務,不做簡報封面式大字。
3. 第一屏有 3 到 6 個可行動 KPI不堆原始資料。
4. 主資料區先呈現結論,再給明細。
5. 空資料、缺表、外部服務未接入都要是可讀狀態。
6. 10 頁巡檢不得出現 `Traceback``UndefinedError``ProgrammingError``relation "``查詢失敗:`
## 驗收指令
### 1. Repo 靜態 UI guard
先跑本地 guard避免把已知 UI/UX 回歸重新帶進 repo
```bash
scripts/check_observability_suite.sh
```
若只想跑靜態檢查:
```bash
python3 scripts/check_observability_ui.py
```
如果修改了 `static/css/observability-system.css`,先同步 Flask 實際服務用的 mirror
```bash
python3 scripts/sync_observability_css.py
python3 scripts/sync_observability_css.py --check
```
或透過既有 quick review 選單執行第 8 項完整 QA 套件、第 6 項靜態 UI guard、第 9 項 CSS mirror sync
```bash
./scripts/quick_review.sh
```
非互動模式可直接用於部署腳本或 CI
```bash
./scripts/quick_review.sh --sync-observability-css
./scripts/quick_review.sh --check-observability-css
./scripts/quick_review.sh --observability-qa
```
指定不同環境時:
```bash
./scripts/quick_review.sh --observability-smoke --base-url https://mo.wooo.work
./scripts/quick_review.sh --observability-qa --base-url https://mo.wooo.work
./scripts/quick_review.sh --observability-qa --skip-production
```
Guard 會檢查:
- 觀測台頁面契約集中在 `scripts/observability_contract.py`,新增/改名頁面先改這裡。
- Times / Georgia 等非規範標題字型。
- SQL/Jinja exception 文案外露。
- 圖表 `style="height:..."` 與靜態 `style="width:..."`
- 過大標題 clamp 與純白 hover/card surface。
- 觀測台 CSS 必要 token / utility class 是否仍存在。
- 側欄是否維持 `AI 中樞 → AI 觀測台 → 分組頁面` 的收納結構。
- 側欄是否維持暖深咖啡背景與第二/三層足夠對比。
- Topbar 是否仍載入觀測台 CSS 與健康 indicator。
- 10 個觀測頁的 `active_page`、側欄 URL、側欄 label、`momo-observability-mode` 掛載清單是否一一對齊。
- 10 個側欄 URL 是否都在 `routes/admin_observability_routes.py` 有對應 Flask route。
- `static/css/observability-system.css` 與實際 Flask static 服務用的 `web/static/css/observability-system.css` 必須一致。
- CD deploy gate 的正反案例 self-test 是否通過。
### 2. Production 10 頁 HTTP 巡檢
建議使用 repo 內建腳本:
```bash
python3 scripts/check_observability_pages.py
```
或透過 quick review 選單執行第 7 項。
此巡檢不只檢查 HTTP 200也會檢查
- `/health` 必須回 200 且包含 healthy marker。
- 不得出現 framework / database exception 文案。
- 每頁必須包含自己的核心標題與內容 marker。
- 每頁必須載入 `momo-observability-mode``observability-system.css` 與 topbar AI 觀測台 indicator。
- `/static/css/observability-system.css` 必須能線上 200 載入,且包含核心觀測台 token/class。
- HTML 不能小到像空殼頁或錯誤 fallback。
```bash
python3 - <<'PY'
import urllib.request
pages=[('/observability/overview','總覽'),('/observability/agent_orchestration','Agent'),('/observability/business_intel','商業'),('/observability/host_health','主機'),('/observability/ai_calls','AI 呼叫'),('/observability/budget','預算'),('/observability/promotion_review','晉升'),('/observability/rag_queries','RAG'),('/observability/quality_trend','品質'),('/observability/ppt_audit_history','PPT')]
needles=['Traceback','UndefinedError','ProgrammingError','Internal Server Error','relation "','查詢失敗:']
for path,label in pages:
with urllib.request.urlopen('https://mo.wooo.work'+path,timeout=12) as r:
html=r.read().decode('utf-8','ignore')
found=[n for n in needles if n in html]
print(f'{label}: HTTP {r.status}, issues={found or "none"}')
PY
```