feat(web): add Runs incident focus chain
Some checks failed
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / tests (push) Successful in 1m36s
CD Pipeline / build-and-deploy (push) Successful in 3m49s
CD Pipeline / post-deploy-checks (push) Successful in 1m29s
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled

This commit is contained in:
Your Name
2026-06-25 22:09:10 +08:00
parent 5a76316a65
commit 4076c3c0e4
5 changed files with 117 additions and 1 deletions

View File

@@ -9554,6 +9554,17 @@
}
}
},
"runsIncidentFocus": {
"title": "事故焦點狀態鏈",
"description": "URL 上的 incident_id 會直接讀回 status-chain、套用閘門與自動化資產總帳避免只看到 Run 摘要卻看不到下一步。",
"states": {
"ready": "已讀回狀態鏈",
"loading": "讀取中",
"missing": "尚未讀回"
},
"loading": "正在讀回事故狀態鏈與自動化資產。",
"missing": "尚未讀回 {incidentId} 的狀態鏈;請先確認來源事件是否已入庫,或查看 API readback。"
},
"listEvidence": {
"column": "AI 證據",
"callbackColumn": "TG Callback",

View File

@@ -9554,6 +9554,17 @@
}
}
},
"runsIncidentFocus": {
"title": "事故焦點狀態鏈",
"description": "URL 上的 incident_id 會直接讀回 status-chain、套用閘門與自動化資產總帳避免只看到 Run 摘要卻看不到下一步。",
"states": {
"ready": "已讀回狀態鏈",
"loading": "讀取中",
"missing": "尚未讀回"
},
"loading": "正在讀回事故狀態鏈與自動化資產。",
"missing": "尚未讀回 {incidentId} 的狀態鏈;請先確認來源事件是否已入庫,或查看 API readback。"
},
"listEvidence": {
"column": "AI 證據",
"callbackColumn": "TG Callback",

View File

@@ -4221,6 +4221,7 @@ export default function RunsPage() {
const tEvidence = useTranslations("awooop.listEvidence");
const tAssetLedger = useTranslations("awooop.automationAssetLedger");
const tCallback = useTranslations("awooop.callbackReply");
const tIncidentFocus = useTranslations("awooop.runsIncidentFocus");
const [runs, setRuns] = useState<Run[]>([]);
const [groupedEvents, setGroupedEvents] = useState<PlatformEvent[]>([]);
const [dossierCoverage, setDossierCoverage] = useState<DossierCoverageResponse | null>(null);
@@ -4555,6 +4556,13 @@ export default function RunsPage() {
refreshKey: lastRefresh?.toISOString() ?? null,
limit: PER_PAGE,
});
const normalizedIncidentFocus = useMemo(() => {
const normalized = incidentFilter.trim().toUpperCase();
return INCIDENT_ID_FILTER_RE.test(normalized) ? normalized : null;
}, [incidentFilter]);
const incidentFocusChain = normalizedIncidentFocus
? runStatusChains[normalizedIncidentFocus]
: null;
return (
<div className="space-y-6">
@@ -4686,6 +4694,53 @@ export default function RunsPage() {
})}
</section>
{normalizedIncidentFocus && (
<section className="border border-[#d8d3c7] bg-white">
<div className="flex flex-wrap items-start justify-between gap-3 border-b border-[#e0ddd4] bg-[#faf9f3] px-4 py-3">
<div className="min-w-0">
<div className="flex flex-wrap items-center gap-2">
<ShieldCheck className="h-4 w-4 text-[#1f5b9b]" aria-hidden="true" />
<p className="text-sm font-semibold text-[#141413]">
{tIncidentFocus("title")}
</p>
<span className="border border-[#c9d7ea] bg-[#edf4ff] px-2 py-0.5 font-mono text-xs font-semibold text-[#1f5b9b]">
{normalizedIncidentFocus}
</span>
</div>
<p className="mt-1 text-xs leading-5 text-[#5f5b52]">
{tIncidentFocus("description")}
</p>
</div>
<span className="border border-[#d8d3c7] bg-white px-2 py-1 text-xs font-semibold text-[#5f5b52]">
{incidentFocusChain
? tIncidentFocus("states.ready")
: runStatusChainsLoading
? tIncidentFocus("states.loading")
: tIncidentFocus("states.missing")}
</span>
</div>
{incidentFocusChain ? (
<div className="grid gap-px bg-[#e0ddd4] lg:grid-cols-[minmax(0,1.45fr)_minmax(300px,0.55fr)]">
<div className="bg-white p-4">
<AwoooPStatusChainPanel chain={incidentFocusChain} />
</div>
<div className="bg-white p-4">
<AwoooPAutomationAssetLedger
chain={incidentFocusChain}
remediationSummary={null}
/>
</div>
</div>
) : (
<div className="px-4 py-5 text-sm text-[#5f5b52]">
{runStatusChainsLoading
? tIncidentFocus("loading")
: tIncidentFocus("missing", { incidentId: normalizedIncidentFocus })}
</div>
)}
</section>
)}
<AutomationFlowGatePanel
summary={automationQualitySummary}
error={automationQualityError}
@@ -4905,7 +4960,10 @@ export default function RunsPage() {
) : (
runs.map((run) => {
const incidentIds = linkedIncidentIds(run.remediation_summary);
const statusChain = incidentIds
const rowIncidentIds = normalizedIncidentFocus
? Array.from(new Set([normalizedIncidentFocus, ...incidentIds]))
: incidentIds;
const statusChain = rowIncidentIds
.map((incidentId) => runStatusChains[incidentId])
.find((chain): chain is AwoooPStatusChain => Boolean(chain));
return (

View File

@@ -35,6 +35,27 @@
**邊界**:本段只補 UI 查詢來源;不寫 DB、不改 run 狀態、不執行修復、不發 Telegram、不開 runtime gate。
## 2026-06-25Runs 新增事故焦點狀態鏈面板
**背景**`5a76316a` 部署後,正式 Runs 頁已無 502、無 JS error、無水平溢出且 DOM 可見 `INC-20260625-977E5F`;但頁面仍未顯示 `乾跑後套用閘門`,因為 status-chain 只被表格列用來補欄位,沒有固定的 incident drilldown 焦點面板。
**完成**
- Runs 頁在 URL `incident_id` 合法時,於 KPI 區下方固定顯示「事故焦點狀態鏈」。
- 焦點面板直接渲染 `AwoooPStatusChainPanel``AwoooPAutomationAssetLedger`,把乾跑、套用審查、驗證回寫、資產總帳放在同一個操作區。
- 表格列在 incident filter 模式下也會使用 URL 焦點 status-chain避免資產總帳欄因 run summary 缺 incident ids 而空白。
- 新增 `awooop.runsIncidentFocus` i18n 文案,維持 zh-TW / en key 鏡像。
**驗證待辦**
- i18n JSON parse / mirror。
- `pnpm --filter @awoooi/web typecheck`
- 下一個 deploy marker 後重跑 `/zh-TW/awooop/runs?project_id=awoooi&incident_id=INC-20260625-977E5F` desktop / mobile smoke。
**完成度同步**
- Runs incident drilldown 可判讀性:`82% -> 90%`
- 真正修復自動執行成功率仍不提高;這段只是把目前卡在 apply gate 的事實清楚呈現。
**邊界**:本段只改前端顯示與 i18n不寫 DB、不改 run 狀態、不執行修復、不發 Telegram、不開 runtime gate。
## 2026-06-25Status-chain 新增乾跑後套用閘門 handoff
**背景**`INC-20260625-977E5F` 類告警目前已能辨識 Ansible `check_mode` 乾跑成功、`apply_total=0``verifier=missing`,但 operator 在 Runs / Telegram 看見的下一步仍容易停在「需人工」或長文字。這會讓 AI 自動化看起來像只是把問題丟回人工,而不是清楚交付下一個可審核 gate。

View File

@@ -279,6 +279,21 @@ Tenants 目前已讀到:
完成度同步Runs incident drilldown 穩定性 `72% -> 82%`AwoooP Runs apply-gate 正式頁驗證需前端修正 deploy 後重跑。
### 2.5.11 Runs 事故焦點狀態鏈面板
2026-06-25 `5a76316a` 部署後,正式 Runs 頁面可載入且沒有 502 / JS error / 水平溢出,但仍未顯示 `乾跑後套用閘門`。原因是 status-chain 只被表格列用來補欄位,沒有固定的 incident drilldown 焦點區;而這次 `INC-20260625-977E5F` 的 run summary 又缺 incident ids讓使用者只看到 Run 摘要和大量文字。
| 項目 | 調整 |
|---|---|
| 焦點面板 | URL `incident_id` 合法時,在 KPI 區下方固定顯示「事故焦點狀態鏈」 |
| 狀態鏈 | 直接渲染 `AwoooPStatusChainPanel`,顯示 verdict、repair_state、apply gate、verifier 等 |
| 資產總帳 | 直接渲染 `AwoooPAutomationAssetLedger`,讓 KM / PlayBook / 乾跑 / 套用 / Verifier 狀態同屏可見 |
| 表格列 | incident filter 模式下,列資料也可使用 URL 焦點 status-chain 補齊來源流程與資產總帳 |
| 文案 | 新增 `awooop.runsIncidentFocus` i18nzh-TW / en key 鏡像 |
| 邊界 | 不新增任何執行按鈕;不寫 DB、不發 Telegram、不開 runtime gate |
完成度同步Runs incident drilldown 可判讀性 `82% -> 90%`;真正修復自動執行成功率仍不提高,下一步要補 owner review apply gate / verifier plan 的實際狀態閉環。
## 3. 頁面 UI/UX 現況盤點
2026-06-25 對正式站桌機 / mobile 抽查: