style(obs): compact observability workbench copy
Some checks failed
CD Pipeline / deploy (push) Has been cancelled

This commit is contained in:
ogt
2026-07-02 19:15:56 +08:00
parent 510223a00f
commit ea74a0c5d4
4 changed files with 109 additions and 16 deletions

View File

@@ -99,6 +99,7 @@
- 2026-07-02 起 `/ai_intelligence``/observability/overview` 必須同樣套用 AI automation benchmark golden signals首屏需有 `AI 自動化狀態` 區塊,直接顯示「已自動落地、已驗證、異動狀態、下一步」。`/ai_intelligence` 需以 `/api/ai/pchome-growth/ai-automation-surface-summary` 只讀更新首屏,該 endpoint 由 `/api/ai/pchome-growth/ai-automation-readiness` 聚合 safe lanes 後輸出 `golden_signals``safe_automation_lanes``surface_contract``primary_human_gate_count=0``writes_database_count=0``next_machine_action``/observability/overview` 需用正式觀測資料分層呈現風險與下一步。raw endpoint、artifact、DB table、manual/human gate 文字不得進首屏訊號。
- 2026-07-02 起 `/ai_intelligence` 商品明細與單品作戰詳情的四格價格證據必須可測PChome 價格、MOMO 參考價、差距、可信度需以 `data-evidence` 固定,並以 `aria-label="價格證據"` 對應可掃描區塊;候選待確認或缺資料只能顯示「候選待確認 / 待補」,不得捏造價格或讓使用者打開 raw payload 才知道判斷依據。
- 2026-07-02 起 `/ai_intelligence` 必須是密集 AI 工作台不得退回大段文字說明頁首屏與明細可見內容只保留短狀態、數字、四格證據與下一步按鈕KPI note、benchmark detail、alert 副句、策略說明、decision copy、來源長句與單品 reason list 不得佔用第一層視覺。`tests/test_ai_intelligence_text_density_guardrails.py` 必須鎖住 `data-density-guardrail="compact-ai-workbench"`、短任務文案、detail meta 與 hidden explanatory copy。
- 2026-07-02 起 `/observability/overview` 也必須採密集 AI 觀測工作台:首屏以 `data-density-guardrail="compact-observability-workbench"``AI 觀測 / 風險優先 / 下一步` 與 golden signals 先呈現狀態、數字與操作入口hero lede、signal note、route desc、host meta 與資料來源長句不得佔用第一層視覺。`tests/test_observability_text_density_guardrails.py` 必須鎖住 compact marker 與 hidden explanatory copy。
- V10.644 起 `/ai_intelligence` 的商品明細列不得只用句子描述比價;每列必須顯示 PChome 價格、MOMO 參考價、差距、可信度四格價格證據,並保留下一步按鈕。單位價候選需顯示單位價與單位,候選待確認或缺資料則以「待補 / 候選待確認」呈現,不得捏造價格。
- V10.645 起 `/ai_intelligence` 的商品明細分流切換後,必須顯示「這類商品怎麼處理」的行動摘要,包含件數、近 7 天業績、平均可信度、最大價差、代表商品與主按鈕;使用者不得只能看到商品列表而不知道下一步。
- V10.646 起 `/ai_intelligence` 的商品明細必須提供搜尋與排序;搜尋至少涵蓋商品、分類、商品編號與 MOMO 候選資訊,排序至少支援優先級、近 7 天業績、價差、下滑幅度與可信度。搜尋/排序後的行動摘要與明細列表必須使用同一批結果。

View File

@@ -101,7 +101,7 @@
## P1 - Product Visibility And Professional Website Experience
狀態: 進行中,本輪已完成 `/ai_intelligence` 密集 AI 工作台文字密度守門。
狀態: 進行中,本輪已完成 `/ai_intelligence``/observability/overview` 密集 AI 工作台文字密度守門。
目的: 讓 AI 自動化在產品裡可見,成為專業營運工作流,而不是只藏在後端。
@@ -124,6 +124,10 @@
- 首屏改成 `data-density-guardrail="compact-ai-workbench"`以「AI 作戰 / 證據優先 / 下一步」短標籤取代長說明。
- KPI note、benchmark detail、alert 副句、mini-card 說明、策略 note、decision copy、來源長句與單品 reason list 從可見 UI 退場。
- `tests/test_ai_intelligence_text_density_guardrails.py` 已鎖住短任務文案、detail meta 與 explanatory copy hidden contract。
- `/observability/overview` 密集 AI 觀測工作台文字密度 guard 已完成:
- 首屏改成 `data-density-guardrail="compact-observability-workbench"`以「AI 觀測 / 風險優先 / 下一步」短標籤承接 golden signals。
- hero lede、signal note、route desc、host meta 與資料來源長句從可見 UI 退場,保留狀態、數字與子頁入口。
- `tests/test_observability_text_density_guardrails.py` 已鎖住 compact marker、第一層狀態數字與 explanatory copy hidden contract。
已完成 / 下一步,必須照順序:
@@ -244,6 +248,7 @@
| P1.2 | UI wording guard for no raw engineering terms | 已完成 | focused wording guard test | P2.1 benchmark guardrails |
| P1.3 | AI intelligence product evidence guardrails | 已完成 | detail rows + single product detail expose four evidence cells + focused guard tests | P2 / P3 automation surfaces |
| P1.4 | AI intelligence compact workbench text-density guardrails | 已完成 | `/ai_intelligence` first viewport and drilldown hide explanatory copy; focused density guard test | P2.2 benchmark guardrails and future AI surfaces |
| P1.5 | Observability compact workbench text-density guardrails | 已完成 | `/observability/overview` first viewport hides explanatory copy; focused density guard test | Apply density guard to remaining high-visibility AI surfaces |
| P2.1 | External benchmark encoded into requirements | 已完成 | benchmark guide + focused guard test + first-viewport status | P3.1 safe lane expansion |
| P3.1 | Extend receipt / replay / drift pattern to more lanes | 已完成 | direct mapping candidate decision lane closeout route + focused tests | P3.2 scheduled automation health summaries |
| P3.2 | Scheduled automation health summaries | 已完成 | `/api/ai-automation/scheduled-health-summary` + smoke service focused tests | P3.3 rollback evidence packages |

View File

@@ -75,11 +75,27 @@
}
.obs-lede {
max-width: 760px;
margin: 0;
display: none;
}
.obs-mode {
display: flex;
flex-wrap: wrap;
gap: 0.45rem;
margin-top: 0.7rem;
}
.obs-mode span {
display: inline-flex;
align-items: center;
min-height: 24px;
border: 1px solid rgba(86, 64, 48, 0.12);
border-radius: 999px;
background: var(--momo-bg-elevated, #fdfaf3);
color: var(--obs-muted);
font-size: var(--momo-text-body, 0.875rem);
line-height: 1.75;
font-size: 0.68rem;
font-weight: 900;
padding: 0.24rem 0.55rem;
}
.obs-command-strip,
@@ -93,7 +109,7 @@
.obs-signal {
position: relative;
z-index: 1;
min-height: 104px;
min-height: 78px;
padding: 0.92rem;
border: 1px solid var(--obs-line);
border-radius: var(--momo-radius-lg, 8px);
@@ -120,9 +136,7 @@
}
.obs-signal-note {
margin-top: 0.25rem;
color: var(--obs-muted);
font-size: 0.82rem;
display: none;
}
.obs-grid {
@@ -202,6 +216,11 @@
font-size: 0.82rem;
}
.obs-host-meta,
.obs-footer-source {
display: none;
}
.obs-sparkline {
height: 54px;
}
@@ -368,11 +387,7 @@
}
.obs-route-desc {
display: block;
margin-top: 0.08rem;
color: var(--obs-muted);
font-size: 0.76rem;
line-height: 1.45;
display: none;
}
.obs-empty {
@@ -421,9 +436,14 @@
{% set risk_count = (summary.budget_alerts|length if summary.budget_alerts else 0) + host_bad.value + (1 if ai and ai.error_rate >= 15 else 0) + (1 if summary.episodes and summary.episodes.pending > 0 else 0) %}
<div class="obs-war-room">
<section class="obs-hero">
<section class="obs-hero" data-density-guardrail="compact-observability-workbench">
<span class="obs-kicker"><i class="fas fa-satellite-dish"></i> 01 指揮總覽 <span class="obs-kicker-date">· {{ today }}</span></span>
<h1 class="obs-title">AI 觀測戰情室</h1>
<div class="obs-mode" aria-label="AI 觀測模式">
<span>AI 觀測</span>
<span>風險優先</span>
<span>下一步</span>
</div>
<p class="obs-lede">
私有 AI 中樞的第一入口模型主機、AI 呼叫、知識學習、工具編排、自癒、預算與 PPT 視覺審核收斂到同一張工作台。所有數字只讀正式資料來源;缺資料時呈現可診斷空狀態。
</p>
@@ -645,7 +665,7 @@
</div>
</section>
<p class="obs-microcopy mt-3">
<p class="obs-microcopy obs-footer-source mt-3">
<i class="fas fa-database me-1"></i>
資料來源主機探測、AI 呼叫、預算、學習事件、知識查詢、工具呼叫、事件與自癒、PPT 審核。
</p>

View File

@@ -0,0 +1,67 @@
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def _template() -> str:
return (ROOT / "templates/admin/observability_overview.html").read_text(encoding="utf-8")
def _any_css_block_has(template: str, selector: str, expected: str) -> bool:
marker = f"{selector} {{"
assert marker in template
return any(expected in block.split("}", 1)[0] for block in template.split(marker)[1:])
def test_observability_overview_first_viewport_is_compact_workbench():
template = _template()
hero = template.split('<section class="obs-hero"', 1)[1].split(
'<section class="obs-grid">',
1,
)[0]
assert 'data-density-guardrail="compact-observability-workbench"' in hero
assert 'aria-label="AI 觀測模式"' in hero
assert "AI 觀測" in hero
assert "風險優先" in hero
assert "下一步" in hero
assert 'aria-label="AI 自動化狀態"' in hero
assert 'data-benchmark-guardrail="golden-signals"' in hero
def test_observability_overview_hides_explanatory_copy_layers():
template = _template()
hidden_selectors = [
".obs-lede",
".obs-signal-note",
".obs-host-meta,\n .obs-footer-source",
".obs-route-desc",
]
for selector in hidden_selectors:
assert _any_css_block_has(template, selector, "display: none")
def test_observability_overview_keeps_first_layer_to_status_numbers_and_actions():
template = _template()
hero = template.split('<section class="obs-hero"', 1)[1].split(
'<section class="obs-grid">',
1,
)[0]
for marker in ["即時風險", "24 小時呼叫", "成本水位", "知識命中率"]:
assert marker in hero
for long_copy in [
"私有 AI 中樞的第一入口",
"主機探測、AI 呼叫、自癒、預算與品質訊號已進總覽",
"以正式資料回讀主機健康、錯誤率、成本與知識命中",
"即時風險分層;詳細事件保留在各子頁",
"持續觀察健康、成本與自癒訊號",
]:
assert long_copy in template
assert long_copy in hero
assert "display: none" in template.split(".obs-signal-note {", 1)[1].split("}", 1)[0]
assert "display: none" in template.split(".obs-lede {", 1)[1].split("}", 1)[0]