feat(web): enrich Runs apply gate handoff
Some checks failed
Code Review / ai-code-review (push) Successful in 15s
CD Pipeline / tests (push) Successful in 1m37s
CD Pipeline / build-and-deploy (push) Successful in 4m35s
CD Pipeline / post-deploy-checks (push) Successful in 1m43s
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled

This commit is contained in:
Your Name
2026-06-25 22:27:10 +08:00
parent a6844ac1a0
commit 6ed461cf11
5 changed files with 143 additions and 7 deletions

View File

@@ -10081,6 +10081,18 @@
"workItem": "Work Item",
"applyCandidate": "Apply 候選",
"verifier": "Verifier 資產",
"ownerGate": "Owner 審查 Gate",
"nextAction": "下一步",
"catalog": "候選 PlayBook",
"risk": "風險",
"matchScore": "匹配分數",
"checkPlaybook": "乾跑 PlayBook",
"applyPlaybook": "套用 PlayBook",
"dryRunAsset": "乾跑資產",
"applyAsset": "套用候選資產",
"verifierAsset": "Verifier 資產",
"checklistTitle": "Owner 審查清單",
"forbiddenTitle": "禁止動作",
"gates": {
"dryRun": "乾跑",
"applyGate": "套用審查",

View File

@@ -10081,6 +10081,18 @@
"workItem": "Work Item",
"applyCandidate": "Apply 候選",
"verifier": "Verifier 資產",
"ownerGate": "Owner 審查 Gate",
"nextAction": "下一步",
"catalog": "候選 PlayBook",
"risk": "風險",
"matchScore": "匹配分數",
"checkPlaybook": "乾跑 PlayBook",
"applyPlaybook": "套用 PlayBook",
"dryRunAsset": "乾跑資產",
"applyAsset": "套用候選資產",
"verifierAsset": "Verifier 資產",
"checklistTitle": "Owner 審查清單",
"forbiddenTitle": "禁止動作",
"gates": {
"dryRun": "乾跑",
"applyGate": "套用審查",

View File

@@ -419,6 +419,10 @@ export function AwoooPStatusChainPanel({
const mcpGatewayTotal = mcpGateway.total ?? 0;
const mcpGatewayProblemTotal = (mcpGateway.failed ?? 0) + (mcpGateway.blocked ?? 0);
const executionTotal = execution.operation_total ?? 0;
const handoffCandidate = automationHandoff?.candidate;
const handoffAssets = automationHandoff?.asset_ids;
const ownerReviewChecklist = automationHandoff?.owner_review_checklist ?? [];
const forbiddenActions = automationHandoff?.forbidden_actions ?? [];
const sourceToolchainTone: SourceFlowTone = sourceCorrelation
? (sourceLinkVerified ? "success" : (sourceVerificationBlocked ? "blocked" : "warning"))
: "neutral";
@@ -516,6 +520,18 @@ export function AwoooPStatusChainPanel({
},
];
const handoffGates = automationHandoff?.gates ?? [];
const handoffCandidateCards = [
{ key: "catalog", label: t("applyGate.catalog"), value: handoffCandidate?.catalog_id },
{ key: "risk", label: t("applyGate.risk"), value: handoffCandidate?.risk_level },
{ key: "matchScore", label: t("applyGate.matchScore"), value: handoffCandidate?.match_score },
{ key: "checkPlaybook", label: t("applyGate.checkPlaybook"), value: handoffCandidate?.check_mode_playbook_path },
{ key: "applyPlaybook", label: t("applyGate.applyPlaybook"), value: handoffCandidate?.apply_playbook_path },
];
const handoffAssetCards = [
{ key: "dryRunAsset", label: t("applyGate.dryRunAsset"), value: handoffAssets?.dry_run },
{ key: "applyAsset", label: t("applyGate.applyAsset"), value: handoffAssets?.apply_candidate },
{ key: "verifierAsset", label: t("applyGate.verifierAsset"), value: handoffAssets?.verifier },
];
const handoffGateLabel = (key: string | null | undefined) => {
const labels: Record<string, string> = {
dry_run: t("applyGate.gates.dryRun"),
@@ -775,23 +791,77 @@ export function AwoooPStatusChainPanel({
<div className="grid gap-px bg-[#e0ddd4] md:grid-cols-3">
<div className="min-w-0 bg-white px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.workItem")}</p>
<p className="mt-2 truncate font-mono text-sm text-[#141413]" title={valueOrEmpty(automationHandoff.work_item_id, emptyLabel)}>
<p className="mt-2 break-all font-mono text-sm text-[#141413]" title={valueOrEmpty(automationHandoff.work_item_id, emptyLabel)}>
{valueOrEmpty(automationHandoff.work_item_id, emptyLabel)}
</p>
</div>
<div className="min-w-0 bg-white px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.applyCandidate")}</p>
<p className="mt-2 truncate font-mono text-sm text-[#141413]" title={valueOrEmpty(automationHandoff.candidate?.apply_playbook_path, emptyLabel)}>
{valueOrEmpty(automationHandoff.candidate?.apply_playbook_path, emptyLabel)}
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.ownerGate")}</p>
<p className="mt-2 break-words font-mono text-sm text-[#141413]" title={valueOrEmpty(automationHandoff.owner_review_gate, emptyLabel)}>
{valueOrEmpty(automationHandoff.owner_review_gate, emptyLabel)}
</p>
</div>
<div className="min-w-0 bg-white px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.verifier")}</p>
<p className="mt-2 truncate font-mono text-sm text-[#141413]" title={valueOrEmpty(automationHandoff.asset_ids?.verifier, emptyLabel)}>
{valueOrEmpty(automationHandoff.asset_ids?.verifier, emptyLabel)}
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.nextAction")}</p>
<p className="mt-2 break-words font-mono text-sm text-[#141413]" title={valueOrEmpty(automationHandoff.next_action, emptyLabel)}>
{valueOrEmpty(automationHandoff.next_action, emptyLabel)}
</p>
</div>
</div>
<div className="grid gap-px bg-[#e0ddd4] md:grid-cols-5">
{handoffCandidateCards.map((item) => (
<div key={item.key} className="min-w-0 bg-white px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{item.label}</p>
<p className="mt-2 break-all font-mono text-sm text-[#141413]" title={valueOrEmpty(item.value, emptyLabel)}>
{valueOrEmpty(item.value, emptyLabel)}
</p>
</div>
))}
</div>
<div className="grid gap-px bg-[#e0ddd4] md:grid-cols-3">
{handoffAssetCards.map((item) => (
<div key={item.key} className="min-w-0 bg-white px-4 py-3">
<p className="text-xs font-semibold text-[#77736a]">{item.label}</p>
<p className="mt-2 break-all font-mono text-sm text-[#141413]" title={valueOrEmpty(item.value, emptyLabel)}>
{valueOrEmpty(item.value, emptyLabel)}
</p>
</div>
))}
</div>
<div className="grid gap-px bg-[#e0ddd4] lg:grid-cols-2">
<div className="min-w-0 bg-white px-4 py-3">
<div className="flex items-center justify-between gap-3">
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.checklistTitle")}</p>
<span className="border border-[#d8d3c7] bg-[#faf9f3] px-2 py-0.5 font-mono text-[11px] text-[#5f5b52]">
{ownerReviewChecklist.length}
</span>
</div>
<ul className="mt-3 space-y-2">
{(ownerReviewChecklist.length ? ownerReviewChecklist : [emptyLabel]).map((item, index) => (
<li key={`${item}-${index}`} className="flex min-w-0 items-start gap-2 text-xs leading-5 text-[#141413]">
<CheckCircle2 className="mt-0.5 h-3.5 w-3.5 shrink-0 text-[#17602a]" aria-hidden="true" />
<span className="min-w-0 break-words font-mono">{item}</span>
</li>
))}
</ul>
</div>
<div className="min-w-0 bg-white px-4 py-3">
<div className="flex items-center justify-between gap-3">
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.forbiddenTitle")}</p>
<span className="border border-[#e2a29b] bg-[#fff0ef] px-2 py-0.5 font-mono text-[11px] text-[#9f2f25]">
{forbiddenActions.length}
</span>
</div>
<ul className="mt-3 space-y-2">
{(forbiddenActions.length ? forbiddenActions : [emptyLabel]).map((item, index) => (
<li key={`${item}-${index}`} className="flex min-w-0 items-start gap-2 text-xs leading-5 text-[#141413]">
<TriangleAlert className="mt-0.5 h-3.5 w-3.5 shrink-0 text-[#9f2f25]" aria-hidden="true" />
<span className="min-w-0 break-words font-mono">{item}</span>
</li>
))}
</ul>
</div>
</div>
</div>
)}

View File

@@ -1,3 +1,28 @@
## 2026-06-25Runs apply gate handoff 補成可審查處置包
**背景**`INC-20260625-977E5F` 已能在 Runs 事故焦點狀態鏈顯示 `ansible_check_mode_only``dry_run=passed``apply_gate=blocked``verifier=blocked`,但 UI 仍只顯示少數欄位。使用者仍無法快速判斷 owner 要審什麼、候選 PlayBook 是哪一個、乾跑 / 套用 / verifier 資產 ID 是什麼,以及哪些動作明確禁止。
**完成**
- `AwoooPStatusChainPanel``automation_handoff` 延伸為可審查處置包。
- 新增候選 PlayBook 卡catalog、risk、match score、check-mode playbook、apply playbook。
- 新增資產卡dry-run asset、apply candidate asset、verifier asset。
- 新增 owner review checklist 與 forbidden actions 區塊;長 ID / playbook path 使用換行處理,避免 mobile 水平溢出。
- `zh-TW` / `en` messages 同步新增 apply gate handoff 文案。
**本地驗證**
- i18n leaf diff`zh=13689``en=13689``missingEn=0``missingZh=0``typeDiff=0`
- `pnpm --filter @awoooi/web typecheck` 通過。
- `python3 scripts/security/source-control-owner-response-guard.py --root .``SOURCE_CONTROL_OWNER_RESPONSE_GUARD_OK`
- `python3 scripts/security/security-mirror-progress-guard.py --root .``SECURITY_MIRROR_PROGRESS_GUARD_OK`
- `git diff --check` 通過。
**完成度同步**
- Runs apply gate handoff 可審查性:`70% -> 78%`
- AwoooP Runs 可判讀性:`71% -> 74%`
- 真正 AI 自動化 verified repair 成功率仍不提高;此段只是把現有 dry-run / owner gate / verifier 缺口變成可審查、可沉澱的操作面板。
**邊界**:本段不執行 Ansible apply、不重啟 `node-exporter-188`、不 SSH、不發 Telegram、不新增操作按鈕、不寫 runtime state、不開 runtime gate。正式站驗證待下一個 deploy marker 後重跑 desktop / mobile smoke。
## 2026-06-25Runs incident filter 預篩選修正避免 502
**背景**`4e329bce` 部署後,正式 API `/api/v1/platform/status-chain?incident_id=INC-20260625-977E5F` 已回 `automation_handoff.kind=ansible_check_mode_apply_gate`,但 `/zh-TW/awooop/runs?incident_id=INC-20260625-977E5F` 看不到 `乾跑後套用閘門`。追查發現 `/api/v1/platform/runs/list` 不帶 incident filter 回 `200`,一帶 `INC-20260625-977E5F``502 Bad Gateway`;原因是舊 filter 先載入 project 下大量 runs再逐筆聚合 message contextproduction 歷史量一大就 timeout。

