diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index 278e0514a..9857c45f9 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -292,6 +292,9 @@ from src.services.ai_agent_tool_adoption_approval_package import ( from src.services.ai_agent_version_freshness_snapshot import ( load_latest_ai_agent_version_freshness_snapshot, ) +from src.services.ai_agent_version_lifecycle_update_proposal import ( + load_latest_ai_agent_version_lifecycle_update_proposal, +) from src.services.ai_provider_route_matrix import ( load_latest_ai_provider_route_matrix, ) @@ -3009,6 +3012,35 @@ async def get_agent_proactive_operations_contract() -> dict[str, Any]: ) from exc +@router.get( + "/agent-version-lifecycle-update-proposal", + response_model=dict[str, Any], + summary="取得 AI Agent 版本生命週期更新提案佇列", + description=( + "讀取最新已提交的 AI Agent / 套件 / 工具 / 服務 / 主機版本生命週期更新提案;" + "此端點只回傳已脫敏治理資料與批准 gate,不啟用排程、不外查 registry/CVE/市場來源、" + "不升級套件、不寫 lockfile、不 pull/build/push image、不操作 host/K3s/stateful、" + "不建立 PR、不 auto merge、不送 Telegram、不讀取機密、不切換 provider、" + "不替換 OpenClaw、不修改生產路由。" + ), +) +async def get_agent_version_lifecycle_update_proposal() -> dict[str, Any]: + """Return the latest read-only AI Agent version lifecycle update proposal.""" + try: + return await asyncio.to_thread(load_latest_ai_agent_version_lifecycle_update_proposal) + except FileNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(exc), + ) from exc + except (json.JSONDecodeError, ValueError) as exc: + logger.error("ai_agent_version_lifecycle_update_proposal_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="AI Agent 版本生命週期更新提案無效", + ) from exc + + @router.get( "/agent-version-freshness-snapshot", response_model=dict[str, Any], diff --git a/apps/api/src/services/ai_agent_version_lifecycle_update_proposal.py b/apps/api/src/services/ai_agent_version_lifecycle_update_proposal.py new file mode 100644 index 000000000..7eb7903a7 --- /dev/null +++ b/apps/api/src/services/ai_agent_version_lifecycle_update_proposal.py @@ -0,0 +1,274 @@ +""" +AI Agent version lifecycle update proposal snapshot. + +Loads the latest committed, read-only proposal queue for AI Agents, packages, +tools, services, hosts, and AI solution updates. This module only exposes +sanitized governance data: it never activates schedules, queries live external +sources, upgrades packages, mutates hosts, sends Telegram messages, changes +provider routes, or replaces OpenClaw. +""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any + +from src.services.snapshot_paths import default_evaluations_dir + +_DEFAULT_EVALUATIONS_DIR = default_evaluations_dir(Path(__file__)) +_SNAPSHOT_PATTERN = "ai_agent_version_lifecycle_update_proposal_*.json" +_SCHEMA_VERSION = "ai_agent_version_lifecycle_update_proposal_v1" +_RUNTIME_AUTHORITY = "version_lifecycle_update_proposal_only_no_write_or_upgrade" +_VALID_AGENTS = {"openclaw", "hermes", "nemotron"} +_VALID_RISK_TIERS = {"low", "medium", "high", "critical"} +_VALID_PRIORITIES = {"P0", "P1", "P2", "P3"} +_BLOCKED_RUNTIME_FLAGS = { + "schedule_activation_allowed", + "external_market_lookup_allowed", + "external_registry_lookup_allowed", + "external_cve_lookup_allowed", + "package_upgrade_allowed", + "lockfile_write_allowed", + "host_upgrade_allowed", + "os_package_upgrade_allowed", + "kernel_upgrade_allowed", + "k3s_upgrade_allowed", + "kubectl_command_allowed", + "node_drain_allowed", + "reboot_allowed", + "stateful_restart_allowed", + "database_migration_allowed", + "image_pull_allowed", + "docker_build_allowed", + "registry_push_allowed", + "workflow_write_allowed", + "pr_creation_allowed", + "auto_merge_allowed", + "provider_route_switch_allowed", + "openclaw_replacement_allowed", + "paid_api_call_allowed", + "secret_read_allowed", + "telegram_direct_send_allowed", + "telegram_gateway_queue_write_allowed", + "production_write_allowed", + "conversation_transcript_display_allowed", +} +_TRANSCRIPT_MARKERS = { + "# In app browser", + "My request for Codex", + "Current URL:", + "AGENTS.md instructions", + "", + "批准!繼續", +} +_SECRET_MARKERS = { + "auth.json", + ".env", + "bot token", + "telegram token", + "secret value", + "private key", +} + + +def load_latest_ai_agent_version_lifecycle_update_proposal( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed AI Agent version lifecycle update proposal.""" + directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR + candidates = sorted(directory.glob(_SNAPSHOT_PATTERN)) + if not candidates: + raise FileNotFoundError( + f"no AI Agent version lifecycle update proposal snapshots found in {directory}" + ) + + latest = candidates[-1] + with latest.open(encoding="utf-8") as handle: + payload = json.load(handle) + + if not isinstance(payload, dict): + raise ValueError(f"{latest}: expected JSON object") + label = str(latest) + _require_schema(payload, _SCHEMA_VERSION, label) + _require_read_only_boundaries(payload, label) + _require_rollup_consistency(payload, label) + _require_domain_and_proposal_integrity(payload, label) + _require_telegram_contract(payload, label) + _require_no_sensitive_public_content(payload, label) + return payload + + +def _require_schema(payload: dict[str, Any], expected: str, label: str) -> None: + actual = payload.get("schema_version") + if actual != expected: + raise ValueError(f"{label}: expected schema_version={expected}, got {actual!r}") + + +def _require_read_only_boundaries(payload: dict[str, Any], label: str) -> None: + program_status = payload.get("program_status") or {} + if program_status.get("read_only_mode") is not True: + raise ValueError(f"{label}: program_status.read_only_mode must be true") + if program_status.get("runtime_authority") != _RUNTIME_AUTHORITY: + raise ValueError(f"{label}: runtime_authority must stay {_RUNTIME_AUTHORITY}") + + boundaries = payload.get("runtime_boundaries") or {} + if boundaries.get("read_only_update_proposal_allowed") is not True: + raise ValueError(f"{label}: read_only_update_proposal_allowed must be true") + + opened = sorted(flag for flag in _BLOCKED_RUNTIME_FLAGS if boundaries.get(flag) is not False) + if opened: + raise ValueError(f"{label}: runtime boundaries must remain false: {opened}") + + +def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None: + domains = payload.get("lifecycle_domains") or [] + proposals = payload.get("update_proposals") or [] + cadences = payload.get("cadence_matrix") or [] + gates = payload.get("approval_gate_matrix") or [] + boundaries = payload.get("runtime_boundaries") or {} + rollups = payload.get("rollups") or {} + + expected_counts = { + "domain_count": len(domains), + "proposal_count": len(proposals), + "cadence_count": len(cadences), + "approval_gate_count": len(gates), + "read_only_proposal_count": sum( + 1 for proposal in proposals if proposal.get("direct_update_allowed") is False + ), + "approval_required_count": sum( + 1 for proposal in proposals if proposal.get("requires_owner_approval") is True + ), + "critical_candidate_count": sum( + 1 for proposal in proposals if proposal.get("risk_tier") == "critical" + ), + "high_candidate_count": sum( + 1 for proposal in proposals if proposal.get("risk_tier") == "high" + ), + "false_runtime_boundary_count": sum( + 1 for flag in _BLOCKED_RUNTIME_FLAGS if boundaries.get(flag) is False + ), + "auto_execution_allowed_count": sum( + 1 for proposal in proposals if proposal.get("auto_execution_allowed") is True + ), + "telegram_direct_send_count": int( + (payload.get("telegram_digest_contract") or {}).get("direct_send_allowed") is True + ), + "telegram_gateway_queue_write_count": int( + (payload.get("telegram_digest_contract") or {}).get("gateway_queue_write_allowed") + is True + ), + "production_write_count": int(boundaries.get("production_write_allowed") is True), + "update_allowed_count": sum( + 1 for flag in _BLOCKED_RUNTIME_FLAGS if flag.endswith("_allowed") and boundaries.get(flag) is True + ), + } + mismatched = { + key: {"expected": expected, "actual": rollups.get(key)} + for key, expected in expected_counts.items() + if rollups.get(key) != expected + } + if mismatched: + raise ValueError(f"{label}: rollup counts must match payload sections: {mismatched}") + + domain_ids = sorted(domain.get("domain_id") for domain in domains) + proposal_ids = sorted(proposal.get("proposal_id") for proposal in proposals) + if sorted(rollups.get("domain_ids") or []) != domain_ids: + raise ValueError(f"{label}: rollups.domain_ids mismatch") + if sorted(rollups.get("proposal_ids") or []) != proposal_ids: + raise ValueError(f"{label}: rollups.proposal_ids mismatch") + + +def _require_domain_and_proposal_integrity(payload: dict[str, Any], label: str) -> None: + domain_ids = {domain.get("domain_id") for domain in payload.get("lifecycle_domains") or []} + gate_ids = {gate.get("gate_id") for gate in payload.get("approval_gate_matrix") or []} + + unsafe_domains = [ + domain.get("domain_id") + for domain in payload.get("lifecycle_domains") or [] + if domain.get("owner_agent") not in _VALID_AGENTS + or domain.get("risk_tier") not in _VALID_RISK_TIERS + or not domain.get("cadence") + or not domain.get("decision_policy") + ] + if unsafe_domains: + raise ValueError(f"{label}: lifecycle domains missing owner/risk/cadence: {unsafe_domains}") + + for proposal in payload.get("update_proposals") or []: + proposal_id = proposal.get("proposal_id") or "" + if proposal.get("domain_id") not in domain_ids: + raise ValueError(f"{label}: proposal {proposal_id} references unknown domain") + if proposal.get("owner_agent") not in _VALID_AGENTS: + raise ValueError(f"{label}: proposal {proposal_id} has invalid owner_agent") + if proposal.get("risk_tier") not in _VALID_RISK_TIERS: + raise ValueError(f"{label}: proposal {proposal_id} has invalid risk_tier") + if proposal.get("priority") not in _VALID_PRIORITIES: + raise ValueError(f"{label}: proposal {proposal_id} has invalid priority") + if proposal.get("approval_gate") not in gate_ids: + raise ValueError(f"{label}: proposal {proposal_id} references unknown approval gate") + if proposal.get("requires_owner_approval") is not True: + raise ValueError(f"{label}: proposal {proposal_id} must require owner approval") + if proposal.get("direct_update_allowed") is not False: + raise ValueError(f"{label}: proposal {proposal_id} must not allow direct update") + if proposal.get("auto_execution_allowed") is not False: + raise ValueError(f"{label}: proposal {proposal_id} must not allow auto execution") + if not proposal.get("evidence_refs"): + raise ValueError(f"{label}: proposal {proposal_id} must include evidence_refs") + if not proposal.get("validation_plan"): + raise ValueError(f"{label}: proposal {proposal_id} must include validation_plan") + if not proposal.get("rollback_plan"): + raise ValueError(f"{label}: proposal {proposal_id} must include rollback_plan") + if not proposal.get("blocked_runtime_actions"): + raise ValueError(f"{label}: proposal {proposal_id} must include blocked_runtime_actions") + + unsafe_gates = [ + gate.get("gate_id") + for gate in payload.get("approval_gate_matrix") or [] + if gate.get("owner_approval_required") is not True + or gate.get("auto_execute_allowed") is not False + or not gate.get("required_evidence") + ] + if unsafe_gates: + raise ValueError(f"{label}: approval gates must stay owner-reviewed: {unsafe_gates}") + + +def _require_telegram_contract(payload: dict[str, Any], label: str) -> None: + contract = payload.get("telegram_digest_contract") or {} + if contract.get("direct_send_allowed") is not False: + raise ValueError(f"{label}: Telegram direct send must remain false") + if contract.get("gateway_queue_write_allowed") is not False: + raise ValueError(f"{label}: Telegram Gateway queue write must remain false") + if contract.get("bot_api_call_allowed") is not False: + raise ValueError(f"{label}: Telegram Bot API call must remain false") + if not contract.get("draft_outputs"): + raise ValueError(f"{label}: Telegram draft outputs must be declared") + + +def _require_no_sensitive_public_content(payload: dict[str, Any], label: str) -> None: + for path, value in _walk_values(payload): + if not isinstance(value, str): + continue + lowered = value.lower() + transcript_hits = sorted(marker for marker in _TRANSCRIPT_MARKERS if marker in value) + if transcript_hits: + raise ValueError( + f"{label}: forbidden work-window conversation content at {path}: {transcript_hits}" + ) + secret_hits = sorted(marker for marker in _SECRET_MARKERS if marker in lowered) + if secret_hits: + raise ValueError(f"{label}: forbidden secret marker at {path}: {secret_hits}") + + +def _walk_values(value: Any, path: str = "$") -> list[tuple[str, Any]]: + if isinstance(value, dict): + results: list[tuple[str, Any]] = [] + for key, child in value.items(): + results.extend(_walk_values(child, f"{path}.{key}")) + return results + if isinstance(value, list): + results = [] + for index, child in enumerate(value): + results.extend(_walk_values(child, f"{path}[{index}]")) + return results + return [(path, value)] diff --git a/apps/api/tests/test_ai_agent_version_lifecycle_update_proposal.py b/apps/api/tests/test_ai_agent_version_lifecycle_update_proposal.py new file mode 100644 index 000000000..6f2d96266 --- /dev/null +++ b/apps/api/tests/test_ai_agent_version_lifecycle_update_proposal.py @@ -0,0 +1,104 @@ +from __future__ import annotations + +import json + +import pytest + +from src.services.ai_agent_version_lifecycle_update_proposal import ( + load_latest_ai_agent_version_lifecycle_update_proposal, +) + + +def test_load_latest_ai_agent_version_lifecycle_update_proposal_reads_committed_snapshot(): + data = load_latest_ai_agent_version_lifecycle_update_proposal() + + assert data["schema_version"] == "ai_agent_version_lifecycle_update_proposal_v1" + assert data["program_status"]["overall_completion_percent"] == 78 + assert data["program_status"]["current_task_id"] == "P2-413" + assert data["program_status"]["next_task_id"] == "P2-414" + assert data["program_status"]["read_only_mode"] is True + assert data["program_status"]["runtime_authority"] == ( + "version_lifecycle_update_proposal_only_no_write_or_upgrade" + ) + assert data["runtime_boundaries"]["read_only_update_proposal_allowed"] is True + assert data["runtime_boundaries"]["package_upgrade_allowed"] is False + assert data["runtime_boundaries"]["host_upgrade_allowed"] is False + assert data["runtime_boundaries"]["k3s_upgrade_allowed"] is False + assert data["runtime_boundaries"]["telegram_direct_send_allowed"] is False + assert data["runtime_boundaries"]["production_write_allowed"] is False + assert data["runtime_boundaries"]["openclaw_replacement_allowed"] is False + assert data["telegram_digest_contract"]["direct_send_allowed"] is False + assert data["telegram_digest_contract"]["gateway_queue_write_allowed"] is False + assert data["telegram_digest_contract"]["bot_api_call_allowed"] is False + assert data["rollups"]["domain_count"] == len(data["lifecycle_domains"]) == 12 + assert data["rollups"]["proposal_count"] == len(data["update_proposals"]) == 12 + assert data["rollups"]["cadence_count"] == len(data["cadence_matrix"]) == 6 + assert data["rollups"]["approval_gate_count"] == len(data["approval_gate_matrix"]) == 9 + assert data["rollups"]["false_runtime_boundary_count"] == 29 + assert data["rollups"]["auto_execution_allowed_count"] == 0 + assert data["rollups"]["telegram_direct_send_count"] == 0 + assert data["rollups"]["production_write_count"] == 0 + assert data["rollups"]["update_allowed_count"] == 0 + assert any( + proposal["proposal_id"] == "openclaw_challenger_replay_bench" + for proposal in data["update_proposals"] + ) + assert all(proposal["requires_owner_approval"] is True for proposal in data["update_proposals"]) + assert all(proposal["direct_update_allowed"] is False for proposal in data["update_proposals"]) + + +def test_ai_agent_version_lifecycle_update_proposal_rejects_runtime_upgrade(tmp_path): + snapshot = _snapshot() + snapshot["runtime_boundaries"]["package_upgrade_allowed"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="runtime boundaries"): + load_latest_ai_agent_version_lifecycle_update_proposal(tmp_path) + + +def test_ai_agent_version_lifecycle_update_proposal_rejects_rollup_mismatch(tmp_path): + snapshot = _snapshot() + snapshot["rollups"]["proposal_count"] = 99 + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="rollup counts"): + load_latest_ai_agent_version_lifecycle_update_proposal(tmp_path) + + +def test_ai_agent_version_lifecycle_update_proposal_rejects_direct_update(tmp_path): + snapshot = _snapshot() + snapshot["update_proposals"][0]["direct_update_allowed"] = True + snapshot["rollups"]["read_only_proposal_count"] = 11 + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="direct update"): + load_latest_ai_agent_version_lifecycle_update_proposal(tmp_path) + + +def test_ai_agent_version_lifecycle_update_proposal_rejects_missing_evidence(tmp_path): + snapshot = _snapshot() + snapshot["update_proposals"][0]["evidence_refs"] = [] + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="evidence_refs"): + load_latest_ai_agent_version_lifecycle_update_proposal(tmp_path) + + +def test_ai_agent_version_lifecycle_update_proposal_rejects_work_window_markers(tmp_path): + snapshot = _snapshot() + snapshot["program_status"]["status_note"] = "不可放入 My request for Codex" + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="forbidden work-window conversation content"): + load_latest_ai_agent_version_lifecycle_update_proposal(tmp_path) + + +def _write_snapshot(tmp_path, snapshot: dict) -> None: + (tmp_path / "ai_agent_version_lifecycle_update_proposal_2026-06-26.json").write_text( + json.dumps(snapshot), + encoding="utf-8", + ) + + +def _snapshot() -> dict: + return json.loads(json.dumps(load_latest_ai_agent_version_lifecycle_update_proposal())) diff --git a/apps/api/tests/test_ai_agent_version_lifecycle_update_proposal_api.py b/apps/api/tests/test_ai_agent_version_lifecycle_update_proposal_api.py new file mode 100644 index 000000000..a784227f7 --- /dev/null +++ b/apps/api/tests/test_ai_agent_version_lifecycle_update_proposal_api.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from src.api.v1.agents import router + + +def test_ai_agent_version_lifecycle_update_proposal_endpoint_returns_committed_snapshot(): + app = FastAPI() + app.include_router(router, prefix="/api/v1") + client = TestClient(app) + + response = client.get("/api/v1/agents/agent-version-lifecycle-update-proposal") + + assert response.status_code == 200 + data = response.json() + assert data["schema_version"] == "ai_agent_version_lifecycle_update_proposal_v1" + assert data["program_status"]["overall_completion_percent"] == 78 + assert data["program_status"]["current_task_id"] == "P2-413" + assert data["program_status"]["next_task_id"] == "P2-414" + assert data["program_status"]["read_only_mode"] is True + assert data["runtime_boundaries"]["package_upgrade_allowed"] is False + assert data["runtime_boundaries"]["k3s_upgrade_allowed"] is False + assert data["runtime_boundaries"]["telegram_direct_send_allowed"] is False + assert data["runtime_boundaries"]["production_write_allowed"] is False + assert data["runtime_boundaries"]["openclaw_replacement_allowed"] is False + assert data["rollups"]["domain_count"] == 12 + assert data["rollups"]["proposal_count"] == 12 + assert data["rollups"]["approval_required_count"] == 12 + assert data["rollups"]["auto_execution_allowed_count"] == 0 + assert data["rollups"]["telegram_direct_send_count"] == 0 + assert data["rollups"]["production_write_count"] == 0 + assert any( + proposal["proposal_id"] == "telegram_bot_gateway_policy_refresh" + for proposal in data["update_proposals"] + ) diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 60007ee14..b48bb0fa3 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -4353,6 +4353,51 @@ "cd_login_banner_observed_os_only": "僅 CD banner 觀察" } }, + "versionLifecycleProposal": { + "title": "P2-413 版本生命週期更新提案", + "subtitle": "{current} → {next};更新提案 {proposals};批准 Gate {gates}。AI Agent 只做評估、排序、批准包與回滾計畫,不自動升級。", + "source": "{generated} · {current} → {next}", + "badges": { + "readOnly": "只讀提案佇列", + "telegram": "Telegram 實發 {value}", + "production": "正式環境寫入 {value}" + }, + "metrics": { + "overall": "P2-413 進度", + "domains": "治理領域", + "proposals": "更新提案", + "critical": "關鍵風險", + "blocked": "封鎖邊界", + "autoExecute": "自動執行" + }, + "sections": { + "proposals": "優先更新提案", + "boundaries": "仍被關閉的 runtime 動作", + "cadence": "日週月與觸發節奏", + "telegram": "Telegram 報告草稿政策" + }, + "labels": { + "owner": "主責", + "risk": "風險", + "gate": "Gate", + "evidence": "證據", + "rollback": "回滾", + "blocked": "封鎖", + "boundaryClosed": "保持 false", + "boundaryOpen": "異常開啟", + "allowedNow": "目前允許", + "ownerGate": "需 owner gate", + "drafts": "草稿輸出", + "redaction": "遮罩" + }, + "boundaries": { + "updates": "套件 / 主機 / K3s 升級", + "containers": "Image pull / build / push", + "automation": "排程 / PR / auto merge", + "telegram": "Telegram / Bot / Gateway", + "production": "生產路由 / OpenClaw 替換" + } + }, "visualOps": { "title": "自動化戰情視覺總覽", "subtitle": "{current} → {next};自動化 {overall}%;決策支援 {support}%。先看流程與 Gate,再下鑽證據。", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 60007ee14..b48bb0fa3 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -4353,6 +4353,51 @@ "cd_login_banner_observed_os_only": "僅 CD banner 觀察" } }, + "versionLifecycleProposal": { + "title": "P2-413 版本生命週期更新提案", + "subtitle": "{current} → {next};更新提案 {proposals};批准 Gate {gates}。AI Agent 只做評估、排序、批准包與回滾計畫,不自動升級。", + "source": "{generated} · {current} → {next}", + "badges": { + "readOnly": "只讀提案佇列", + "telegram": "Telegram 實發 {value}", + "production": "正式環境寫入 {value}" + }, + "metrics": { + "overall": "P2-413 進度", + "domains": "治理領域", + "proposals": "更新提案", + "critical": "關鍵風險", + "blocked": "封鎖邊界", + "autoExecute": "自動執行" + }, + "sections": { + "proposals": "優先更新提案", + "boundaries": "仍被關閉的 runtime 動作", + "cadence": "日週月與觸發節奏", + "telegram": "Telegram 報告草稿政策" + }, + "labels": { + "owner": "主責", + "risk": "風險", + "gate": "Gate", + "evidence": "證據", + "rollback": "回滾", + "blocked": "封鎖", + "boundaryClosed": "保持 false", + "boundaryOpen": "異常開啟", + "allowedNow": "目前允許", + "ownerGate": "需 owner gate", + "drafts": "草稿輸出", + "redaction": "遮罩" + }, + "boundaries": { + "updates": "套件 / 主機 / K3s 升級", + "containers": "Image pull / build / push", + "automation": "排程 / PR / auto merge", + "telegram": "Telegram / Bot / Gateway", + "production": "生產路由 / OpenClaw 替換" + } + }, "visualOps": { "title": "自動化戰情視覺總覽", "subtitle": "{current} → {next};自動化 {overall}%;決策支援 {support}%。先看流程與 Gate,再下鑽證據。", 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 200a3adc7..f229cd286 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 @@ -66,6 +66,7 @@ import { type AiAgentOperationPermissionModelSnapshot, type AiAgentPostWriteVerifierPackageSnapshot, type AiAgentProactiveOperationsContractSnapshot, + type AiAgentVersionLifecycleUpdateProposalSnapshot, type AiAgentRedisDryRunGateSnapshot, type AiAgentOwnerApprovedResultCaptureDryRunSnapshot, type AiAgentOwnerApprovedResultCaptureReadbackSnapshot, @@ -867,6 +868,7 @@ export function AutomationInventoryTab() { const [actionOwnerAcceptanceEventBus, setActionOwnerAcceptanceEventBus] = useState(null) const [hostRunawayAiops, setHostRunawayAiops] = useState(null) const [proactiveOperations, setProactiveOperations] = useState(null) + const [versionLifecycleProposal, setVersionLifecycleProposal] = useState(null) const [interactionLearningProof, setInteractionLearningProof] = useState(null) const [liveReadModelGate, setLiveReadModelGate] = useState(null) const [redisDryRunGate, setRedisDryRunGate] = useState(null) @@ -964,6 +966,7 @@ export function AutomationInventoryTab() { apiClient.getAiAgentActionOwnerAcceptanceEventBus(), apiClient.getHostRunawayAiopsLoopReadiness(), apiClient.getAiAgentProactiveOperationsContract(), + apiClient.getAiAgentVersionLifecycleUpdateProposal(), apiClient.getAiAgentInteractionLearningProof(), apiClient.getAiAgentLiveReadModelGate(), apiClient.getAiAgentRedisDryRunGate(), @@ -1054,6 +1057,7 @@ export function AutomationInventoryTab() { actionOwnerAcceptanceEventBusResult, hostRunawayAiopsResult, proactiveOperationsResult, + versionLifecycleProposalResult, interactionLearningProofResult, liveReadModelGateResult, redisDryRunGateResult, @@ -1141,6 +1145,7 @@ export function AutomationInventoryTab() { setActionOwnerAcceptanceEventBus(settledPublicValue(actionOwnerAcceptanceEventBusResult)) setHostRunawayAiops(settledPublicValue(hostRunawayAiopsResult)) setProactiveOperations(settledPublicValue(proactiveOperationsResult)) + setVersionLifecycleProposal(settledPublicValue(versionLifecycleProposalResult)) setInteractionLearningProof(settledPublicValue(interactionLearningProofResult)) setLiveReadModelGate(settledPublicValue(liveReadModelGateResult)) setRedisDryRunGate(settledPublicValue(redisDryRunGateResult)) @@ -1228,6 +1233,7 @@ export function AutomationInventoryTab() { highRiskOwnerReviewQueueResult, hostRunawayAiopsResult, proactiveOperationsResult, + versionLifecycleProposalResult, interactionLearningProofResult, liveReadModelGateResult, redisDryRunGateResult, @@ -2791,6 +2797,23 @@ export function AutomationInventoryTab() { .slice(0, 5) }, [dependencySupplyChainDriftMonitor]) + const visibleVersionLifecycleProposals = useMemo(() => { + if (!versionLifecycleProposal) return [] + const priorityOrder = { P1: 0, P2: 1, P3: 2, P0: 3 } as Record + const riskPriority = { critical: 0, high: 1, medium: 2, low: 3 } as Record + return [...versionLifecycleProposal.update_proposals] + .sort((a, b) => { + const leftPriority = priorityOrder[a.priority] ?? 4 + const rightPriority = priorityOrder[b.priority] ?? 4 + if (leftPriority !== rightPriority) return leftPriority - rightPriority + const leftRisk = riskPriority[a.risk_tier] ?? 4 + const rightRisk = riskPriority[b.risk_tier] ?? 4 + if (leftRisk !== rightRisk) return leftRisk - rightRisk + return a.proposal_id.localeCompare(b.proposal_id) + }) + .slice(0, 6) + }, [versionLifecycleProposal]) + if (loading) { if (reportTruthActionabilityReview) { return ( @@ -2835,7 +2858,7 @@ export function AutomationInventoryTab() { ) } - if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !professionalTaskExpansion || !receiptReadbackOwnerReview || !reportNoWriteAnalysisRuntime || !lowMediumRiskWhitelist || !highRiskOwnerReviewQueue || !actionAuditLedger || !actionOwnerAcceptanceEventBus || !hostRunawayAiops || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !resultCaptureReleaseDecisionHold || !resultCaptureReleaseDecisionReadback || !resultCaptureReleaseDecisionNextHandoff || !resultCaptureReleaseDecisionInputPrep || !resultCaptureReleaseDecisionOwnerResponsePreflight || !resultCaptureReleaseDecisionOwnerResponseReadback || !resultCaptureReleaseDecisionOwnerResponseAcceptanceGate || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !dependencySupplyChainDriftMonitor || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { + if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !professionalTaskExpansion || !receiptReadbackOwnerReview || !reportNoWriteAnalysisRuntime || !lowMediumRiskWhitelist || !highRiskOwnerReviewQueue || !actionAuditLedger || !actionOwnerAcceptanceEventBus || !hostRunawayAiops || !proactiveOperations || !versionLifecycleProposal || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !resultCaptureReleaseDecisionHold || !resultCaptureReleaseDecisionReadback || !resultCaptureReleaseDecisionNextHandoff || !resultCaptureReleaseDecisionInputPrep || !resultCaptureReleaseDecisionOwnerResponsePreflight || !resultCaptureReleaseDecisionOwnerResponseReadback || !resultCaptureReleaseDecisionOwnerResponseAcceptanceGate || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !dependencySupplyChainDriftMonitor || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { return (
@@ -3025,6 +3048,43 @@ export function AutomationInventoryTab() { active: dependencySupplyChainDriftMonitor.monitor_boundaries.production_write_allowed, }, ] + const versionLifecycleBoundaryRows = [ + { + key: 'updates', + label: t('versionLifecycleProposal.boundaries.updates'), + active: versionLifecycleProposal.runtime_boundaries.package_upgrade_allowed + || versionLifecycleProposal.runtime_boundaries.host_upgrade_allowed + || versionLifecycleProposal.runtime_boundaries.k3s_upgrade_allowed, + }, + { + key: 'containers', + label: t('versionLifecycleProposal.boundaries.containers'), + active: versionLifecycleProposal.runtime_boundaries.image_pull_allowed + || versionLifecycleProposal.runtime_boundaries.docker_build_allowed + || versionLifecycleProposal.runtime_boundaries.registry_push_allowed, + }, + { + key: 'automation', + label: t('versionLifecycleProposal.boundaries.automation'), + active: versionLifecycleProposal.runtime_boundaries.schedule_activation_allowed + || versionLifecycleProposal.runtime_boundaries.pr_creation_allowed + || versionLifecycleProposal.runtime_boundaries.auto_merge_allowed, + }, + { + key: 'telegram', + label: t('versionLifecycleProposal.boundaries.telegram'), + active: versionLifecycleProposal.telegram_digest_contract.direct_send_allowed + || versionLifecycleProposal.telegram_digest_contract.gateway_queue_write_allowed + || versionLifecycleProposal.telegram_digest_contract.bot_api_call_allowed, + }, + { + key: 'production', + label: t('versionLifecycleProposal.boundaries.production'), + active: versionLifecycleProposal.runtime_boundaries.provider_route_switch_allowed + || versionLifecycleProposal.runtime_boundaries.production_write_allowed + || versionLifecycleProposal.runtime_boundaries.openclaw_replacement_allowed, + }, + ] const receiptOwnerReviewOverall = receiptReadbackOwnerReview.program_status.overall_completion_percent const receiptOwnerReviewSources = receiptReadbackOwnerReview.rollups.source_readback_count const receiptOwnerReviewCadences = receiptReadbackOwnerReview.rollups.report_cadence_count @@ -6681,6 +6741,148 @@ export function AutomationInventoryTab() {
+ +
+
+
+
+ +
+
+ + {t('versionLifecycleProposal.title')} + + + {t('versionLifecycleProposal.subtitle', { + current: versionLifecycleProposal.program_status.current_task_id, + next: versionLifecycleProposal.program_status.next_task_id, + proposals: versionLifecycleProposal.rollups.proposal_count, + gates: versionLifecycleProposal.rollups.approval_gate_count, + })} + +
+
+
+ + + +
+
+ +
+ {[ + { label: t('versionLifecycleProposal.metrics.overall'), value: `${versionLifecycleProposal.program_status.overall_completion_percent}%`, tone: 'ok' as const, icon: }, + { label: t('versionLifecycleProposal.metrics.domains'), value: versionLifecycleProposal.rollups.domain_count, tone: 'ok' as const, icon: }, + { label: t('versionLifecycleProposal.metrics.proposals'), value: versionLifecycleProposal.rollups.proposal_count, tone: 'warn' as const, icon: }, + { label: t('versionLifecycleProposal.metrics.critical'), value: versionLifecycleProposal.rollups.critical_candidate_count, tone: 'warn' as const, icon: }, + { label: t('versionLifecycleProposal.metrics.blocked'), value: versionLifecycleProposal.rollups.false_runtime_boundary_count, tone: 'ok' as const, icon: }, + { label: t('versionLifecycleProposal.metrics.autoExecute'), value: versionLifecycleProposal.rollups.auto_execution_allowed_count, tone: 'ok' as const, icon: }, + ].map(metric => { + const color = toneColor(metric.tone) + return ( +
+
+
{metric.icon}
+ {metric.label} +
+
+ {metric.value} +
+
+ ) + })} +
+ +
+
+ {t('versionLifecycleProposal.sections.proposals')} + {visibleVersionLifecycleProposals.map(proposal => ( +
+
+ + {proposal.display_name} + + +
+
+ {redactPublicText(proposal.summary)} +
+
+ + + + + + +
+
+ ))} +
+ +
+
+ {t('versionLifecycleProposal.sections.boundaries')} +
+ {versionLifecycleBoundaryRows.map(row => ( + + ))} +
+
+
+ {t('versionLifecycleProposal.sections.cadence')} +
+ {versionLifecycleProposal.cadence_matrix.slice(0, 4).map(cadence => ( +
+
+ + {cadence.cadence_id} + + +
+ + {redactPublicText(cadence.output)} + +
+ ))} +
+
+
+ {t('versionLifecycleProposal.sections.telegram')} +
+ + + +
+
+ +
+
+
+
+
+
+
diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index b1481a19e..cd9598234 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -397,6 +397,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentVersionLifecycleUpdateProposal() { + const res = await fetch(`${API_BASE_URL}/agents/agent-version-lifecycle-update-proposal`) + return handleResponse(res) + }, + async getAiAgentInteractionLearningProof() { const res = await fetch(`${API_BASE_URL}/agents/agent-interaction-learning-proof`) return handleResponse(res) @@ -2447,6 +2452,104 @@ export interface AiAgentProactiveOperationsContractSnapshot { } } +export interface AiAgentVersionLifecycleUpdateProposalSnapshot { + schema_version: 'ai_agent_version_lifecycle_update_proposal_v1' + generated_at: 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 + runtime_authority: string + status_note: string + } + source_refs: string[] + agent_roles: Array<{ + agent_id: 'openclaw' | 'hermes' | 'nemotron' + role: string + responsibility: string + }> + lifecycle_domains: Array<{ + domain_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + risk_tier: 'low' | 'medium' | 'high' | 'critical' + cadence: string + decision_policy: string + current_authority: string + }> + update_proposals: Array<{ + proposal_id: string + domain_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + priority: 'P0' | 'P1' | 'P2' | 'P3' + risk_tier: 'low' | 'medium' | 'high' | 'critical' + status: string + summary: string + evidence_refs: string[] + approval_gate: string + requires_owner_approval: true + direct_update_allowed: false + auto_execution_allowed: false + validation_plan: string[] + rollback_plan: string[] + blocked_runtime_actions: string[] + telegram_policy: string + }> + cadence_matrix: Array<{ + cadence_id: string + frequency: string + scope: string + allowed_now: boolean + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + output: string + }> + approval_gate_matrix: Array<{ + gate_id: string + risk_tier: 'low' | 'medium' | 'high' | 'critical' + owner_approval_required: true + auto_execute_allowed: false + required_evidence: string[] + }> + telegram_digest_contract: { + status: string + direct_send_allowed: false + gateway_queue_write_allowed: false + bot_api_call_allowed: false + success_noise_suppression: boolean + draft_outputs: string[] + redaction_required: boolean + } + runtime_boundaries: Record + rollups: { + domain_count: number + proposal_count: number + cadence_count: number + approval_gate_count: number + read_only_proposal_count: number + approval_required_count: number + critical_candidate_count: number + high_candidate_count: number + false_runtime_boundary_count: number + auto_execution_allowed_count: number + telegram_direct_send_count: number + telegram_gateway_queue_write_count: number + production_write_count: number + update_allowed_count: number + domain_ids: string[] + proposal_ids: string[] + } + next_actions: Array<{ + task_id: string + priority: 'P0' | 'P1' | 'P2' | 'P3' + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + summary: string + gate: string + }> +} + export interface HostRunawayAiopsLoopReadinessSnapshot { schema_version: 'host_runaway_aiops_loop_readiness_v1' generated_at: string diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 774164642..52d6458eb 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -31,6 +31,34 @@ **邊界**:本段只改 Work Items 前端 read model 顯示;不 SSH、不重啟服務、不執行 Ansible apply、不發 Telegram、不寫 KM、不改 PlayBook trust、不讀 secret、不開 runtime gate。 +## 2026-06-26|P2-413 AI Agent / 套件 / 工具 / 服務 / 主機版本生命週期更新提案本地完成 + +**背景**:使用者要求 AI Agents 不只監控市場 Agent,也要定期更新所有 AI Agent、套件、服務、工具、主機等版本,並由 OpenClaw / Hermes / NemoTron 主動判斷、分工、告警與提出解法;但高風險仍需 owner 審核,中低風險也不得在 gate 未開前偽裝成 runtime 自動升級。 + +**完成**: +- 新增 `ai_agent_version_lifecycle_update_proposal_v1` committed snapshot:12 個版本生命週期 domain、12 個更新提案、6 種日 / 週 / 月 / triggered cadence、9 個 owner approval gate。 +- 新增 API service guard:`load_latest_ai_agent_version_lifecycle_update_proposal()` 會檢查 schema、read-only runtime authority、rollup 一致性、owner approval、rollback / validation plan、Telegram no-send 與工作視窗 / 機密字串遮罩。 +- 新增 `GET /api/v1/agents/agent-version-lifecycle-update-proposal`;端點只回傳治理資料,不外查 registry/CVE/市場來源、不升級、不寫 lockfile、不 pull/build/push image、不操作 host/K3s/stateful、不建立 PR、不 auto merge、不送 Telegram、不切 provider、不替換 OpenClaw。 +- `automation-inventory` governance UI 新增 `P2-413 版本生命週期更新提案` 卡片,顯示整體進度、12 個治理領域、12 個更新提案、4 個 critical、5 個 high、29 個封鎖 runtime boundary、0 個自動執行、0 次 Telegram 實發、0 次 production write。 +- 更新 `AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md`,把 P2-413 從待辦推進到本地驗證完成,版本生命週期自動化完成度 `45% -> 62%`。 + +**驗證**: +- `python3.11 -m json.tool docs/evaluations/ai_agent_version_lifecycle_update_proposal_2026-06-26.json`:通過。 +- `python3.11 -m json.tool apps/web/messages/zh-TW.json` / `apps/web/messages/en.json`:通過。 +- `DATABASE_URL=sqlite:////tmp/awoooi-p2-413-tests.db python3.11 -m pytest apps/api/tests/test_ai_agent_version_lifecycle_update_proposal.py apps/api/tests/test_ai_agent_version_lifecycle_update_proposal_api.py apps/api/tests/test_ai_agent_proactive_operations_contract.py apps/api/tests/test_ai_agent_host_stateful_version_inventory.py apps/api/tests/test_dependency_supply_chain_drift_monitor.py -q`:`26 passed`。 +- `pnpm --filter @awoooi/web exec eslint 'src/app/[locale]/governance/tabs/automation-inventory-tab.tsx' 'src/lib/api-client.ts'`:通過。 +- `pnpm --filter @awoooi/web typecheck`:通過。 +- `git diff --check`:通過。 + +**完成度同步**: +- P2-413 本地 schema / snapshot / API / UI / tests:`100%`。 +- 版本生命週期自動化:`45% -> 62%`;這代表 proposal queue、owner gate、rollback / validation plan 與治理頁可見,不代表正式自動升級已開。 +- 產品總治理完成度仍依 Start Here / scorecard:`42.2%`。 + +**邊界**: +- package upgrade、lockfile write、host upgrade、kernel / OS / K3s upgrade、kubectl、node drain、reboot、stateful restart、DB migration、image pull/build/push、workflow write、PR creation、auto merge、paid API、secret read、Telegram direct send、Gateway queue write、production write、provider route switch、OpenClaw replacement 全部維持 `0 / false`。 +- 下一步:推送 Gitea main、等待正式 CD,再做 production API 與 `/zh-TW/governance?tab=automation-inventory` desktop / mobile readback。 + ## 2026-06-26|P2-412 市場主流 AI Agent / AI 技術雷達正式上線:Primary Source、審核佇列與 production readback 完成 **背景**:使用者要求不要再用歷史定位保護 OpenClaw,也不要讓 NemoTron / Nemotron 只停在口號;必須用市場主流 AI Agent、AI 技術、版本、官方 primary-source 與 AWOOOI 可重跑數據決定角色。此段承接同日 P2-412 本地完成記錄,補上 Gitea main、deploy marker、production API 與桌機 / 手機讀回。 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 932c59a84..efcf0e78c 100644 --- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md +++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md @@ -13,15 +13,15 @@ | 項目 | 目前完成度 | 本次判讀 | 下一個有效動作 | |---|---:|---|---| | 本工作清單細化 | 100% | 已把所有工作流拆成 P0 / P1 / P2 / P3 | 同步 LOGBOOK 與 MASTER §8 | -| AgentOps 治理與可觀測基礎 | 94% | 已有 schema / snapshot / API / UI / gate;P2-407 已正式把日報 / 週報 / 月報、P2-406B receipt owner review、P2-004 drift monitor 與 P2-403J 報表真相串成 no-write 分析草稿;P2-408 已正式部署中 / 低風險白名單、dry-run verifier、rollback proof、audit reason 與 production readback;P2-409 已正式完成高風險 Owner Review Queue production API / governance UI / desktop smoke;P2-410 已正式完成 action audit ledger production API / governance UI / desktop + mobile smoke;P2-411 已正式完成 owner acceptance / handoff event / RAG proposal no-write 基線;P2-412 已正式完成市場主流 AI Agent / AI 技術 primary-source 雷達、API / 桌機 / 手機 production readback | 補 Runs mobile smoke;Observability / Tenants / Knowledge Base 接同一資產沉澱總帳 | +| AgentOps 治理與可觀測基礎 | 95% | 已有 schema / snapshot / API / UI / gate;P2-407 已正式把日報 / 週報 / 月報、P2-406B receipt owner review、P2-004 drift monitor 與 P2-403J 報表真相串成 no-write 分析草稿;P2-408 已正式部署中 / 低風險白名單、dry-run verifier、rollback proof、audit reason 與 production readback;P2-409 已正式完成高風險 Owner Review Queue production API / governance UI / desktop smoke;P2-410 已正式完成 action audit ledger production API / governance UI / desktop + mobile smoke;P2-411 已正式完成 owner acceptance / handoff event / RAG proposal no-write 基線;P2-412 已正式完成市場主流 AI Agent / AI 技術 primary-source 雷達、API / 桌機 / 手機 production readback;P2-413 已本地完成版本生命週期更新提案 queue、API、UI 與測試 | 補 P2-413 production readback;Observability / Tenants / Knowledge Base 接同一資產沉澱總帳 | | OpenClaw / Hermes / NemoTron 佈建布局 | 47% | 目前是只讀 layout、治理頁可視化與 P2-411 event bus 接手協議,不是主機上 live agent worker | 建立 runtime agent registry 與 AgentSession ledger | | Agent 主動溝通 / 接手 / 學習 | 設計證據 100%,runtime 38% | 互動證據、War Room、readback gate 與 P2-411 no-write handoff event / RAG proposal 基線已齊;Event Bus publish、RAG writeback、PlayBook trust 寫入仍未開 | 先完成 P2-411 正式讀回,再做 Owner-approved writeback | | 日報 / 週報 / 月報 | 可視化 100%,no-write 分析 100%,實發 0% | 報表批准包、P2-406B owner review、P2-407 no-write analyst 與治理頁已可見;Telegram 實發、receipt production write、AI analysis live runtime 仍為 0 | P2-408 白名單與 P2-406F no-send scheduler | | Telegram Bot / TG 群組 | 契約 54%,實發 0% | no-send preview、dry-run、owner review gate、P2-406B receipt readback owner review、Telegram egress inventory / owner request draft、P2-409 高風險 queue、P2-410 no-send / no-new-bypass audit template 與 P2-411 Telegram 出口驗收 lane 已串起;live send / Bot API / Gateway queue 未批准 | P2-406D no-send envelope ledger、P2-406E failure-only digest route;收到合格 owner approval 後才評估 P2-406C one-message canary | | 中低風險自動化 | 66% | P2-408 已正式部署 6 筆候選白名單、5 個 dry-run verifier、5 個 rollback proof、6 個 audit reason 與 3 類高風險分流;P2-409 已把 high / critical 與 medium live execution 風險接到 Owner Review Queue;P2-410 已補 low / medium audit event;P2-411 已本地建立中低風險 worker 範圍驗收 lane;實際 auto worker 仍未開 | P2-411 正式讀回;之後才評估 dry-run auto worker | | 高風險審核 | 87% | P2-409 已正式完成 7 個 high / critical queue item、7 份 approval packet、8 條 rejection guard、7 份 reviewer checklist;P2-410 已補 high-risk pause、critical rejection 與 owner queue audit event;P2-411 已本地建立 high / critical owner acceptance lanes;owner accepted 仍為 0 | P2-411 正式讀回與 P2-412 fixture-only rehearsal | -| 市場主流 Agent 追蹤 | 78% | 已有市場治理頁、weekly watch 與 2026-06-26 P2-412 primary-source refresh,且已完成正式部署與 production readback;Agent 市場側 13 候選 / 36 來源 / 5 changed / source failure 0;AI 技術側 21 技術 / 52 來源 / 5 changed / source failure 0;OpenAI / NVIDIA / LangGraph / MCP / A2A / OpenTelemetry GenAI 已納入主流實務對齊 | 下一步才進 P2-413 版本生命週期;仍不得安裝 SDK、切 provider 或替換 OpenClaw | -| 版本生命週期自動化 | 45% | repo-only snapshot 與採用批准包已完成;安裝、升級、PR creation、host update 仍未開 | P2-413 版本情報與 no-write upgrade proposal | +| 市場主流 Agent 追蹤 | 78% | 已有市場治理頁、weekly watch 與 2026-06-26 P2-412 primary-source refresh,且已完成正式部署與 production readback;Agent 市場側 13 候選 / 36 來源 / 5 changed / source failure 0;AI 技術側 21 技術 / 52 來源 / 5 changed / source failure 0;OpenAI / NVIDIA / LangGraph / MCP / A2A / OpenTelemetry GenAI 已納入主流實務對齊;P2-413 已把市場資料接入版本更新提案 queue | 下一步補 OpenClaw challenger replay bench;仍不得安裝 SDK、切 provider 或替換 OpenClaw | +| 版本生命週期自動化 | 62% | P2-413 已本地完成 12 個版本 domain、12 個更新提案、6 種 cadence、9 個 owner gate、API、governance UI、rollback / validation plan 與 29 個 false runtime boundary;安裝、升級、PR creation、host update 仍未開 | P2-413 production readback 後進 P2-414 報表欄位對齊與 P2-415 challenger replay bench | 目前最重要的事實邊界: @@ -71,7 +71,7 @@ | 10 | P2-410 | P0 | Agent action audit ledger | Hermes + Security | 正式驗證完成 | `ai_agent_action_audit_ledger_v1` schema / snapshot / API / tests / governance UI 已正式部署;API deploy marker `38e60192`、UI deploy marker `7a9e1cfd`;production API 回 current `P2-410`、next `P2-411`、completion `100`;desktop / mobile browser smoke 可見 P2-410、行動審計事件、Verifier receipt gates、`live write total 0`;7 個 source readback、8 個 audit event template、4 個 low / medium event、3 個 high-risk event、1 個 critical event、2 個 report gap event、2 個 Telegram event、5 個 verifier receipt gate、48 個 required audit field、23 個 blocked runtime action;audit DB / timeline / KM / PlayBook trust / Gateway / Telegram / Bot API / production write 全部 `0` | | 11 | P2-411 | P1 | Owner acceptance / Agent Event Bus / RAG proposal no-write 基線 | OpenClaw + Hermes + NemoTron | 正式驗證完成 | `ai_agent_action_owner_acceptance_event_bus_v1` schema / snapshot / API / tests / governance UI 已正式讀回;6 條 owner acceptance lane、6 個 handoff event template、4 個 RAG memory proposal、6 個 verifier gate、38 個 required owner field、16 個 blocked runtime action;owner response received / accepted、event bus publish、KM / PlayBook trust write、Gateway queue、Telegram send、Bot API、worker dispatch、production write 全部 `0` | | 12 | P2-412 | P1 | 市場主流 AI Agent 定期評估 | Market + OpenClaw | 正式驗證完成 | `889b7b42` / deploy marker `4b18a3d8` 已正式部署,後續最新 deploy marker `1969b552` 仍持續讀回;2026-06-26 已刷新 Agent market watch / integration review / discovery / promotion / governance snapshot / AI technology watch / readback snapshot;Agent 市場側 13 候選 / 36 來源 / 5 changed / 5 blocked / source failure 0;AI 技術側 21 技術 / 52 來源 / 5 changed / 5 review queue / source failure 0;Production API、desktop `1440x1000`、mobile `390x844` 均可見高優先市場審核佇列與官方 primary-source 對齊;所有 SDK install / paid API / provider switch / Telegram live send / host write / OpenClaw replacement 仍為 `0 / false` | -| 13 | P2-413 | P1 | AI Agent / 套件 / 工具 / 服務 / 主機版本生命週期 | DevOps + NemoTron | 待辦 | 版本 inventory、release diff、升級建議、PR 草稿 lane、rollback plan | +| 13 | P2-413 | P1 | AI Agent / 套件 / 工具 / 服務 / 主機版本生命週期 | DevOps + NemoTron | 本地驗證完成,待正式讀回 | `ai_agent_version_lifecycle_update_proposal_v1` 已建立 12 個版本 domain、12 個更新提案、6 種 cadence、9 個 owner gate、API、治理頁 P2-413 卡片、rollback / validation plan;package upgrade / lockfile / host / K3s / image / workflow / PR / Telegram / production / provider switch / OpenClaw replacement 全部 `0 / false` | | 14 | P2-414 | P1 | MCP tool registry / capability attestation | Security + DevOps | 待辦 | 工具能力、風險、scope、owner、consent、blocked actions 可讀 | | 15 | P2-415 | P1 | RAG / KM / PlayBook 成長閉環 | Hermes + OpenClaw | 待辦 | memory type、retention、trust update proposal、negative reinforcement | | 16 | P2-416 | P1 | OTel GenAI / Agent / MCP spans | SRE + DevOps | 待辦 | trace_id 串 Agent、LLM、tool、MCP、Telegram、approval | @@ -161,6 +161,7 @@ | P2-410 AI Agent action audit ledger | 100%(正式驗證完成) | 已把 AI Agent 分類、拒收、no-send preview、high-risk pause、critical rejection 與 result route blocked 固定成 immutable audit event template 與 verifier receipt gate;production API 已讀回,治理頁 action audit projection 已正式驗證;下一步是 P2-411 Event Bus | `ai_agent_action_audit_ledger_v1` schema、`docs/evaluations/ai_agent_action_audit_ledger_2026-06-19.json`、`GET /api/v1/agents/agent-action-audit-ledger`;API deploy marker `38e60192`、UI deploy marker `7a9e1cfd`;7 個 source readback、8 個 audit event template、4 個 low / medium event、3 個 high-risk event、1 個 critical event、2 個 report gap event、2 個 Telegram event、5 個 verifier receipt gate、48 個 required audit field、23 個 blocked runtime action;本地 API/service regression `11 passed`,P2-409 + P2-410 regression `24 passed`;production API readback `HTTP 200`;desktop / mobile governance smoke 可見 P2-410、行動審計事件、Verifier receipt gates、`live write total 0`;console error `0`、水平溢位 `false`、工作視窗片語 `0`;audit DB / timeline / KM / PlayBook trust / Gateway / Telegram / Bot API / production write 全部 `0 / false` | | P2-411 Owner acceptance / Agent Event Bus / RAG proposal no-write 基線 | 100%(正式驗證完成) | 已把 P2-409 高風險 queue、P2-410 action audit ledger、12-Agent War Room 與 communication learning contract 收斂成 owner acceptance lane、handoff event template、RAG memory proposal 與 verifier gate;正式 production API / governance UI 已讀回;event bus publish / RAG write / Telegram send 尚未開 | `ai_agent_action_owner_acceptance_event_bus_v1` schema、`docs/evaluations/ai_agent_action_owner_acceptance_event_bus_2026-06-19.json`、`GET /api/v1/agents/agent-action-owner-acceptance-event-bus`;6 條 owner acceptance lane、6 個 handoff event template、4 個 RAG memory proposal、6 個 verifier gate、38 個 required owner field、16 個 blocked runtime action;owner response received / accepted、external response ingested、event bus publish、audit DB / timeline / KM / PlayBook trust write、Gateway queue、Telegram send、Bot API、worker dispatch、receipt / production write、secret、paid API、host、kubectl、destructive 全部 `0 / false` | | P2-412 市場主流 AI Agent 定期評估 | 100%(本地驗證完成,待正式讀回) | 已把「用市場主流評估數據說話」落成 read-only pipeline:AI Agent market watch、integration review、discovery review / classification、promotion review、governance snapshot、AI technology watch、market radar readback 與 technology radar readback;治理頁 `agent-market` 新增高優先審核佇列與官方 primary-source 對齊,可看到 OpenAI / NVIDIA / LangGraph / MCP / A2A / OpenTelemetry GenAI 對 AWOOOI 的專業分工與 Gate | `docs/evaluations/agent_market_watch_report_2026-06-26.json`:13 候選 / 36 來源 / 5 changed / failure 0;`docs/evaluations/agent_market_governance_snapshot_2026-06-26.json`:5 blocked / replacement approved 0;`docs/evaluations/ai_technology_watch_report_2026-06-26.json`:21 技術 / 52 來源 / 5 changed / 5 review queue / failure 0;`docs/operations/ai-agent-market-radar-readback.snapshot.json` 與 `docs/operations/ai-technology-radar-readback.snapshot.json` 已更新;SDK install、paid API、provider switch、Telegram live send、Bot API、host write、production routing、OpenClaw replacement 全部 `0 / false` | +| P2-413 AI Agent / 套件 / 工具 / 服務 / 主機版本生命週期 | 100%(本地驗證完成,待正式讀回) | 已把 AI Agent、Python、pnpm、container、host OS、K3s、stateful、observability、Telegram、MCP/RAG、Gitea runner、Backup/DR 統一成 no-write 更新提案 queue;OpenClaw / Hermes / Nemotron 只做評估、排序、批准包、validation plan 與 rollback plan | `ai_agent_version_lifecycle_update_proposal_v1`、`docs/evaluations/ai_agent_version_lifecycle_update_proposal_2026-06-26.json`、`GET /api/v1/agents/agent-version-lifecycle-update-proposal`、governance `automation-inventory` P2-413 卡片;12 個版本 domain、12 個更新提案、6 種 cadence、9 個 owner gate、4 個 critical、5 個 high、29 個 false runtime boundary;auto execution / package upgrade / lockfile / host / K3s / image / workflow / PR / Telegram / production / provider switch / OpenClaw replacement 全部 `0 / false` | | Owner response 預檢與拒收邊界 | 100% | P2-143 已完成正式部署與 production readback;承接 P2-141 input prep 與 P2-142 War Room,只建立 owner / verifier / rollback / maintenance / live-apply 五類外部回覆的 intake 預檢、必填欄位與拒收規則;正式 owner response 尚未收到、未接受、未寫入 | `ai_agent_result_capture_release_decision_owner_response_preflight_v1`、`GET /api/v1/agents/agent-result-capture-release-decision-owner-response-preflight`、feature commit `755b0a8d`、deploy marker `667d6329`、Gitea code-review `2961` / CD `2960` success、5 個 response intake lane、18 個 required owner field、6 個 validation check、6 個 rejection guard、5 個 operator action;owner response received / accepted / redacted payload / reviewer queue / Gateway / Telegram / Bot API / production write / secret read / destructive operation 全為 `0` | | Owner response 回讀狀態 | 100% | P2-144 已完成正式部署與 production readback;承接 P2-143 preflight,只讀回五類外部回覆仍未收到、未接受、未拒絕、未保存 | `ai_agent_result_capture_release_decision_owner_response_readback_v1`、`GET /api/v1/agents/agent-result-capture-release-decision-owner-response-readback`、feature commit `8795f100`、deploy marker `ac938037`、Gitea code-review `2965` / CD `2964` success、5 個 response readback lane、18 個 required owner field、6 個 readback validation check、6 個 readback rejection guard、5 個 operator action、waiting external response `5`、no external response received `5`;owner response received / accepted / redacted payload / reviewer queue / Gateway / Telegram / Bot API / production write / secret read / destructive operation 全為 `0` | | 本工作清單與分析報告 | 100% | 已完成 | 本 MD 文件 | diff --git a/docs/evaluations/ai_agent_version_lifecycle_update_proposal_2026-06-26.json b/docs/evaluations/ai_agent_version_lifecycle_update_proposal_2026-06-26.json new file mode 100644 index 000000000..e374a0ac0 --- /dev/null +++ b/docs/evaluations/ai_agent_version_lifecycle_update_proposal_2026-06-26.json @@ -0,0 +1,832 @@ +{ + "schema_version": "ai_agent_version_lifecycle_update_proposal_v1", + "generated_at": "2026-06-26T18:20:00+08:00", + "program_status": { + "overall_completion_percent": 78, + "current_priority": "P2", + "current_task_id": "P2-413", + "next_task_id": "P2-414", + "read_only_mode": true, + "runtime_authority": "version_lifecycle_update_proposal_only_no_write_or_upgrade", + "status_note": "P2-413 將 AI Agent、套件、工具、服務、主機、K3s、stateful、Telegram 與 MCP/RAG 的版本生命週期統一整理成更新提案佇列。Agent 可主動分析、排序、產生批准包與回滾驗證計畫;實際升級、外查、PR、排程、Telegram 發送、主機操作與生產路由變更仍全部關閉。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json", + "docs/evaluations/ai_agent_host_stateful_version_inventory_2026-06-11.json", + "docs/evaluations/dependency_supply_chain_drift_monitor_2026-06-18.json", + "docs/evaluations/ai_agent_market_radar_readback_2026-06-26.json", + "docs/evaluations/ai_technology_radar_readback_2026-06-26.json", + "docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md" + ], + "agent_roles": [ + { + "agent_id": "openclaw", + "role": "仲裁者", + "responsibility": "高風險版本變更、主機/K3s/stateful 維護窗、OpenClaw challenger 評估、回滾與最終 gate 判斷。" + }, + { + "agent_id": "hermes", + "role": "營運與知識執行者", + "responsibility": "套件/CI/觀測/Telegram 版本差異整理、報告化、runbook 草稿、批准包欄位完整性。" + }, + { + "agent_id": "nemotron", + "role": "AI 技術評測執行者", + "responsibility": "AI Agent/模型/SDK/MCP/RAG 候選的市場資料摘要、離線 replay 評估設計、schema 與工具鏈兼容性檢查。" + } + ], + "lifecycle_domains": [ + { + "domain_id": "ai_agents_models", + "display_name": "AI Agent / 模型 / SDK", + "owner_agent": "nemotron", + "risk_tier": "high", + "cadence": "weekly_primary_source + triggered_on_major_release", + "decision_policy": "市場 scorecard、replay、shadow、canary、成本、安全與可觀測性證據不足時只保留提案。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "backend_python_packages", + "display_name": "FastAPI / Python 套件", + "owner_agent": "hermes", + "risk_tier": "high", + "cadence": "daily_repo_manifest + weekly_primary_source", + "decision_policy": "只產生 dependency approval packet;不得寫 lockfile、不得安裝或升級。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "frontend_javascript_packages", + "display_name": "Next.js / pnpm 套件", + "owner_agent": "hermes", + "risk_tier": "medium", + "cadence": "daily_repo_manifest + weekly_primary_source", + "decision_policy": "只整理 UI/runtime 相容性、lockfile 差異與煙測計畫;不得改 lockfile。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "container_images", + "display_name": "Docker base image / image digest", + "owner_agent": "openclaw", + "risk_tier": "high", + "cadence": "weekly_digest_review + triggered_on_security", + "decision_policy": "只產 SBOM/digest pin 提案;不得 pull/build/push image。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "host_os_packages", + "display_name": "主機 OS / kernel / Nginx / SSH", + "owner_agent": "openclaw", + "risk_tier": "critical", + "cadence": "monthly_maintenance_review + triggered_on_security", + "decision_policy": "必須先有維護窗、備份、rollback owner、smoke plan;不得執行主機指令。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "k3s_kubernetes_components", + "display_name": "K3s / Kubernetes / CNI / Ingress", + "owner_agent": "openclaw", + "risk_tier": "critical", + "cadence": "monthly_skew_policy_review + triggered_on_eol", + "decision_policy": "先做 version skew 與節點維護窗批准包;不得 kubectl、drain、restart。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "stateful_services", + "display_name": "PostgreSQL / Redis / MinIO / Harbor / Gitea", + "owner_agent": "openclaw", + "risk_tier": "critical", + "cadence": "monthly_stateful_review + triggered_on_security", + "decision_policy": "任何更新前必須證明備份新鮮、restore drill 與資料相容性;不得 restart/migration。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "observability_stack", + "display_name": "Prometheus / Alertmanager / Grafana / OTEL / Sentry", + "owner_agent": "hermes", + "risk_tier": "medium", + "cadence": "weekly_freshness + monthly_upgrade_review", + "decision_policy": "只提出 route/receiver/collector 相容性矩陣;不得寫告警路由。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "telegram_bot_gateway", + "display_name": "Telegram Bot / Gateway / digest policy", + "owner_agent": "hermes", + "risk_tier": "high", + "cadence": "weekly_policy_review + triggered_on_delivery_failure", + "decision_policy": "只產 no-send digest 與收斂策略;不得直送 Bot 或寫 Gateway queue。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "mcp_rag_tool_registry", + "display_name": "MCP / RAG / tool registry", + "owner_agent": "nemotron", + "risk_tier": "medium", + "cadence": "weekly_contract_review + triggered_on_tool_release", + "decision_policy": "只整理工具能力、資料保留、redaction 與審核需求;不得啟用新工具或外部服務。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "ci_cd_runner_tools", + "display_name": "Gitea Actions / runner / deploy tooling", + "owner_agent": "hermes", + "risk_tier": "high", + "cadence": "weekly_runner_health + triggered_on_ci_failure", + "decision_policy": "只產 workflow/runner owner packet;不得修改 workflow 或自動 merge。", + "current_authority": "L2_approval_package_only" + }, + { + "domain_id": "backup_dr_tooling", + "display_name": "Backup / DR / restore tooling", + "owner_agent": "openclaw", + "risk_tier": "critical", + "cadence": "weekly_backup_freshness + monthly_restore_readiness", + "decision_policy": "只整理 restore drill 與 escrow readiness;不得刪備份、restore 或 prune。", + "current_authority": "L2_approval_package_only" + } + ], + "update_proposals": [ + { + "proposal_id": "ai_agent_market_primary_source_radar", + "domain_id": "ai_agents_models", + "display_name": "AI Agent 市場主流版本雷達", + "owner_agent": "nemotron", + "priority": "P2", + "risk_tier": "high", + "status": "proposal_ready_owner_review_required", + "summary": "持續把 OpenClaw、Hermes、NemoTron 與市場主流 Agent/SDK/MCP/A2A 能力放入 scorecard;只產生候選與差距,不切換 provider。", + "evidence_refs": [ + "docs/evaluations/ai_agent_market_radar_readback_2026-06-26.json", + "docs/evaluations/ai_technology_radar_readback_2026-06-26.json" + ], + "approval_gate": "market_replay_shadow_canary_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "primary source freshness readback", + "candidate scorecard replay fixture", + "成本/延遲/安全/可觀測性欄位完整性檢查" + ], + "rollback_plan": [ + "維持 incumbent route", + "撤回候選標籤", + "保留舊 scorecard 作為比較基線" + ], + "blocked_runtime_actions": [ + "provider route switch", + "paid API call", + "OpenClaw replacement" + ], + "telegram_policy": "action_required_digest_draft_only" + }, + { + "proposal_id": "openclaw_challenger_replay_bench", + "domain_id": "ai_agents_models", + "display_name": "OpenClaw challenger replay 評測台", + "owner_agent": "openclaw", + "priority": "P1", + "risk_tier": "critical", + "status": "blocked_until_replay_shadow_canary_evidence", + "summary": "建立可讓 NemoTron 或其他 challenger 用相同任務集比較仲裁品質的 replay 評測台;未完成 shadow/canary 前不得替換 OpenClaw。", + "evidence_refs": [ + "docs/HARD_RULES.md", + "docs/evaluations/ai_provider_route_matrix_2026-06-04.json" + ], + "approval_gate": "market_replay_shadow_canary_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "sanitized historical task replay", + "shadow decision disagreement review", + "canary stop condition table" + ], + "rollback_plan": [ + "OpenClaw remains arbitration default", + "disable challenger route flag", + "archive failed challenger scorecard" + ], + "blocked_runtime_actions": [ + "OpenClaw replacement", + "production routing", + "runtime agent arbitration switch" + ], + "telegram_policy": "critical_owner_review_draft_only" + }, + { + "proposal_id": "python_dependency_authority_alignment", + "domain_id": "backend_python_packages", + "display_name": "Python dependency authority 對齊", + "owner_agent": "hermes", + "priority": "P2", + "risk_tier": "high", + "status": "action_required_dependency_packet", + "summary": "把 API Python manifest、套件風險、測試矩陣與 rollback plan 收成一份 owner packet;本階段不安裝、不升級。", + "evidence_refs": [ + "docs/evaluations/dependency_supply_chain_drift_monitor_2026-06-18.json", + "apps/api/pyproject.toml" + ], + "approval_gate": "dependency_upgrade_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "pytest target matrix", + "API smoke plan", + "dependency conflict review" + ], + "rollback_plan": [ + "restore previous lock snapshot", + "revert dependency PR branch", + "rerun API smoke" + ], + "blocked_runtime_actions": [ + "package upgrade", + "lockfile write", + "workflow trigger" + ], + "telegram_policy": "failure_or_owner_action_draft_only" + }, + { + "proposal_id": "frontend_pnpm_freshness_plan", + "domain_id": "frontend_javascript_packages", + "display_name": "前端 pnpm / Next.js 新鮮度計畫", + "owner_agent": "hermes", + "priority": "P3", + "risk_tier": "medium", + "status": "proposal_ready_owner_review_required", + "summary": "整理前端套件、i18n、瀏覽器煙測與 build/typecheck gate,低中風險也先形成可審核草案。", + "evidence_refs": [ + "apps/web/package.json", + "apps/web/pnpm-lock.yaml", + "docs/evaluations/dependency_supply_chain_drift_monitor_2026-06-18.json" + ], + "approval_gate": "dependency_upgrade_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "pnpm typecheck", + "eslint target files", + "desktop/mobile governance smoke" + ], + "rollback_plan": [ + "revert package update branch", + "restore previous lockfile", + "re-run production smoke after deploy marker" + ], + "blocked_runtime_actions": [ + "package upgrade", + "lockfile write", + "auto merge" + ], + "telegram_policy": "weekly_digest_draft_only" + }, + { + "proposal_id": "container_digest_sbom_pin_packet", + "domain_id": "container_images", + "display_name": "Container digest / SBOM pin 批准包", + "owner_agent": "openclaw", + "priority": "P2", + "risk_tier": "high", + "status": "blocked_until_sbom_and_image_gate", + "summary": "為 base image 與 runtime image 建立 digest pin、SBOM、漏洞摘要與部署煙測計畫;不 pull/build/push image。", + "evidence_refs": [ + "docs/evaluations/dependency_supply_chain_drift_monitor_2026-06-18.json", + "docs/evaluations/docker_build_surface_inventory_2026-06-04.json" + ], + "approval_gate": "container_sbom_digest_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "SBOM source plan", + "image digest diff review", + "deployment smoke plan" + ], + "rollback_plan": [ + "retain previous image digest", + "revert manifest proposal", + "block rollout until smoke passes" + ], + "blocked_runtime_actions": [ + "image pull", + "docker build", + "registry push" + ], + "telegram_policy": "critical_image_digest_draft_only" + }, + { + "proposal_id": "k3s_skew_maintenance_window_packet", + "domain_id": "k3s_kubernetes_components", + "display_name": "K3s version skew 維護窗批准包", + "owner_agent": "openclaw", + "priority": "P1", + "risk_tier": "critical", + "status": "blocked_until_maintenance_window", + "summary": "將 K3s/Kubernetes skew policy、節點順序、備援與煙測列成批准包;不得 kubectl、drain、restart。", + "evidence_refs": [ + "docs/evaluations/ai_agent_host_stateful_version_inventory_2026-06-11.json", + "https://kubernetes.io/releases/version-skew-policy/" + ], + "approval_gate": "k3s_version_skew_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "version skew table", + "node-by-node maintenance sequence", + "public route smoke plan" + ], + "rollback_plan": [ + "pause node sequence", + "restore previous control-plane state", + "run post-check readback" + ], + "blocked_runtime_actions": [ + "kubectl command", + "node drain", + "k3s upgrade" + ], + "telegram_policy": "critical_owner_review_draft_only" + }, + { + "proposal_id": "host_os_security_maintenance_packet", + "domain_id": "host_os_packages", + "display_name": "Host OS 安全維護窗批准包", + "owner_agent": "openclaw", + "priority": "P1", + "risk_tier": "critical", + "status": "blocked_until_host_maintenance_window", + "summary": "把 OS/kernel/Nginx/OpenSSH 更新變成維護窗提案;本階段只用既有 inventory,不 SSH、不 apt、不 reboot。", + "evidence_refs": [ + "docs/evaluations/ai_agent_host_stateful_version_inventory_2026-06-11.json", + "docs/runbooks/K3S-OPTIMIZATION-RUNBOOK.md" + ], + "approval_gate": "host_maintenance_window_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "host impact matrix", + "pre-change backup check", + "post-change service smoke plan" + ], + "rollback_plan": [ + "maintenance abort condition", + "service restore sequence", + "owner communication plan" + ], + "blocked_runtime_actions": [ + "host command", + "os package upgrade", + "reboot" + ], + "telegram_policy": "critical_owner_review_draft_only" + }, + { + "proposal_id": "stateful_backup_first_upgrade_packet", + "domain_id": "stateful_services", + "display_name": "Stateful backup-first 升級批准包", + "owner_agent": "openclaw", + "priority": "P1", + "risk_tier": "critical", + "status": "blocked_until_backup_and_restore_evidence", + "summary": "PostgreSQL/Redis/MinIO/Harbor/Gitea 更新前先要求備份新鮮度、restore drill 與資料相容性;不 restart、不 migration。", + "evidence_refs": [ + "docs/evaluations/ai_agent_host_stateful_version_inventory_2026-06-11.json", + "docs/evaluations/backup_dr_readiness_matrix_2026-06-04.json" + ], + "approval_gate": "stateful_backup_restore_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "backup freshness readback", + "restore drill owner packet", + "data compatibility smoke" + ], + "rollback_plan": [ + "restore snapshot selection", + "service-level rollback owner", + "read-only consistency check" + ], + "blocked_runtime_actions": [ + "stateful restart", + "database migration", + "restore execution" + ], + "telegram_policy": "critical_owner_review_draft_only" + }, + { + "proposal_id": "observability_agentops_stack_freshness", + "domain_id": "observability_stack", + "display_name": "AgentOps 觀測堆疊新鮮度", + "owner_agent": "hermes", + "priority": "P3", + "risk_tier": "medium", + "status": "proposal_ready_owner_review_required", + "summary": "追蹤 Prometheus、Alertmanager、Grafana、OTEL、Sentry 與 GenAI telemetry 更新,只產相容性矩陣與告警降噪提案。", + "evidence_refs": [ + "docs/evaluations/observability_contract_matrix_2026-06-04.json", + "docs/evaluations/service_health_gap_matrix_2026-06-04.json" + ], + "approval_gate": "workflow_runner_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "alert route compatibility matrix", + "dashboard readback smoke", + "noise reduction acceptance criteria" + ], + "rollback_plan": [ + "retain previous alert route", + "disable candidate dashboard flag", + "restore previous receiver policy" + ], + "blocked_runtime_actions": [ + "alert route write", + "workflow write", + "production write" + ], + "telegram_policy": "failure_only_digest_draft" + }, + { + "proposal_id": "telegram_bot_gateway_policy_refresh", + "domain_id": "telegram_bot_gateway", + "display_name": "Telegram Bot / Gateway policy refresh", + "owner_agent": "hermes", + "priority": "P2", + "risk_tier": "high", + "status": "blocked_until_no_send_receipt_gate", + "summary": "把日報、週報、月報與 action-required 告警收斂成 no-send digest、receipt readback 與 owner review;不得實發 Telegram。", + "evidence_refs": [ + "docs/evaluations/ai_agent_telegram_action_required_digest_policy_2026-06-04.json", + "docs/evaluations/ai_agent_report_live_delivery_approval_package_2026-06-04.json" + ], + "approval_gate": "telegram_gateway_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "sanitized message preview", + "dedup key review", + "receipt readback dry-run" + ], + "rollback_plan": [ + "mute candidate digest type", + "restore previous no-send policy", + "owner-visible failure receipt" + ], + "blocked_runtime_actions": [ + "Telegram direct send", + "Gateway queue write", + "Bot API call" + ], + "telegram_policy": "no_send_owner_review_required" + }, + { + "proposal_id": "mcp_rag_tool_registry_freshness", + "domain_id": "mcp_rag_tool_registry", + "display_name": "MCP / RAG / tool registry 新鮮度", + "owner_agent": "nemotron", + "priority": "P3", + "risk_tier": "medium", + "status": "proposal_ready_owner_review_required", + "summary": "整理 MCP server、RAG memory、tool permission 與 redaction policy 版本差異;不得啟用新外部工具或寫入記憶。", + "evidence_refs": [ + "docs/evaluations/ai_agent_proactive_operations_contract_2026-06-11.json", + "docs/evaluations/ai_agent_interaction_learning_proof_2026-06-04.json" + ], + "approval_gate": "mcp_rag_tool_registry_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "tool permission matrix", + "RAG retention and redaction review", + "sandbox replay for tool-call changes" + ], + "rollback_plan": [ + "remove candidate tool from registry draft", + "retain previous memory contract", + "mark replay pack rejected" + ], + "blocked_runtime_actions": [ + "new external tool activation", + "memory write", + "paid external service" + ], + "telegram_policy": "weekly_digest_draft_only" + }, + { + "proposal_id": "gitea_runner_deploy_tooling_refresh", + "domain_id": "ci_cd_runner_tools", + "display_name": "Gitea runner / deploy tooling refresh", + "owner_agent": "hermes", + "priority": "P2", + "risk_tier": "high", + "status": "blocked_until_workflow_owner_packet", + "summary": "整理 Gitea runner、deploy marker、workflow attestation 與 smoke gate 更新提案;不得改 workflow、建立 PR 或 auto merge。", + "evidence_refs": [ + "docs/evaluations/gitea_workflow_runner_health_2026-06-04.json", + "docs/LOGBOOK.md" + ], + "approval_gate": "workflow_runner_owner_review", + "requires_owner_approval": true, + "direct_update_allowed": false, + "auto_execution_allowed": false, + "validation_plan": [ + "runner attestation readback", + "deploy marker verification plan", + "public smoke route list" + ], + "rollback_plan": [ + "keep previous workflow", + "pause candidate deploy marker", + "manual owner review before merge" + ], + "blocked_runtime_actions": [ + "workflow write", + "PR creation", + "auto merge" + ], + "telegram_policy": "deployment_failure_digest_draft_only" + } + ], + "cadence_matrix": [ + { + "cadence_id": "daily_repo_manifest_readback", + "frequency": "daily", + "scope": "repo-only manifests and committed snapshots", + "allowed_now": true, + "owner_agent": "hermes", + "output": "只讀差異摘要與過期來源標記" + }, + { + "cadence_id": "weekly_primary_source_market_review", + "frequency": "weekly", + "scope": "AI Agent / SDK / MCP / RAG primary source review", + "allowed_now": false, + "owner_agent": "nemotron", + "output": "外部來源批准包,未批准不得 live lookup" + }, + { + "cadence_id": "weekly_dependency_supply_chain_review", + "frequency": "weekly", + "scope": "dependency, image, SBOM, license, CVE proposal", + "allowed_now": false, + "owner_agent": "hermes", + "output": "dependency owner packet" + }, + { + "cadence_id": "monthly_host_k3s_maintenance_review", + "frequency": "monthly", + "scope": "host OS, kernel, K3s, Kubernetes skew", + "allowed_now": false, + "owner_agent": "openclaw", + "output": "maintenance window proposal" + }, + { + "cadence_id": "monthly_stateful_backup_restore_review", + "frequency": "monthly", + "scope": "PostgreSQL, Redis, MinIO, Harbor, Gitea, backup/restore", + "allowed_now": false, + "owner_agent": "openclaw", + "output": "backup-first approval package" + }, + { + "cadence_id": "triggered_critical_security_or_eol_review", + "frequency": "triggered", + "scope": "critical vulnerability, EOL, runner failure, delivery failure", + "allowed_now": false, + "owner_agent": "openclaw", + "output": "urgent owner review packet" + } + ], + "approval_gate_matrix": [ + { + "gate_id": "market_replay_shadow_canary_review", + "risk_tier": "critical", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "market scorecard", + "sanitized replay fixture", + "shadow/canary stop conditions", + "cost and latency comparison" + ] + }, + { + "gate_id": "dependency_upgrade_owner_review", + "risk_tier": "high", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "manifest diff", + "test matrix", + "rollback branch plan" + ] + }, + { + "gate_id": "container_sbom_digest_owner_review", + "risk_tier": "high", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "SBOM plan", + "digest diff", + "deployment smoke" + ] + }, + { + "gate_id": "host_maintenance_window_owner_review", + "risk_tier": "critical", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "maintenance window", + "affected hosts", + "rollback owner", + "service smoke" + ] + }, + { + "gate_id": "k3s_version_skew_owner_review", + "risk_tier": "critical", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "version skew policy", + "node sequence", + "cluster health readback" + ] + }, + { + "gate_id": "stateful_backup_restore_owner_review", + "risk_tier": "critical", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "backup freshness", + "restore drill", + "data compatibility" + ] + }, + { + "gate_id": "telegram_gateway_owner_review", + "risk_tier": "high", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "sanitized preview", + "dedup key", + "receipt dry-run" + ] + }, + { + "gate_id": "workflow_runner_owner_review", + "risk_tier": "high", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "runner attestation", + "workflow diff", + "deploy smoke plan" + ] + }, + { + "gate_id": "mcp_rag_tool_registry_review", + "risk_tier": "medium", + "owner_approval_required": true, + "auto_execute_allowed": false, + "required_evidence": [ + "tool permission matrix", + "retention policy", + "redaction review" + ] + } + ], + "telegram_digest_contract": { + "status": "draft_only_no_send", + "direct_send_allowed": false, + "gateway_queue_write_allowed": false, + "bot_api_call_allowed": false, + "success_noise_suppression": true, + "draft_outputs": [ + "日報版本候選摘要", + "週報市場與依賴漂移摘要", + "月報維護窗與高風險 gate 摘要", + "action-required owner review 草稿" + ], + "redaction_required": true + }, + "runtime_boundaries": { + "read_only_update_proposal_allowed": true, + "schedule_activation_allowed": false, + "external_market_lookup_allowed": false, + "external_registry_lookup_allowed": false, + "external_cve_lookup_allowed": false, + "package_upgrade_allowed": false, + "lockfile_write_allowed": false, + "host_upgrade_allowed": false, + "os_package_upgrade_allowed": false, + "kernel_upgrade_allowed": false, + "k3s_upgrade_allowed": false, + "kubectl_command_allowed": false, + "node_drain_allowed": false, + "reboot_allowed": false, + "stateful_restart_allowed": false, + "database_migration_allowed": false, + "image_pull_allowed": false, + "docker_build_allowed": false, + "registry_push_allowed": false, + "workflow_write_allowed": false, + "pr_creation_allowed": false, + "auto_merge_allowed": false, + "provider_route_switch_allowed": false, + "openclaw_replacement_allowed": false, + "paid_api_call_allowed": false, + "secret_read_allowed": false, + "telegram_direct_send_allowed": false, + "telegram_gateway_queue_write_allowed": false, + "production_write_allowed": false, + "conversation_transcript_display_allowed": false + }, + "rollups": { + "domain_count": 12, + "proposal_count": 12, + "cadence_count": 6, + "approval_gate_count": 9, + "read_only_proposal_count": 12, + "approval_required_count": 12, + "critical_candidate_count": 4, + "high_candidate_count": 5, + "false_runtime_boundary_count": 29, + "auto_execution_allowed_count": 0, + "telegram_direct_send_count": 0, + "telegram_gateway_queue_write_count": 0, + "production_write_count": 0, + "update_allowed_count": 0, + "domain_ids": [ + "ai_agents_models", + "backend_python_packages", + "backup_dr_tooling", + "ci_cd_runner_tools", + "container_images", + "frontend_javascript_packages", + "host_os_packages", + "k3s_kubernetes_components", + "mcp_rag_tool_registry", + "observability_stack", + "stateful_services", + "telegram_bot_gateway" + ], + "proposal_ids": [ + "ai_agent_market_primary_source_radar", + "container_digest_sbom_pin_packet", + "frontend_pnpm_freshness_plan", + "gitea_runner_deploy_tooling_refresh", + "host_os_security_maintenance_packet", + "k3s_skew_maintenance_window_packet", + "mcp_rag_tool_registry_freshness", + "observability_agentops_stack_freshness", + "openclaw_challenger_replay_bench", + "python_dependency_authority_alignment", + "stateful_backup_first_upgrade_packet", + "telegram_bot_gateway_policy_refresh" + ] + }, + "next_actions": [ + { + "task_id": "P2-414", + "priority": "P2", + "owner_agent": "hermes", + "summary": "把日報、週報、月報的版本生命週期欄位與 P2-413 proposal queue 對齊。", + "gate": "report_schema_update_no_send" + }, + { + "task_id": "P2-415", + "priority": "P1", + "owner_agent": "openclaw", + "summary": "設計 OpenClaw challenger replay bench 的 sanitized task set 與評分欄位。", + "gate": "market_replay_shadow_canary_review" + }, + { + "task_id": "P2-416", + "priority": "P2", + "owner_agent": "nemotron", + "summary": "把 MCP/RAG/tool registry 的保留、遮罩與權限欄位轉成 owner packet。", + "gate": "mcp_rag_tool_registry_review" + }, + { + "task_id": "P2-417", + "priority": "P1", + "owner_agent": "openclaw", + "summary": "建立 host/K3s/stateful 維護窗批准包的 owner review readback。", + "gate": "maintenance_window_owner_review" + } + ] +}