diff --git a/apps/api/tests/test_ai_agent_automation_backlog_snapshot_api.py b/apps/api/tests/test_ai_agent_automation_backlog_snapshot_api.py index 247dd518..10990379 100644 --- a/apps/api/tests/test_ai_agent_automation_backlog_snapshot_api.py +++ b/apps/api/tests/test_ai_agent_automation_backlog_snapshot_api.py @@ -18,11 +18,11 @@ def test_ai_agent_automation_backlog_snapshot_endpoint_returns_committed_snapsho assert data["schema_version"] == "ai_agent_automation_backlog_v1" assert data["program_status"]["overall_completion_percent"] == 100 assert data["program_status"]["read_only_mode"] is True - assert data["program_status"]["current_task_id"] == "P1-103" - assert data["program_status"]["next_task_id"] == "P1-104" - assert data["rollups"]["total_items"] == len(data["backlog_items"]) == 18 - assert data["rollups"]["by_priority"]["P1"] == 16 - assert data["rollups"]["by_status"]["done"] == 11 + assert data["program_status"]["current_task_id"] == "P1-104" + assert data["program_status"]["next_task_id"] == "P1-105" + assert data["rollups"]["total_items"] == len(data["backlog_items"]) == 19 + assert data["rollups"]["by_priority"]["P1"] == 17 + assert data["rollups"]["by_status"]["done"] == 12 assert data["approval_boundaries"]["sdk_installation_allowed"] is False assert data["approval_boundaries"]["paid_api_call_allowed"] is False assert data["approval_boundaries"]["production_routing_allowed"] is False @@ -30,4 +30,5 @@ def test_ai_agent_automation_backlog_snapshot_endpoint_returns_committed_snapsho assert any(item["item_id"] == "AUTO-P1-205" for item in data["backlog_items"]) assert any(item["item_id"] == "AUTO-P1-206" for item in data["backlog_items"]) assert any(item["item_id"] == "AUTO-P1-103" for item in data["backlog_items"]) + assert any(item["item_id"] == "AUTO-P1-104" for item in data["backlog_items"]) assert any(item["item_id"] == "AUTO-P3-001" for item in data["backlog_items"]) diff --git a/apps/api/tests/test_ai_agent_automation_inventory_snapshot_api.py b/apps/api/tests/test_ai_agent_automation_inventory_snapshot_api.py index 534fdd14..dbcbb078 100644 --- a/apps/api/tests/test_ai_agent_automation_inventory_snapshot_api.py +++ b/apps/api/tests/test_ai_agent_automation_inventory_snapshot_api.py @@ -18,8 +18,8 @@ def test_ai_agent_automation_inventory_snapshot_endpoint_returns_committed_snaps assert data["schema_version"] == "ai_agent_automation_inventory_snapshot_v1" assert data["program_status"]["overall_completion_percent"] == 100 assert data["program_status"]["read_only_mode"] is True - assert data["program_status"]["current_task_id"] == "P1-103" - assert data["program_status"]["next_task_id"] == "P1-104" + assert data["program_status"]["current_task_id"] == "P1-104" + assert data["program_status"]["next_task_id"] == "P1-105" assert data["approval_boundaries"]["sdk_installation_allowed"] is False assert data["approval_boundaries"]["paid_api_call_allowed"] is False assert data["approval_boundaries"]["production_routing_allowed"] is False @@ -28,6 +28,7 @@ def test_ai_agent_automation_inventory_snapshot_endpoint_returns_committed_snaps assert any(task["task_id"] == "P1-205" for task in data["tasks"]) assert any(task["task_id"] == "P1-206" for task in data["tasks"]) assert any(task["task_id"] == "P1-103" for task in data["tasks"]) + assert any(task["task_id"] == "P1-104" for task in data["tasks"]) assert any(evidence["evidence_id"] == "dependency_risk_policy_api" for evidence in data["evidence"]) assert any(evidence["evidence_id"] == "dependency_drift_check_plan_api" for evidence in data["evidence"]) assert any( @@ -35,3 +36,4 @@ def test_ai_agent_automation_inventory_snapshot_endpoint_returns_committed_snaps for evidence in data["evidence"] ) assert any(evidence["evidence_id"] == "backup_notification_policy_api" for evidence in data["evidence"]) + assert any(evidence["evidence_id"] == "backup_dr_evidence_ui" for evidence in data["evidence"]) diff --git a/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx b/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx index cf3f4803..28a34158 100644 --- a/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx +++ b/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx @@ -9,7 +9,20 @@ */ import { useEffect, useMemo, useState, type ReactNode } from 'react' -import { AlertTriangle, Boxes, Database, Lock, PackageCheck, RefreshCw, Server, ShieldCheck } from 'lucide-react' +import { + AlertTriangle, + Archive, + BellOff, + BellRing, + Boxes, + Database, + HardDrive, + Lock, + PackageCheck, + RefreshCw, + Server, + ShieldCheck, +} from 'lucide-react' import { useTranslations } from 'next-intl' import { GlassCard } from '@/components/ui/glass-card' import { StatusOrb } from '@/components/ui/status-orb' @@ -17,6 +30,9 @@ import { apiClient, type AiAgentAutomationBacklogSnapshot, type AiAgentAutomationInventorySnapshot, + type BackupDrReadinessMatrixSnapshot, + type BackupDrTargetInventorySnapshot, + type BackupNotificationPolicySnapshot, } from '@/lib/api-client' function formatDateTime(value: string): string { @@ -75,6 +91,13 @@ function Chip({ value, muted = false }: { value: string; muted?: boolean }) { ) } +function evidenceTone(value: string): 'ok' | 'warn' | 'danger' | 'neutral' { + if (value === 'ready' || value === 'verified' || value === 'active') return 'ok' + if (value === 'action_required' || value === 'approval_required' || value === 'needs_metric_binding') return 'warn' + if (value === 'blocked' || value === 'failed') return 'danger' + return 'neutral' +} + function MetricCard({ label, value, @@ -177,6 +200,9 @@ export function AutomationInventoryTab() { const t = useTranslations('governance.automationInventory') const [snapshot, setSnapshot] = useState(null) const [backlog, setBacklog] = useState(null) + const [backupTargets, setBackupTargets] = useState(null) + const [backupReadiness, setBackupReadiness] = useState(null) + const [backupPolicy, setBackupPolicy] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(false) @@ -185,10 +211,16 @@ export function AutomationInventoryTab() { Promise.all([ apiClient.getAiAgentAutomationInventorySnapshot(), apiClient.getAiAgentAutomationBacklogSnapshot(), + apiClient.getBackupDrTargetInventory(), + apiClient.getBackupDrReadinessMatrix(), + apiClient.getBackupNotificationPolicy(), ]) - .then(([inventoryData, backlogData]) => { + .then(([inventoryData, backlogData, targetData, readinessData, policyData]) => { setSnapshot(inventoryData) setBacklog(backlogData) + setBackupTargets(targetData) + setBackupReadiness(readinessData) + setBackupPolicy(policyData) setError(false) }) .catch(() => setError(true)) @@ -224,6 +256,32 @@ export function AutomationInventoryTab() { .filter(group => group.items.length > 0) }, [backlog]) + const visibleReadinessRows = useMemo(() => { + if (!backupReadiness) return [] + const priority = { blocked: 0, action_required: 1, deferred: 2, ready: 3 } as Record + return [...backupReadiness.readiness_rows] + .sort((a, b) => { + const left = priority[a.overall_readiness] ?? 4 + const right = priority[b.overall_readiness] ?? 4 + if (left !== right) return left - right + return a.target_id.localeCompare(b.target_id) + }) + .slice(0, 8) + }, [backupReadiness]) + + const visibleBackupTargets = useMemo(() => { + if (!backupTargets) return [] + const priority = { critical: 0, high: 1, medium: 2, low: 3 } as Record + return [...backupTargets.backup_targets] + .sort((a, b) => { + const left = priority[a.risk_level] ?? 4 + const right = priority[b.risk_level] ?? 4 + if (left !== right) return left - right + return a.target_id.localeCompare(b.target_id) + }) + .slice(0, 6) + }, [backupTargets]) + if (loading) { return (
@@ -237,7 +295,7 @@ export function AutomationInventoryTab() { ) } - if (error || !snapshot || !backlog) { + if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy) { return (
@@ -275,10 +333,23 @@ export function AutomationInventoryTab() { const criticalAssets = snapshot.assets.filter(asset => asset.risk_level === 'critical').length const completedTasks = snapshot.tasks.filter(task => task.status === 'done').length const p1BacklogCount = backlog.rollups.by_priority.P1 ?? 0 + const readyBackupRows = backupReadiness.rollups.by_overall_readiness.ready ?? 0 + const actionRequiredBackupRows = backupReadiness.rollups.by_overall_readiness.action_required ?? 0 + const blockedBackupRows = backupReadiness.rollups.by_overall_readiness.blocked ?? 0 + const suppressedSuccessRules = backupPolicy.rollups.by_decision.suppress_immediate_success ?? 0 + const immediateEscalationRules = backupPolicy.rollups.by_decision.escalate_immediate ?? 0 const blockedApprovals = Object.entries(snapshot.approval_boundaries) .filter(([, allowed]) => allowed === false) .map(([key]) => key) + const statusLabel = (value: string) => { + try { + return t(`backupEvidence.statuses.${value}` as never) + } catch { + return value + } + } + return (
@@ -450,6 +521,125 @@ export function AutomationInventoryTab() {
+ +
+
+
+ + + {t('backupEvidence.title')} + +
+
+ {t('backupEvidence.source', { + targets: formatDateTime(backupTargets.generated_at), + readiness: formatDateTime(backupReadiness.generated_at), + policy: formatDateTime(backupPolicy.generated_at), + })} +
+
+ +
+ } /> + } /> + } /> + 0 ? 'danger' : 'ok'} icon={} /> + } /> + } /> +
+ +
+
+
+ + {t('backupEvidence.readinessTitle')} + + +
+
+ {visibleReadinessRows.map(row => ( +
+
+
+ + {row.display_name} + + +
+
+ + + + +
+
+ {row.blocker_summary || t('backupEvidence.noBlocker')} +
+
+ + +
+
+
+ ))} +
+
+ +
+
+ + {t('backupEvidence.policyTitle')} + + {backupPolicy.policy_rules.slice(0, 5).map(rule => ( +
+
+ + {rule.rule_id} + + +
+
+ {rule.message_contract} +
+
+ ))} +
+ +
+ + {t('backupEvidence.targetsTitle')} + +
+ {visibleBackupTargets.map(target => ( +
+
+ + {target.display_name} + + +
+
+ + + +
+
+ ))} +
+
+
+
+
+
+
@@ -511,6 +701,9 @@ export function AutomationInventoryTab() { .automation-inventory-workstream-grid, .automation-inventory-domain-grid, .automation-inventory-backlog-grid, + .automation-inventory-backup-kpi-grid, + .automation-inventory-backup-evidence-grid, + .automation-inventory-backup-readiness-grid, .automation-inventory-bottom-grid, .automation-inventory-task-grid { grid-template-columns: 1fr !important; diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index 72516602..a77ee1ce 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -261,6 +261,21 @@ export const apiClient = { const res = await fetch(`${API_BASE_URL}/agents/automation-backlog-snapshot`) return handleResponse(res) }, + + async getBackupDrTargetInventory() { + const res = await fetch(`${API_BASE_URL}/agents/backup-dr-target-inventory`) + return handleResponse(res) + }, + + async getBackupDrReadinessMatrix() { + const res = await fetch(`${API_BASE_URL}/agents/backup-dr-readiness-matrix`) + return handleResponse(res) + }, + + async getBackupNotificationPolicy() { + const res = await fetch(`${API_BASE_URL}/agents/backup-notification-policy`) + return handleResponse(res) + }, } // ========================================================================= @@ -721,3 +736,126 @@ export interface AiAgentAutomationBacklogSnapshot { false > } + +export interface BackupDrTargetInventorySnapshot { + schema_version: 'backup_dr_target_inventory_v1' + generated_at: string + source_refs: string[] + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: string + next_task_id: string + read_only_mode: true + } + rollups: { + total_targets: number + by_status: Record + by_target_type: Record + by_gate_status: Record + blocked_target_ids: string[] + } + backup_targets: Array<{ + target_id: string + display_name: string + target_type: string + status: string + risk_level: 'low' | 'medium' | 'high' | 'critical' + owner_host: string + primary_script: string + schedule: string + rpo: string + storage_class: string + storage_ref: string + offsite_policy: string + automation_gate_status: string + restore_gate_status: string + secret_policy: string + evidence_refs: string[] + next_action: string + }> + approval_boundaries: Record + operation_boundaries: Record +} + +export interface BackupDrReadinessMatrixSnapshot { + schema_version: 'backup_dr_readiness_matrix_v1' + generated_at: string + source_target_inventory_ref: string + source_refs: string[] + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: string + next_task_id: string + read_only_mode: true + } + rollups: { + total_rows: number + by_overall_readiness: Record + by_restore_drill_status: Record + by_offsite_status: Record + blocked_row_ids: string[] + action_required_row_ids: string[] + } + readiness_rows: Array<{ + target_id: string + display_name: string + overall_readiness: string + freshness_status: string + integrity_status: string + restore_drill_status: string + offsite_status: string + notification_policy: string + gate_status: string + evidence_level: string + evidence_refs: string[] + blocker_summary: string + next_action: string + }> + approval_boundaries: Record + operation_boundaries: Record +} + +export interface BackupNotificationPolicySnapshot { + schema_version: 'backup_notification_policy_v1' + generated_at: string + source_readiness_matrix_ref: string + source_refs: string[] + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: string + next_task_id: string + read_only_mode: true + } + rollups: { + total_rules: number + by_decision: Record + immediate_escalation_rule_ids: string[] + suppressed_success_rule_ids: string[] + } + notification_channels: Array<{ + channel_id: string + purpose: string + immediate_allowed: boolean + success_immediate_allowed: boolean + requires_operator_action: boolean + }> + policy_rules: Array<{ + rule_id: string + event_kind: string + backup_state: string + severity: string + decision: string + channels: string[] + owner_agent: string + requires_incident: boolean + requires_approval_record: boolean + message_contract: string + evidence_refs: string[] + }> + daily_summary_contract: Record + approval_boundaries: Record + operation_boundaries: Record +} diff --git a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md index 90c4f4c3..d30dfee4 100644 --- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md +++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md @@ -686,6 +686,52 @@ API: - Backup notification policy service + API tests `9 passed`。 - `py_compile` 通過。 +### P1-104 Backup / DR 證據 UI 摘要 + +正式 UI: + +- `/zh-TW/governance?tab=automation-inventory` + +接入只讀 API: + +- `GET /api/v1/agents/backup-dr-target-inventory` +- `GET /api/v1/agents/backup-dr-readiness-matrix` +- `GET /api/v1/agents/backup-notification-policy` + +顯示內容: + +- Backup / DR 目標:`17` +- Ready:`12` +- 需處置:`2` +- 阻擋:`2` +- 成功即時抑制:`2` +- failure / warning / core blocker 立即升級:`4` +- 通知規則:`8` + +核心裁決: + +- UI 只顯示備份目標、readiness matrix、通知政策、關鍵 blocker 與 evidence ref。 +- `configs_capture` 與 `credential_escrow_markers` 仍為 blocked,不得宣稱 full DR green。 +- `signoz` 顯示 disruptive backup guard;Agent 不得任意觸發會短暫停止 collector 的備份。 +- 成功備份仍不即時送 Telegram / AwoooP;成功狀態由每日摘要與查詢承載。 +- warning、failed、action-required、core blocker 才能進即時升級。 + +實作邊界: + +- 不執行 backup。 +- 不執行 restore。 +- 不執行 offsite sync。 +- 不寫 credential marker。 +- 不改排程、不寫 workflow。 +- 不發 Telegram 測試訊息。 + +驗證: + +- `pnpm --filter @awoooi/web exec tsc --noEmit --tsBuildInfoFile /tmp/awoooi-p1-104-backup-evidence.tsbuildinfo` 通過。 +- 本地 desktop `/zh-TW/governance?tab=automation-inventory&_v=p1-104-backup-evidence-local`:Backup / DR 證據、準備度矩陣、通知政策、成功抑制、即時升級、Gitea 與 SignOz disruptive guard 可見;無載入錯誤;`horizontalOverflow=-6`。 +- 本地 390px mobile `/zh-TW/governance?tab=automation-inventory&_v=p1-104-backup-evidence-local`:Backup / DR 證據、準備度矩陣、通知政策、成功抑制、即時升級、Gitea 與 SignOz disruptive guard 可見;無載入錯誤;`horizontalOverflow=-6`。 +- 截圖:`/tmp/awoooi-p1-104-backup-evidence-local-desktop.png`、`/tmp/awoooi-p1-104-backup-evidence-local-mobile.png`。 + ### P0 - 治理與 Inventory 基礎 | ID | 狀態 | % | 負責 Agent | 任務 | 產出 | 關卡 | @@ -718,7 +764,7 @@ API: | P1-101 | 完成 | 100 | Hermes | 把備份 runbook / 腳本轉成機器可讀目標盤點 | `docs/evaluations/backup_dr_target_inventory_2026-06-04.json` | 只讀 | | P1-102 | 完成 | 100 | OpenClaw | 顯示備份新鮮度、完整性、復原演練狀態 | `docs/evaluations/backup_dr_readiness_matrix_2026-06-04.json` | 不執行 restore | | P1-103 | 完成 | 100 | Hermes | 對齊備份通知政策 | `docs/evaluations/backup_notification_policy_2026-06-04.json` | 不發成功洗版 | -| P1-104 | 待辦 | 0 | OpenClaw | 在 AwoooP / governance UI 加備份證據 | 備份卡片 | 瀏覽器驗證 | +| P1-104 | 完成 | 100 | OpenClaw | 在 AwoooP / governance UI 加備份證據 | `/zh-TW/governance?tab=automation-inventory` | 只讀 + 瀏覽器驗證 | | P1-105 | 待辦 | 0 | OpenClaw | 定義復原演練批准包 | 復原計畫範本 | 人工批准 | | P1-106 | 待辦 | 0 | Hermes | 顯示異地 / escrow 準備度狀態 | DR 準備度區塊 | 不暴露 credential | @@ -860,13 +906,24 @@ API: 任何完成宣告前,必須同步更新本文件或後續生成的 JSON 快照。 +本次同步: + +```text +進度:100%。 +目前優先級:P1。 +目前任務:P1-104 在 AwoooP / governance UI 加備份證據。 +狀態變更:待辦 -> 完成。 +證據:typecheck 通過;本地 desktop 與 390px mobile governance automation-inventory tab 驗證 Backup / DR 證據、準備度矩陣、通知政策、成功抑制、即時升級、Gitea 與 SignOz disruptive guard 可見;無載入錯誤;horizontalOverflow <= 0。 +阻擋:無;backup、restore、offsite sync、credential marker、排程、workflow、Telegram 測試通知仍未批准。 +下一步:P1-105 定義復原演練批准包。 +``` + ## 13. 立即執行順序 -1. P1-104:在 AwoooP / governance UI 加備份證據。 -2. P1-105:定義復原演練批准包。 -3. P1-106:顯示異地 / escrow 準備度狀態。 -4. P1-305 / P1-306:補每個任務的批准邊界與進度彙總細節。 -5. P2 / P3 必須等 P1 可見且關卡穩定後再做。 +1. P1-105:定義復原演練批准包。 +2. P1-106:顯示異地 / escrow 準備度狀態。 +3. P1-305 / P1-306:補每個任務的批准邊界與進度彙總細節。 +4. P2 / P3 必須等 P1 可見且關卡穩定後再做。 ## 14. 目前風險 diff --git a/docs/evaluations/ai_agent_automation_backlog_2026-06-04.json b/docs/evaluations/ai_agent_automation_backlog_2026-06-04.json index 8b46a952..f5feda4b 100644 --- a/docs/evaluations/ai_agent_automation_backlog_2026-06-04.json +++ b/docs/evaluations/ai_agent_automation_backlog_2026-06-04.json @@ -5,30 +5,30 @@ "program_status": { "overall_completion_percent": 100, "current_priority": "P1", - "current_task_id": "P1-103", - "next_task_id": "P1-104", + "current_task_id": "P1-104", + "next_task_id": "P1-105", "read_only_mode": true }, "rollups": { - "total_items": 18, + "total_items": 19, "by_priority": { - "P1": 16, + "P1": 17, "P2": 1, "P3": 1 }, "by_status": { "planned": 7, - "done": 11 + "done": 12 }, "by_gate_status": { - "read_only_allowed": 15, + "read_only_allowed": 16, "production_change_blocked": 1, "cost_approval_required": 1, "blocked_by_evidence": 1 }, "by_owner_agent": { "hermes": 10, - "openclaw": 7, + "openclaw": 8, "nemotron": 1 } }, @@ -280,6 +280,33 @@ ], "next_review": "P1-103" }, + { + "item_id": "AUTO-P1-104", + "priority": "P1", + "status": "done", + "workstream_id": "WS4", + "source_asset_id": "backup_dr_readiness_matrix", + "source_signal_kind": "ui_visibility_gap", + "title": "在 AwoooP / governance UI 加備份證據", + "owner_agent": "openclaw", + "recommended_action": "在 automation inventory tab 顯示 Backup / DR 目標、readiness matrix、通知政策、blocked / action-required 與 success-noise suppression 證據。", + "action_class": "execute_read_only", + "gate_status": "read_only_allowed", + "risk_level": "high", + "evidence_refs": [ + "apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx", + "GET /api/v1/agents/backup-dr-target-inventory", + "GET /api/v1/agents/backup-dr-readiness-matrix", + "GET /api/v1/agents/backup-notification-policy" + ], + "acceptance_criteria": [ + "顯示 Backup / DR 證據但不提供 backup、restore、offsite sync、credential marker、schedule 或 workflow 操作", + "顯示 ready、action-required、blocked、success suppressed 與 immediate escalation rollup", + "desktop 與 390px mobile 無橫向溢出", + "成功備份仍不得即時送 Telegram / AwoooP 洗版" + ], + "next_review": "P1-104" + }, { "item_id": "AUTO-P1-201", "priority": "P1", diff --git a/docs/evaluations/ai_agent_automation_inventory_snapshot_2026-06-04_static_seed.json b/docs/evaluations/ai_agent_automation_inventory_snapshot_2026-06-04_static_seed.json index b059b62a..7372b27c 100644 --- a/docs/evaluations/ai_agent_automation_inventory_snapshot_2026-06-04_static_seed.json +++ b/docs/evaluations/ai_agent_automation_inventory_snapshot_2026-06-04_static_seed.json @@ -4,8 +4,8 @@ "program_status": { "overall_completion_percent": 100, "current_priority": "P1", - "current_task_id": "P1-103", - "next_task_id": "P1-104", + "current_task_id": "P1-104", + "next_task_id": "P1-105", "read_only_mode": true }, "status_taxonomy": { @@ -425,7 +425,7 @@ "display_name": "備份與 DR 自動化", "completion_percent": 67, "status": "in_progress", - "next_task_id": "P1-104" + "next_task_id": "P1-105" }, { "workstream_id": "WS5", @@ -451,9 +451,9 @@ { "workstream_id": "WS8", "display_name": "產品 UI", - "completion_percent": 75, + "completion_percent": 82, "status": "in_progress", - "next_task_id": "P1-104" + "next_task_id": "P1-305" } ], "tasks": [ @@ -620,7 +620,18 @@ "title": "對齊備份通知政策", "output": "docs/evaluations/backup_notification_policy_2026-06-04.json", "gate_status": "read_only_allowed", - "next_action": "完成,P1-104 在 AwoooP / governance UI 加備份證據。" + "next_action": "完成,P1-104 Backup / DR 證據 UI 已推進。" + }, + { + "task_id": "P1-104", + "priority": "P1", + "status": "done", + "completion_percent": 100, + "owner_agent": "openclaw", + "title": "在 AwoooP / governance UI 加備份證據", + "output": "/zh-TW/governance?tab=automation-inventory", + "gate_status": "read_only_allowed", + "next_action": "完成,P1-105 定義復原演練批准包。" }, { "task_id": "P1-201", @@ -810,6 +821,12 @@ "ref": "GET /api/v1/agents/backup-notification-policy", "result": "備份通知政策只讀 API 已新增,不送通知、不執行備份/restore/offsite sync、不寫 credential marker、不改排程或 workflow。" }, + { + "evidence_id": "backup_dr_evidence_ui", + "kind": "browser", + "ref": "/zh-TW/governance?tab=automation-inventory", + "result": "P1-104 Backup / DR 證據 UI 已接入 automation inventory tab;本地 desktop 與 390px mobile 驗證 Backup / DR 證據、準備度矩陣、通知政策、成功抑制、即時升級、Gitea 與 SignOz disruptive guard 可見,無載入錯誤,horizontalOverflow <= 0。" + }, { "evidence_id": "package_supply_chain_inventory_schema", "kind": "schema", diff --git a/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md b/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md index 70dfc086..2d632402 100644 --- a/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md +++ b/docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md @@ -3370,3 +3370,26 @@ Phase 6 完成後 - P1-103:備份通知政策已完成,schema 位於 `docs/schemas/backup_notification_policy_v1.schema.json`,快照位於 `docs/evaluations/backup_notification_policy_2026-06-04.json`,API 為 `GET /api/v1/agents/backup-notification-policy`;8 條規則中 2 條成功即時抑制、4 條 immediate escalation、2 條 action-required,每日成功摘要由 06:05 台北時間承載。 **裁決:** P0 基礎已完成,P1 產品面已接上分組 UI,Backup / DR 目標盤點、準備度矩陣、備份通知政策與 WS5 套件 / 供應鏈自動化已進入只讀 API 並達 `100%`。下一輪推進必須從 P1-104 備份證據 UI 開始,保持只讀;不得執行 restore、不得寫 credential marker、不得送 Telegram / AwoooP 測試通知、不得安裝依賴、不得升級套件、不得寫 lockfile、不得查外部 CVE、不得查外部 license、不得查外部 registry 或 Agent market 來源、不得啟用排程、不得寫 workflow、不得執行 npm audit、不得執行 pnpm install、不得執行 docker build、不得 pull image、不得重建 image、不得 push registry、不得新增 SDK、不得呼叫付費 API、不得改生產路由、不得把任何 Agent 推入 shadow/canary。 + +### 2026-06-05 凌晨 (台北) — P1-104 Backup / DR 證據 UI 完成 + +**觸發**:統帥批准繼續,要求持續更新工作清單、完成度與工作狀態,並記得推版到正式環境。 + +**已推進:** +- P1-104:AwoooP / governance automation inventory tab 已接入 Backup / DR 證據區塊,讀取 `GET /api/v1/agents/backup-dr-target-inventory`、`GET /api/v1/agents/backup-dr-readiness-matrix`、`GET /api/v1/agents/backup-notification-policy`。 +- UI 顯示 Backup / DR 目標 `17`、ready `12`、action_required `2`、blocked `2`、成功即時抑制 `2`、immediate escalation `4`,並顯示準備度矩陣、通知政策與關鍵備份目標。 +- `docs/evaluations/ai_agent_automation_inventory_snapshot_2026-06-04_static_seed.json` 已將 `current_task_id` 推進到 `P1-104`、`next_task_id` 推進到 `P1-105`;產品 UI workstream 由 `75%` 推進到 `82%`。 +- `docs/evaluations/ai_agent_automation_backlog_2026-06-04.json` 新增 `AUTO-P1-104` done item,rollup 更新為 total `19`、P1 `17`、done `12`、read_only_allowed `16`、OpenClaw owner `8`。 +- `docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md` 已新增 P1-104 摘要、進度同步紀錄與下一步順序。 + +**驗證:** +- `pnpm --filter @awoooi/web exec tsc --noEmit --tsBuildInfoFile /tmp/awoooi-p1-104-backup-evidence.tsbuildinfo` 通過。 +- 本地 desktop `/zh-TW/governance?tab=automation-inventory&_v=p1-104-backup-evidence-local`:Backup / DR 證據、準備度矩陣、通知政策、成功抑制、即時升級、Gitea 與 SignOz disruptive guard 可見;無載入錯誤;`horizontalOverflow=-6`。 +- 本地 390px mobile `/zh-TW/governance?tab=automation-inventory&_v=p1-104-backup-evidence-local`:Backup / DR 證據、準備度矩陣、通知政策、成功抑制、即時升級、Gitea 與 SignOz disruptive guard 可見;無載入錯誤;`horizontalOverflow=-6`。 + +**下一步:** +1. P1-105:定義復原演練批准包。 +2. P1-106:顯示異地 / escrow 準備度狀態。 +3. P1-305 / P1-306:補任務批准邊界與進度彙總細節。 + +**裁決:** P1-104 已完成,但仍只屬於 read-only evidence surface。不得執行 backup、restore、offsite sync、credential marker 寫入、排程變更、workflow 寫入或 Telegram 測試通知;不得把 Backup / DR UI 可見解讀成 full DR green。下一步只能產生復原演練與 escrow review 的批准包,必須保留 OpenClaw 仲裁與人工批准邊界。