View File

@@ -310,6 +310,23 @@ Tenants 目前已讀到:
完成度同步Runs incident drilldown 可判讀性 `90% -> 100%`AwoooP Runs apply-gate 正式頁驗證 `100%`;真正修復自動執行成功率仍不提高。
### 2.5.13 Runs apply gate handoff 審查包
`INC-20260625-977E5F` 已有 `automation_handoff`,但畫面只呈現三段 Gate 與部分 IDoperator 仍不容易知道「要審什麼、為什麼不能按、下一步資產在哪」。本段把 API 已有欄位完整產品化成審查包。
| 區塊 | 調整 |
|---|---|
| Owner Gate | 顯示 `work_item_id``owner_review_gate``next_action` |
| 候選 PlayBook | 顯示 catalog、risk、match score、check-mode playbook、apply playbook |
| 資產 ID | 顯示 dry-run asset、apply candidate asset、verifier asset |
| 審查清單 | 顯示 `owner_review_checklist`,讓人工接手不再只看到「需人工」 |
| 禁止動作 | 顯示 `forbidden_actions`,避免誤把 dry-run 或 approval UI 當執行授權 |
| Mobile | 長 ID / playbook path 改用換行處理,避免水平溢出 |
本地驗證i18n leaf diff `0``pnpm --filter @awoooi/web typecheck``source-control-owner-response-guard.py``security-mirror-progress-guard.py``git diff --check` 皆通過。
完成度同步Runs apply gate handoff 可審查性 `70% -> 78%`AwoooP Runs 可判讀性 `71% -> 74%`。真正自動修復成功率不提高;正式站驗證需 deploy marker 後重跑。
## 3. 頁面 UI/UX 現況盤點
2026-06-25 對正式站桌機 / mobile 抽查: