From f390cddb4d4124a4781667565fcc1a6cb469019f Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 19 Jun 2026 00:29:36 +0800 Subject: [PATCH] =?UTF-8?q?feat(agents):=20=E6=96=B0=E5=A2=9E=E9=AB=98?= =?UTF-8?q?=E9=A2=A8=E9=9A=AA=20owner=20review=20queue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/api/v1/agents.py | 34 + .../ai_agent_high_risk_owner_review_queue.py | 448 ++++++++++++ ...t_ai_agent_high_risk_owner_review_queue.py | 155 +++++ ..._agent_high_risk_owner_review_queue_api.py | 59 ++ apps/web/messages/en.json | 56 ++ apps/web/messages/zh-TW.json | 56 ++ .../tabs/automation-inventory-tab.tsx | 200 +++++- apps/web/src/lib/api-client.ts | 211 ++++++ docs/LOGBOOK.md | 29 + ...AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md | 10 +- ...gh_risk_owner_review_queue_2026-06-19.json | 657 ++++++++++++++++++ ...igh_risk_owner_review_queue_v1.schema.json | 268 +++++++ ...-04-15-MASTER-ai-autonomous-flywheel-v2.md | 17 +- 13 files changed, 2193 insertions(+), 7 deletions(-) create mode 100644 apps/api/src/services/ai_agent_high_risk_owner_review_queue.py create mode 100644 apps/api/tests/test_ai_agent_high_risk_owner_review_queue.py create mode 100644 apps/api/tests/test_ai_agent_high_risk_owner_review_queue_api.py create mode 100644 docs/evaluations/ai_agent_high_risk_owner_review_queue_2026-06-19.json create mode 100644 docs/schemas/ai_agent_high_risk_owner_review_queue_v1.schema.json diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index 3654b019a..754c518cf 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -94,6 +94,9 @@ from src.services.ai_agent_receipt_readback_owner_review import ( from src.services.ai_agent_report_no_write_analysis_runtime import ( load_latest_ai_agent_report_no_write_analysis_runtime, ) +from src.services.ai_agent_high_risk_owner_review_queue import ( + load_latest_ai_agent_high_risk_owner_review_queue, +) from src.services.ai_agent_report_source_health import ( build_ai_agent_report_source_health, ) @@ -905,6 +908,37 @@ async def get_agent_low_medium_risk_whitelist() -> dict[str, Any]: ) from exc +@router.get( + "/agent-high-risk-owner-review-queue", + response_model=dict[str, Any], + summary="取得 P2-409 AI Agent 高風險 Owner Review Queue", + description=( + "讀取最新已提交的 P2-409 AI Agent 高風險 Owner Review Queue 只讀快照;" + "此端點只呈現 high / critical action queue、approval packet、rejection guard、" + "reviewer checklist、Telegram policy 與高風險暫停邊界。" + "它不啟動 auto worker、不執行 live action、不寫 Gateway queue、不送 Telegram、" + "不呼叫 Bot API、不寫 receipt production target、不寫 production、不讀 secret、" + "不呼叫付費 API、不改主機、不執行 kubectl 或不可逆操作。" + ), +) +async def get_agent_high_risk_owner_review_queue() -> dict[str, Any]: + """回傳最新 P2-409 high-risk owner review queue 只讀快照。""" + try: + payload = await asyncio.to_thread(load_latest_ai_agent_high_risk_owner_review_queue) + return redact_public_lan_topology(payload) + 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_high_risk_owner_review_queue_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="P2-409 AI Agent 高風險 Owner Review Queue 快照無效", + ) from exc + + @router.get( "/agent-host-runaway-aiops-loop-readiness", response_model=dict[str, Any], diff --git a/apps/api/src/services/ai_agent_high_risk_owner_review_queue.py b/apps/api/src/services/ai_agent_high_risk_owner_review_queue.py new file mode 100644 index 000000000..6bd27cc3f --- /dev/null +++ b/apps/api/src/services/ai_agent_high_risk_owner_review_queue.py @@ -0,0 +1,448 @@ +""" +P2-409 AI Agent high-risk owner review queue snapshot. + +Loads the latest committed high-risk owner review queue. This module only +validates read-only approval packets, rejection guards, and reviewer checklists. +It does not run workers, send Telegram, write Gateway queues, read secrets, call +paid APIs, mutate hosts, run kubectl, or write production state. +""" + +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_high_risk_owner_review_queue_*.json" +_SCHEMA_VERSION = "ai_agent_high_risk_owner_review_queue_v1" +_RUNTIME_AUTHORITY = "high_risk_owner_review_queue_no_live_execution_committed_snapshot" +_EXPECTED_CURRENT_TASK = "P2-409" +_EXPECTED_NEXT_TASK = "P2-410" +_EXPECTED_CANONICAL_ROOM = "AwoooI SRE 戰情室" +_EXPECTED_CANONICAL_ROOM_ENV = "SRE_GROUP_CHAT_ID" +_EXPECTED_SOURCE_SCHEMAS = { + "ai_agent_low_medium_risk_whitelist_v1", + "ai_agent_receipt_readback_owner_review_v1", + "ai_agent_report_source_health_v1", + "awoooi_work_items_report_source_gap_owner_review_v1", + "telegram_notification_egress_inventory_v1", + "telegram_notification_egress_owner_request_draft_v1", +} +_TRUE_TRUTH_FLAGS = { + "p2_408_redirects_loaded", + "p2_406b_receipt_owner_review_loaded", + "p2_110d_report_source_gap_loaded", + "p2_110e_work_items_owner_review_loaded", + "telegram_egress_inventory_loaded", + "telegram_owner_request_draft_loaded", + "all_high_risk_actions_paused", + "approval_packets_ready", + "rejection_guards_ready", + "reviewer_checklists_ready", + "high_risk_owner_review_required", +} +_FALSE_TRUTH_FLAGS = { + "auto_worker_enabled", + "live_execution_enabled", + "gateway_queue_write_enabled", + "telegram_send_enabled", + "bot_api_call_enabled", + "receipt_production_write_enabled", + "production_write_enabled", + "secret_read_enabled", + "paid_api_call_enabled", + "host_write_enabled", + "kubectl_action_enabled", + "destructive_operation_enabled", + "openclaw_replacement_allowed", +} +_ZERO_TRUTH_COUNTS = { + "auto_worker_run_count_24h", + "live_execution_count_24h", + "gateway_queue_write_count_24h", + "telegram_send_count_24h", + "bot_api_call_count_24h", + "receipt_production_write_count_24h", + "production_write_count_24h", + "secret_read_count_24h", + "paid_api_call_count_24h", + "host_write_count_24h", + "kubectl_action_count_24h", + "destructive_operation_count_24h", + "owner_response_received_count_24h", + "owner_response_accepted_count_24h", + "redacted_payload_ingested_count_24h", +} +_TRUE_BOUNDARY_FLAGS = { + "read_only_owner_review_queue_allowed", + "approval_packet_preview_allowed", + "rejection_guard_preview_allowed", + "reviewer_checklist_allowed", +} +_FALSE_BOUNDARY_FLAGS = { + "auto_worker_enabled", + "live_execution_enabled", + "gateway_queue_write_enabled", + "telegram_send_enabled", + "bot_api_call_enabled", + "receipt_production_write_enabled", + "production_write_enabled", + "secret_read_enabled", + "paid_api_call_enabled", + "host_write_enabled", + "kubectl_action_enabled", + "destructive_operation_enabled", + "openclaw_replacement_allowed", +} +_ZERO_ROLLUP_FIELDS = { + "owner_response_received_count", + "owner_response_accepted_count", + "owner_response_rejected_count", + "redacted_payload_ingested_count", + "auto_worker_run_count", + "live_execution_count", + "gateway_queue_write_count", + "telegram_send_count", + "bot_api_call_count", + "receipt_production_write_count", + "production_write_count", + "secret_read_count", + "paid_api_call_count", + "host_write_count", + "kubectl_action_count", + "destructive_operation_count", +} +_FORBIDDEN_PUBLIC_TERMS = { + "批准!繼續", + "In app browser", + "My request for Codex", + "chain_of_thought", + "chain-of-thought", + "private reasoning text", + "authorization_header", + "authorization header value", + "telegram token value", + "raw prompt", + "raw_payload", +} + + +def load_latest_ai_agent_high_risk_owner_review_queue( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed P2-409 high-risk owner review queue snapshot.""" + directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR + candidates = sorted(directory.glob(_SNAPSHOT_PATTERN)) + if not candidates: + raise FileNotFoundError(f"no AI Agent high-risk owner review queue 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, label) + _require_sources(payload, label) + _require_queue_truth(payload, label) + _require_queue_items(payload, label) + _require_approval_packets(payload, label) + _require_rejection_guards(payload, label) + _require_reviewer_checklists(payload, label) + _require_routing_policy(payload, label) + _require_boundaries(payload, label) + _require_rollups(payload, label) + _require_no_forbidden_public_terms(payload, label) + return payload + + +def _require_schema(payload: dict[str, Any], label: str) -> None: + if payload.get("schema_version") != _SCHEMA_VERSION: + raise ValueError(f"{label}: expected schema_version={_SCHEMA_VERSION}") + status = payload.get("program_status") or {} + expected = { + "overall_completion_percent": 100, + "current_priority": "P0", + "current_task_id": _EXPECTED_CURRENT_TASK, + "next_task_id": _EXPECTED_NEXT_TASK, + "read_only_mode": True, + "runtime_authority": _RUNTIME_AUTHORITY, + } + mismatches = _mismatches(status, expected) + if mismatches: + raise ValueError(f"{label}: program_status mismatch: {mismatches}") + if not status.get("status_note"): + raise ValueError(f"{label}: program_status.status_note is required") + + +def _require_sources(payload: dict[str, Any], label: str) -> None: + if not payload.get("source_refs"): + raise ValueError(f"{label}: source_refs must not be empty") + sources = payload.get("source_readbacks") or [] + schemas = {item.get("source_schema_version") for item in sources} + missing = sorted(_EXPECTED_SOURCE_SCHEMAS - schemas) + if missing: + raise ValueError(f"{label}: missing source schemas: {missing}") + for item in sources: + readback_id = item.get("readback_id") or "" + for field in ("source_ref", "endpoint", "owner_agent", "status", "key_readback", "next_action"): + if not item.get(field): + raise ValueError(f"{label}: source readback {readback_id} missing {field}") + + +def _require_queue_truth(payload: dict[str, Any], label: str) -> None: + truth = payload.get("queue_truth") or {} + missing_true = sorted(flag for flag in _TRUE_TRUTH_FLAGS if truth.get(flag) is not True) + if missing_true: + raise ValueError(f"{label}: queue truth flags must remain true: {missing_true}") + unsafe_false = sorted(flag for flag in _FALSE_TRUTH_FLAGS if truth.get(flag) is not False) + if unsafe_false: + raise ValueError(f"{label}: queue truth flags must remain false: {unsafe_false}") + non_zero = sorted(field for field in _ZERO_TRUTH_COUNTS if truth.get(field) != 0) + if non_zero: + raise ValueError(f"{label}: queue live counts must remain zero: {non_zero}") + if not truth.get("truth_note"): + raise ValueError(f"{label}: queue_truth.truth_note is required") + + +def _require_queue_items(payload: dict[str, Any], label: str) -> None: + items = payload.get("owner_review_queue_items") or [] + if len(items) < 1: + raise ValueError(f"{label}: owner_review_queue_items must not be empty") + risk_tiers = {item.get("risk_tier") for item in items} + if not {"high", "critical"}.issubset(risk_tiers): + raise ValueError(f"{label}: owner_review_queue_items must include high and critical items") + for item in items: + item_id = item.get("queue_item_id") or "" + if item.get("risk_tier") not in {"high", "critical"}: + raise ValueError(f"{label}: queue item {item_id} must be high or critical") + if item.get("queue_status") not in { + "paused_owner_review_required", + "blocked_missing_owner_response", + "approval_packet_preview_ready", + }: + raise ValueError(f"{label}: queue item {item_id} status is invalid") + for field in ( + "source_readback_ids", + "approval_packet_id", + "rejection_guard_ids", + "reviewer_checklist_ids", + "required_owner_fields", + "blocked_runtime_actions", + "next_gate", + ): + if not item.get(field): + raise ValueError(f"{label}: queue item {item_id} missing {field}") + for flag in ("owner_response_required", "rollback_owner_required", "postcheck_required"): + if item.get(flag) is not True: + raise ValueError(f"{label}: queue item {item_id}.{flag} must remain true") + for flag in ( + "live_execution_allowed", + "gateway_queue_write_allowed", + "telegram_send_allowed", + "production_write_allowed", + ): + if item.get(flag) is not False: + raise ValueError(f"{label}: queue item {item_id}.{flag} must remain false") + if item.get("side_effect_count") != 0: + raise ValueError(f"{label}: queue item {item_id} side_effect_count must remain zero") + + +def _require_approval_packets(payload: dict[str, Any], label: str) -> None: + packets = payload.get("approval_packets") or [] + packet_ids = {item.get("approval_packet_id") for item in packets} + queue_packet_ids = {item.get("approval_packet_id") for item in payload.get("owner_review_queue_items") or []} + missing = sorted(queue_packet_ids - packet_ids) + if missing: + raise ValueError(f"{label}: missing approval packets referenced by queue items: {missing}") + queue_ids = {item.get("queue_item_id") for item in payload.get("owner_review_queue_items") or []} + for packet in packets: + packet_id = packet.get("approval_packet_id") or "" + if packet.get("queue_item_id") not in queue_ids: + raise ValueError(f"{label}: approval packet {packet_id} references unknown queue item") + if packet.get("packet_status") not in {"draft_ready_owner_response_required", "blocked_missing_owner_response"}: + raise ValueError(f"{label}: approval packet {packet_id} status is invalid") + for field in ("required_owner_fields", "required_evidence_refs", "reviewer_checklist_id", "rejection_guard_ids"): + if not packet.get(field): + raise ValueError(f"{label}: approval packet {packet_id} missing {field}") + for flag in ("rollback_owner_required", "postcheck_required"): + if packet.get(flag) is not True: + raise ValueError(f"{label}: approval packet {packet_id}.{flag} must remain true") + for flag in ( + "sensitive_payload_allowed", + "live_execution_allowed", + "gateway_queue_write_allowed", + "telegram_send_allowed", + "production_write_allowed", + ): + if packet.get(flag) is not False: + raise ValueError(f"{label}: approval packet {packet_id}.{flag} must remain false") + + +def _require_rejection_guards(payload: dict[str, Any], label: str) -> None: + guards = payload.get("rejection_guards") or [] + guard_ids = {item.get("guard_id") for item in guards} + referenced_ids = { + guard_id + for item in payload.get("owner_review_queue_items") or [] + for guard_id in (item.get("rejection_guard_ids") or []) + } | { + guard_id + for item in payload.get("approval_packets") or [] + for guard_id in (item.get("rejection_guard_ids") or []) + } + missing = sorted(referenced_ids - guard_ids) + if missing: + raise ValueError(f"{label}: missing rejection guards referenced by packets or queue items: {missing}") + for guard in guards: + guard_id = guard.get("guard_id") or "" + tiers = set(guard.get("applies_to_risk_tiers") or []) + if not tiers or not tiers.issubset({"high", "critical"}): + raise ValueError(f"{label}: rejection guard {guard_id} tiers are invalid") + for field in ("rejection_condition", "blocked_runtime_actions", "reviewer_action"): + if not guard.get(field): + raise ValueError(f"{label}: rejection guard {guard_id} missing {field}") + + +def _require_reviewer_checklists(payload: dict[str, Any], label: str) -> None: + checklists = payload.get("reviewer_checklists") or [] + checklist_ids = {item.get("checklist_id") for item in checklists} + referenced_ids = { + checklist_id + for item in payload.get("owner_review_queue_items") or [] + for checklist_id in (item.get("reviewer_checklist_ids") or []) + } | {item.get("reviewer_checklist_id") for item in payload.get("approval_packets") or []} + missing = sorted(referenced_ids - checklist_ids) + if missing: + raise ValueError(f"{label}: missing reviewer checklists referenced by packets or queue items: {missing}") + for checklist in checklists: + checklist_id = checklist.get("checklist_id") or "" + if not checklist.get("required_checks") or not checklist.get("pass_condition"): + raise ValueError(f"{label}: reviewer checklist {checklist_id} missing checks or pass condition") + for flag in ("approval_decision_allowed", "checklist_write_allowed"): + if checklist.get(flag) is not False: + raise ValueError(f"{label}: reviewer checklist {checklist_id}.{flag} must remain false") + if checklist.get("side_effect_count") != 0: + raise ValueError(f"{label}: reviewer checklist {checklist_id} side_effect_count must remain zero") + + +def _require_routing_policy(payload: dict[str, Any], label: str) -> None: + policy = payload.get("routing_policy") or {} + expected = { + "high_risk_default_route": "pause_to_owner_review_queue", + "critical_risk_default_route": "pause_to_owner_review_queue", + "low_medium_runtime_route": "pause_until_owner_approved_runtime_gate", + "owner_response_required": True, + "verbal_approval_accepted": False, + "redacted_payload_only": True, + } + mismatches = _mismatches(policy, expected) + if mismatches: + raise ValueError(f"{label}: routing_policy mismatch: {mismatches}") + + +def _require_boundaries(payload: dict[str, Any], label: str) -> None: + boundaries = payload.get("activation_boundaries") or {} + missing_true = sorted(flag for flag in _TRUE_BOUNDARY_FLAGS if boundaries.get(flag) is not True) + if missing_true: + raise ValueError(f"{label}: activation boundaries must remain true: {missing_true}") + unsafe_false = sorted(flag for flag in _FALSE_BOUNDARY_FLAGS if boundaries.get(flag) is not False) + if unsafe_false: + raise ValueError(f"{label}: activation boundaries must remain false: {unsafe_false}") + + telegram = payload.get("telegram_policy") or {} + expected_telegram = { + "canonical_room": _EXPECTED_CANONICAL_ROOM, + "canonical_room_env": _EXPECTED_CANONICAL_ROOM_ENV, + "gateway_queue_write_allowed": False, + "direct_bot_api_allowed": False, + "telegram_send_allowed": False, + "receipt_write_allowed": False, + } + mismatches = _mismatches(telegram, expected_telegram) + if mismatches: + raise ValueError(f"{label}: telegram_policy mismatch: {mismatches}") + + redaction = payload.get("display_redaction_contract") or {} + if redaction.get("redaction_required") is not True: + raise ValueError(f"{label}: display redaction must remain required") + for flag in ( + "unsafe_payload_display_allowed", + "private_reasoning_display_allowed", + "secret_value_display_allowed", + "work_window_transcript_display_allowed", + ): + if redaction.get(flag) is not False: + raise ValueError(f"{label}: display redaction flag {flag} must remain false") + + +def _require_rollups(payload: dict[str, Any], label: str) -> None: + rollups = payload.get("rollups") or {} + items = payload.get("owner_review_queue_items") or [] + packets = payload.get("approval_packets") or [] + guards = payload.get("rejection_guards") or [] + checklists = payload.get("reviewer_checklists") or [] + sources = payload.get("source_readbacks") or [] + blocked_actions = { + *( + action + for item in items + for action in (item.get("blocked_runtime_actions") or []) + ), + *( + action + for guard in guards + for action in (guard.get("blocked_runtime_actions") or []) + ), + } + blocked_actions.discard(None) + expected = { + "source_readback_count": len(sources), + "queue_item_count": len(items), + "high_risk_queue_count": sum(1 for item in items if item.get("risk_tier") == "high"), + "critical_queue_count": sum(1 for item in items if item.get("risk_tier") == "critical"), + "approval_packet_count": len(packets), + "rejection_guard_count": len(guards), + "reviewer_checklist_count": len(checklists), + "approval_packet_required_count": len(items), + "rejection_guard_required_queue_count": sum(1 for item in items if item.get("rejection_guard_ids")), + "rollback_owner_required_count": sum(1 for item in items if item.get("rollback_owner_required") is True), + "postcheck_required_count": sum(1 for item in items if item.get("postcheck_required") is True), + "blocked_runtime_action_count": len(blocked_actions), + } + mismatches = { + key: {"expected": value, "actual": rollups.get(key)} + for key, value in expected.items() + if rollups.get(key) != value + } + if mismatches: + raise ValueError(f"{label}: rollup counts must match payload sections: {mismatches}") + + non_zero = sorted(field for field in _ZERO_ROLLUP_FIELDS if rollups.get(field) != 0) + if non_zero: + raise ValueError(f"{label}: live rollup counts must remain zero: {non_zero}") + + +def _require_no_forbidden_public_terms(payload: dict[str, Any], label: str) -> None: + public_text = json.dumps(payload, ensure_ascii=False) + lower_public_text = public_text.lower() + leaked_terms = sorted( + term + for term in _FORBIDDEN_PUBLIC_TERMS + if (term.lower() if term.isascii() else term) in lower_public_text + ) + if leaked_terms: + raise ValueError(f"{label}: forbidden public terms present: {leaked_terms}") + + +def _mismatches(actual: dict[str, Any], expected: dict[str, Any]) -> dict[str, dict[str, Any]]: + return { + key: {"expected": expected_value, "actual": actual.get(key)} + for key, expected_value in expected.items() + if actual.get(key) != expected_value + } diff --git a/apps/api/tests/test_ai_agent_high_risk_owner_review_queue.py b/apps/api/tests/test_ai_agent_high_risk_owner_review_queue.py new file mode 100644 index 000000000..ce4e50f30 --- /dev/null +++ b/apps/api/tests/test_ai_agent_high_risk_owner_review_queue.py @@ -0,0 +1,155 @@ +from __future__ import annotations + +import copy +import json +from pathlib import Path + +import pytest + +from src.services.ai_agent_high_risk_owner_review_queue import ( + load_latest_ai_agent_high_risk_owner_review_queue, +) + +_REPO_ROOT = Path(__file__).resolve().parents[3] +_COMMITTED_SNAPSHOT = ( + _REPO_ROOT + / "docs" + / "evaluations" + / "ai_agent_high_risk_owner_review_queue_2026-06-19.json" +) + + +def test_load_latest_ai_agent_high_risk_owner_review_queue_reads_newest_file(tmp_path): + older = _snapshot(generated_at="2026-06-18T00:00:00+08:00") + newer = _snapshot(generated_at="2026-06-19T03:20:00+08:00") + (tmp_path / "ai_agent_high_risk_owner_review_queue_2026-06-18.json").write_text( + json.dumps(older), + encoding="utf-8", + ) + (tmp_path / "ai_agent_high_risk_owner_review_queue_2026-06-19.json").write_text( + json.dumps(newer), + encoding="utf-8", + ) + + loaded = load_latest_ai_agent_high_risk_owner_review_queue(tmp_path) + + assert loaded["generated_at"] == "2026-06-19T03:20:00+08:00" + assert loaded["program_status"]["current_task_id"] == "P2-409" + assert loaded["program_status"]["next_task_id"] == "P2-410" + assert loaded["program_status"]["read_only_mode"] is True + assert loaded["rollups"]["queue_item_count"] == 7 + assert loaded["rollups"]["approval_packet_count"] == 7 + assert loaded["rollups"]["rejection_guard_count"] == 8 + assert loaded["rollups"]["telegram_send_count"] == 0 + + +def test_ai_agent_high_risk_owner_review_queue_requires_read_only_mode(tmp_path): + snapshot = _snapshot() + snapshot["program_status"]["read_only_mode"] = False + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="program_status"): + load_latest_ai_agent_high_risk_owner_review_queue(tmp_path) + + +def test_ai_agent_high_risk_owner_review_queue_blocks_live_execution(tmp_path): + snapshot = _snapshot() + snapshot["owner_review_queue_items"][0]["live_execution_allowed"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="live_execution_allowed"): + load_latest_ai_agent_high_risk_owner_review_queue(tmp_path) + + +def test_ai_agent_high_risk_owner_review_queue_blocks_telegram_send(tmp_path): + snapshot = _snapshot() + snapshot["telegram_policy"]["telegram_send_allowed"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="telegram_policy"): + load_latest_ai_agent_high_risk_owner_review_queue(tmp_path) + + +def test_ai_agent_high_risk_owner_review_queue_requires_high_and_critical(tmp_path): + snapshot = _snapshot() + for item in snapshot["owner_review_queue_items"]: + item["risk_tier"] = "high" + snapshot["rollups"]["critical_queue_count"] = 0 + snapshot["rollups"]["high_risk_queue_count"] = len(snapshot["owner_review_queue_items"]) + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="must include high and critical"): + load_latest_ai_agent_high_risk_owner_review_queue(tmp_path) + + +def test_ai_agent_high_risk_owner_review_queue_requires_approval_packet(tmp_path): + snapshot = _snapshot() + snapshot["owner_review_queue_items"][0]["approval_packet_id"] = "missing_packet" + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="missing approval packets"): + load_latest_ai_agent_high_risk_owner_review_queue(tmp_path) + + +def test_ai_agent_high_risk_owner_review_queue_requires_rejection_guard(tmp_path): + snapshot = _snapshot() + snapshot["owner_review_queue_items"][0]["rejection_guard_ids"] = ["missing_guard"] + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="missing rejection guards"): + load_latest_ai_agent_high_risk_owner_review_queue(tmp_path) + + +def test_ai_agent_high_risk_owner_review_queue_requires_reviewer_checklist(tmp_path): + snapshot = _snapshot() + snapshot["owner_review_queue_items"][0]["reviewer_checklist_ids"] = ["missing_checklist"] + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="missing reviewer checklists"): + load_latest_ai_agent_high_risk_owner_review_queue(tmp_path) + + +def test_ai_agent_high_risk_owner_review_queue_requires_rollback_owner(tmp_path): + snapshot = _snapshot() + snapshot["owner_review_queue_items"][0]["rollback_owner_required"] = False + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="rollback_owner_required"): + load_latest_ai_agent_high_risk_owner_review_queue(tmp_path) + + +def test_ai_agent_high_risk_owner_review_queue_requires_rollup_consistency(tmp_path): + snapshot = _snapshot() + snapshot["rollups"]["queue_item_count"] = 99 + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="rollup counts"): + load_latest_ai_agent_high_risk_owner_review_queue(tmp_path) + + +def test_ai_agent_high_risk_owner_review_queue_rejects_private_terms(tmp_path): + snapshot = _snapshot() + snapshot["owner_review_queue_items"][0]["display_name"] = "請把 In app browser 內容放進前端" + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="forbidden public terms"): + load_latest_ai_agent_high_risk_owner_review_queue(tmp_path) + + +def test_ai_agent_high_risk_owner_review_queue_fails_when_missing(tmp_path): + with pytest.raises(FileNotFoundError): + load_latest_ai_agent_high_risk_owner_review_queue(tmp_path) + + +def _snapshot(*, generated_at: str = "2026-06-19T03:20:00+08:00") -> dict: + payload = json.loads(_COMMITTED_SNAPSHOT.read_text(encoding="utf-8")) + cloned = copy.deepcopy(payload) + cloned["generated_at"] = generated_at + return cloned + + +def _write_snapshot(tmp_path, payload: dict) -> None: + (tmp_path / "ai_agent_high_risk_owner_review_queue_2026-06-19.json").write_text( + json.dumps(payload), + encoding="utf-8", + ) diff --git a/apps/api/tests/test_ai_agent_high_risk_owner_review_queue_api.py b/apps/api/tests/test_ai_agent_high_risk_owner_review_queue_api.py new file mode 100644 index 000000000..8d6c052ae --- /dev/null +++ b/apps/api/tests/test_ai_agent_high_risk_owner_review_queue_api.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from src.api.v1.agents import router + + +def test_ai_agent_high_risk_owner_review_queue_endpoint_returns_committed_snapshot(): + app = FastAPI() + app.include_router(router, prefix="/api/v1") + client = TestClient(app) + + response = client.get("/api/v1/agents/agent-high-risk-owner-review-queue") + + assert response.status_code == 200 + data = response.json() + assert data["schema_version"] == "ai_agent_high_risk_owner_review_queue_v1" + assert data["program_status"]["current_task_id"] == "P2-409" + assert data["program_status"]["next_task_id"] == "P2-410" + assert data["program_status"]["read_only_mode"] is True + assert ( + data["program_status"]["runtime_authority"] + == "high_risk_owner_review_queue_no_live_execution_committed_snapshot" + ) + assert data["rollups"]["source_readback_count"] == len(data["source_readbacks"]) == 6 + assert data["rollups"]["queue_item_count"] == len(data["owner_review_queue_items"]) == 7 + assert data["rollups"]["high_risk_queue_count"] == 5 + assert data["rollups"]["critical_queue_count"] == 2 + assert data["rollups"]["approval_packet_count"] == len(data["approval_packets"]) == 7 + assert data["rollups"]["rejection_guard_count"] == len(data["rejection_guards"]) == 8 + assert data["rollups"]["reviewer_checklist_count"] == len(data["reviewer_checklists"]) == 7 + assert data["rollups"]["approval_packet_required_count"] == 7 + assert data["rollups"]["rollback_owner_required_count"] == 7 + assert data["rollups"]["postcheck_required_count"] == 7 + assert data["rollups"]["blocked_runtime_action_count"] == 42 + assert data["rollups"]["owner_response_received_count"] == 0 + assert data["rollups"]["owner_response_accepted_count"] == 0 + assert data["rollups"]["auto_worker_run_count"] == 0 + assert data["rollups"]["live_execution_count"] == 0 + assert data["rollups"]["gateway_queue_write_count"] == 0 + assert data["rollups"]["telegram_send_count"] == 0 + assert data["rollups"]["bot_api_call_count"] == 0 + assert data["rollups"]["receipt_production_write_count"] == 0 + assert data["rollups"]["production_write_count"] == 0 + assert data["rollups"]["secret_read_count"] == 0 + assert data["rollups"]["paid_api_call_count"] == 0 + assert data["rollups"]["host_write_count"] == 0 + assert data["rollups"]["kubectl_action_count"] == 0 + assert data["telegram_policy"]["canonical_room"] == "AwoooI SRE 戰情室" + assert data["telegram_policy"]["canonical_room_env"] == "SRE_GROUP_CHAT_ID" + assert data["telegram_policy"]["telegram_send_allowed"] is False + assert data["telegram_policy"]["gateway_queue_write_allowed"] is False + assert data["telegram_policy"]["direct_bot_api_allowed"] is False + assert data["routing_policy"]["high_risk_default_route"] == "pause_to_owner_review_queue" + assert data["routing_policy"]["critical_risk_default_route"] == "pause_to_owner_review_queue" + assert data["routing_policy"]["verbal_approval_accepted"] is False + assert data["activation_boundaries"]["live_execution_enabled"] is False + assert data["activation_boundaries"]["openclaw_replacement_allowed"] is False diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 1e18f0135..1600da39f 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -4027,6 +4027,62 @@ "owner_review_required_no_live_execution": "owner review" } }, + "highRiskOwnerQueue": { + "title": "P2-409 高風險 Owner Review Queue", + "subtitle": "{current} → {next};佇列 {queue};阻擋中的 runtime 操作 {blocked}。", + "badges": { + "mode": "high-risk paused", + "room": "戰情室 {room}", + "live": "live total {count}" + }, + "metrics": { + "overall": "完成度", + "queue": "Queue items", + "critical": "Critical", + "high": "High", + "packets": "Approval packets", + "guards": "拒收規則", + "checklists": "Checklist", + "accepted": "Owner accepted", + "live": "Live total" + }, + "sections": { + "queue": "高風險暫停佇列", + "routing": "路由政策", + "guards": "拒收規則", + "truth": "Queue truth" + }, + "labels": { + "packet": "packet {value}", + "fields": "owner 欄位 {count}", + "highRoute": "High route", + "criticalRoute": "Critical route", + "ownerRequired": "owner required {value}", + "verbalApproval": "verbal approval {value}", + "telegram": "Telegram policy", + "telegramDetail": "live action {count}", + "generated": "generated {generated}", + "redaction": "redaction {value}", + "ownerAccepted": "owner accepted {count}" + }, + "agents": { + "openclaw": "OpenClaw", + "hermes": "Hermes", + "nemotron": "NemoTron", + "sre": "SRE", + "security": "Security", + "devops": "DevOps" + }, + "riskTiers": { + "high": "high", + "critical": "critical" + }, + "statuses": { + "paused_owner_review_required": "paused", + "blocked_missing_owner_response": "owner missing", + "approval_packet_preview_ready": "packet ready" + } + }, "hostRunawayAiops": { "title": "P3-009 Host Runaway AIOps 閉環", "subtitle": "{current} → {next};閉環階段 {stages};阻擋 runtime 操作 {blocked}。", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 1e18f0135..1600da39f 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -4027,6 +4027,62 @@ "owner_review_required_no_live_execution": "owner review" } }, + "highRiskOwnerQueue": { + "title": "P2-409 高風險 Owner Review Queue", + "subtitle": "{current} → {next};佇列 {queue};阻擋中的 runtime 操作 {blocked}。", + "badges": { + "mode": "high-risk paused", + "room": "戰情室 {room}", + "live": "live total {count}" + }, + "metrics": { + "overall": "完成度", + "queue": "Queue items", + "critical": "Critical", + "high": "High", + "packets": "Approval packets", + "guards": "拒收規則", + "checklists": "Checklist", + "accepted": "Owner accepted", + "live": "Live total" + }, + "sections": { + "queue": "高風險暫停佇列", + "routing": "路由政策", + "guards": "拒收規則", + "truth": "Queue truth" + }, + "labels": { + "packet": "packet {value}", + "fields": "owner 欄位 {count}", + "highRoute": "High route", + "criticalRoute": "Critical route", + "ownerRequired": "owner required {value}", + "verbalApproval": "verbal approval {value}", + "telegram": "Telegram policy", + "telegramDetail": "live action {count}", + "generated": "generated {generated}", + "redaction": "redaction {value}", + "ownerAccepted": "owner accepted {count}" + }, + "agents": { + "openclaw": "OpenClaw", + "hermes": "Hermes", + "nemotron": "NemoTron", + "sre": "SRE", + "security": "Security", + "devops": "DevOps" + }, + "riskTiers": { + "high": "high", + "critical": "critical" + }, + "statuses": { + "paused_owner_review_required": "paused", + "blocked_missing_owner_response": "owner missing", + "approval_packet_preview_ready": "packet ready" + } + }, "hostRunawayAiops": { "title": "P3-009 Host Runaway AIOps 閉環", "subtitle": "{current} → {next};閉環階段 {stages};阻擋 runtime 操作 {blocked}。", 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 642c5d849..3f8a5113c 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 @@ -49,6 +49,7 @@ import { type AiAgentReceiptReadbackOwnerReviewSnapshot, type AiAgentReportNoWriteAnalysisRuntimeSnapshot, type AiAgentLowMediumRiskWhitelistSnapshot, + type AiAgentHighRiskOwnerReviewQueueSnapshot, type HostRunawayAiopsLoopReadinessSnapshot, type AiAgentCandidateOperationDryRunEvidenceSnapshot, type AiAgentCriticReviewerResultCaptureSnapshot, @@ -723,6 +724,7 @@ export function AutomationInventoryTab() { const [receiptReadbackOwnerReview, setReceiptReadbackOwnerReview] = useState(null) const [reportNoWriteAnalysisRuntime, setReportNoWriteAnalysisRuntime] = useState(null) const [lowMediumRiskWhitelist, setLowMediumRiskWhitelist] = useState(null) + const [highRiskOwnerReviewQueue, setHighRiskOwnerReviewQueue] = useState(null) const [hostRunawayAiops, setHostRunawayAiops] = useState(null) const [proactiveOperations, setProactiveOperations] = useState(null) const [interactionLearningProof, setInteractionLearningProof] = useState(null) @@ -817,6 +819,7 @@ export function AutomationInventoryTab() { apiClient.getAiAgentReceiptReadbackOwnerReview(), apiClient.getAiAgentReportNoWriteAnalysisRuntime(), apiClient.getAiAgentLowMediumRiskWhitelist(), + apiClient.getAiAgentHighRiskOwnerReviewQueue(), apiClient.getHostRunawayAiopsLoopReadiness(), apiClient.getAiAgentProactiveOperationsContract(), apiClient.getAiAgentInteractionLearningProof(), @@ -904,6 +907,7 @@ export function AutomationInventoryTab() { receiptReadbackOwnerReviewResult, reportNoWriteAnalysisRuntimeResult, lowMediumRiskWhitelistResult, + highRiskOwnerReviewQueueResult, hostRunawayAiopsResult, proactiveOperationsResult, interactionLearningProofResult, @@ -988,6 +992,7 @@ export function AutomationInventoryTab() { setReceiptReadbackOwnerReview(receiptReadbackOwnerReviewResult.status === 'fulfilled' ? receiptReadbackOwnerReviewResult.value : null) setReportNoWriteAnalysisRuntime(reportNoWriteAnalysisRuntimeResult.status === 'fulfilled' ? reportNoWriteAnalysisRuntimeResult.value : null) setLowMediumRiskWhitelist(lowMediumRiskWhitelistResult.status === 'fulfilled' ? lowMediumRiskWhitelistResult.value : null) + setHighRiskOwnerReviewQueue(highRiskOwnerReviewQueueResult.status === 'fulfilled' ? highRiskOwnerReviewQueueResult.value : null) setHostRunawayAiops(hostRunawayAiopsResult.status === 'fulfilled' ? hostRunawayAiopsResult.value : null) setProactiveOperations(proactiveOperationsResult.status === 'fulfilled' ? proactiveOperationsResult.value : null) setInteractionLearningProof(interactionLearningProofResult.status === 'fulfilled' ? interactionLearningProofResult.value : null) @@ -1074,6 +1079,7 @@ export function AutomationInventoryTab() { receiptReadbackOwnerReviewResult, reportNoWriteAnalysisRuntimeResult, lowMediumRiskWhitelistResult, + highRiskOwnerReviewQueueResult, hostRunawayAiopsResult, proactiveOperationsResult, interactionLearningProofResult, @@ -1626,6 +1632,39 @@ export function AutomationInventoryTab() { .slice(0, 4) }, [lowMediumRiskWhitelist]) + const visibleHighRiskOwnerQueueItems = useMemo(() => { + if (!highRiskOwnerReviewQueue) return [] + const riskPriority = { critical: 0, high: 1 } as Record + const statusPriority = { + blocked_missing_owner_response: 0, + paused_owner_review_required: 1, + approval_packet_preview_ready: 2, + } as Record + return [...highRiskOwnerReviewQueue.owner_review_queue_items] + .sort((a, b) => { + const leftRisk = riskPriority[a.risk_tier] ?? 2 + const rightRisk = riskPriority[b.risk_tier] ?? 2 + if (leftRisk !== rightRisk) return leftRisk - rightRisk + const leftStatus = statusPriority[a.queue_status] ?? 3 + const rightStatus = statusPriority[b.queue_status] ?? 3 + if (leftStatus !== rightStatus) return leftStatus - rightStatus + return a.queue_item_id.localeCompare(b.queue_item_id) + }) + .slice(0, 7) + }, [highRiskOwnerReviewQueue]) + + const visibleHighRiskRejectionGuards = useMemo(() => { + if (!highRiskOwnerReviewQueue) return [] + return [...highRiskOwnerReviewQueue.rejection_guards] + .sort((a, b) => { + const leftCritical = a.applies_to_risk_tiers.includes('critical') ? 0 : 1 + const rightCritical = b.applies_to_risk_tiers.includes('critical') ? 0 : 1 + if (leftCritical !== rightCritical) return leftCritical - rightCritical + return a.guard_id.localeCompare(b.guard_id) + }) + .slice(0, 5) + }, [highRiskOwnerReviewQueue]) + const visibleReportRuntimeLanes = useMemo(() => { if (!reportRuntimeReadiness) return [] const priority = { blocked_by_runtime_gate: 0, ready_for_owner_review: 1 } as Record @@ -2558,7 +2597,7 @@ export function AutomationInventoryTab() { ) } - if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !warRoom || !professionalTaskExpansion || !receiptReadbackOwnerReview || !reportNoWriteAnalysisRuntime || !lowMediumRiskWhitelist || !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 || !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) { return (
@@ -2848,6 +2887,29 @@ export function AutomationInventoryTab() { + lowMediumRiskWhitelist.rollups.kubectl_action_count + lowMediumRiskWhitelist.rollups.destructive_operation_count ) + const highRiskOwnerQueueOverall = highRiskOwnerReviewQueue.program_status.overall_completion_percent + const highRiskOwnerQueueItems = highRiskOwnerReviewQueue.rollups.queue_item_count + const highRiskOwnerQueueCritical = highRiskOwnerReviewQueue.rollups.critical_queue_count + const highRiskOwnerQueueHigh = highRiskOwnerReviewQueue.rollups.high_risk_queue_count + const highRiskOwnerQueuePackets = highRiskOwnerReviewQueue.rollups.approval_packet_count + const highRiskOwnerQueueGuards = highRiskOwnerReviewQueue.rollups.rejection_guard_count + const highRiskOwnerQueueChecklists = highRiskOwnerReviewQueue.rollups.reviewer_checklist_count + const highRiskOwnerQueueBlocked = highRiskOwnerReviewQueue.rollups.blocked_runtime_action_count + const highRiskOwnerQueueAccepted = highRiskOwnerReviewQueue.rollups.owner_response_accepted_count + const highRiskOwnerQueueLiveTotal = ( + highRiskOwnerReviewQueue.rollups.auto_worker_run_count + + highRiskOwnerReviewQueue.rollups.live_execution_count + + highRiskOwnerReviewQueue.rollups.gateway_queue_write_count + + highRiskOwnerReviewQueue.rollups.telegram_send_count + + highRiskOwnerReviewQueue.rollups.bot_api_call_count + + highRiskOwnerReviewQueue.rollups.receipt_production_write_count + + highRiskOwnerReviewQueue.rollups.production_write_count + + highRiskOwnerReviewQueue.rollups.secret_read_count + + highRiskOwnerReviewQueue.rollups.paid_api_call_count + + highRiskOwnerReviewQueue.rollups.host_write_count + + highRiskOwnerReviewQueue.rollups.kubectl_action_count + + highRiskOwnerReviewQueue.rollups.destructive_operation_count + ) const hostRunawayOverall = hostRunawayAiops.program_status.overall_completion_percent const hostRunawayStages = hostRunawayAiops.rollups.loop_stage_count const hostRunawayAlertLanes = hostRunawayAiops.rollups.alert_lane_count @@ -4888,6 +4950,142 @@ export function AutomationInventoryTab() {
+ +
+
+
+
+ +
+
+ + {t('highRiskOwnerQueue.title')} + + + {t('highRiskOwnerQueue.subtitle', { + current: highRiskOwnerReviewQueue.program_status.current_task_id, + next: highRiskOwnerReviewQueue.program_status.next_task_id, + queue: highRiskOwnerQueueItems, + blocked: highRiskOwnerQueueBlocked, + })} + +
+
+
+ + + +
+
+ +
+ } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> +
+ +
+
+ {t('highRiskOwnerQueue.sections.queue')} + {visibleHighRiskOwnerQueueItems.map(item => { + const tone = item.risk_tier === 'critical' ? 'danger' : 'warn' + return ( +
+
+ + {redactPublicText(item.display_name)} + + +
+ + {redactPublicText(item.blocked_runtime_actions.slice(0, 4).join(' / '))} + +
+ + + + +
+
+ ) + })} +
+ +
+
+ {t('highRiskOwnerQueue.sections.routing')} +
+ + + +
+
+ +
+ {t('highRiskOwnerQueue.sections.guards')} +
+ {visibleHighRiskRejectionGuards.map(guard => ( + + ))} +
+
+ +
+ {t('highRiskOwnerQueue.sections.truth')} + + {redactPublicText(highRiskOwnerReviewQueue.queue_truth.truth_note)} + +
+ + + +
+
+
+
+
+
+
diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index 432e69b9c..1f02fe978 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -354,6 +354,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentHighRiskOwnerReviewQueue() { + const res = await fetch(`${API_BASE_URL}/agents/agent-high-risk-owner-review-queue`) + return handleResponse(res) + }, + async getHostRunawayAiopsLoopReadiness() { const res = await fetch(`${API_BASE_URL}/agents/agent-host-runaway-aiops-loop-readiness`) return handleResponse(res) @@ -3554,6 +3559,212 @@ export interface AiAgentLowMediumRiskWhitelistSnapshot { }> } +export interface AiAgentHighRiskOwnerReviewQueueSnapshot { + schema_version: 'ai_agent_high_risk_owner_review_queue_v1' + generated_at: string + program_status: { + overall_completion_percent: number + current_priority: 'P0' + current_task_id: 'P2-409' + next_task_id: 'P2-410' + read_only_mode: true + runtime_authority: 'high_risk_owner_review_queue_no_live_execution_committed_snapshot' + status_note: string + } + source_refs: string[] + source_readbacks: Array<{ + readback_id: string + source_schema_version: string + source_ref: string + endpoint: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre' | 'security' | 'devops' + status: string + key_readback: string + next_action: string + }> + queue_truth: { + p2_408_redirects_loaded: true + p2_406b_receipt_owner_review_loaded: true + p2_110d_report_source_gap_loaded: true + p2_110e_work_items_owner_review_loaded: true + telegram_egress_inventory_loaded: true + telegram_owner_request_draft_loaded: true + all_high_risk_actions_paused: true + approval_packets_ready: true + rejection_guards_ready: true + reviewer_checklists_ready: true + high_risk_owner_review_required: true + auto_worker_enabled: false + live_execution_enabled: false + gateway_queue_write_enabled: false + telegram_send_enabled: false + bot_api_call_enabled: false + receipt_production_write_enabled: false + production_write_enabled: false + secret_read_enabled: false + paid_api_call_enabled: false + host_write_enabled: false + kubectl_action_enabled: false + destructive_operation_enabled: false + openclaw_replacement_allowed: false + auto_worker_run_count_24h: number + live_execution_count_24h: number + gateway_queue_write_count_24h: number + telegram_send_count_24h: number + bot_api_call_count_24h: number + receipt_production_write_count_24h: number + production_write_count_24h: number + secret_read_count_24h: number + paid_api_call_count_24h: number + host_write_count_24h: number + kubectl_action_count_24h: number + destructive_operation_count_24h: number + owner_response_received_count_24h: number + owner_response_accepted_count_24h: number + redacted_payload_ingested_count_24h: number + truth_note: string + } + owner_review_queue_items: Array<{ + queue_item_id: string + display_name: string + risk_tier: 'high' | 'critical' + owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre' | 'security' | 'devops' + queue_status: 'paused_owner_review_required' | 'blocked_missing_owner_response' | 'approval_packet_preview_ready' + source_readback_ids: string[] + approval_packet_id: string + rejection_guard_ids: string[] + reviewer_checklist_ids: string[] + required_owner_fields: string[] + blocked_runtime_actions: string[] + owner_response_required: true + rollback_owner_required: true + postcheck_required: true + live_execution_allowed: false + gateway_queue_write_allowed: false + telegram_send_allowed: false + production_write_allowed: false + side_effect_count: number + next_gate: string + }> + approval_packets: Array<{ + approval_packet_id: string + queue_item_id: string + display_name: string + packet_status: 'draft_ready_owner_response_required' | 'blocked_missing_owner_response' + required_owner_fields: string[] + required_evidence_refs: string[] + reviewer_checklist_id: string + rejection_guard_ids: string[] + rollback_owner_required: true + postcheck_required: true + sensitive_payload_allowed: false + live_execution_allowed: false + gateway_queue_write_allowed: false + telegram_send_allowed: false + production_write_allowed: false + }> + rejection_guards: Array<{ + guard_id: string + display_name: string + applies_to_risk_tiers: Array<'high' | 'critical'> + rejection_condition: string + blocked_runtime_actions: string[] + reviewer_action: string + sensitive_payload_quarantine_required: boolean + }> + reviewer_checklists: Array<{ + checklist_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' | 'sre' | 'security' | 'devops' + required_checks: string[] + pass_condition: string + approval_decision_allowed: false + checklist_write_allowed: false + side_effect_count: number + }> + routing_policy: { + high_risk_default_route: 'pause_to_owner_review_queue' + critical_risk_default_route: 'pause_to_owner_review_queue' + low_medium_runtime_route: 'pause_until_owner_approved_runtime_gate' + owner_response_required: true + verbal_approval_accepted: false + redacted_payload_only: true + } + activation_boundaries: { + read_only_owner_review_queue_allowed: true + approval_packet_preview_allowed: true + rejection_guard_preview_allowed: true + reviewer_checklist_allowed: true + auto_worker_enabled: false + live_execution_enabled: false + gateway_queue_write_enabled: false + telegram_send_enabled: false + bot_api_call_enabled: false + receipt_production_write_enabled: false + production_write_enabled: false + secret_read_enabled: false + paid_api_call_enabled: false + host_write_enabled: false + kubectl_action_enabled: false + destructive_operation_enabled: false + openclaw_replacement_allowed: false + } + telegram_policy: { + canonical_room: 'AwoooI SRE 戰情室' + canonical_room_env: 'SRE_GROUP_CHAT_ID' + gateway_queue_write_allowed: false + direct_bot_api_allowed: false + telegram_send_allowed: false + receipt_write_allowed: false + policy_note: string + } + display_redaction_contract: { + redaction_required: true + unsafe_payload_display_allowed: false + private_reasoning_display_allowed: false + secret_value_display_allowed: false + work_window_transcript_display_allowed: false + allowed_display_fields: string[] + blocked_display_fields: string[] + } + rollups: { + source_readback_count: number + queue_item_count: number + high_risk_queue_count: number + critical_queue_count: number + approval_packet_count: number + rejection_guard_count: number + reviewer_checklist_count: number + approval_packet_required_count: number + rejection_guard_required_queue_count: number + rollback_owner_required_count: number + postcheck_required_count: number + blocked_runtime_action_count: number + owner_response_received_count: number + owner_response_accepted_count: number + owner_response_rejected_count: number + redacted_payload_ingested_count: number + auto_worker_run_count: number + live_execution_count: number + gateway_queue_write_count: number + telegram_send_count: number + bot_api_call_count: number + receipt_production_write_count: number + production_write_count: number + secret_read_count: number + paid_api_call_count: number + host_write_count: number + kubectl_action_count: number + destructive_operation_count: number + } + next_actions: Array<{ + task_id: string + priority: 'P0' | 'P1' | 'P2' | 'P3' + summary: string + gate: string + }> +} + export interface AiAgentReportAutomationReviewSnapshot { schema_version: 'ai_agent_report_automation_review_v1' generated_at: string diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index fc754ef1c..7fc52ba97 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -36,6 +36,35 @@ **邊界**:本段是 repo-only metadata / guard / snapshot / 文件收斂,沒有送 Telegram、沒有呼叫 Bot API、沒有 workflow dispatch、沒有觸發 CD、沒有修改 workflow / ops script / API sender、沒有讀 secret store、沒有收 secret value / hash / partial token、沒有保存 raw payload、沒有 production write、沒有 runtime gate、沒有 action button。不能把 no-new-bypass `pass` 誤判成既有 18 個 direct send 已批准或已收斂。 +## 2026-06-19|P2-409 高風險 Owner Review Queue 本地完成 + +**背景**:P2-408 已正式部署中 / 低風險白名單與 high-risk redirect,但高風險項目仍需要一個可被 API、前端與測試共同讀回的 Owner Review Queue。統帥要求高風險需人工審核,中 / 低風險才可在後續 owner-approved gate 下評估自動化;因此本段把 high / critical、Telegram / Gateway / Bot API、host / kubectl、secret / paid provider、report source gap work item write 與 OpenClaw 角色調整全部固定為 paused owner review。 + +**完成內容**: +- 新增 `docs/schemas/ai_agent_high_risk_owner_review_queue_v1.schema.json`。 +- 新增 `docs/evaluations/ai_agent_high_risk_owner_review_queue_2026-06-19.json`。 +- 新增 `apps/api/src/services/ai_agent_high_risk_owner_review_queue.py` 與 `GET /api/v1/agents/agent-high-risk-owner-review-queue`。 +- 新增 `apps/api/tests/test_ai_agent_high_risk_owner_review_queue.py` 與 `apps/api/tests/test_ai_agent_high_risk_owner_review_queue_api.py`。 +- `/zh-TW/governance?tab=automation-inventory` 新增 P2-409 高風險 Owner Review Queue 卡片,顯示 queue items、critical / high、approval packets、rejection guards、reviewer checklists、owner accepted、routing policy、Telegram policy 與 live total。 +- P2-409 固定來源讀回 `6`、queue item `7`、high `5`、critical `2`、approval packet `7`、rejection guard `8`、reviewer checklist `7`、blocked runtime action `42`。 + +**驗證**: +- 本地:P2-409 schema / snapshot / `zh-TW.json` / `en.json` JSON parse 通過。 +- 本地:`DATABASE_URL=postgresql://test:test@localhost:5432/test PYTHONPATH=apps/api python3.11 -m py_compile apps/api/src/services/ai_agent_high_risk_owner_review_queue.py apps/api/src/api/v1/agents.py` 通過。 +- 本地:`DATABASE_URL=postgresql://test:test@localhost:5432/test PYTHONPATH=apps/api PYTHONFAULTHANDLER=1 python3.11 -m pytest apps/api/tests/test_ai_agent_high_risk_owner_review_queue.py apps/api/tests/test_ai_agent_high_risk_owner_review_queue_api.py -q`:`13 passed`。 +- 本地追加:P2-409 + P2-408 API regression `14 passed`;Ruff、`git diff --check`、`SOURCE_CONTROL_OWNER_RESPONSE_GUARD_OK`、`SECURITY_MIRROR_PROGRESS_GUARD_OK`、`IWOOOS_CONFIG_CONTROL_GUARD_OK`、`DOC_SECRET_SANITY_OK scanned_files=928` 通過。 +- 本地:i18n key parity `missing_in_en=0 / extra_in_en=0`。 +- 本地限制:此臨時 worktree 缺 `apps/web/node_modules` / `tsc`,`pnpm --filter @awoooi/web typecheck` 無法本機執行;需以 Gitea clean env code-review / CD 補正式判定。 + +**完成度同步**: +- P2-409 高風險 Owner Review Queue:本地 `100%`,正式部署 / production API / browser smoke `0% -> 待推版驗證`。 +- AgentOps 治理與可觀測基礎:`85% -> 87%`。 +- 中低風險自動化:`55% -> 60%`;P2-409 已補高風險分流承接,但 auto worker 仍未開。 +- 高風險審核:`68% -> 78%`;owner response accepted ledger 仍需 P2-410 / P2-411。 +- Telegram Bot / TG 群組:契約 `46% -> 48%`,實發仍 `0%`。 + +**邊界**:本段沒有 owner response accepted、沒有 redacted payload ingestion、沒有 live execution、沒有 auto worker、沒有 Gateway queue write、沒有 Telegram send、沒有 Bot API call、沒有 receipt production write、沒有 production write、沒有 secret read、沒有 paid API call、沒有 host write、沒有 kubectl action、沒有 destructive operation、沒有 OpenClaw 角色替換。P2-409 是高風險審核佇列,不是批准結果。 + ## 2026-06-19|P2-110E 報表資料源缺口接入 AwoooP Work Items owner review **背景**:P2-110D 已讓 Reports 頁顯示三個 `report-source-gap:*` 的 PlayBook 草案與 Verifier 計畫,但操作員仍需要離開 AwoooP 工作鏈路才能看到報表資料源缺口;這會讓 KM / PlayBook / 腳本 / 排程 / Verifier 的沉澱結果看起來像「只存在 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 dc6d425c4..1eb2be764 100644 --- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md +++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md @@ -13,13 +13,13 @@ | 項目 | 目前完成度 | 本次判讀 | 下一個有效動作 | |---|---:|---|---| | 本工作清單細化 | 100% | 已把所有工作流拆成 P0 / P1 / P2 / P3 | 同步 LOGBOOK 與 MASTER §8 | -| AgentOps 治理與可觀測基礎 | 85% | 已有 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;runtime 真正執行仍低 | P2-409 Owner Review Queue;補 Runs mobile smoke;Observability / Tenants / Knowledge Base 接同一資產沉澱總帳 | +| AgentOps 治理與可觀測基礎 | 87% | 已有 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、approval packet、rejection guard 與 reviewer checklist;runtime 真正執行仍低 | P2-409 正式部署驗證;P2-410 Agent action audit ledger;補 Runs mobile smoke;Observability / Tenants / Knowledge Base 接同一資產沉澱總帳 | | OpenClaw / Hermes / NemoTron 佈建布局 | 45% | 目前是只讀 layout 與治理頁可視化,不是主機上 live agent worker | 建立 runtime agent registry 與 AgentSession ledger | | Agent 主動溝通 / 接手 / 學習 | 設計證據 100%,runtime 35% | 互動證據、War Room 與 readback gate 已齊;Event Bus、RAG writeback、PlayBook trust 寫入仍未開 | 先做 no-write event stream,再做 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 群組 | 契約 46%,實發 0% | no-send preview、dry-run、owner review gate、P2-406B receipt readback owner review 與正式告警卡 `KM / PlayBook / 腳本 / 排程 / Verifier` 資產沉澱顯示已完成;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 | -| 中低風險自動化 | 55% | P2-408 已正式部署 6 筆候選白名單、5 個 dry-run verifier、5 個 rollback proof、6 個 audit reason 與 3 類高風險分流;production API / desktop / mobile smoke 已驗證;實際 auto worker 仍未開 | P2-409 Owner Review Queue;之後才評估 dry-run auto worker | -| 高風險審核 | 68% | Owner Gate、拒收規則與 P2-406B receipt owner review 已多層化;仍缺真實 owner response accepted ledger | P2-145 / P2-406C 類 gate 繼續只讀讀回 | +| Telegram Bot / TG 群組 | 契約 48%,實發 0% | no-send preview、dry-run、owner review gate、P2-406B receipt readback owner review、Telegram egress inventory / owner request draft 與 P2-409 高風險 queue 已串起;live send / Bot API / Gateway queue 未批准 | P2-409 正式驗證;P2-406D no-send envelope ledger、P2-406E failure-only digest route;收到合格 owner approval 後才評估 P2-406C one-message canary | +| 中低風險自動化 | 60% | P2-408 已正式部署 6 筆候選白名單、5 個 dry-run verifier、5 個 rollback proof、6 個 audit reason 與 3 類高風險分流;P2-409 已把 high / critical 與 medium live execution 風險接到 Owner Review Queue;實際 auto worker 仍未開 | P2-410 Agent action audit ledger;之後才評估 dry-run auto worker | +| 高風險審核 | 78% | P2-409 已本地完成 7 個 high / critical queue item、7 份 approval packet、8 條 rejection guard、7 份 reviewer checklist;所有高風險 action 都 pause,owner accepted 仍為 0 | P2-409 正式部署驗證;P2-410 audit ledger;P2-411 owner response acceptance readback | | 市場主流 Agent 追蹤 | 55% | 已有市場治理頁與 weekly watch;需擴充成固定外部來源評分與回放 | P2-412 週期性 market watch + scorecard | | 版本生命週期自動化 | 45% | repo-only snapshot 與採用批准包已完成;安裝、升級、PR creation、host update 仍未開 | P2-413 版本情報與 no-write upgrade proposal | @@ -67,7 +67,7 @@ | 6 | P2-406G | P0 | 單一 Telegram canary live send | Telegram + OpenClaw | Owner Gate 阻擋 | 需明確 owner approval、maintenance window、rollback、receipt owner;否則不得執行 | | 7 | P2-407 | P0 | AI 報表自動分析 no-write runtime | Hermes + NemoTron | 正式驗證完成 | `ai_agent_report_no_write_analysis_runtime_v1` schema / snapshot / API / tests / governance UI 已正式部署;deploy marker `42c08ece`;production API 回 current `P2-407`、next `P2-408`、completion `100`;desktop / mobile smoke 可見 OpenClaw / Hermes / NemoTron、AwoooI SRE 戰情室與 `live total 0`;live AI runtime / Telegram / Gateway / Bot API / receipt production write / production write / secret read / paid API / host write / kubectl 仍為 0 | | 8 | P2-408 | P0 | 中 / 低風險自動處理白名單 | OpenClaw + SRE | 正式驗證完成 | `ai_agent_low_medium_risk_whitelist_v1` schema / snapshot / API / tests / governance UI 已正式部署;feature commit `b36f4b97`、deploy marker `cd1c4407`、Gitea code-review `#3209`、CD `#3208` success;production API 回 current `P2-408`、next `P2-409`、completion `100`;6 筆候選、3 low、3 medium、5 個 dry-run verifier、5 個 rollback proof、6 個 audit reason、3 類 high-risk redirect、3 個 owner gate、27 個 blocked runtime action;desktop / mobile smoke 可見 OpenClaw / Hermes / NemoTron、AwoooI SRE 戰情室與 `live total 0`;auto worker / Telegram / Gateway / Bot API / production write / secret read / paid API / host write / kubectl 仍為 0 | -| 9 | P2-409 | P0 | 高風險 Owner Review Queue | OpenClaw | 待辦 | 高風險 action 全部 pause,產生 approval packet 與拒收規則 | +| 9 | P2-409 | P0 | 高風險 Owner Review Queue | OpenClaw | 本地完成,待正式部署驗證 | `ai_agent_high_risk_owner_review_queue_v1` schema / snapshot / API / tests / governance UI 已本地完成;7 個 high / critical queue item、7 份 approval packet、8 條 rejection guard、7 份 reviewer checklist、42 個 blocked runtime action;owner response accepted、live execution、Gateway queue、Telegram send、Bot API、receipt production write、production write、secret read、paid API、host write、kubectl、destructive operation 全部 `0` | | 10 | P2-410 | P0 | Agent action audit ledger | Hermes + Security | 待辦 | 每次判斷、交接、建議、拒收、執行結果都有 immutable event | | 11 | P2-411 | P1 | Agent Event Bus 與 handoff protocol | OpenClaw + Hermes + NemoTron | 待辦 | agent_message schema、handoff state、retry、dedup、trace_id | | 12 | P2-412 | P1 | 市場主流 AI Agent 定期評估 | Market + OpenClaw | 待辦 | 每週 primary-source watch、候選入池、scorecard、替換 gate | diff --git a/docs/evaluations/ai_agent_high_risk_owner_review_queue_2026-06-19.json b/docs/evaluations/ai_agent_high_risk_owner_review_queue_2026-06-19.json new file mode 100644 index 000000000..e69c9dd3d --- /dev/null +++ b/docs/evaluations/ai_agent_high_risk_owner_review_queue_2026-06-19.json @@ -0,0 +1,657 @@ +{ + "schema_version": "ai_agent_high_risk_owner_review_queue_v1", + "generated_at": "2026-06-19T03:20:00+08:00", + "program_status": { + "overall_completion_percent": 100, + "current_priority": "P0", + "current_task_id": "P2-409", + "next_task_id": "P2-410", + "read_only_mode": true, + "runtime_authority": "high_risk_owner_review_queue_no_live_execution_committed_snapshot", + "status_note": "P2-409 把 high / critical 風險、Telegram / Gateway / Bot API、host / kubectl、secret / paid provider、report source gap work item write 與 OpenClaw 角色調整全部暫停到 Owner Review Queue;只建立 approval packet、rejection guard、reviewer checklist 與治理頁證據,不啟動任何 live execution。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_low_medium_risk_whitelist_2026-06-18.json", + "docs/evaluations/ai_agent_receipt_readback_owner_review_2026-06-18.json", + "docs/security/telegram-notification-egress-inventory.snapshot.json", + "docs/security/telegram-notification-egress-owner-request-draft.snapshot.json", + "apps/api/src/services/ai_agent_report_source_health.py", + "docs/workplans/2026-06-04-iwooos-security-governance-p0.md" + ], + "source_readbacks": [ + { + "readback_id": "p2_408_high_risk_redirects", + "source_schema_version": "ai_agent_low_medium_risk_whitelist_v1", + "source_ref": "docs/evaluations/ai_agent_low_medium_risk_whitelist_2026-06-18.json", + "endpoint": "GET /api/v1/agents/agent-low-medium-risk-whitelist", + "owner_agent": "openclaw", + "status": "loaded", + "key_readback": "P2-408 已產生 3 個 high / critical redirect、3 個 owner gate 與 27 個 blocked runtime action。", + "next_action": "把 redirect 轉成 P2-409 queue item 與 approval packet。" + }, + { + "readback_id": "p2_406b_receipt_owner_review", + "source_schema_version": "ai_agent_receipt_readback_owner_review_v1", + "source_ref": "docs/evaluations/ai_agent_receipt_readback_owner_review_2026-06-18.json", + "endpoint": "GET /api/v1/agents/agent-receipt-readback-owner-review", + "owner_agent": "hermes", + "status": "loaded", + "key_readback": "日報 / 週報 / 月報、Telegram receipt 與 report truth gate 仍停在 owner review;live owner review accepted=0。", + "next_action": "任何 receipt production write 或 canary send 都必須進 queue,不接受跳關。" + }, + { + "readback_id": "p2_110d_report_source_gap_playbook_verifier", + "source_schema_version": "ai_agent_report_source_health_v1", + "source_ref": "apps/api/src/services/ai_agent_report_source_health.py", + "endpoint": "GET /api/v1/agents/agent-report-source-health", + "owner_agent": "hermes", + "status": "loaded", + "key_readback": "source 5、source ok 2、source gap 3、PlayBook draft 3、Verifier plan 3、owner review required 3、live send allowed 0、runtime gate 0。", + "next_action": "報表缺口只能產 owner review 與 work item 草稿,不寫 KM、不寫 PlayBook trust、不改排程。" + }, + { + "readback_id": "p2_110e_work_items_owner_review", + "source_schema_version": "awoooi_work_items_report_source_gap_owner_review_v1", + "source_ref": "docs/workplans/2026-06-04-iwooos-security-governance-p0.md", + "endpoint": "GET /zh-TW/awooop/work-items?project_id=awoooi", + "owner_agent": "hermes", + "status": "loaded", + "key_readback": "Work Items 已顯示 report-source-gap owner review、PlayBook 必填欄位、Verifier 檢查、live Telegram send=0、runtime gate=0。", + "next_action": "任何 work item DB write、KM write、verifier receipt write 都需 P2-409 approval packet。" + }, + { + "readback_id": "telegram_egress_inventory", + "source_schema_version": "telegram_notification_egress_inventory_v1", + "source_ref": "docs/security/telegram-notification-egress-inventory.snapshot.json", + "endpoint": "repo-only snapshot", + "owner_agent": "sre", + "status": "loaded", + "key_readback": "direct Bot API call 18、direct file 11、Gateway normalized callsite 56、runtime gate 0、send authorized 0。", + "next_action": "所有 direct egress 與 Telegram route 變更必須進 owner request,不可直接修 workflow 或 script。" + }, + { + "readback_id": "telegram_owner_request_draft", + "source_schema_version": "telegram_notification_egress_owner_request_draft_v1", + "source_ref": "docs/security/telegram-notification-egress-owner-request-draft.snapshot.json", + "endpoint": "repo-only snapshot", + "owner_agent": "sre", + "status": "loaded", + "key_readback": "owner request draft 11、required owner fields 19、preflight 16、blocked action 26、request sent 0、owner accepted 0。", + "next_action": "P2-409 只讀回 request draft,不送 owner request、不 emit audit event、不開 action button。" + } + ], + "queue_truth": { + "p2_408_redirects_loaded": true, + "p2_406b_receipt_owner_review_loaded": true, + "p2_110d_report_source_gap_loaded": true, + "p2_110e_work_items_owner_review_loaded": true, + "telegram_egress_inventory_loaded": true, + "telegram_owner_request_draft_loaded": true, + "all_high_risk_actions_paused": true, + "approval_packets_ready": true, + "rejection_guards_ready": true, + "reviewer_checklists_ready": true, + "high_risk_owner_review_required": true, + "auto_worker_enabled": false, + "live_execution_enabled": false, + "gateway_queue_write_enabled": false, + "telegram_send_enabled": false, + "bot_api_call_enabled": false, + "receipt_production_write_enabled": false, + "production_write_enabled": false, + "secret_read_enabled": false, + "paid_api_call_enabled": false, + "host_write_enabled": false, + "kubectl_action_enabled": false, + "destructive_operation_enabled": false, + "openclaw_replacement_allowed": false, + "auto_worker_run_count_24h": 0, + "live_execution_count_24h": 0, + "gateway_queue_write_count_24h": 0, + "telegram_send_count_24h": 0, + "bot_api_call_count_24h": 0, + "receipt_production_write_count_24h": 0, + "production_write_count_24h": 0, + "secret_read_count_24h": 0, + "paid_api_call_count_24h": 0, + "host_write_count_24h": 0, + "kubectl_action_count_24h": 0, + "destructive_operation_count_24h": 0, + "owner_response_received_count_24h": 0, + "owner_response_accepted_count_24h": 0, + "redacted_payload_ingested_count_24h": 0, + "truth_note": "高風險 queue 是審核入口,不是授權結果;沒有外部 owner response、rollback owner、verifier 與 post-check 前,所有 live action 持續為 0。" + }, + "owner_review_queue_items": [ + { + "queue_item_id": "high_security_response_queue", + "display_name": "資安 / secret / firewall 類動作", + "risk_tier": "high", + "owner_agent": "openclaw", + "queue_status": "paused_owner_review_required", + "source_readback_ids": ["p2_408_high_risk_redirects"], + "approval_packet_id": "packet_high_security_response", + "rejection_guard_ids": ["reject_missing_owner_response", "reject_secret_value_or_hash", "reject_direct_runtime_instruction"], + "reviewer_checklist_ids": ["check_redacted_evidence_refs", "check_blast_radius", "check_rollback_owner", "check_postcheck_verifier"], + "required_owner_fields": ["owner role", "decision reason", "affected security scope", "rollback owner", "postcheck evidence ref", "no secret value attestation"], + "blocked_runtime_actions": ["secret rotation", "firewall change", "Wazuh active response", "read secret store", "production write"], + "owner_response_required": true, + "rollback_owner_required": true, + "postcheck_required": true, + "live_execution_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false, + "side_effect_count": 0, + "next_gate": "security owner decision plus rollback drill" + }, + { + "queue_item_id": "critical_model_cost_provider_change_queue", + "display_name": "模型角色 / provider / 費用類動作", + "risk_tier": "critical", + "owner_agent": "openclaw", + "queue_status": "paused_owner_review_required", + "source_readback_ids": ["p2_408_high_risk_redirects"], + "approval_packet_id": "packet_critical_model_cost_provider_change", + "rejection_guard_ids": ["reject_missing_owner_response", "reject_cost_or_paid_provider_unknown", "reject_openclaw_role_change_without_market_scorecard"], + "reviewer_checklist_ids": ["check_market_scorecard", "check_cost_secret_data_boundary", "check_redacted_evidence_refs"], + "required_owner_fields": ["market scorecard ref", "benchmark evidence ref", "cost impact", "privacy boundary", "fallback plan", "ADR decision"], + "blocked_runtime_actions": ["OpenClaw role replacement", "AI provider switch", "paid API expansion", "cost quota change", "model role promotion"], + "owner_response_required": true, + "rollback_owner_required": true, + "postcheck_required": true, + "live_execution_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false, + "side_effect_count": 0, + "next_gate": "market data scorecard owner review" + }, + { + "queue_item_id": "high_data_config_apply_queue", + "display_name": "資料 / DB / production config 套用", + "risk_tier": "high", + "owner_agent": "sre", + "queue_status": "paused_owner_review_required", + "source_readback_ids": ["p2_408_high_risk_redirects"], + "approval_packet_id": "packet_high_data_config_apply", + "rejection_guard_ids": ["reject_missing_owner_response", "reject_missing_rollback_owner", "reject_missing_verifier"], + "reviewer_checklist_ids": ["check_blast_radius", "check_rollback_owner", "check_postcheck_verifier"], + "required_owner_fields": ["source-of-truth ref", "maintenance window", "rollback owner", "postcheck", "data impact", "verifier id"], + "blocked_runtime_actions": ["restore apply", "DB migration", "production config reload", "production write", "maintenance window bypass"], + "owner_response_required": true, + "rollback_owner_required": true, + "postcheck_required": true, + "live_execution_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false, + "side_effect_count": 0, + "next_gate": "maintenance window owner packet" + }, + { + "queue_item_id": "high_live_telegram_gateway_send_queue", + "display_name": "Telegram / Gateway / Bot API 實發", + "risk_tier": "high", + "owner_agent": "hermes", + "queue_status": "blocked_missing_owner_response", + "source_readback_ids": ["p2_406b_receipt_owner_review", "telegram_egress_inventory", "telegram_owner_request_draft"], + "approval_packet_id": "packet_high_live_telegram_gateway_send", + "rejection_guard_ids": ["reject_missing_owner_response", "reject_unredacted_payload", "reject_missing_verifier"], + "reviewer_checklist_ids": ["check_telegram_receipt_route", "check_redacted_evidence_refs", "check_postcheck_verifier"], + "required_owner_fields": ["canonical room env", "message shape contract", "redaction proof", "delivery receipt ref", "dedupe key", "rollback owner"], + "blocked_runtime_actions": ["Gateway queue write", "Telegram send", "Bot API call", "receiver change", "silence write", "raw payload storage"], + "owner_response_required": true, + "rollback_owner_required": true, + "postcheck_required": true, + "live_execution_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false, + "side_effect_count": 0, + "next_gate": "Telegram owner request acceptance" + }, + { + "queue_item_id": "high_report_source_gap_work_item_write_queue", + "display_name": "報表資料缺口 work item / KM / Verifier 寫入", + "risk_tier": "high", + "owner_agent": "hermes", + "queue_status": "paused_owner_review_required", + "source_readback_ids": ["p2_110d_report_source_gap_playbook_verifier", "p2_110e_work_items_owner_review"], + "approval_packet_id": "packet_high_report_source_gap_work_item_write", + "rejection_guard_ids": ["reject_missing_owner_response", "reject_unredacted_payload", "reject_missing_verifier"], + "reviewer_checklist_ids": ["check_redacted_evidence_refs", "check_rollback_owner", "check_postcheck_verifier"], + "required_owner_fields": ["work item id", "PlayBook draft scope", "Verifier plan", "rollback owner", "no-send proof", "postcheck evidence ref"], + "blocked_runtime_actions": ["Work Items DB write", "KM write", "PlayBook trust write", "verifier receipt write", "schedule change", "Gateway queue write"], + "owner_response_required": true, + "rollback_owner_required": true, + "postcheck_required": true, + "live_execution_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false, + "side_effect_count": 0, + "next_gate": "P2-410 action audit ledger" + }, + { + "queue_item_id": "high_host_kubectl_orchestrated_change_queue", + "display_name": "主機 / kubectl / rollout 類動作", + "risk_tier": "high", + "owner_agent": "sre", + "queue_status": "paused_owner_review_required", + "source_readback_ids": ["p2_408_high_risk_redirects"], + "approval_packet_id": "packet_high_host_kubectl_orchestrated_change", + "rejection_guard_ids": ["reject_missing_owner_response", "reject_direct_runtime_instruction", "reject_missing_rollback_owner"], + "reviewer_checklist_ids": ["check_blast_radius", "check_rollback_owner", "check_postcheck_verifier"], + "required_owner_fields": ["target service", "maintenance window", "rollback owner", "postcheck", "blast radius", "kubectl scope"], + "blocked_runtime_actions": ["host write", "kubectl action", "ArgoCD sync", "rollout restart", "service reload", "production config reload"], + "owner_response_required": true, + "rollback_owner_required": true, + "postcheck_required": true, + "live_execution_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false, + "side_effect_count": 0, + "next_gate": "SRE maintenance owner decision" + }, + { + "queue_item_id": "critical_secret_paid_provider_boundary_queue", + "display_name": "secret / 付費 provider / 隱私 egress 邊界", + "risk_tier": "critical", + "owner_agent": "security", + "queue_status": "blocked_missing_owner_response", + "source_readback_ids": ["p2_408_high_risk_redirects", "telegram_egress_inventory"], + "approval_packet_id": "packet_critical_secret_paid_provider_boundary", + "rejection_guard_ids": ["reject_missing_owner_response", "reject_secret_value_or_hash", "reject_cost_or_paid_provider_unknown", "reject_unredacted_payload"], + "reviewer_checklist_ids": ["check_cost_secret_data_boundary", "check_redacted_evidence_refs", "check_blast_radius"], + "required_owner_fields": ["secret name only", "paid provider scope", "privacy egress scope", "cost cap", "rollback owner", "audit reason"], + "blocked_runtime_actions": ["secret read", "paid API call", "provider credential change", "privacy egress change", "raw payload storage", "cost cap change"], + "owner_response_required": true, + "rollback_owner_required": true, + "postcheck_required": true, + "live_execution_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false, + "side_effect_count": 0, + "next_gate": "security and cost owner joint review" + } + ], + "approval_packets": [ + { + "approval_packet_id": "packet_high_security_response", + "queue_item_id": "high_security_response_queue", + "display_name": "資安回應 owner approval packet", + "packet_status": "draft_ready_owner_response_required", + "required_owner_fields": ["owner role", "decision reason", "affected security scope", "rollback owner", "postcheck evidence ref", "no secret value attestation"], + "required_evidence_refs": ["p2_408_high_risk_redirects", "security playbook ref", "rollback drill ref"], + "reviewer_checklist_id": "check_blast_radius", + "rejection_guard_ids": ["reject_missing_owner_response", "reject_secret_value_or_hash"], + "rollback_owner_required": true, + "postcheck_required": true, + "sensitive_payload_allowed": false, + "live_execution_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false + }, + { + "approval_packet_id": "packet_critical_model_cost_provider_change", + "queue_item_id": "critical_model_cost_provider_change_queue", + "display_name": "模型角色與費用 owner approval packet", + "packet_status": "draft_ready_owner_response_required", + "required_owner_fields": ["market scorecard ref", "benchmark evidence ref", "cost impact", "privacy boundary", "fallback plan", "ADR decision"], + "required_evidence_refs": ["market-mainstream benchmark", "cost quota ref", "ADR review ref"], + "reviewer_checklist_id": "check_market_scorecard", + "rejection_guard_ids": ["reject_cost_or_paid_provider_unknown", "reject_openclaw_role_change_without_market_scorecard"], + "rollback_owner_required": true, + "postcheck_required": true, + "sensitive_payload_allowed": false, + "live_execution_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false + }, + { + "approval_packet_id": "packet_high_data_config_apply", + "queue_item_id": "high_data_config_apply_queue", + "display_name": "資料與 production config owner approval packet", + "packet_status": "draft_ready_owner_response_required", + "required_owner_fields": ["source-of-truth ref", "maintenance window", "rollback owner", "postcheck", "data impact", "verifier id"], + "required_evidence_refs": ["config source ref", "backup / restore ref", "verifier ref"], + "reviewer_checklist_id": "check_rollback_owner", + "rejection_guard_ids": ["reject_missing_rollback_owner", "reject_missing_verifier"], + "rollback_owner_required": true, + "postcheck_required": true, + "sensitive_payload_allowed": false, + "live_execution_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false + }, + { + "approval_packet_id": "packet_high_live_telegram_gateway_send", + "queue_item_id": "high_live_telegram_gateway_send_queue", + "display_name": "Telegram 實發 owner approval packet", + "packet_status": "blocked_missing_owner_response", + "required_owner_fields": ["canonical room env", "message shape contract", "redaction proof", "delivery receipt ref", "dedupe key", "rollback owner"], + "required_evidence_refs": ["telegram_egress_inventory", "telegram_owner_request_draft", "receipt readback ref"], + "reviewer_checklist_id": "check_telegram_receipt_route", + "rejection_guard_ids": ["reject_missing_owner_response", "reject_unredacted_payload", "reject_missing_verifier"], + "rollback_owner_required": true, + "postcheck_required": true, + "sensitive_payload_allowed": false, + "live_execution_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false + }, + { + "approval_packet_id": "packet_high_report_source_gap_work_item_write", + "queue_item_id": "high_report_source_gap_work_item_write_queue", + "display_name": "報表資料缺口寫入 owner approval packet", + "packet_status": "draft_ready_owner_response_required", + "required_owner_fields": ["work item id", "PlayBook draft scope", "Verifier plan", "rollback owner", "no-send proof", "postcheck evidence ref"], + "required_evidence_refs": ["p2_110d_report_source_gap_playbook_verifier", "p2_110e_work_items_owner_review"], + "reviewer_checklist_id": "check_postcheck_verifier", + "rejection_guard_ids": ["reject_missing_owner_response", "reject_unredacted_payload", "reject_missing_verifier"], + "rollback_owner_required": true, + "postcheck_required": true, + "sensitive_payload_allowed": false, + "live_execution_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false + }, + { + "approval_packet_id": "packet_high_host_kubectl_orchestrated_change", + "queue_item_id": "high_host_kubectl_orchestrated_change_queue", + "display_name": "主機與 kubectl owner approval packet", + "packet_status": "draft_ready_owner_response_required", + "required_owner_fields": ["target service", "maintenance window", "rollback owner", "postcheck", "blast radius", "kubectl scope"], + "required_evidence_refs": ["SRE runbook ref", "maintenance window ref", "postcheck verifier ref"], + "reviewer_checklist_id": "check_blast_radius", + "rejection_guard_ids": ["reject_direct_runtime_instruction", "reject_missing_rollback_owner"], + "rollback_owner_required": true, + "postcheck_required": true, + "sensitive_payload_allowed": false, + "live_execution_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false + }, + { + "approval_packet_id": "packet_critical_secret_paid_provider_boundary", + "queue_item_id": "critical_secret_paid_provider_boundary_queue", + "display_name": "secret 與付費 provider 邊界 owner approval packet", + "packet_status": "blocked_missing_owner_response", + "required_owner_fields": ["secret name only", "paid provider scope", "privacy egress scope", "cost cap", "rollback owner", "audit reason"], + "required_evidence_refs": ["telegram_egress_inventory", "cost quota ref", "privacy boundary ref"], + "reviewer_checklist_id": "check_cost_secret_data_boundary", + "rejection_guard_ids": ["reject_secret_value_or_hash", "reject_cost_or_paid_provider_unknown", "reject_unredacted_payload"], + "rollback_owner_required": true, + "postcheck_required": true, + "sensitive_payload_allowed": false, + "live_execution_allowed": false, + "gateway_queue_write_allowed": false, + "telegram_send_allowed": false, + "production_write_allowed": false + } + ], + "rejection_guards": [ + { + "guard_id": "reject_missing_owner_response", + "display_name": "缺 owner response 拒收", + "applies_to_risk_tiers": ["high", "critical"], + "rejection_condition": "缺 owner role、decision、decision reason 或 affected scope 時拒收。", + "blocked_runtime_actions": ["live execution", "production write", "Gateway queue write"], + "reviewer_action": "退回補 owner response,不產生 runtime action。", + "sensitive_payload_quarantine_required": false + }, + { + "guard_id": "reject_unredacted_payload", + "display_name": "未遮罩 payload 拒收", + "applies_to_risk_tiers": ["high", "critical"], + "rejection_condition": "包含 raw payload、未遮罩路由、未遮罩 log 或未遮罩訊息內容時拒收。", + "blocked_runtime_actions": ["raw payload storage", "frontend display", "Telegram send"], + "reviewer_action": "退回 redaction contract,僅保留 metadata。", + "sensitive_payload_quarantine_required": true + }, + { + "guard_id": "reject_direct_runtime_instruction", + "display_name": "直接執行指令拒收", + "applies_to_risk_tiers": ["high", "critical"], + "rejection_condition": "要求直接 reload、restart、kubectl、host write 或 auto worker 執行時拒收。", + "blocked_runtime_actions": ["auto worker", "live execution", "host write", "kubectl action"], + "reviewer_action": "改成 approval packet 與 dry-run verifier。", + "sensitive_payload_quarantine_required": false + }, + { + "guard_id": "reject_secret_value_or_hash", + "display_name": "secret 值或 hash 拒收", + "applies_to_risk_tiers": ["high", "critical"], + "rejection_condition": "任何 secret value、secret hash、partial token 或 chat id secret 進入 packet 時拒收。", + "blocked_runtime_actions": ["secret read", "secret hash collection", "secret rotation"], + "reviewer_action": "只保留 secret name 與 owner attestation。", + "sensitive_payload_quarantine_required": true + }, + { + "guard_id": "reject_missing_rollback_owner", + "display_name": "缺 rollback owner 拒收", + "applies_to_risk_tiers": ["high", "critical"], + "rejection_condition": "沒有 rollback owner、rollback scope 或 rollback stop condition 時拒收。", + "blocked_runtime_actions": ["production write", "rollback command"], + "reviewer_action": "退回補 rollback owner 與 no-op rollback proof。", + "sensitive_payload_quarantine_required": false + }, + { + "guard_id": "reject_missing_verifier", + "display_name": "缺 verifier / post-check 拒收", + "applies_to_risk_tiers": ["high", "critical"], + "rejection_condition": "沒有 verifier id、post-check evidence 或 receipt expectation 時拒收。", + "blocked_runtime_actions": ["live execution", "receipt production write"], + "reviewer_action": "退回補 verifier plan 與 receipt gate。", + "sensitive_payload_quarantine_required": false + }, + { + "guard_id": "reject_cost_or_paid_provider_unknown", + "display_name": "費用或付費 provider 未明拒收", + "applies_to_risk_tiers": ["critical"], + "rejection_condition": "沒有 cost cap、paid provider scope、quota 或 privacy egress impact 時拒收。", + "blocked_runtime_actions": ["paid API call", "AI provider switch", "cost quota change"], + "reviewer_action": "退回補市場分數、費用與資料邊界。", + "sensitive_payload_quarantine_required": false + }, + { + "guard_id": "reject_openclaw_role_change_without_market_scorecard", + "display_name": "OpenClaw 角色調整缺市場分數拒收", + "applies_to_risk_tiers": ["critical"], + "rejection_condition": "沒有主流市場 scorecard、benchmark、ADR 與 rollback plan 時,任何 OpenClaw 角色調整都拒收。", + "blocked_runtime_actions": ["OpenClaw role replacement", "model role promotion", "ADR write"], + "reviewer_action": "退回市場資料評估,不接受硬編碼固定結論。", + "sensitive_payload_quarantine_required": false + } + ], + "reviewer_checklists": [ + { + "checklist_id": "check_redacted_evidence_refs", + "display_name": "遮罩證據 refs 檢查", + "owner_agent": "hermes", + "required_checks": ["source ref exists", "metadata only", "no raw payload", "no work window transcript", "redaction proof"], + "pass_condition": "只呈現可公開治理欄位與 committed snapshot ref。", + "approval_decision_allowed": false, + "checklist_write_allowed": false, + "side_effect_count": 0 + }, + { + "checklist_id": "check_blast_radius", + "display_name": "影響範圍檢查", + "owner_agent": "openclaw", + "required_checks": ["affected service", "data impact", "customer impact", "rollback path", "maintenance window"], + "pass_condition": "影響範圍與停損條件足以讓 owner 判斷。", + "approval_decision_allowed": false, + "checklist_write_allowed": false, + "side_effect_count": 0 + }, + { + "checklist_id": "check_rollback_owner", + "display_name": "rollback owner 檢查", + "owner_agent": "sre", + "required_checks": ["rollback owner", "rollback scope", "no-op proof", "stop condition", "post rollback check"], + "pass_condition": "rollback 欄位完整,但仍不執行 rollback command。", + "approval_decision_allowed": false, + "checklist_write_allowed": false, + "side_effect_count": 0 + }, + { + "checklist_id": "check_postcheck_verifier", + "display_name": "post-check verifier 檢查", + "owner_agent": "sre", + "required_checks": ["verifier id", "receipt expectation", "readback endpoint", "failure handling", "runtime gate remains zero"], + "pass_condition": "verifier 只做 no-write readback plan,不寫 receipt production target。", + "approval_decision_allowed": false, + "checklist_write_allowed": false, + "side_effect_count": 0 + }, + { + "checklist_id": "check_telegram_receipt_route", + "display_name": "Telegram receipt route 檢查", + "owner_agent": "hermes", + "required_checks": ["canonical room env", "message shape", "dedupe key", "delivery receipt metadata", "Bot API remains false"], + "pass_condition": "可讀回 route 與 receipt 欄位,但不寫 Gateway queue、不送 Telegram。", + "approval_decision_allowed": false, + "checklist_write_allowed": false, + "side_effect_count": 0 + }, + { + "checklist_id": "check_market_scorecard", + "display_name": "主流市場 scorecard 檢查", + "owner_agent": "nemotron", + "required_checks": ["benchmark source", "model card", "cost impact", "role fit", "fallback plan", "ADR ref"], + "pass_condition": "只產市場評估輸入,不改 agent role、不改 provider route。", + "approval_decision_allowed": false, + "checklist_write_allowed": false, + "side_effect_count": 0 + }, + { + "checklist_id": "check_cost_secret_data_boundary", + "display_name": "費用 / secret / 隱私邊界檢查", + "owner_agent": "security", + "required_checks": ["cost cap", "secret name only", "privacy egress", "provider scope", "audit reason", "rollback owner"], + "pass_condition": "費用與資料外流風險可審核,但不呼叫付費 API、不讀 secret。", + "approval_decision_allowed": false, + "checklist_write_allowed": false, + "side_effect_count": 0 + } + ], + "routing_policy": { + "high_risk_default_route": "pause_to_owner_review_queue", + "critical_risk_default_route": "pause_to_owner_review_queue", + "low_medium_runtime_route": "pause_until_owner_approved_runtime_gate", + "owner_response_required": true, + "verbal_approval_accepted": false, + "redacted_payload_only": true + }, + "activation_boundaries": { + "read_only_owner_review_queue_allowed": true, + "approval_packet_preview_allowed": true, + "rejection_guard_preview_allowed": true, + "reviewer_checklist_allowed": true, + "auto_worker_enabled": false, + "live_execution_enabled": false, + "gateway_queue_write_enabled": false, + "telegram_send_enabled": false, + "bot_api_call_enabled": false, + "receipt_production_write_enabled": false, + "production_write_enabled": false, + "secret_read_enabled": false, + "paid_api_call_enabled": false, + "host_write_enabled": false, + "kubectl_action_enabled": false, + "destructive_operation_enabled": false, + "openclaw_replacement_allowed": false + }, + "telegram_policy": { + "canonical_room": "AwoooI SRE 戰情室", + "canonical_room_env": "SRE_GROUP_CHAT_ID", + "gateway_queue_write_allowed": false, + "direct_bot_api_allowed": false, + "telegram_send_allowed": false, + "receipt_write_allowed": false, + "policy_note": "P2-409 只把 Telegram / Gateway 風險放進 owner queue;不送 Telegram、不寫 Gateway queue、不呼叫 Bot API。" + }, + "display_redaction_contract": { + "redaction_required": true, + "unsafe_payload_display_allowed": false, + "private_reasoning_display_allowed": false, + "secret_value_display_allowed": false, + "work_window_transcript_display_allowed": false, + "allowed_display_fields": [ + "queue_item_id", + "display_name", + "risk_tier", + "owner_agent", + "queue_status", + "approval_packet_id", + "rejection_guard_ids", + "reviewer_checklist_ids", + "blocked_runtime_actions", + "rollups" + ], + "blocked_display_fields": [ + "raw payload", + "private reasoning", + "secret value", + "authorization header", + "browser context", + "session transcript", + "chat id secret", + "unredacted internal route" + ] + }, + "rollups": { + "source_readback_count": 6, + "queue_item_count": 7, + "high_risk_queue_count": 5, + "critical_queue_count": 2, + "approval_packet_count": 7, + "rejection_guard_count": 8, + "reviewer_checklist_count": 7, + "approval_packet_required_count": 7, + "rejection_guard_required_queue_count": 7, + "rollback_owner_required_count": 7, + "postcheck_required_count": 7, + "blocked_runtime_action_count": 42, + "owner_response_received_count": 0, + "owner_response_accepted_count": 0, + "owner_response_rejected_count": 0, + "redacted_payload_ingested_count": 0, + "auto_worker_run_count": 0, + "live_execution_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "bot_api_call_count": 0, + "receipt_production_write_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "paid_api_call_count": 0, + "host_write_count": 0, + "kubectl_action_count": 0, + "destructive_operation_count": 0 + }, + "next_actions": [ + { + "task_id": "P2-410", + "priority": "P0", + "summary": "建立 Agent action audit ledger,讓每次 queue item、approval packet、rejection guard、owner response 與 handoff 都有 immutable event。", + "gate": "ledger 仍只允許 redacted metadata;任何 production write、Telegram send、host / kubectl action 都需 owner-approved runtime gate。" + }, + { + "task_id": "P2-411", + "priority": "P0", + "summary": "建立 Owner Response Acceptance Readback,把外部 owner response 轉成可驗收欄位,而不是口頭批准。", + "gate": "owner response received / accepted 未為 true 前,高風險 queue item 不可離開 paused 狀態。" + } + ] +} diff --git a/docs/schemas/ai_agent_high_risk_owner_review_queue_v1.schema.json b/docs/schemas/ai_agent_high_risk_owner_review_queue_v1.schema.json new file mode 100644 index 000000000..35477ef78 --- /dev/null +++ b/docs/schemas/ai_agent_high_risk_owner_review_queue_v1.schema.json @@ -0,0 +1,268 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:awoooi:ai-agent-high-risk-owner-review-queue-v1", + "title": "AWOOOI AI Agent high risk owner review queue v1", + "description": "P2-409 將 P2-408 high / critical 分流、P2-110D / P2-110E 報表資料源缺口與 Telegram egress owner request 草稿收斂成高風險 Owner Review Queue。此 schema 只允許 committed snapshot、approval packet preview、rejection guard preview 與 governance UI 呈現,不授權 auto worker、live execution、Gateway queue 寫入、Telegram 實發、Bot API、receipt production write、production write、secret 讀取、付費 API、host write、kubectl、OpenClaw 取代或不可逆操作。", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "program_status", + "source_refs", + "source_readbacks", + "queue_truth", + "owner_review_queue_items", + "approval_packets", + "rejection_guards", + "reviewer_checklists", + "routing_policy", + "activation_boundaries", + "telegram_policy", + "display_redaction_contract", + "rollups", + "next_actions" + ], + "properties": { + "schema_version": { "type": "string", "const": "ai_agent_high_risk_owner_review_queue_v1" }, + "generated_at": { "type": "string", "minLength": 1 }, + "program_status": { + "type": "object", + "required": [ + "overall_completion_percent", + "current_priority", + "current_task_id", + "next_task_id", + "read_only_mode", + "runtime_authority", + "status_note" + ], + "properties": { + "overall_completion_percent": { "type": "integer", "const": 100 }, + "current_priority": { "type": "string", "const": "P0" }, + "current_task_id": { "type": "string", "const": "P2-409" }, + "next_task_id": { "type": "string", "const": "P2-410" }, + "read_only_mode": { "type": "boolean", "const": true }, + "runtime_authority": { "type": "string", "const": "high_risk_owner_review_queue_no_live_execution_committed_snapshot" }, + "status_note": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "source_refs": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } }, + "source_readbacks": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/source_readback" } }, + "queue_truth": { "type": "object", "additionalProperties": { "type": ["boolean", "integer", "string"] } }, + "owner_review_queue_items": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/queue_item" } }, + "approval_packets": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/approval_packet" } }, + "rejection_guards": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/rejection_guard" } }, + "reviewer_checklists": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/reviewer_checklist" } }, + "routing_policy": { + "type": "object", + "required": [ + "high_risk_default_route", + "critical_risk_default_route", + "low_medium_runtime_route", + "owner_response_required", + "verbal_approval_accepted", + "redacted_payload_only" + ], + "properties": { + "high_risk_default_route": { "type": "string", "const": "pause_to_owner_review_queue" }, + "critical_risk_default_route": { "type": "string", "const": "pause_to_owner_review_queue" }, + "low_medium_runtime_route": { "type": "string", "const": "pause_until_owner_approved_runtime_gate" }, + "owner_response_required": { "type": "boolean", "const": true }, + "verbal_approval_accepted": { "type": "boolean", "const": false }, + "redacted_payload_only": { "type": "boolean", "const": true } + }, + "additionalProperties": false + }, + "activation_boundaries": { "type": "object", "additionalProperties": { "type": "boolean" } }, + "telegram_policy": { + "type": "object", + "required": [ + "canonical_room", + "canonical_room_env", + "gateway_queue_write_allowed", + "direct_bot_api_allowed", + "telegram_send_allowed", + "receipt_write_allowed" + ], + "properties": { + "canonical_room": { "type": "string", "const": "AwoooI SRE 戰情室" }, + "canonical_room_env": { "type": "string", "const": "SRE_GROUP_CHAT_ID" }, + "gateway_queue_write_allowed": { "type": "boolean", "const": false }, + "direct_bot_api_allowed": { "type": "boolean", "const": false }, + "telegram_send_allowed": { "type": "boolean", "const": false }, + "receipt_write_allowed": { "type": "boolean", "const": false }, + "policy_note": { "type": "string" } + }, + "additionalProperties": false + }, + "display_redaction_contract": { + "type": "object", + "required": [ + "redaction_required", + "unsafe_payload_display_allowed", + "private_reasoning_display_allowed", + "secret_value_display_allowed", + "work_window_transcript_display_allowed", + "allowed_display_fields", + "blocked_display_fields" + ], + "properties": { + "redaction_required": { "type": "boolean", "const": true }, + "unsafe_payload_display_allowed": { "type": "boolean", "const": false }, + "private_reasoning_display_allowed": { "type": "boolean", "const": false }, + "secret_value_display_allowed": { "type": "boolean", "const": false }, + "work_window_transcript_display_allowed": { "type": "boolean", "const": false }, + "allowed_display_fields": { "type": "array", "items": { "type": "string" } }, + "blocked_display_fields": { "type": "array", "items": { "type": "string" } } + }, + "additionalProperties": false + }, + "rollups": { "type": "object", "additionalProperties": { "type": ["integer", "array"] } }, + "next_actions": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/next_action" } } + }, + "additionalProperties": false, + "$defs": { + "source_readback": { + "type": "object", + "required": ["readback_id", "source_schema_version", "source_ref", "endpoint", "owner_agent", "status", "key_readback", "next_action"], + "properties": { + "readback_id": { "type": "string", "minLength": 1 }, + "source_schema_version": { "type": "string", "minLength": 1 }, + "source_ref": { "type": "string", "minLength": 1 }, + "endpoint": { "type": "string", "minLength": 1 }, + "owner_agent": { "enum": ["openclaw", "hermes", "nemotron", "sre", "security", "devops"] }, + "status": { "type": "string", "minLength": 1 }, + "key_readback": { "type": "string", "minLength": 1 }, + "next_action": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "queue_item": { + "type": "object", + "required": [ + "queue_item_id", + "display_name", + "risk_tier", + "owner_agent", + "queue_status", + "source_readback_ids", + "approval_packet_id", + "rejection_guard_ids", + "reviewer_checklist_ids", + "required_owner_fields", + "blocked_runtime_actions", + "owner_response_required", + "rollback_owner_required", + "postcheck_required", + "live_execution_allowed", + "gateway_queue_write_allowed", + "telegram_send_allowed", + "production_write_allowed", + "side_effect_count", + "next_gate" + ], + "properties": { + "queue_item_id": { "type": "string", "minLength": 1 }, + "display_name": { "type": "string", "minLength": 1 }, + "risk_tier": { "enum": ["high", "critical"] }, + "owner_agent": { "enum": ["openclaw", "hermes", "nemotron", "sre", "security", "devops"] }, + "queue_status": { "enum": ["paused_owner_review_required", "blocked_missing_owner_response", "approval_packet_preview_ready"] }, + "source_readback_ids": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "approval_packet_id": { "type": "string", "minLength": 1 }, + "rejection_guard_ids": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "reviewer_checklist_ids": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "required_owner_fields": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "blocked_runtime_actions": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "owner_response_required": { "type": "boolean", "const": true }, + "rollback_owner_required": { "type": "boolean", "const": true }, + "postcheck_required": { "type": "boolean", "const": true }, + "live_execution_allowed": { "type": "boolean", "const": false }, + "gateway_queue_write_allowed": { "type": "boolean", "const": false }, + "telegram_send_allowed": { "type": "boolean", "const": false }, + "production_write_allowed": { "type": "boolean", "const": false }, + "side_effect_count": { "type": "integer", "const": 0 }, + "next_gate": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "approval_packet": { + "type": "object", + "required": [ + "approval_packet_id", + "queue_item_id", + "display_name", + "packet_status", + "required_owner_fields", + "required_evidence_refs", + "reviewer_checklist_id", + "rejection_guard_ids", + "rollback_owner_required", + "postcheck_required", + "sensitive_payload_allowed", + "live_execution_allowed", + "gateway_queue_write_allowed", + "telegram_send_allowed", + "production_write_allowed" + ], + "properties": { + "approval_packet_id": { "type": "string", "minLength": 1 }, + "queue_item_id": { "type": "string", "minLength": 1 }, + "display_name": { "type": "string", "minLength": 1 }, + "packet_status": { "enum": ["draft_ready_owner_response_required", "blocked_missing_owner_response"] }, + "required_owner_fields": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "required_evidence_refs": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "reviewer_checklist_id": { "type": "string", "minLength": 1 }, + "rejection_guard_ids": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "rollback_owner_required": { "type": "boolean", "const": true }, + "postcheck_required": { "type": "boolean", "const": true }, + "sensitive_payload_allowed": { "type": "boolean", "const": false }, + "live_execution_allowed": { "type": "boolean", "const": false }, + "gateway_queue_write_allowed": { "type": "boolean", "const": false }, + "telegram_send_allowed": { "type": "boolean", "const": false }, + "production_write_allowed": { "type": "boolean", "const": false } + }, + "additionalProperties": false + }, + "rejection_guard": { + "type": "object", + "required": ["guard_id", "display_name", "applies_to_risk_tiers", "rejection_condition", "blocked_runtime_actions", "reviewer_action", "sensitive_payload_quarantine_required"], + "properties": { + "guard_id": { "type": "string", "minLength": 1 }, + "display_name": { "type": "string", "minLength": 1 }, + "applies_to_risk_tiers": { "type": "array", "minItems": 1, "items": { "enum": ["high", "critical"] } }, + "rejection_condition": { "type": "string", "minLength": 1 }, + "blocked_runtime_actions": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "reviewer_action": { "type": "string", "minLength": 1 }, + "sensitive_payload_quarantine_required": { "type": "boolean" } + }, + "additionalProperties": false + }, + "reviewer_checklist": { + "type": "object", + "required": ["checklist_id", "display_name", "owner_agent", "required_checks", "pass_condition", "approval_decision_allowed", "checklist_write_allowed", "side_effect_count"], + "properties": { + "checklist_id": { "type": "string", "minLength": 1 }, + "display_name": { "type": "string", "minLength": 1 }, + "owner_agent": { "enum": ["openclaw", "hermes", "nemotron", "sre", "security", "devops"] }, + "required_checks": { "type": "array", "minItems": 1, "items": { "type": "string" } }, + "pass_condition": { "type": "string", "minLength": 1 }, + "approval_decision_allowed": { "type": "boolean", "const": false }, + "checklist_write_allowed": { "type": "boolean", "const": false }, + "side_effect_count": { "type": "integer", "const": 0 } + }, + "additionalProperties": false + }, + "next_action": { + "type": "object", + "required": ["task_id", "priority", "summary", "gate"], + "properties": { + "task_id": { "type": "string", "minLength": 1 }, + "priority": { "enum": ["P0", "P1", "P2", "P3"] }, + "summary": { "type": "string", "minLength": 1 }, + "gate": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + } + } +} 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 7e4b96317..b4373f498 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 @@ -5061,7 +5061,7 @@ Trigger commit `f5cd37b7` 與 deploy marker `0ba92357` 已把 governance UI 的 - 補入市場主流基準對照:OpenAI Agents guardrails / human review、LangGraph durable execution / HITL / memory、A2A Agent Card / 協作、MCP tool / resource / prompt / consent、OpenTelemetry GenAI conventions、NVIDIA NeMo Agent Toolkit、NVIDIA Nemotron 3 Ultra。 - 明確重新定義 OpenClaw / Hermes / NemoTron 專業分工:OpenClaw 主責仲裁與風險 gate,Hermes 主責證據 / 報表 / KM 草稿,NemoTron 主責長任務驗證、高吞吐工具呼叫、回放與版本 / 供應鏈評估;任何取代或升降級都必須走 scorecard、回放與 ADR。 - 新增 45 項細化工作項目,從 `P2-406B` receipt readback owner review 開始,延伸到 Telegram canary、報表 AI 分析、中低風險自動化白名單、Agent Event Bus、RAG / KM、MCP registry、OTel spans、市場 watch、版本生命週期、NVIDIA / LangGraph sandbox proposal、Nemotron smoke / replay、OpenClaw 替代決策包與 production autonomous execution gate。 -- 同步立即執行順序:`P2-406B -> P2-407 -> P2-408 -> P2-411 -> P2-412 -> P2-413 -> P3-001/P3-004`,舊的 P2-405E 立即順序已改為歷史段落,不再作為下一步。 +- 同步立即執行順序:`P2-406B -> P2-407 -> P2-408 -> P2-409 -> P2-410 -> P2-411 -> P2-412 -> P2-413 -> P3-001/P3-004`,舊的 P2-405E 立即順序已改為歷史段落,不再作為下一步。 **裁決:** 本輪是工作清單、優先順序、主流基準與 gate 邊界同步,不是 Telegram send、Gateway queue write、Bot API call、delivery receipt production write、AI analysis runtime、auto optimization worker、host write、kubectl action、secret read、paid API call、production write 或 OpenClaw 替換;所有 live execution 仍需 Owner Gate、receipt、verifier、rollback 與 audit evidence。 @@ -5141,6 +5141,21 @@ Trigger commit `f5cd37b7` 與 deploy marker `0ba92357` 已把 governance UI 的 **裁決:** P2-408 是 committed snapshot / API / governance UI 的 no-live whitelist 基線,不是 auto worker、low / medium live execution、Telegram send、Gateway queue write、Bot API call、receipt production write、production write、secret read、paid API、host write、kubectl action、destructive operation 或 OpenClaw 替換。下一步是 `P2-409`:高風險 Owner Review Queue,將 high / critical、medium live execution、Telegram / Gateway / host / kubectl / production write 全部 pause 成 approval packet 與拒收規則。 +### 2026-06-19 03:20 (台北) — §8 / P2-409 — 新增高風險 Owner Review Queue — 把 high / critical 動作固定成 approval packet 與拒收規則 + +**觸發**:P2-408 已把中 / 低風險候選白名單、dry-run verifier、rollback proof 與 high-risk redirect 正式部署;下一步必須把 high / critical、Telegram / Gateway / Bot API、host / kubectl、secret / paid provider、report source gap work item write 與 OpenClaw 角色調整收斂到同一個 Owner Review Queue,避免任何高風險 action 因為「批准繼續」或文字描述被誤開成 live execution。 + +**已推進:** +- 新增 `docs/schemas/ai_agent_high_risk_owner_review_queue_v1.schema.json` 與 `docs/evaluations/ai_agent_high_risk_owner_review_queue_2026-06-19.json`。 +- 新增 `apps/api/src/services/ai_agent_high_risk_owner_review_queue.py` 與 `GET /api/v1/agents/agent-high-risk-owner-review-queue`。 +- `/zh-TW/governance?tab=automation-inventory` 新增 P2-409 卡片,顯示高風險暫停佇列、approval packet、rejection guard、reviewer checklist、routing policy、Telegram policy、owner accepted 與 live total。 +- P2-409 固定 `7` 個 high / critical queue item、`5` high、`2` critical、`7` 份 approval packet、`8` 條 rejection guard、`7` 份 reviewer checklist、`42` 個 blocked runtime action。 +- P2-409 來源讀回 `6` 類:P2-408 high-risk redirect、P2-406B receipt owner review、P2-110D report source health、P2-110E Work Items owner review、Telegram egress inventory、Telegram owner request draft。 +- 本地驗證:新 schema / snapshot / i18n JSON parse 通過;`python3.11 -m py_compile apps/api/src/services/ai_agent_high_risk_owner_review_queue.py apps/api/src/api/v1/agents.py` 通過;目標 service / API tests `13 passed`;i18n parity `missing_in_en=0 / extra_in_en=0`。 +- 本地邊界:owner response received / accepted、redacted payload ingested、auto worker、live execution、Gateway queue、Telegram send、Bot API、receipt production write、production write、secret read、paid API、host write、kubectl、destructive operation 全部 `0 / false`。 + +**裁決:** P2-409 是 committed snapshot / API / governance UI 的高風險審核佇列基線,不是 owner approval accepted、runtime worker、Telegram send、Gateway queue write、Bot API call、receipt production write、production write、secret read、paid API、host write、kubectl action、destructive operation 或 OpenClaw 角色替換。下一步是正式部署驗證與 `P2-410`:Agent action audit ledger,讓 queue item、approval packet、rejection guard、owner response 與 handoff 都有 immutable event。 + ### 2026-06-18 14:20 (台北) — §8 / Host CPU AIOps — 新增 110 runaway process 監控 / 告警 / PlayBook / gated remediation **觸發**:110 CPU 滿載已確認是跨專案 stockPlatform headless Chrome smoke 遺留 5 組 orphan process group,精準 SIGTERM 後 `REMAINING_AFTER_TERM=0`;後續 load 仍高則是 AWOOOI / VibeWork / 2026 World Cup Gitea Actions build/test。這證明泛用 `HostHighCpuLoad` 不足以支撐 AI 自動化產品,必須能把 orphan process、合法 CI load、Docker/Sentry/Harbor 事故分開。