diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index d4da493f..b5399880 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -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": "套用審查", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index d4da493f..b5399880 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -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": "套用審查", diff --git a/apps/web/src/components/awooop/status-chain.tsx b/apps/web/src/components/awooop/status-chain.tsx index ecacd0b6..34f2266d 100644 --- a/apps/web/src/components/awooop/status-chain.tsx +++ b/apps/web/src/components/awooop/status-chain.tsx @@ -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 = { dry_run: t("applyGate.gates.dryRun"), @@ -775,23 +791,77 @@ export function AwoooPStatusChainPanel({

{t("applyGate.workItem")}

-

+

{valueOrEmpty(automationHandoff.work_item_id, emptyLabel)}

-

{t("applyGate.applyCandidate")}

-

- {valueOrEmpty(automationHandoff.candidate?.apply_playbook_path, emptyLabel)} +

{t("applyGate.ownerGate")}

+

+ {valueOrEmpty(automationHandoff.owner_review_gate, emptyLabel)}

-

{t("applyGate.verifier")}

-

- {valueOrEmpty(automationHandoff.asset_ids?.verifier, emptyLabel)} +

{t("applyGate.nextAction")}

+

+ {valueOrEmpty(automationHandoff.next_action, emptyLabel)}

+
+ {handoffCandidateCards.map((item) => ( +
+

{item.label}

+

+ {valueOrEmpty(item.value, emptyLabel)} +

+
+ ))} +
+
+ {handoffAssetCards.map((item) => ( +
+

{item.label}

+

+ {valueOrEmpty(item.value, emptyLabel)} +

+
+ ))} +
+
+
+
+

{t("applyGate.checklistTitle")}

+ + {ownerReviewChecklist.length} + +
+
    + {(ownerReviewChecklist.length ? ownerReviewChecklist : [emptyLabel]).map((item, index) => ( +
  • +
  • + ))} +
+
+
+
+

{t("applyGate.forbiddenTitle")}

+ + {forbiddenActions.length} + +
+
    + {(forbiddenActions.length ? forbiddenActions : [emptyLabel]).map((item, index) => ( +
  • +
  • + ))} +
+
+
)} diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 8ccf1a4d..2dfe2eed 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,28 @@ +## 2026-06-25|Runs 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-25|Runs 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 context,production 歷史量一大就 timeout。 diff --git a/docs/workplans/2026-06-25-awoooi-product-uiux-inventory.md b/docs/workplans/2026-06-25-awoooi-product-uiux-inventory.md index f09fa246..bb22d83a 100644 --- a/docs/workplans/2026-06-25-awoooi-product-uiux-inventory.md +++ b/docs/workplans/2026-06-25-awoooi-product-uiux-inventory.md @@ -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 與部分 ID,operator 仍不容易知道「要審什麼、為什麼不能按、下一步資產在哪」。本段把 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 抽查: