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
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:
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -35,6 +35,27 @@
|
||||
|
||||
**邊界**:本段只補 UI 查詢來源;不寫 DB、不改 run 狀態、不執行修復、不發 Telegram、不開 runtime gate。
|
||||
|
||||
## 2026-06-25|Runs 新增事故焦點狀態鏈面板
|
||||
|
||||
**背景**:`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-25|Status-chain 新增乾跑後套用閘門 handoff
|
||||
|
||||
**背景**:`INC-20260625-977E5F` 類告警目前已能辨識 Ansible `check_mode` 乾跑成功、`apply_total=0`、`verifier=missing`,但 operator 在 Runs / Telegram 看見的下一步仍容易停在「需人工」或長文字。這會讓 AI 自動化看起來像只是把問題丟回人工,而不是清楚交付下一個可審核 gate。
|
||||
|
||||
@@ -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` i18n,zh-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 抽查:
|
||||
|
||||
Reference in New Issue
Block a user