From 9dbbc579d23714d97561b575797552185a72848d Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 12 Jun 2026 15:45:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(governance):=20=E6=96=B0=E5=A2=9E=E5=80=99?= =?UTF-8?q?=E9=81=B8=E6=93=8D=E4=BD=9C=20dry-run=20=E8=AD=89=E6=93=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/api/v1/agents.py | 31 + ...nt_candidate_operation_dry_run_evidence.py | 298 +++++++++ ...nt_candidate_operation_dry_run_evidence.py | 130 ++++ ...andidate_operation_dry_run_evidence_api.py | 40 ++ apps/web/messages/en.json | 58 ++ apps/web/messages/zh-TW.json | 58 ++ .../tabs/automation-inventory-tab.tsx | 204 +++++- apps/web/src/lib/api-client.ts | 135 ++++ docs/LOGBOOK.md | 25 + ...AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md | 10 +- ...T_INTERACTION_LEARNING_PROOF_2026-06-11.md | 20 +- ...operation_dry_run_evidence_2026-06-12.json | 580 ++++++++++++++++++ ..._operation_dry_run_evidence_v1.schema.json | 336 ++++++++++ ...-04-15-MASTER-ai-autonomous-flywheel-v2.md | 16 +- 14 files changed, 1927 insertions(+), 14 deletions(-) create mode 100644 apps/api/src/services/ai_agent_candidate_operation_dry_run_evidence.py create mode 100644 apps/api/tests/test_ai_agent_candidate_operation_dry_run_evidence.py create mode 100644 apps/api/tests/test_ai_agent_candidate_operation_dry_run_evidence_api.py create mode 100644 docs/evaluations/ai_agent_candidate_operation_dry_run_evidence_2026-06-12.json create mode 100644 docs/schemas/ai_agent_candidate_operation_dry_run_evidence_v1.schema.json diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index 6fa679291..7ce7cd37c 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -49,6 +49,9 @@ from src.services.ai_agent_automation_backlog_snapshot import ( from src.services.ai_agent_automation_inventory_snapshot import ( load_latest_ai_agent_automation_inventory_snapshot, ) +from src.services.ai_agent_candidate_operation_dry_run_evidence import ( + load_latest_ai_agent_candidate_operation_dry_run_evidence, +) from src.services.ai_agent_communication_learning_contract import ( load_latest_ai_agent_communication_learning_contract, ) @@ -1066,6 +1069,34 @@ async def get_agent_operation_permission_model() -> dict[str, Any]: ) from exc +@router.get( + "/agent-candidate-operation-dry-run-evidence", + response_model=dict[str, Any], + summary="取得 AI Agent 候選操作 dry-run 證據", + description=( + "讀取最新已提交的 P2-102 候選操作 dry-run 證據;此端點只回傳 candidate operation、" + "dry-run evidence hash、side-effect count、verifier plan、gate requirement 與 operator handoff," + "不啟動 runtime worker、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、" + "不寫讀報回執、不執行 verifier live readback、不寫 production target、不讀 secret。" + ), +) +async def get_agent_candidate_operation_dry_run_evidence() -> dict[str, Any]: + """Return the latest read-only AI Agent candidate operation dry-run evidence.""" + try: + return await asyncio.to_thread(load_latest_ai_agent_candidate_operation_dry_run_evidence) + 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_candidate_operation_dry_run_evidence_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="AI Agent 候選操作 dry-run 證據無效", + ) from exc + + @router.get( "/agent-owner-approved-fixture-dry-run", response_model=dict[str, Any], diff --git a/apps/api/src/services/ai_agent_candidate_operation_dry_run_evidence.py b/apps/api/src/services/ai_agent_candidate_operation_dry_run_evidence.py new file mode 100644 index 000000000..6c376a078 --- /dev/null +++ b/apps/api/src/services/ai_agent_candidate_operation_dry_run_evidence.py @@ -0,0 +1,298 @@ +""" +AI Agent candidate operation dry-run evidence snapshot. + +Loads the latest committed P2-102 candidate operation dry-run evidence. +This module validates repo-committed evidence only; it never starts runtime +workers, writes Gateway queues, sends Telegram messages, reads secrets, or +writes production targets. +""" + +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_candidate_operation_dry_run_evidence_*.json" +_SCHEMA_VERSION = "ai_agent_candidate_operation_dry_run_evidence_v1" +_RUNTIME_AUTHORITY = "candidate_operation_dry_run_evidence_only_no_live_execution_or_send" + + +def load_latest_ai_agent_candidate_operation_dry_run_evidence( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed AI Agent candidate operation dry-run evidence.""" + directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR + candidates = sorted(directory.glob(_SNAPSHOT_PATTERN)) + if not candidates: + raise FileNotFoundError(f"no AI Agent candidate operation dry-run evidence 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") + _require_schema(payload, str(latest)) + _require_no_live_boundaries(payload, str(latest)) + _require_candidate_operations(payload, str(latest)) + _require_verifier_plans(payload, str(latest)) + _require_gate_requirements(payload, str(latest)) + _require_operator_handoffs(payload, str(latest)) + _require_redaction_contract(payload, str(latest)) + _require_rollup_consistency(payload, str(latest)) + 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 {} + if status.get("read_only_mode") is not True: + raise ValueError(f"{label}: program_status.read_only_mode must be true") + if status.get("runtime_authority") != _RUNTIME_AUTHORITY: + raise ValueError(f"{label}: runtime_authority must remain {_RUNTIME_AUTHORITY}") + if status.get("current_task_id") != "P2-102": + raise ValueError(f"{label}: current_task_id must be P2-102") + if status.get("next_task_id") != "P2-103": + raise ValueError(f"{label}: next_task_id must be P2-103") + + +def _require_no_live_boundaries(payload: dict[str, Any], label: str) -> None: + truth = payload.get("dry_run_truth") or {} + required_true = { + "p2_101_permission_model_loaded", + "dry_run_evidence_gate_ready", + "all_candidate_operations_have_dry_run_evidence", + "side_effect_counter_ready", + "verifier_plan_ready", + "rollback_or_noop_plan_ready", + "owner_review_packet_ready", + } + missing = sorted(field for field in required_true if truth.get(field) is not True) + if missing: + raise ValueError(f"{label}: dry-run readiness flags must remain true: {missing}") + + required_false = { + "runtime_execution_enabled", + "gateway_queue_write_enabled", + "telegram_send_enabled", + "telegram_bot_api_call_enabled", + "delivery_receipt_write_enabled", + "ai_runtime_worker_enabled", + "medium_low_auto_worker_enabled", + "post_action_verifier_live_readback_enabled", + "production_write_enabled", + "secret_value_read_enabled", + "paid_provider_call_enabled", + "host_or_cluster_command_enabled", + "destructive_operation_enabled", + "work_window_transcript_display_allowed", + } + unsafe = sorted(field for field in required_false if truth.get(field) is not False) + if unsafe: + raise ValueError(f"{label}: live execution/send/write flags must remain false: {unsafe}") + + zero_counts = { + "runtime_execution_count_24h", + "gateway_queue_write_count_24h", + "telegram_send_count_24h", + "telegram_bot_api_call_count_24h", + "delivery_receipt_write_count_24h", + "ai_runtime_worker_run_count_24h", + "medium_low_auto_execution_count_24h", + "post_action_verifier_live_readback_count_24h", + "production_write_count_24h", + "secret_value_read_count_24h", + "paid_provider_call_count_24h", + "host_or_cluster_command_count_24h", + "destructive_operation_count_24h", + } + non_zero = sorted(field for field in zero_counts if truth.get(field) != 0) + if non_zero: + raise ValueError(f"{label}: live execution/send/write counts must remain zero: {non_zero}") + + +def _require_candidate_operations(payload: dict[str, Any], label: str) -> None: + candidates = payload.get("candidate_operations") or [] + candidate_ids = {candidate.get("candidate_id") for candidate in candidates} + required = { + "candidate_observe_inventory_read", + "candidate_diagnose_correlate_evidence", + "candidate_report_digest_queue", + "candidate_shadow_no_write_replay", + "candidate_manual_sop_draft", + "candidate_repair_candidate_proposal", + "candidate_low_risk_noop_execution", + "candidate_medium_risk_repair_execution", + "candidate_post_action_verifier_live_readback", + "candidate_telegram_gateway_queue_write", + "candidate_production_config_or_data_write", + "candidate_secret_or_paid_provider_access", + "candidate_destructive_host_or_cluster_action", + } + if candidate_ids != required: + raise ValueError(f"{label}: candidate operations must match {sorted(required)}") + + valid_statuses = {"passed_no_write", "needs_owner_review", "blocked_until_allowlist", "blocked_by_policy"} + for candidate in candidates: + candidate_id = candidate.get("candidate_id") + if candidate.get("dry_run_status") not in valid_statuses: + raise ValueError(f"{label}: candidate {candidate_id} dry_run_status is invalid") + if not _is_redacted_sha256(candidate.get("input_evidence_hash")): + raise ValueError(f"{label}: candidate {candidate_id} must expose input_evidence_hash") + if not _is_redacted_sha256(candidate.get("output_evidence_hash")): + raise ValueError(f"{label}: candidate {candidate_id} must expose output_evidence_hash") + zero_fields = { + "side_effect_count", + "production_write_count", + "gateway_queue_write_count", + "telegram_send_count", + "secret_value_read_count", + "destructive_action_count", + } + non_zero = sorted(field for field in zero_fields if candidate.get(field) != 0) + if non_zero: + raise ValueError(f"{label}: candidate {candidate_id} side-effect counts must remain zero: {non_zero}") + if not candidate.get("blocked_actions"): + raise ValueError(f"{label}: candidate {candidate_id} must list blocked_actions") + if not candidate.get("required_human_decision"): + raise ValueError(f"{label}: candidate {candidate_id} must list required_human_decision") + if not candidate.get("verifier_plan_id"): + raise ValueError(f"{label}: candidate {candidate_id} must bind verifier_plan_id") + if not candidate.get("next_gate"): + raise ValueError(f"{label}: candidate {candidate_id} must list next_gate") + + +def _require_verifier_plans(payload: dict[str, Any], label: str) -> None: + plans = payload.get("verifier_plans") or [] + plan_ids = {plan.get("plan_id") for plan in plans} + required = { + "verifier_redacted_evidence_hash", + "verifier_gateway_queue_preview", + "verifier_shadow_replay_fixture", + "verifier_repair_candidate_consistency", + "verifier_live_readback_allowlist", + "verifier_destructive_boundary_preflight", + } + if plan_ids != required: + raise ValueError(f"{label}: verifier plans must match {sorted(required)}") + for plan in plans: + plan_id = plan.get("plan_id") + if plan.get("live_readback_enabled") is not False: + raise ValueError(f"{label}: verifier {plan_id} live_readback_enabled must remain false") + if plan.get("writes_result") is not False: + raise ValueError(f"{label}: verifier {plan_id} writes_result must remain false") + if plan.get("requires_secret_value") is not False: + raise ValueError(f"{label}: verifier {plan_id} requires_secret_value must remain false") + if not _is_redacted_sha256(plan.get("evidence_hash")): + raise ValueError(f"{label}: verifier {plan_id} must expose evidence_hash") + + +def _require_gate_requirements(payload: dict[str, Any], label: str) -> None: + gates = payload.get("gate_evidence_requirements") or [] + gate_ids = {gate.get("gate_id") for gate in gates} + required = { + "p2_102_dry_run_evidence_gate", + "gateway_queue_write_permission_gate", + "medium_low_auto_worker_permission_gate", + "post_action_verifier_live_gate", + "production_write_permission_gate", + "secret_or_paid_provider_gate", + "break_glass_or_destructive_action_gate", + } + if gate_ids != required: + raise ValueError(f"{label}: gate evidence requirements must match {sorted(required)}") + for gate in gates: + gate_id = gate.get("gate_id") + if gate.get("opens_live_execution") is not False: + raise ValueError(f"{label}: gate {gate_id} opens_live_execution must remain false") + if not gate.get("required_evidence"): + raise ValueError(f"{label}: gate {gate_id} must list required_evidence") + + +def _require_operator_handoffs(payload: dict[str, Any], label: str) -> None: + handoffs = payload.get("operator_handoffs") or [] + handoff_ids = {handoff.get("handoff_id") for handoff in handoffs} + required = { + "handoff_collect_missing_evidence", + "handoff_review_repair_candidate", + "handoff_review_sre_queue_preview", + "handoff_review_verifier_allowlist", + "handoff_escalate_blocked_operation", + } + if handoff_ids != required: + raise ValueError(f"{label}: operator handoffs must match {sorted(required)}") + for handoff in handoffs: + handoff_id = handoff.get("handoff_id") + if handoff.get("creates_runtime_action") is not False: + raise ValueError(f"{label}: handoff {handoff_id} creates_runtime_action must remain false") + if handoff.get("requires_human_review") is not True: + raise ValueError(f"{label}: handoff {handoff_id} requires_human_review must remain true") + + +def _require_redaction_contract(payload: dict[str, Any], label: str) -> None: + contract = payload.get("display_redaction_contract") or {} + required_false = { + "raw_prompt_display_allowed", + "private_reasoning_display_allowed", + "secret_value_display_allowed", + "raw_telegram_payload_display_allowed", + "work_window_transcript_display_allowed", + } + if contract.get("redaction_required") is not True: + raise ValueError(f"{label}: display redaction must remain required") + unsafe = sorted(field for field in required_false if contract.get(field) is not False) + if unsafe: + raise ValueError(f"{label}: display redaction fields must remain false: {unsafe}") + + +def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None: + rollups = payload.get("rollups") or {} + truth = payload.get("dry_run_truth") or {} + candidates = payload.get("candidate_operations") or [] + plans = payload.get("verifier_plans") or [] + gates = payload.get("gate_evidence_requirements") or [] + handoffs = payload.get("operator_handoffs") or [] + + expected = { + "candidate_operation_count": len(candidates), + "candidate_with_dry_run_evidence_count": sum( + 1 + for candidate in candidates + if _is_redacted_sha256(candidate.get("input_evidence_hash")) + and _is_redacted_sha256(candidate.get("output_evidence_hash")) + ), + "passed_no_write_count": sum(1 for candidate in candidates if candidate.get("dry_run_status") == "passed_no_write"), + "needs_owner_review_count": sum(1 for candidate in candidates if candidate.get("dry_run_status") == "needs_owner_review"), + "blocked_until_allowlist_count": sum(1 for candidate in candidates if candidate.get("dry_run_status") == "blocked_until_allowlist"), + "blocked_by_policy_count": sum(1 for candidate in candidates if candidate.get("dry_run_status") == "blocked_by_policy"), + "verifier_plan_count": len(plans), + "gate_evidence_requirement_count": len(gates), + "operator_handoff_count": len(handoffs), + "side_effect_count": sum(candidate.get("side_effect_count", 0) for candidate in candidates), + "runtime_execution_count": truth.get("runtime_execution_count_24h"), + "gateway_queue_write_count": truth.get("gateway_queue_write_count_24h"), + "telegram_send_count": truth.get("telegram_send_count_24h"), + "production_write_count": truth.get("production_write_count_24h"), + "secret_value_read_count": truth.get("secret_value_read_count_24h"), + "destructive_operation_count": truth.get("destructive_operation_count_24h"), + } + mismatches = { + key: {"expected": expected_value, "actual": rollups.get(key)} + for key, expected_value in expected.items() + if rollups.get(key) != expected_value + } + if mismatches: + raise ValueError(f"{label}: rollup counts mismatch: {mismatches}") + + +def _is_redacted_sha256(value: Any) -> bool: + if not isinstance(value, str): + return False + if not value.startswith("sha256:") or len(value) != 71: + return False + return all(char in "0123456789abcdef" for char in value.removeprefix("sha256:")) diff --git a/apps/api/tests/test_ai_agent_candidate_operation_dry_run_evidence.py b/apps/api/tests/test_ai_agent_candidate_operation_dry_run_evidence.py new file mode 100644 index 000000000..220f2e9b7 --- /dev/null +++ b/apps/api/tests/test_ai_agent_candidate_operation_dry_run_evidence.py @@ -0,0 +1,130 @@ +import copy +import json + +import pytest + +from src.services.ai_agent_candidate_operation_dry_run_evidence import ( + load_latest_ai_agent_candidate_operation_dry_run_evidence, +) + + +def _write_snapshot(tmp_path, payload): + path = tmp_path / "ai_agent_candidate_operation_dry_run_evidence_2026-06-12.json" + path.write_text(json.dumps(payload), encoding="utf-8") + return path + + +def test_load_latest_ai_agent_candidate_operation_dry_run_evidence(): + data = load_latest_ai_agent_candidate_operation_dry_run_evidence() + + assert data["schema_version"] == "ai_agent_candidate_operation_dry_run_evidence_v1" + assert data["program_status"]["current_task_id"] == "P2-102" + assert data["program_status"]["next_task_id"] == "P2-103" + assert data["program_status"]["overall_completion_percent"] == 98 + assert data["dry_run_truth"]["dry_run_evidence_gate_ready"] is True + assert data["dry_run_truth"]["all_candidate_operations_have_dry_run_evidence"] is True + assert data["dry_run_truth"]["runtime_execution_enabled"] is False + assert data["dry_run_truth"]["gateway_queue_write_enabled"] is False + assert data["dry_run_truth"]["telegram_send_enabled"] is False + assert data["dry_run_truth"]["telegram_bot_api_call_enabled"] is False + assert data["dry_run_truth"]["production_write_enabled"] is False + assert data["dry_run_truth"]["secret_value_read_enabled"] is False + assert data["dry_run_truth"]["destructive_operation_enabled"] is False + assert data["rollups"]["candidate_operation_count"] == 13 + assert data["rollups"]["candidate_with_dry_run_evidence_count"] == 13 + assert data["rollups"]["passed_no_write_count"] == 4 + assert data["rollups"]["needs_owner_review_count"] == 3 + assert data["rollups"]["blocked_until_allowlist_count"] == 2 + assert data["rollups"]["blocked_by_policy_count"] == 4 + assert data["rollups"]["verifier_plan_count"] == 6 + assert data["rollups"]["gate_evidence_requirement_count"] == 7 + assert data["rollups"]["operator_handoff_count"] == 5 + assert data["rollups"]["side_effect_count"] == 0 + assert data["rollups"]["runtime_execution_count"] == 0 + assert data["rollups"]["gateway_queue_write_count"] == 0 + assert data["rollups"]["telegram_send_count"] == 0 + assert data["rollups"]["production_write_count"] == 0 + assert data["rollups"]["secret_value_read_count"] == 0 + assert data["rollups"]["destructive_operation_count"] == 0 + + +def test_rejects_runtime_execution_enabled(tmp_path): + data = load_latest_ai_agent_candidate_operation_dry_run_evidence() + bad = copy.deepcopy(data) + bad["dry_run_truth"]["runtime_execution_enabled"] = True + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="live execution/send/write flags"): + load_latest_ai_agent_candidate_operation_dry_run_evidence(tmp_path) + + +def test_rejects_gateway_queue_write_count(tmp_path): + data = load_latest_ai_agent_candidate_operation_dry_run_evidence() + bad = copy.deepcopy(data) + bad["dry_run_truth"]["gateway_queue_write_count_24h"] = 1 + bad["rollups"]["gateway_queue_write_count"] = 1 + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="live execution/send/write counts"): + load_latest_ai_agent_candidate_operation_dry_run_evidence(tmp_path) + + +def test_rejects_candidate_side_effect(tmp_path): + data = load_latest_ai_agent_candidate_operation_dry_run_evidence() + bad = copy.deepcopy(data) + bad["candidate_operations"][0]["side_effect_count"] = 1 + bad["rollups"]["side_effect_count"] = 1 + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="side-effect counts"): + load_latest_ai_agent_candidate_operation_dry_run_evidence(tmp_path) + + +def test_rejects_candidate_without_hash(tmp_path): + data = load_latest_ai_agent_candidate_operation_dry_run_evidence() + bad = copy.deepcopy(data) + bad["candidate_operations"][0]["output_evidence_hash"] = "missing" + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="output_evidence_hash"): + load_latest_ai_agent_candidate_operation_dry_run_evidence(tmp_path) + + +def test_rejects_verifier_live_readback(tmp_path): + data = load_latest_ai_agent_candidate_operation_dry_run_evidence() + bad = copy.deepcopy(data) + bad["verifier_plans"][0]["live_readback_enabled"] = True + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="live_readback_enabled"): + load_latest_ai_agent_candidate_operation_dry_run_evidence(tmp_path) + + +def test_rejects_gate_opening_live_execution(tmp_path): + data = load_latest_ai_agent_candidate_operation_dry_run_evidence() + bad = copy.deepcopy(data) + bad["gate_evidence_requirements"][0]["opens_live_execution"] = True + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="opens_live_execution"): + load_latest_ai_agent_candidate_operation_dry_run_evidence(tmp_path) + + +def test_rejects_handoff_runtime_action(tmp_path): + data = load_latest_ai_agent_candidate_operation_dry_run_evidence() + bad = copy.deepcopy(data) + bad["operator_handoffs"][0]["creates_runtime_action"] = True + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="creates_runtime_action"): + load_latest_ai_agent_candidate_operation_dry_run_evidence(tmp_path) + + +def test_rejects_rollup_mismatch(tmp_path): + data = load_latest_ai_agent_candidate_operation_dry_run_evidence() + bad = copy.deepcopy(data) + bad["rollups"]["candidate_operation_count"] = 999 + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="rollup counts"): + load_latest_ai_agent_candidate_operation_dry_run_evidence(tmp_path) diff --git a/apps/api/tests/test_ai_agent_candidate_operation_dry_run_evidence_api.py b/apps/api/tests/test_ai_agent_candidate_operation_dry_run_evidence_api.py new file mode 100644 index 000000000..d7d2e7c46 --- /dev/null +++ b/apps/api/tests/test_ai_agent_candidate_operation_dry_run_evidence_api.py @@ -0,0 +1,40 @@ +from fastapi.testclient import TestClient + +from src.main import app + + +def test_get_ai_agent_candidate_operation_dry_run_evidence_api(): + client = TestClient(app) + response = client.get("/api/v1/agents/agent-candidate-operation-dry-run-evidence") + + assert response.status_code == 200 + data = response.json() + assert data["schema_version"] == "ai_agent_candidate_operation_dry_run_evidence_v1" + assert data["program_status"]["current_task_id"] == "P2-102" + assert data["program_status"]["next_task_id"] == "P2-103" + assert data["program_status"]["overall_completion_percent"] == 98 + assert data["dry_run_truth"]["dry_run_evidence_gate_ready"] is True + assert data["dry_run_truth"]["all_candidate_operations_have_dry_run_evidence"] is True + assert data["dry_run_truth"]["runtime_execution_enabled"] is False + assert data["dry_run_truth"]["gateway_queue_write_enabled"] is False + assert data["dry_run_truth"]["telegram_send_enabled"] is False + assert data["dry_run_truth"]["telegram_bot_api_call_enabled"] is False + assert data["dry_run_truth"]["production_write_enabled"] is False + assert data["dry_run_truth"]["secret_value_read_enabled"] is False + assert data["dry_run_truth"]["destructive_operation_enabled"] is False + assert data["rollups"]["candidate_operation_count"] == 13 + assert data["rollups"]["candidate_with_dry_run_evidence_count"] == 13 + assert data["rollups"]["passed_no_write_count"] == 4 + assert data["rollups"]["needs_owner_review_count"] == 3 + assert data["rollups"]["blocked_until_allowlist_count"] == 2 + assert data["rollups"]["blocked_by_policy_count"] == 4 + assert data["rollups"]["verifier_plan_count"] == 6 + assert data["rollups"]["gate_evidence_requirement_count"] == 7 + assert data["rollups"]["operator_handoff_count"] == 5 + assert data["rollups"]["side_effect_count"] == 0 + assert data["rollups"]["runtime_execution_count"] == 0 + assert data["rollups"]["gateway_queue_write_count"] == 0 + assert data["rollups"]["telegram_send_count"] == 0 + assert data["rollups"]["production_write_count"] == 0 + assert data["rollups"]["secret_value_read_count"] == 0 + assert data["rollups"]["destructive_operation_count"] == 0 diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index d59f5526c..c15fdcad4 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -4436,6 +4436,64 @@ "high": "高風險", "critical": "關鍵阻擋" } + }, + "candidateOperationDryRunEvidence": { + "title": "P2-102 候選操作 dry-run 證據", + "source": "{generated} · {current} → {next}", + "truthTitle": "dry-run 證據真相", + "boundaryTitle": "副作用邊界", + "boundarySummary": "目前 side-effect {sideEffects}、Gateway queue write {queue}、Telegram send {send}、production write {write}、secret read {secret};本段只產生證據與人工 handoff,不開執行。", + "metrics": { + "overall": "P2-102 進度", + "candidates": "候選操作", + "evidence": "有證據", + "passed": "no-write 通過", + "needsReview": "需審查", + "allowlistBlocked": "等 allow-list", + "policyBlocked": "政策阻擋", + "verifierPlans": "verifier plan", + "sideEffects": "side-effect", + "queueWrites": "queue writes", + "telegramSends": "TG sends", + "productionWrites": "prod writes", + "secretReads": "secret reads", + "destructive": "破壞性" + }, + "flags": { + "modelLoaded": "P2-101 loaded: {value}", + "gateReady": "dry-run gate: {value}", + "allHaveEvidence": "all evidence: {value}", + "verifierReady": "verifier ready: {value}", + "runtime": "runtime enabled: {value}", + "queueWrite": "queue write: {value}", + "send": "send: {value}", + "productionWrite": "prod write: {value}", + "secret": "secret value: {value}", + "destructive": "destructive: {value}", + "liveReadback": "live readback: {value}", + "resultWrite": "result write: {value}", + "runtimeAction": "runtime action: {value}" + }, + "labels": { + "sideEffect": "side-effect {count}", + "humanDecision": "人工判斷: {value}", + "outputHash": "output hash: {value}", + "verifier": "verifier: {value}", + "nextGate": "next gate: {value}", + "reviewRequired": "人工審查: {value}" + }, + "statuses": { + "passed_no_write": "no-write 通過", + "needs_owner_review": "需 owner 審查", + "blocked_until_allowlist": "等 allow-list", + "blocked_by_policy": "政策阻擋" + }, + "riskTiers": { + "low": "低風險", + "medium": "中風險", + "high": "高風險", + "critical": "關鍵阻擋" + } } } }, diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index d59f5526c..c15fdcad4 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -4436,6 +4436,64 @@ "high": "高風險", "critical": "關鍵阻擋" } + }, + "candidateOperationDryRunEvidence": { + "title": "P2-102 候選操作 dry-run 證據", + "source": "{generated} · {current} → {next}", + "truthTitle": "dry-run 證據真相", + "boundaryTitle": "副作用邊界", + "boundarySummary": "目前 side-effect {sideEffects}、Gateway queue write {queue}、Telegram send {send}、production write {write}、secret read {secret};本段只產生證據與人工 handoff,不開執行。", + "metrics": { + "overall": "P2-102 進度", + "candidates": "候選操作", + "evidence": "有證據", + "passed": "no-write 通過", + "needsReview": "需審查", + "allowlistBlocked": "等 allow-list", + "policyBlocked": "政策阻擋", + "verifierPlans": "verifier plan", + "sideEffects": "side-effect", + "queueWrites": "queue writes", + "telegramSends": "TG sends", + "productionWrites": "prod writes", + "secretReads": "secret reads", + "destructive": "破壞性" + }, + "flags": { + "modelLoaded": "P2-101 loaded: {value}", + "gateReady": "dry-run gate: {value}", + "allHaveEvidence": "all evidence: {value}", + "verifierReady": "verifier ready: {value}", + "runtime": "runtime enabled: {value}", + "queueWrite": "queue write: {value}", + "send": "send: {value}", + "productionWrite": "prod write: {value}", + "secret": "secret value: {value}", + "destructive": "destructive: {value}", + "liveReadback": "live readback: {value}", + "resultWrite": "result write: {value}", + "runtimeAction": "runtime action: {value}" + }, + "labels": { + "sideEffect": "side-effect {count}", + "humanDecision": "人工判斷: {value}", + "outputHash": "output hash: {value}", + "verifier": "verifier: {value}", + "nextGate": "next gate: {value}", + "reviewRequired": "人工審查: {value}" + }, + "statuses": { + "passed_no_write": "no-write 通過", + "needs_owner_review": "需 owner 審查", + "blocked_until_allowlist": "等 allow-list", + "blocked_by_policy": "政策阻擋" + }, + "riskTiers": { + "low": "低風險", + "medium": "中風險", + "high": "高風險", + "critical": "關鍵阻擋" + } } } }, 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 dcc88a03a..52076d7d3 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 @@ -35,6 +35,7 @@ import { GlassCard } from '@/components/ui/glass-card' import { StatusOrb } from '@/components/ui/status-orb' import { apiClient, + type AiAgentCandidateOperationDryRunEvidenceSnapshot, type AiAgentDeploymentLayoutSnapshot, type AiAgentHostStatefulVersionInventorySnapshot, type AiAgentInteractionLearningProofSnapshot, @@ -348,6 +349,7 @@ export function AutomationInventoryTab() { const [reportRuntimeFixtureReadback, setReportRuntimeFixtureReadback] = useState(null) const [runtimeWorkerShadowGate, setRuntimeWorkerShadowGate] = useState(null) const [operationPermissionModel, setOperationPermissionModel] = useState(null) + const [candidateOperationDryRunEvidence, setCandidateOperationDryRunEvidence] = useState(null) const [reportTruthActionabilityReview, setReportTruthActionabilityReview] = useState(null) const [ownerDryRunPackage, setOwnerDryRunPackage] = useState(null) const [hostStatefulInventory, setHostStatefulInventory] = useState(null) @@ -386,6 +388,7 @@ export function AutomationInventoryTab() { apiClient.getAiAgentReportRuntimeFixtureReadback(), apiClient.getAiAgentRuntimeWorkerShadowGate(), apiClient.getAiAgentOperationPermissionModel(), + apiClient.getAiAgentCandidateOperationDryRunEvidence(), apiClient.getAiAgentReportTruthActionabilityReview(), apiClient.getAiAgentOwnerApprovedFixtureDryRun(), apiClient.getAiAgentHostStatefulVersionInventory(), @@ -423,6 +426,7 @@ export function AutomationInventoryTab() { reportRuntimeFixtureReadbackResult, runtimeWorkerShadowGateResult, operationPermissionModelResult, + candidateOperationDryRunEvidenceResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -457,6 +461,7 @@ export function AutomationInventoryTab() { setReportRuntimeFixtureReadback(reportRuntimeFixtureReadbackResult.status === 'fulfilled' ? reportRuntimeFixtureReadbackResult.value : null) setRuntimeWorkerShadowGate(runtimeWorkerShadowGateResult.status === 'fulfilled' ? runtimeWorkerShadowGateResult.value : null) setOperationPermissionModel(operationPermissionModelResult.status === 'fulfilled' ? operationPermissionModelResult.value : null) + setCandidateOperationDryRunEvidence(candidateOperationDryRunEvidenceResult.status === 'fulfilled' ? candidateOperationDryRunEvidenceResult.value : null) setReportTruthActionabilityReview(reportTruthActionabilityReviewResult.status === 'fulfilled' ? reportTruthActionabilityReviewResult.value : null) setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null) setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null) @@ -489,6 +494,7 @@ export function AutomationInventoryTab() { reportRuntimeFixtureReadbackResult, runtimeWorkerShadowGateResult, operationPermissionModelResult, + candidateOperationDryRunEvidenceResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -1089,6 +1095,42 @@ export function AutomationInventoryTab() { .slice(0, 8) }, [operationPermissionModel]) + const visibleCandidateOperationDryRuns = useMemo(() => { + if (!candidateOperationDryRunEvidence) return [] + const statusPriority = { + blocked_by_policy: 0, + blocked_until_allowlist: 1, + needs_owner_review: 2, + passed_no_write: 3, + } as Record + const riskPriority = { critical: 0, high: 1, medium: 2, low: 3 } as Record + return [...candidateOperationDryRunEvidence.candidate_operations] + .sort((a, b) => { + const leftStatus = statusPriority[a.dry_run_status] ?? 4 + const rightStatus = statusPriority[b.dry_run_status] ?? 4 + if (leftStatus !== rightStatus) return leftStatus - rightStatus + const leftRisk = riskPriority[a.risk_tier] ?? 4 + const rightRisk = riskPriority[b.risk_tier] ?? 4 + if (leftRisk !== rightRisk) return leftRisk - rightRisk + return a.candidate_id.localeCompare(b.candidate_id) + }) + .slice(0, 8) + }, [candidateOperationDryRunEvidence]) + + const visibleCandidateVerifierPlans = useMemo(() => { + if (!candidateOperationDryRunEvidence) return [] + return [...candidateOperationDryRunEvidence.verifier_plans] + .sort((a, b) => a.plan_id.localeCompare(b.plan_id)) + .slice(0, 5) + }, [candidateOperationDryRunEvidence]) + + const visibleCandidateOperatorHandoffs = useMemo(() => { + if (!candidateOperationDryRunEvidence) return [] + return [...candidateOperationDryRunEvidence.operator_handoffs] + .sort((a, b) => a.handoff_id.localeCompare(b.handoff_id)) + .slice(0, 5) + }, [candidateOperationDryRunEvidence]) + const visibleReportTruthFindings = useMemo(() => { if (!reportTruthActionabilityReview) return [] const priority = { critical: 0, high: 1, medium: 2, low: 3 } as Record @@ -1308,7 +1350,7 @@ export function AutomationInventoryTab() { ) } - if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { + if (error || !snapshot || !backlog || !backupTargets || !backupReadiness || !backupPolicy || !offsiteEscrow || !giteaHealth || !observabilityMatrix || !providerRouteMatrix || !deploymentLayout || !proactiveOperations || !interactionLearningProof || !liveReadModelGate || !redisDryRunGate || !learningWritebackPackage || !telegramReceiptPackage || !ownerApprovedLearningDryRun || !runtimeWriteGateReview || !postWriteVerifierPackage || !runtimeVerifierEvidenceReview || !reportAutomationReview || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { return (
@@ -1516,6 +1558,20 @@ export function AutomationInventoryTab() { const operationPermissionProductionWrites = operationPermissionModel.rollups.production_write_count const operationPermissionSecretReads = operationPermissionModel.rollups.secret_value_read_count const operationPermissionDestructive = operationPermissionModel.rollups.destructive_operation_count + const candidateDryRunOverall = candidateOperationDryRunEvidence.program_status.overall_completion_percent + const candidateDryRunCount = candidateOperationDryRunEvidence.rollups.candidate_operation_count + const candidateDryRunEvidenceCount = candidateOperationDryRunEvidence.rollups.candidate_with_dry_run_evidence_count + const candidateDryRunPassed = candidateOperationDryRunEvidence.rollups.passed_no_write_count + const candidateDryRunNeedsReview = candidateOperationDryRunEvidence.rollups.needs_owner_review_count + const candidateDryRunAllowlistBlocked = candidateOperationDryRunEvidence.rollups.blocked_until_allowlist_count + const candidateDryRunPolicyBlocked = candidateOperationDryRunEvidence.rollups.blocked_by_policy_count + const candidateDryRunVerifierPlans = candidateOperationDryRunEvidence.rollups.verifier_plan_count + const candidateDryRunSideEffects = candidateOperationDryRunEvidence.rollups.side_effect_count + const candidateDryRunQueueWrites = candidateOperationDryRunEvidence.rollups.gateway_queue_write_count + const candidateDryRunSends = candidateOperationDryRunEvidence.rollups.telegram_send_count + const candidateDryRunProductionWrites = candidateOperationDryRunEvidence.rollups.production_write_count + const candidateDryRunSecretReads = candidateOperationDryRunEvidence.rollups.secret_value_read_count + const candidateDryRunDestructive = candidateOperationDryRunEvidence.rollups.destructive_operation_count const reportTruthOverall = reportTruthActionabilityReview.program_status.overall_completion_percent const reportTruthFindings = reportTruthActionabilityReview.rollups.zero_signal_finding_count const reportTruthCritical = reportTruthActionabilityReview.rollups.critical_finding_count @@ -3130,6 +3186,152 @@ export function AutomationInventoryTab() {
+
+
+
+ + + {t('candidateOperationDryRunEvidence.title')} + +
+ +
+ +
+ } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> +
+ +
+
+ {t('candidateOperationDryRunEvidence.truthTitle')} + + {candidateOperationDryRunEvidence.dry_run_truth.truth_note} + +
+ + + + +
+
+ +
+ {t('candidateOperationDryRunEvidence.boundaryTitle')} + + {t('candidateOperationDryRunEvidence.boundarySummary', { + sideEffects: candidateDryRunSideEffects, + queue: candidateDryRunQueueWrites, + send: candidateDryRunSends, + write: candidateDryRunProductionWrites, + secret: candidateDryRunSecretReads, + })} + +
+ + + + + + +
+
+
+ +
+ {visibleCandidateOperationDryRuns.map(candidate => ( +
+
+ + {candidate.display_name} + + +
+
+ + + + +
+ + {candidate.dry_run_scope} + + + {t('candidateOperationDryRunEvidence.labels.humanDecision', { value: candidate.required_human_decision })} + +
+ + + +
+
+ ))} +
+ +
+ {visibleCandidateVerifierPlans.map(plan => ( +
+
+ + {plan.display_name} + + +
+ + {plan.expected_signal} + + + {plan.failure_lane} + +
+ + + +
+
+ ))} +
+ +
+ {visibleCandidateOperatorHandoffs.map(handoff => ( +
+
+ + {handoff.display_name} + + +
+ + {handoff.human_instruction} + +
+ + +
+
+ ))} +
+
+
diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index 075a84fee..2bedaf80c 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -352,6 +352,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentCandidateOperationDryRunEvidence() { + const res = await fetch(`${API_BASE_URL}/agents/agent-candidate-operation-dry-run-evidence`) + return handleResponse(res) + }, + async getAiAgentOwnerApprovedFixtureDryRun() { const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-fixture-dry-run`) return handleResponse(res) @@ -2748,6 +2753,136 @@ export interface AiAgentOperationPermissionModelSnapshot { } } +export interface AiAgentCandidateOperationDryRunEvidenceSnapshot { + schema_version: 'ai_agent_candidate_operation_dry_run_evidence_v1' + generated_at: string + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: 'P2-102' + next_task_id: 'P2-103' + read_only_mode: true + runtime_authority: 'candidate_operation_dry_run_evidence_only_no_live_execution_or_send' + status_note: string + } + source_refs: string[] + dry_run_truth: { + p2_101_permission_model_loaded: true + dry_run_evidence_gate_ready: true + all_candidate_operations_have_dry_run_evidence: true + side_effect_counter_ready: true + verifier_plan_ready: true + rollback_or_noop_plan_ready: true + owner_review_packet_ready: true + runtime_execution_enabled: false + gateway_queue_write_enabled: false + telegram_send_enabled: false + telegram_bot_api_call_enabled: false + delivery_receipt_write_enabled: false + ai_runtime_worker_enabled: false + medium_low_auto_worker_enabled: false + post_action_verifier_live_readback_enabled: false + production_write_enabled: false + secret_value_read_enabled: false + paid_provider_call_enabled: false + host_or_cluster_command_enabled: false + destructive_operation_enabled: false + work_window_transcript_display_allowed: false + runtime_execution_count_24h: number + gateway_queue_write_count_24h: number + telegram_send_count_24h: number + telegram_bot_api_call_count_24h: number + delivery_receipt_write_count_24h: number + ai_runtime_worker_run_count_24h: number + medium_low_auto_execution_count_24h: number + post_action_verifier_live_readback_count_24h: number + production_write_count_24h: number + secret_value_read_count_24h: number + paid_provider_call_count_24h: number + host_or_cluster_command_count_24h: number + destructive_operation_count_24h: number + truth_note: string + } + candidate_operations: Array<{ + candidate_id: string + source_category_id: string + display_name: string + risk_tier: 'low' | 'medium' | 'high' | 'critical' + permission_lane: 'observe_only' | 'no_write_replay_allowed' | 'proposal_only' | 'human_approval_required' | 'explicitly_blocked' + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + dry_run_status: 'passed_no_write' | 'needs_owner_review' | 'blocked_until_allowlist' | 'blocked_by_policy' + dry_run_scope: string + input_evidence_hash: string + output_evidence_hash: string + side_effect_count: number + production_write_count: number + gateway_queue_write_count: number + telegram_send_count: number + secret_value_read_count: number + destructive_action_count: number + blocked_actions: string[] + required_human_decision: string + verifier_plan_id: string + rollback_or_noop_plan: string + next_gate: string + }> + verifier_plans: Array<{ + plan_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + verifier_scope: string + expected_signal: string + failure_lane: string + live_readback_enabled: false + writes_result: false + requires_secret_value: false + evidence_hash: string + }> + gate_evidence_requirements: Array<{ + gate_id: string + display_name: string + required_evidence: string[] + missing_or_blocked: string[] + opens_live_execution: false + }> + operator_handoffs: Array<{ + handoff_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + human_instruction: string + creates_runtime_action: false + requires_human_review: true + }> + display_redaction_contract: { + redaction_required: true + raw_prompt_display_allowed: false + private_reasoning_display_allowed: false + secret_value_display_allowed: false + raw_telegram_payload_display_allowed: false + work_window_transcript_display_allowed: false + allowed_display_fields: string[] + blocked_display_fields: string[] + } + rollups: { + candidate_operation_count: number + candidate_with_dry_run_evidence_count: number + passed_no_write_count: number + needs_owner_review_count: number + blocked_until_allowlist_count: number + blocked_by_policy_count: number + verifier_plan_count: number + gate_evidence_requirement_count: number + operator_handoff_count: number + side_effect_count: number + runtime_execution_count: number + gateway_queue_write_count: number + telegram_send_count: number + production_write_count: number + secret_value_read_count: number + destructive_operation_count: number + } +} + export interface AiAgentOwnerApprovedFixtureDryRunSnapshot { schema_version: 'ai_agent_owner_approved_fixture_dry_run_v1' generated_at: string diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 2eb719d7b..a30059396 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,28 @@ +## 2026-06-12|P2-102 候選操作 dry-run 證據 + +**背景**:P2-101 已把所有 AI Agent 操作分成只讀、no-write replay、提案、人工批准與明確阻擋;統帥持續指出 TG 批准後仍沒有形成完整自動化流程,也缺少可操作的人工下一步。P2-102 先把每類候選操作固定成 dry-run evidence、side-effect count、verifier plan、rollback/no-op plan 與 operator handoff,讓後續 P2-103 能把結果接回 KM / LOGBOOK / 稽核軌跡。 + +**完成(本地)**: + +- 新增 `ai_agent_candidate_operation_dry_run_evidence_v1` schema、committed snapshot 與 backend loader,強制 runtime execution、Gateway queue write、Telegram send、Bot API、delivery receipt、AI runtime worker、中低風險 auto worker、verifier live readback、production write、secret value read、paid provider call、host / cluster command 與 destructive action 全部維持 `false / 0`。 +- 新增 `GET /api/v1/agents/agent-candidate-operation-dry-run-evidence` 只讀 API 與測試;API 只回傳 13 類候選操作、13 組 dry-run evidence、6 個 verifier plan、7 個 gate evidence requirement 與 5 個 operator handoff,不寫 queue、不送 Telegram、不啟動 worker。 +- 治理頁 `/zh-TW/governance?tab=automation-inventory` 新增 P2-102 區塊,顯示候選操作、dry-run 證據、side-effect count、verifier plan、gate evidence requirement、operator handoff 與不可誤讀合約。 +- 更新 `AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md`、`AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md` 與 MASTER §3.2 / §5,將 P2-102 標記為完成,下一步改為 `P2-103` 結果寫回治理軌跡。 + +**驗證(本地)**: + +- `python3 -m json.tool` 檢查 P2-102 snapshot / schema 通過。 +- `python3 -m py_compile apps/api/src/services/ai_agent_candidate_operation_dry_run_evidence.py apps/api/src/api/v1/agents.py` 通過。 +- `DATABASE_URL='postgresql+asyncpg://test:test@localhost/test' PYTHONPATH=apps/api pytest -q apps/api/tests/test_ai_agent_candidate_operation_dry_run_evidence.py apps/api/tests/test_ai_agent_candidate_operation_dry_run_evidence_api.py`:`10 passed`。 + +**完成度同步**: + +- P2-102:本地 `98%`;候選操作 `13`、dry-run evidence `13`、verifier plan `6`、gate evidence requirement `7`、operator handoff `5`。 +- side-effect、runtime execution、Gateway queue write、Telegram send、Telegram Bot API、delivery receipt write、AI runtime worker、中低風險 auto worker、verifier live readback、production write、secret value read、paid provider call、host / cluster command、destructive action:全部仍為 `0`。 +- P2-103:下一步要把任務結果接回 KM / LOGBOOK / 稽核軌跡;完成前不得 live worker、queue write、Telegram send、production write 或 verifier live readback。 + +**邊界**:本段不送 Telegram、不寫 Gateway queue、不呼叫 Bot API、不寫 delivery receipt、不啟動 live AI runtime worker、不啟動中低風險 auto worker、不跑 verifier live readback、不讀 secret、不呼叫付費 provider、不執行 host / cluster / destructive action、不顯示內部協作內容、不提供前端批准 / 執行 / 發送按鈕;不得把 candidate dry-run evidence 解讀成 runtime loop 已運作。 + ## 2026-06-12|P2-101 操作類別權限模型 **背景**:P2-404 已把 fixture/readback/verifier dry-run promotion 成 runtime worker shadow / no-write execution evidence gate;統帥指出 TG 批准後仍沒有真正自動化,也沒有產生可操作的人工下一步。P2-101 先把每一類操作的權限、風險、Agent 責任、下一個 gate 與人工處置模板固定成可查模型,避免批准後落到 `learning_recorded` 或 `manual_review` 但沒有 SOP。 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 b2b921741..6854b5baa 100644 --- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md +++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md @@ -12,7 +12,7 @@ | Nemotron 實際整合應用 | 30% | 完整回放前仍被關卡擋下 | `blocked_needs_evidence`,下一關是 `refresh_source_evidence_then_5_record_smoke_only` | | 工具 / 服務 / 套件 AI 自動化 | 92% | P0 已完成;P1 服務 / runtime / 監控 / provider / service health / 備份 / DR / 套件與供應鏈只讀基線已完成;P1-007 失敗限定通知合約與前端 redaction 合約已完成;下一主線是 P2-004 依賴 / 供應鏈漂移監控 | 狀態分類、盤點 schema、權限矩陣、靜態盤點種子、只讀 API、UI 骨架、驗證、自動化待辦 schema / 快照 / API / 分組 UI、Backup / DR 目標盤點、準備度矩陣、備份通知政策、Backup / DR 證據 UI、復原演練批准包模板、異地 / escrow 準備度狀態、任務批准邊界、確定性進度彙總、Python 套件 / 供應鏈只讀基線、JS pnpm/npm 只讀基線、Docker build surface 只讀基線、CVE / license / drift 嚴重度政策、定期依賴漂移與外部資料來源檢查設計、依賴升級批准包模板、runtime_surface_inventory_v1 schema / snapshot / API / UI、gitea_workflow_runner_health_v1 schema / snapshot / API / UI、observability_contract_matrix_v1 schema / snapshot / API / UI、ai_provider_route_matrix_v1 schema / snapshot / API / UI、service_health_gap_matrix_v1 schema / snapshot / API / UI、service health evidence cards UI、service_health_failure_notification_policy_v1 schema / snapshot / API / UI 已完成 | | OpenClaw / Hermes / NemoTron 佈建布局 | 45% | P1-401 / P1-402 已完成;仍是只讀 layout 與治理頁顯示,不是 runtime deploy | `ai_agent_deployment_layout_v1` schema、`ai_agent_deployment_layout_2026-06-11.json`、`GET /api/v1/agents/agent-deployment-layout`、治理頁自動化盤點 UI、`AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md` | -| OpenClaw / Hermes / NemoTron 主動溝通、學習與成長證據 | 100% | P2-401A 已完成只讀 contract;P2-403A 已完成互動 / 接手 / 學習 / 成長證據面板;P2-403B 已完成 AgentSession / Redis Streams live read model gate;P2-403C 已完成 Redis Streams consumer group dry-run、handoff envelope、ack / dead-letter / replay gate;P2-403D 已完成 learning writeback approval package;P2-403E 已完成 Telegram receipt approval package;P2-403F 已完成 owner-approved learning dry-run preview、人工操作選項與 fixture-only dry-run 總包;P2-403G 已完成 runtime write gate review,固定雙重批准、dry-run hash、post-write verifier 與 redaction 欄位;P2-403H 已完成 post-write verifier implementation package、rollback lane、failure lane 與人工操作選項;P2-403I 已完成 runtime verifier evidence implementation review;P2-403J 已完成報表真相、告警有效性、日報、週報、月報、每個 Agent 工作量、圖表化報告、AI 分析建議與高 / 中 / 低風險自動化政策審查;P2-403K 已完成本地程式層 SRE 戰情室路由收斂;P2-403L 已完成報表派送、Telegram Gateway queue、讀報回執、AI 讀報後分析、中低風險自動處理與高風險審核的啟動前閘門;P2-403M 已完成報表 runtime no-write dry-run 證據包、SRE 戰情室 Gateway queue 草案與 readback verifier 草案;P2-403N 已完成 fixture smoke、queue preview readback 與 verifier dry-run 證據包。runtime worker、DB migration、production Redis consumer group、Telegram 實發、delivery receipt E2E、report delivery、Gateway queue write、AI analysis runtime、中低風險 auto worker、KM / PlayBook trust / timeline / replay score 寫入、SDK / 付費服務仍未開 gate | `ai_agent_communication_learning_contract_v1`、`ai_agent_interaction_learning_proof_v1`、`ai_agent_live_read_model_gate_v1`、`ai_agent_redis_dry_run_gate_v1`、`ai_agent_learning_writeback_approval_package_v1`、`ai_agent_telegram_receipt_approval_package_v1`、`ai_agent_owner_approved_learning_dry_run_v1`、`ai_agent_owner_approved_fixture_dry_run_v1`、`GET /api/v1/agents/agent-communication-learning-contract`、`GET /api/v1/agents/agent-interaction-learning-proof`、`GET /api/v1/agents/agent-live-read-model-gate`、`GET /api/v1/agents/agent-redis-dry-run-gate`、`GET /api/v1/agents/agent-learning-writeback-approval-package`、`GET /api/v1/agents/agent-telegram-receipt-approval-package`、`GET /api/v1/agents/agent-owner-approved-learning-dry-run`、`GET /api/v1/agents/agent-owner-approved-fixture-dry-run`、`ai_agent_runtime_write_gate_review_v1`、`GET /api/v1/agents/agent-runtime-write-gate-review`、`ai_agent_post_write_verifier_package_v1`、`GET /api/v1/agents/agent-post-write-verifier-package`、`ai_agent_runtime_verifier_evidence_review_v1`、`GET /api/v1/agents/agent-runtime-verifier-evidence-review`、`ai_agent_report_truth_actionability_review_v1`、`GET /api/v1/agents/agent-report-truth-actionability-review`、`ai_agent_report_automation_review_v1`、`GET /api/v1/agents/agent-report-automation-review`、`ai_agent_report_runtime_readiness_v1`、`GET /api/v1/agents/agent-report-runtime-readiness`、`ai_agent_report_runtime_dry_run_v1`、`GET /api/v1/agents/agent-report-runtime-dry-run`、`ai_agent_report_runtime_fixture_readback_v1`、`GET /api/v1/agents/agent-report-runtime-fixture-readback`、`/zh-TW/governance?tab=automation-inventory`、MASTER §3.2.1b / §3.2.1d / §3.4.3 | +| OpenClaw / Hermes / NemoTron 主動溝通、學習與成長證據 | 100% | P2-401A 已完成只讀 contract;P2-403A 已完成互動 / 接手 / 學習 / 成長證據面板;P2-403B 已完成 AgentSession / Redis Streams live read model gate;P2-403C 已完成 Redis Streams consumer group dry-run、handoff envelope、ack / dead-letter / replay gate;P2-403D 已完成 learning writeback approval package;P2-403E 已完成 Telegram receipt approval package;P2-403F 已完成 owner-approved learning dry-run preview、人工操作選項與 fixture-only dry-run 總包;P2-403G 已完成 runtime write gate review;P2-403H 已完成 post-write verifier implementation package;P2-403I 已完成 runtime verifier evidence implementation review;P2-403J 已完成報表真相、告警有效性、日週月報、Agent 工作量、圖表化報告、AI 建議與風險自動化政策審查;P2-403K / L / M / N 已把 SRE 戰情室路由、報表派送啟動前閘門、no-write dry-run 與 fixture/readback/verifier dry-run 固定;P2-404 已完成 runtime worker shadow / no-write evidence;P2-101 已完成操作類別權限模型;P2-102 已完成 13 類候選操作 dry-run 證據。runtime worker、DB migration、production Redis consumer group、Telegram 實發、delivery receipt E2E、report delivery、Gateway queue write、AI analysis runtime、中低風險 auto worker、KM / PlayBook trust / timeline / replay score 寫入、SDK / 付費服務仍未開 gate | `ai_agent_communication_learning_contract_v1`、`ai_agent_interaction_learning_proof_v1`、`ai_agent_operation_permission_model_v1`、`ai_agent_candidate_operation_dry_run_evidence_v1`、`GET /api/v1/agents/agent-operation-permission-model`、`GET /api/v1/agents/agent-candidate-operation-dry-run-evidence`、`/zh-TW/governance?tab=automation-inventory`、MASTER §3.2.1b / §3.2.1d / §3.4.3 | | AI Agent 主動營運委派與版本生命週期 | 100% | P2-402A / P2-402B / P2-402C / P2-402D / P2-402E / P2-402F / P2-402G 已完成;已建立 repo-only 版本新鮮度快照、工具採用批准包、Telegram action-required digest policy、Gitea PR 草案 lane、host / K3s / stateful 版本只讀盤點、API 與 governance UI。定期排程、外部版本查詢、工具安裝、CI 變更、套件升級、主機更新、container pull、實際 PR creation、auto merge、Telegram 實發、SSH、kubectl、重啟仍未開 gate | `ai_agent_proactive_operations_contract_v1`、`ai_agent_version_freshness_snapshot_v1`、`ai_agent_tool_adoption_approval_package_v1`、`ai_agent_telegram_action_required_digest_policy_v1`、`ai_agent_gitea_pr_draft_lane_v1`、`ai_agent_host_stateful_version_inventory_v1`、`GET /api/v1/agents/agent-proactive-operations-contract`、`GET /api/v1/agents/agent-version-freshness-snapshot`、`GET /api/v1/agents/agent-tool-adoption-approval-package`、`GET /api/v1/agents/agent-telegram-action-required-digest-policy`、`GET /api/v1/agents/agent-gitea-pr-draft-lane`、`GET /api/v1/agents/agent-host-stateful-version-inventory`、`/zh-TW/governance?tab=automation-inventory`、MASTER §3.2.1c | | 本工作清單與分析報告 | 100% | 已完成 | 本 MD 文件 | @@ -20,9 +20,9 @@ AI Agent 自動化工作包目前完成度:**92%**。本工作清單文件本 三 Agent 佈建布局目前完成度:**45%**。第一波已完成只讀 schema / snapshot / API / 測試 / 報告,第二波已接入治理頁自動化盤點 UI;正式 runtime 佈署、Telegram E2E 發送與 AgentSession 工作流仍需逐項 gate。 -三 Agent 主動溝通、學習與成長證據目前完成度:**100%**。已完成只讀契約、互動 / 接手 / 學習 / 成長證據面板、P2-403B live read model gate、P2-403C Redis dry-run gate、P2-403D learning writeback approval package、P2-403E Telegram receipt approval package、P2-403F owner-approved learning dry-run preview、人工操作選項與 fixture-only dry-run 總包、P2-403G runtime write gate review、P2-403H post-write verifier implementation package、P2-403I runtime verifier evidence implementation review、P2-403J 報表真相 / 告警有效性 / 日週月報 / Agent 工作量 / 圖表化報告 / AI 建議 / 風險自動化政策審查、P2-403K SRE 戰情室路由程式收斂、P2-403L 報表派送 / Telegram queue / 讀報回執 / AI 讀報分析 / 中低風險自動處理 / 高風險審核啟動前閘門、P2-403M no-write dry-run / SRE 戰情室 Gateway queue 草案 / readback verifier、P2-403N fixture smoke / queue preview readback / verifier dry-run、P2-404 runtime worker shadow / no-write execution evidence gate,以及 P2-101 操作類別權限模型;目前 live AgentSession、Agent message、handoff、learning write、Telegram receipt、Gateway queue write、runtime verifier execution、report delivery、AI analysis runtime、中低風險 auto worker、Telegram 實發、shadow worker live 與 delivery receipt E2E 仍全部為 `0`,下一步依優先順序推 `P2-102` 候選操作 dry-run 證據,但在批准前仍不得啟動 runtime loop。 +三 Agent 主動溝通、學習與成長證據目前完成度:**100%**。已完成只讀契約、互動 / 接手 / 學習 / 成長證據面板、P2-403B live read model gate、P2-403C Redis dry-run gate、P2-403D learning writeback approval package、P2-403E Telegram receipt approval package、P2-403F owner-approved learning dry-run preview、P2-403G runtime write gate review、P2-403H post-write verifier implementation package、P2-403I runtime verifier evidence implementation review、P2-403J 報表真相 / 告警有效性 / 日週月報 / Agent 工作量 / 圖表化報告 / AI 建議 / 風險自動化政策審查、P2-403K/L/M/N 報表與 SRE 戰情室 dry-run 鏈、P2-404 runtime worker shadow / no-write execution evidence gate、P2-101 操作類別權限模型,以及 P2-102 候選操作 dry-run 證據;目前 live AgentSession、Agent message、handoff、learning write、Telegram receipt、Gateway queue write、runtime verifier execution、report delivery、AI analysis runtime、中低風險 auto worker、Telegram 實發、shadow worker live 與 delivery receipt E2E 仍全部為 `0`,下一步依優先順序推 `P2-103`,把任務結果接回 KM / LOGBOOK / 稽核軌跡。 -AI Agent 主動營運委派與版本生命週期目前完成度:**100%**。已完成 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory、只讀 API、`P2-402B` repo-only daily version freshness snapshot、`P2-402C` Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包、`P2-402D` Telegram action-required digest policy、`P2-402E` Gitea PR 草案 lane、`P2-402F` host OS / K3s / stateful services 版本只讀盤點,以及 `P2-402G` governance UI 顯示可委派能力;`P2-403A` 到 `P2-404` 已先補互動、學習證據面、live read model gate、Redis dry-run gate、learning writeback approval package、Telegram receipt approval package、owner-approved learning dry-run preview、runtime write gate review、post-write verifier package、runtime verifier evidence review、報表真相、TG 戰情室收斂、日週月報、Agent 工作量、圖表化報告、風險自動化政策、報表 runtime 啟動前閘門、no-write dry-run 證據包、fixture/readback/verifier dry-run 證據包、shadow/no-write execution gate 與 P2-101 操作類別權限模型。下一步是 `P2-102` 候選操作 dry-run 證據,外部 registry / package source / host probe / SSH / kubectl / 工具安裝 / CI 變更 / 實際 PR creation / Telegram 實發與 learning write 仍需 gate。 +AI Agent 主動營運委派與版本生命週期目前完成度:**100%**。已完成 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory、只讀 API、`P2-402B` repo-only daily version freshness snapshot、`P2-402C` Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包、`P2-402D` Telegram action-required digest policy、`P2-402E` Gitea PR 草案 lane、`P2-402F` host OS / K3s / stateful services 版本只讀盤點,以及 `P2-402G` governance UI 顯示可委派能力;`P2-403A` 到 `P2-102` 已補互動、學習證據面、live read model gate、Redis dry-run gate、learning writeback approval package、Telegram receipt approval package、owner-approved learning dry-run preview、runtime write gate review、post-write verifier package、runtime verifier evidence review、報表真相、TG 戰情室收斂、日週月報、Agent 工作量、圖表化報告、風險自動化政策、報表 runtime 啟動前閘門、no-write dry-run 證據包、fixture/readback/verifier dry-run 證據包、shadow/no-write execution gate、操作類別權限模型與 13 類候選操作 dry-run 證據。下一步是 `P2-103` 結果寫回治理軌跡;外部 registry / package source / host probe / SSH / kubectl / 工具安裝 / CI 變更 / 實際 PR creation / Telegram 實發與 learning write 仍需 gate。 完成度計算模型: @@ -973,8 +973,8 @@ UI: | P2-403M | 完成 | 91 | OpenClaw + Hermes + NemoTron | 報表 runtime no-write dry-run、SRE 戰情室 Gateway queue 草案、readback verifier 草案 | `ai_agent_report_runtime_dry_run_v1` / snapshot / 只讀 API / governance UI;5 個 dry-run artifact、3 個 queue digest 草案、4 個 readback verifier case、3 個 Agent dry-run role、6 個 operator checkpoint;live delivery / queue write / Bot API / receipt write / AI runtime / 中低風險 auto worker / verifier live readback 全部 `0` | 不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 delivery receipt、不啟動 AI runtime worker、不啟動中低風險 auto worker、不執行 verifier live readback、不讀 secret、不顯示內部對話內容 | | P2-403N | 完成 | 94 | Hermes + NemoTron + OpenClaw | fixture smoke / queue preview readback / verifier dry-run | `ai_agent_report_runtime_fixture_readback_v1` / snapshot / 只讀 API / governance UI;5 個 fixture smoke、3 個 queue preview readback、4 個 verifier dry-run case、3 個 Agent fixture role、5 個 operator checkpoint;live delivery / queue write / Telegram send / Bot API / receipt write / AI runtime / 中低風險 auto worker / verifier live readback 全部 `0` | 仍不得 live send / live write;中低風險自動處理與高風險審核需另行批准 | | P2-404 | 完成 | 96 | OpenClaw + Hermes + NemoTron | runtime worker shadow / no-write execution evidence gate | `ai_agent_runtime_worker_shadow_gate_v1` / snapshot / 只讀 API / governance UI;5 個 shadow candidate、4 個 no-write replay、4 個 verifier shadow case、3 個 Agent shadow role、6 個 operator checkpoint;shadow live / Gateway queue write / Telegram send / Bot API / receipt write / AI runtime / 中低風險 auto worker / verifier live readback / production write 全部 `0` | 下一步 P2-101 操作類別權限模型;未完成前不得 live worker、queue write、Telegram send 或 production write | -| P2-101 | 完成 | 97 | OpenClaw + Hermes + NemoTron | 定義操作類別權限模型 | `ai_agent_operation_permission_model_v1` / snapshot / 只讀 API / governance UI;5 條 permission lane、13 類操作、3 個 Agent permission role、8 個 gate transition、5 個人工操作模板;runtime execution / Gateway queue write / Telegram send / Bot API / receipt write / AI runtime worker / 中低風險 auto worker / verifier live readback / production write / secret read / paid provider / host command / destructive action 全部 `0` | 下一步 P2-102 候選操作 dry-run 證據;未完成前不得 live worker、queue write、Telegram send 或 production write | -| P2-102 | 待辦 | 0 | OpenClaw | 所有候選操作都要有 dry-run 證據 | dry-run 合約 | 不直接 apply | +| P2-101 | 完成 | 97 | OpenClaw + Hermes + NemoTron | 定義操作類別權限模型 | `ai_agent_operation_permission_model_v1` / snapshot / 只讀 API / governance UI;5 條 permission lane、13 類操作、3 個 Agent permission role、8 個 gate transition、5 個人工操作模板;runtime execution / Gateway queue write / Telegram send / Bot API / receipt write / AI runtime worker / 中低風險 auto worker / verifier live readback / production write / secret read / paid provider / host command / destructive action 全部 `0` | 已由 P2-102 承接;不得把權限模型解讀成 runtime 授權 | +| P2-102 | 完成 | 98 | OpenClaw + Hermes + NemoTron | 所有候選操作都要有 dry-run 證據 | `ai_agent_candidate_operation_dry_run_evidence_v1` / snapshot / 只讀 API / governance UI;13 類候選操作、13 組 dry-run evidence、6 個 verifier plan、7 個 gate evidence requirement、5 個 operator handoff;side-effect / runtime / queue / Telegram / production write / secret / destructive 全部 `0` | 下一步 P2-103 把任務結果接回 KM / LOGBOOK / 稽核軌跡;不直接 apply、不送 Telegram、不寫 Gateway queue | | P2-103 | 待辦 | 0 | Hermes | 把任務結果接回 KM / LOGBOOK / 稽核軌跡 | 證據寫入器 | 不洩漏 secret | | P2-104 | 待辦 | 0 | OpenClaw | 修復 `matched_playbook_id` 學習缺口 | playbook trust 更新 | 測試 + live 證據 | | P2-105 | 待辦 | 0 | OpenClaw | 批准前加入 critic / reviewer 評分 | 多 Agent 評分 | 不自動批准 | diff --git a/docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md b/docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md index cde541d90..999758d40 100644 --- a/docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md +++ b/docs/ai/AI_AGENT_INTERACTION_LEARNING_PROOF_2026-06-11.md @@ -1,8 +1,8 @@ # AI Agent 互動、溝通、學習與成長證據報告 > 日期:2026-06-11(台北時間) -> 文件定位:P2-403A 證據面 + P2-403B AgentSession / Redis Streams live read model gate + P2-403C Redis dry-run gate + P2-403D learning writeback approval package + P2-403E Telegram receipt approval package + P2-403F owner-approved learning dry-run / fixture dry-run、P2-403G runtime write gate review、P2-403H post-write verifier package、P2-403I runtime verifier evidence implementation review、P2-403J 報表真相 / 日週月報 / Agent 工作量 / 風險自動化 review、P2-403L 報表派送與自動處理啟動前閘門、P2-403M 報表 runtime no-write dry-run 證據包、P2-403N fixture smoke / queue preview readback / verifier dry-run、P2-404 runtime worker shadow / no-write execution evidence gate、P2-101 操作類別權限模型、API 與治理頁 UI。 -> 事實邊界:本波只建立可見證據面、read model gate、報表治理 review、runtime readiness gate、no-write dry-run、fixture/readback/verifier dry-run、shadow/no-write execution 證據包與 operation permission lane,不啟動 runtime worker、不建立 DB migration、不開 Redis consumer group、不發 Telegram、不寫 Gateway queue、不寫 delivery receipt、不排程實發報告、不啟動中低風險 auto worker、不執行 verifier live readback、不執行生產優化、不顯示工作視窗對話內容。 +> 文件定位:P2-403A 證據面 + P2-403B AgentSession / Redis Streams live read model gate + P2-403C Redis dry-run gate + P2-403D learning writeback approval package + P2-403E Telegram receipt approval package + P2-403F owner-approved learning dry-run / fixture dry-run、P2-403G runtime write gate review、P2-403H post-write verifier package、P2-403I runtime verifier evidence implementation review、P2-403J 報表真相 / 日週月報 / Agent 工作量 / 風險自動化 review、P2-403L 報表派送與自動處理啟動前閘門、P2-403M 報表 runtime no-write dry-run 證據包、P2-403N fixture smoke / queue preview readback / verifier dry-run、P2-404 runtime worker shadow / no-write execution evidence gate、P2-101 操作類別權限模型、P2-102 候選操作 dry-run 證據、API 與治理頁 UI。 +> 事實邊界:本波只建立可見證據面、read model gate、報表治理 review、runtime readiness gate、no-write dry-run、fixture/readback/verifier dry-run、shadow/no-write execution 證據包、operation permission lane 與 candidate dry-run evidence,不啟動 runtime worker、不建立 DB migration、不開 Redis consumer group、不發 Telegram、不寫 Gateway queue、不寫 delivery receipt、不排程實發報告、不啟動中低風險 auto worker、不執行 verifier live readback、不執行生產優化、不顯示內部協作內容。 ## 0. P2-403J 補記:報表真相、日週月報與風險自動化 Review @@ -40,9 +40,15 @@ 本段把 P2-404 的 shadow/no-write handoff 轉成 5 條 permission lane、13 類 operation category、3 個 Agent permission role、8 個 gate transition 與 5 個 operator decision template。OpenClaw 負責操作類別、風險層級、approval lane 與修復候選仲裁;Hermes 負責 KM / Runbook / 報表 / SRE 戰情室 queue candidate 草案;NemoTron 負責 no-write replay、verifier fixture、redaction / cost / secret boundary review。本段仍不啟動 runtime worker、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 delivery receipt、不跑 verifier live readback、不寫 production target、不讀 secret、不呼叫付費 provider、不執行 host / cluster / destructive action,所有 live count 仍為 `0`。 +## 0.6 P2-102 補記:候選操作 dry-run 證據 + +2026-06-12 已新增 P2-102:`ai_agent_candidate_operation_dry_run_evidence_v1`、`docs/evaluations/ai_agent_candidate_operation_dry_run_evidence_2026-06-12.json`、`GET /api/v1/agents/agent-candidate-operation-dry-run-evidence` 與治理頁區塊。 + +本段把 P2-101 的 13 類操作轉成候選操作 dry-run 證據:13 類 candidate operation、13 組 input / output evidence hash、6 個 verifier plan、7 個 gate evidence requirement 與 5 個 operator handoff。OpenClaw 負責修復候選、風險阻擋與 destructive gate;Hermes 負責報表 / SRE 戰情室 queue preview 與補證 handoff;NemoTron 負責 no-write replay、verifier allow-list、redaction / cost / secret boundary。所有 side-effect、runtime execution、Gateway queue write、Telegram send、production write、secret value read、paid provider call、host / cluster command 與 destructive action 仍為 `0`。 + ## 1. 結論 -已完成 P2-403A、P2-403B、P2-403C、P2-403D、P2-403E、P2-403F、P2-403G、P2-403H、P2-403I、P2-403J、P2-403L、P2-403M、P2-403N、P2-404 與 P2-101:讓統帥能在治理頁看到 OpenClaw / Hermes / NemoTron 的互動、接手、學習與成長是否真的有證據,並看到 live read model、Redis dry-run、handoff envelope、ack / dead-letter / replay、learning writeback approval、Telegram receipt approval、fixture dry-run、runtime write gate review、post-write verifier package、runtime verifier evidence review、報表真相、日週月報、Agent 工作量、圖表化報告、風險自動化政策、報表 runtime 啟動前閘門、no-write dry-run 證據包、fixture/readback/verifier dry-run 證據包、shadow/no-write execution evidence gate 與操作類別權限模型下一步要通過哪些 gate。 +已完成 P2-403A、P2-403B、P2-403C、P2-403D、P2-403E、P2-403F、P2-403G、P2-403H、P2-403I、P2-403J、P2-403L、P2-403M、P2-403N、P2-404、P2-101 與 P2-102:讓統帥能在治理頁看到 OpenClaw / Hermes / NemoTron 的互動、接手、學習與成長是否真的有證據,並看到 live read model、Redis dry-run、handoff envelope、ack / dead-letter / replay、learning writeback approval、Telegram receipt approval、fixture dry-run、runtime write gate review、post-write verifier package、runtime verifier evidence review、報表真相、日週月報、Agent 工作量、圖表化報告、風險自動化政策、報表 runtime 啟動前閘門、no-write dry-run 證據包、fixture/readback/verifier dry-run 證據包、shadow/no-write execution evidence gate、操作類別權限模型與候選操作 dry-run 證據下一步要通過哪些 gate。 目前真相: @@ -66,6 +72,7 @@ | P2-403N fixture readback package | 已完成,queue write / Telegram send / Bot API / worker / verifier live readback 全為 `0` | | P2-404 runtime worker shadow gate | 已完成,shadow worker live / queue write / Telegram send / production write 全為 `0` | | P2-101 operation permission model | 已完成,13 類操作已歸入只讀 / no-write replay / 提案 / 人工批准 / 明確阻擋,runtime execution / queue write / Telegram send / production write 全為 `0` | +| P2-102 candidate dry-run evidence | 已完成,13 類候選操作已有 dry-run evidence、verifier plan 與 operator handoff,所有 side-effect / runtime / queue / send / write 全為 `0` | 這代表使用者現在可以看見「哪裡已準備好、哪裡仍未運作、被哪個 gate 阻擋、下一步要如何驗證」。但還不能宣稱三個 Agent 已經在 production runtime 主動互傳訊息或自主學習。 @@ -130,17 +137,20 @@ | `docs/schemas/ai_agent_operation_permission_model_v1.schema.json` | P2-101 操作類別權限模型 schema;強制 runtime execution、queue write、Telegram send、Bot API、receipt write、auto worker、verifier live readback、production write、secret / paid provider、host command 與 destructive action 維持未授權 | | `docs/evaluations/ai_agent_operation_permission_model_2026-06-12.json` | P2-101 committed snapshot,完成度 `97%`,5 條 permission lane、13 類 operation category、3 個 Agent permission role、8 個 gate transition、5 個 operator decision template;所有 live counts 全為 `0` | | `GET /api/v1/agents/agent-operation-permission-model` | 只讀 API;不啟動 runtime worker、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 production target、不讀 secret | +| `docs/schemas/ai_agent_candidate_operation_dry_run_evidence_v1.schema.json` | P2-102 候選操作 dry-run 證據 schema;強制 side-effect、runtime execution、queue write、Telegram send、production write、secret / paid provider 與 destructive action 維持 `0 / false` | +| `docs/evaluations/ai_agent_candidate_operation_dry_run_evidence_2026-06-12.json` | P2-102 committed snapshot,完成度 `98%`,13 類候選操作、13 組 dry-run evidence、6 個 verifier plan、7 個 gate evidence requirement、5 個 operator handoff;所有 live counts 全為 `0` | +| `GET /api/v1/agents/agent-candidate-operation-dry-run-evidence` | 只讀 API;不啟動 runtime worker、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 production target、不讀 secret、不執行 destructive action | | `apps/api/src/services/ai_agent_interaction_learning_proof.py` | 只讀 loader 與安全驗證 | | `apps/api/src/services/ai_agent_live_read_model_gate.py` | P2-403B 只讀 loader;拒絕 live DB query、Redis consumer、unsafe fields、Telegram 與 writeback | | `GET /api/v1/agents/agent-interaction-learning-proof` | 只讀 API,不啟動 worker、不碰 Redis / DB runtime、不發 Telegram | | `GET /api/v1/agents/agent-live-read-model-gate` | 只讀 API,不連 DB、不讀寫 Redis、不發 Telegram | -| governance UI | 新增證據階梯、目前真相、P2-403B live read gate、P2-403C Redis dry-run gate、P2-403D learning writeback approval package、P2-403E Telegram receipt approval package、P2-403F owner-approved learning dry-run / fixture dry-run、P2-403G runtime write gate review、P2-403H post-write verifier package、P2-403I runtime verifier evidence review、P2-403J 報表真相 / 日週月報 / Agent 工作量 / 圖表 / AI 建議、P2-403L 報表 runtime readiness、P2-403M no-write dry-run、P2-403N fixture readback、P2-404 shadow gate、P2-101 operation permission model、Agent lane、可觀測訊號、runtime gates、前端 redaction | +| governance UI | 新增證據階梯、目前真相、P2-403B live read gate、P2-403C Redis dry-run gate、P2-403D learning writeback approval package、P2-403E Telegram receipt approval package、P2-403F owner-approved learning dry-run / fixture dry-run、P2-403G runtime write gate review、P2-403H post-write verifier package、P2-403I runtime verifier evidence review、P2-403J 報表真相 / 日週月報 / Agent 工作量 / 圖表 / AI 建議、P2-403L 報表 runtime readiness、P2-403M no-write dry-run、P2-403N fixture readback、P2-404 shadow gate、P2-101 operation permission model、P2-102 candidate dry-run evidence、Agent lane、可觀測訊號、runtime gates、前端 redaction | ## 5. 後續優先順序 | 優先 | ID | 工作 | gate | |---:|---|---|---| -| 1 | P2-102 | 所有候選操作都要有 dry-run 證據 | dry-run 合約 | +| 1 | P2-103 | 把任務結果接回 KM / LOGBOOK / 稽核軌跡 | 證據寫入器 | ## 6. 紅線 diff --git a/docs/evaluations/ai_agent_candidate_operation_dry_run_evidence_2026-06-12.json b/docs/evaluations/ai_agent_candidate_operation_dry_run_evidence_2026-06-12.json new file mode 100644 index 000000000..3cce3e874 --- /dev/null +++ b/docs/evaluations/ai_agent_candidate_operation_dry_run_evidence_2026-06-12.json @@ -0,0 +1,580 @@ +{ + "schema_version": "ai_agent_candidate_operation_dry_run_evidence_v1", + "generated_at": "2026-06-12T16:20:00+08:00", + "program_status": { + "overall_completion_percent": 98, + "current_priority": "P2", + "current_task_id": "P2-102", + "next_task_id": "P2-103", + "read_only_mode": true, + "runtime_authority": "candidate_operation_dry_run_evidence_only_no_live_execution_or_send", + "status_note": "P2-102 承接 P2-101 操作類別權限模型,把所有候選操作固定成 no-write dry-run 證據、side-effect count、verifier plan 與人工 handoff;目前仍不啟動 live worker、不寫 Gateway queue、不送 Telegram、不寫 production target。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_operation_permission_model_2026-06-12.json", + "docs/evaluations/ai_agent_runtime_worker_shadow_gate_2026-06-12.json", + "docs/evaluations/ai_agent_report_runtime_fixture_readback_2026-06-12.json", + "docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md", + "docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md" + ], + "dry_run_truth": { + "p2_101_permission_model_loaded": true, + "dry_run_evidence_gate_ready": true, + "all_candidate_operations_have_dry_run_evidence": true, + "side_effect_counter_ready": true, + "verifier_plan_ready": true, + "rollback_or_noop_plan_ready": true, + "owner_review_packet_ready": true, + "runtime_execution_enabled": false, + "gateway_queue_write_enabled": false, + "telegram_send_enabled": false, + "telegram_bot_api_call_enabled": false, + "delivery_receipt_write_enabled": false, + "ai_runtime_worker_enabled": false, + "medium_low_auto_worker_enabled": false, + "post_action_verifier_live_readback_enabled": false, + "production_write_enabled": false, + "secret_value_read_enabled": false, + "paid_provider_call_enabled": false, + "host_or_cluster_command_enabled": false, + "destructive_operation_enabled": false, + "work_window_transcript_display_allowed": false, + "runtime_execution_count_24h": 0, + "gateway_queue_write_count_24h": 0, + "telegram_send_count_24h": 0, + "telegram_bot_api_call_count_24h": 0, + "delivery_receipt_write_count_24h": 0, + "ai_runtime_worker_run_count_24h": 0, + "medium_low_auto_execution_count_24h": 0, + "post_action_verifier_live_readback_count_24h": 0, + "production_write_count_24h": 0, + "secret_value_read_count_24h": 0, + "paid_provider_call_count_24h": 0, + "host_or_cluster_command_count_24h": 0, + "destructive_operation_count_24h": 0, + "truth_note": "本段只代表候選操作都具備可審核 dry-run 證據與 verifier plan;所有 runtime、queue、Telegram、production write、secret / paid provider 與 destructive action 仍為 0 / false。" + }, + "candidate_operations": [ + { + "candidate_id": "candidate_observe_inventory_read", + "source_category_id": "observe_inventory_read", + "display_name": "只讀清冊盤點 dry-run", + "risk_tier": "low", + "permission_lane": "observe_only", + "owner_agent": "hermes", + "dry_run_status": "passed_no_write", + "dry_run_scope": "讀取 committed snapshot / schema / source refs,產生 freshness、coverage 與缺口摘要;不讀 live host、不改任何目標。", + "input_evidence_hash": "sha256:0101010101010101010101010101010101010101010101010101010101010101", + "output_evidence_hash": "sha256:0111111111111111111111111111111111111111111111111111111111111111", + "side_effect_count": 0, + "production_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "secret_value_read_count": 0, + "destructive_action_count": 0, + "blocked_actions": ["live host read", "production write", "notification send"], + "required_human_decision": "人工確認 committed evidence 是否足以支撐下一步;若需要 live readback,必須另開 allow-list gate。", + "verifier_plan_id": "verifier_redacted_evidence_hash", + "rollback_or_noop_plan": "no-op;只讀清冊輸出不產生 rollback 需求。", + "next_gate": "operator evidence review" + }, + { + "candidate_id": "candidate_diagnose_correlate_evidence", + "source_category_id": "diagnose_correlate_evidence", + "display_name": "診斷證據關聯 dry-run", + "risk_tier": "low", + "permission_lane": "observe_only", + "owner_agent": "openclaw", + "dry_run_status": "passed_no_write", + "dry_run_scope": "使用 committed incident / MCP evidence refs 重放分類與關聯,輸出紅線摘要與下一步建議。", + "input_evidence_hash": "sha256:1010101010101010101010101010101010101010101010101010101010101010", + "output_evidence_hash": "sha256:1111111111111111111111111111111111111111111111111111111111111111", + "side_effect_count": 0, + "production_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "secret_value_read_count": 0, + "destructive_action_count": 0, + "blocked_actions": ["repair execution", "queue write", "Telegram send"], + "required_human_decision": "若 evidence 缺口仍在,人工只需補來源或標記 no-action,不得直接執行修復。", + "verifier_plan_id": "verifier_redacted_evidence_hash", + "rollback_or_noop_plan": "no-op;只讀 evidence replay 無需 rollback。", + "next_gate": "operator evidence review" + }, + { + "candidate_id": "candidate_report_digest_queue", + "source_category_id": "report_digest_queue_candidate", + "display_name": "SRE 戰情室 queue digest dry-run", + "risk_tier": "medium", + "permission_lane": "no_write_replay_allowed", + "owner_agent": "hermes", + "dry_run_status": "needs_owner_review", + "dry_run_scope": "把日報 / 週報 / action-required digest 轉成 queue preview,不寫 Gateway queue、不呼叫 Bot API。", + "input_evidence_hash": "sha256:2020202020202020202020202020202020202020202020202020202020202020", + "output_evidence_hash": "sha256:2222222222222222222222222222222222222222222222222222222222222222", + "side_effect_count": 0, + "production_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "secret_value_read_count": 0, + "destructive_action_count": 0, + "blocked_actions": ["Gateway queue write", "Telegram sendMessage", "delivery receipt write"], + "required_human_decision": "人工確認 route、dedupe key、failure-only 門檻與 redaction 後,才可進 queue write approval gate。", + "verifier_plan_id": "verifier_gateway_queue_preview", + "rollback_or_noop_plan": "no-op;preview 不進 queue,若內容不合格只回退到 Hermes digest draft。", + "next_gate": "gateway_queue_write_permission_gate" + }, + { + "candidate_id": "candidate_shadow_no_write_replay", + "source_category_id": "shadow_no_write_replay", + "display_name": "shadow no-write replay dry-run", + "risk_tier": "medium", + "permission_lane": "no_write_replay_allowed", + "owner_agent": "nemotron", + "dry_run_status": "passed_no_write", + "dry_run_scope": "重放 P2-404 shadow candidate,產生 promotion hash、輸出差異與 verifier input。", + "input_evidence_hash": "sha256:3030303030303030303030303030303030303030303030303030303030303030", + "output_evidence_hash": "sha256:3333333333333333333333333333333333333333333333333333333333333333", + "side_effect_count": 0, + "production_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "secret_value_read_count": 0, + "destructive_action_count": 0, + "blocked_actions": ["live worker", "result write", "verifier live readback"], + "required_human_decision": "人工確認 replay hash 與 verifier fixture 是否一致;未通過不得升級成 worker。", + "verifier_plan_id": "verifier_shadow_replay_fixture", + "rollback_or_noop_plan": "no-op;只保留 replay 證據,不寫任何 runtime result。", + "next_gate": "runtime worker approval package" + }, + { + "candidate_id": "candidate_manual_sop_draft", + "source_category_id": "manual_sop_draft", + "display_name": "人工 SOP 產生 dry-run", + "risk_tier": "medium", + "permission_lane": "proposal_only", + "owner_agent": "hermes", + "dry_run_status": "passed_no_write", + "dry_run_scope": "把修復候選轉成人工步驟、驗證步驟與 rollback 草案,不執行任何命令。", + "input_evidence_hash": "sha256:4040404040404040404040404040404040404040404040404040404040404040", + "output_evidence_hash": "sha256:4444444444444444444444444444444444444444444444444444444444444444", + "side_effect_count": 0, + "production_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "secret_value_read_count": 0, + "destructive_action_count": 0, + "blocked_actions": ["host command", "service restart", "config reload"], + "required_human_decision": "人工依 SOP 決定是否進 owner approval;AI 不自行套用。", + "verifier_plan_id": "verifier_manual_sop_completeness", + "rollback_or_noop_plan": "no-op;SOP 只是文字草案,若不採用則關閉候選。", + "next_gate": "owner review" + }, + { + "candidate_id": "candidate_repair_candidate_proposal", + "source_category_id": "repair_candidate_proposal", + "display_name": "修復候選提案 dry-run", + "risk_tier": "medium", + "permission_lane": "proposal_only", + "owner_agent": "openclaw", + "dry_run_status": "needs_owner_review", + "dry_run_scope": "用 MCP evidence、PlayBook trust 與 blast radius 產生修復候選,不跑 Ansible / kubectl / restart。", + "input_evidence_hash": "sha256:5050505050505050505050505050505050505050505050505050505050505050", + "output_evidence_hash": "sha256:5555555555555555555555555555555555555555555555555555555555555555", + "side_effect_count": 0, + "production_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "secret_value_read_count": 0, + "destructive_action_count": 0, + "blocked_actions": ["Ansible apply", "kubectl apply", "service restart"], + "required_human_decision": "人工審查 MCP evidence、PlayBook trust、rollback owner 與 verifier plan;缺任一項就退回補證。", + "verifier_plan_id": "verifier_repair_candidate_consistency", + "rollback_or_noop_plan": "若 dry-run mismatch,標記候選 degraded 並回到 evidence collection。", + "next_gate": "repair owner approval" + }, + { + "candidate_id": "candidate_low_risk_noop_execution", + "source_category_id": "low_risk_noop_execution", + "display_name": "低風險 no-op 自動處理 dry-run", + "risk_tier": "medium", + "permission_lane": "human_approval_required", + "owner_agent": "openclaw", + "dry_run_status": "needs_owner_review", + "dry_run_scope": "只產生 no-op plan、expected result 與 post-action verifier,不啟動中低風險 worker。", + "input_evidence_hash": "sha256:6060606060606060606060606060606060606060606060606060606060606060", + "output_evidence_hash": "sha256:6666666666666666666666666666666666666666666666666666666666666666", + "side_effect_count": 0, + "production_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "secret_value_read_count": 0, + "destructive_action_count": 0, + "blocked_actions": ["auto worker execution", "result write", "delivery receipt write"], + "required_human_decision": "人工確認這真的是 no-op;批准前只能顯示候選,不產生任何 runtime action。", + "verifier_plan_id": "verifier_noop_result_contract", + "rollback_or_noop_plan": "no-op;若 verifier plan 無法定義,候選自動降級人工 SOP。", + "next_gate": "medium_low_auto_worker_permission_gate" + }, + { + "candidate_id": "candidate_medium_risk_repair_execution", + "source_category_id": "medium_risk_repair_execution", + "display_name": "中風險修復執行 dry-run", + "risk_tier": "high", + "permission_lane": "human_approval_required", + "owner_agent": "openclaw", + "dry_run_status": "blocked_by_policy", + "dry_run_scope": "只能保留 dry-run evidence、rollback owner 與維護窗口草案;不可 apply 或 restart。", + "input_evidence_hash": "sha256:7070707070707070707070707070707070707070707070707070707070707070", + "output_evidence_hash": "sha256:7777777777777777777777777777777777777777777777777777777777777777", + "side_effect_count": 0, + "production_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "secret_value_read_count": 0, + "destructive_action_count": 0, + "blocked_actions": ["repair apply", "restart", "config reload"], + "required_human_decision": "需要 owner approval、maintenance window、rollback owner、post-action verifier 四者齊備。", + "verifier_plan_id": "verifier_repair_postcheck_plan", + "rollback_or_noop_plan": "未批准時維持 no-op;批准後仍需另行 runtime write gate。", + "next_gate": "production_write_permission_gate" + }, + { + "candidate_id": "candidate_post_action_verifier_live_readback", + "source_category_id": "post_action_verifier_live_readback", + "display_name": "post-action verifier readback dry-run", + "risk_tier": "high", + "permission_lane": "human_approval_required", + "owner_agent": "nemotron", + "dry_run_status": "blocked_until_allowlist", + "dry_run_scope": "先定義 readback allow-list、redaction 與 failure lane;不讀 live target、不寫結果。", + "input_evidence_hash": "sha256:8080808080808080808080808080808080808080808080808080808080808080", + "output_evidence_hash": "sha256:8888888888888888888888888888888888888888888888888888888888888888", + "side_effect_count": 0, + "production_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "secret_value_read_count": 0, + "destructive_action_count": 0, + "blocked_actions": ["canonical target readback", "result write", "failure receipt send"], + "required_human_decision": "人工確認 allow-list 與 redaction 後,才可進 live verifier gate。", + "verifier_plan_id": "verifier_live_readback_allowlist", + "rollback_or_noop_plan": "allow-list 不完整時維持 no-op,並產生人工檢查清單。", + "next_gate": "post_action_verifier_live_gate" + }, + { + "candidate_id": "candidate_telegram_gateway_queue_write", + "source_category_id": "telegram_gateway_queue_write", + "display_name": "Telegram Gateway queue write dry-run", + "risk_tier": "high", + "permission_lane": "human_approval_required", + "owner_agent": "hermes", + "dry_run_status": "blocked_until_allowlist", + "dry_run_scope": "只產生 SRE 戰情室 queue candidate、dedupe key 與 delivery verifier;不寫 queue、不送 Bot API。", + "input_evidence_hash": "sha256:9090909090909090909090909090909090909090909090909090909090909090", + "output_evidence_hash": "sha256:9999999999999999999999999999999999999999999999999999999999999999", + "side_effect_count": 0, + "production_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "secret_value_read_count": 0, + "destructive_action_count": 0, + "blocked_actions": ["Gateway queue write", "Bot API sendMessage", "route mutation"], + "required_human_decision": "人工確認唯一通道為 AwoooI SRE 戰情室、route / dedupe / receipt 完整後才可進下一 gate。", + "verifier_plan_id": "verifier_gateway_queue_preview", + "rollback_or_noop_plan": "未批准時只保留 preview;若 route 不符,標記 legacy route gap。", + "next_gate": "telegram_send_permission_gate" + }, + { + "candidate_id": "candidate_production_config_or_data_write", + "source_category_id": "production_config_or_data_write", + "display_name": "production config / data write dry-run", + "risk_tier": "critical", + "permission_lane": "explicitly_blocked", + "owner_agent": "openclaw", + "dry_run_status": "blocked_by_policy", + "dry_run_scope": "只能產生 source-of-truth diff、rollback ref、maintenance window 草案;不寫 production。", + "input_evidence_hash": "sha256:a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0", + "output_evidence_hash": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "side_effect_count": 0, + "production_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "secret_value_read_count": 0, + "destructive_action_count": 0, + "blocked_actions": ["DB write", "ConfigMap apply", "Nginx reload", "workflow mutation"], + "required_human_decision": "必須另行 owner approval、rollback owner、maintenance window 與 post-check,不可由 P2-102 開 gate。", + "verifier_plan_id": "verifier_production_write_preflight", + "rollback_or_noop_plan": "no-op;任何 apply 都需新的批准包。", + "next_gate": "production_write_permission_gate" + }, + { + "candidate_id": "candidate_secret_or_paid_provider_access", + "source_category_id": "secret_or_paid_provider_access", + "display_name": "secret / paid provider boundary dry-run", + "risk_tier": "critical", + "permission_lane": "explicitly_blocked", + "owner_agent": "nemotron", + "dry_run_status": "blocked_by_policy", + "dry_run_scope": "只產生 metadata-only checklist、費用批准需求與降級提案;不讀 secret、不呼叫付費 provider。", + "input_evidence_hash": "sha256:b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0", + "output_evidence_hash": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "side_effect_count": 0, + "production_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "secret_value_read_count": 0, + "destructive_action_count": 0, + "blocked_actions": ["secret value read", "paid API call", "authorization header read"], + "required_human_decision": "只能審查 metadata、secret name、費用批准與供應商 fallback 理由;明文與付費呼叫需另行批准。", + "verifier_plan_id": "verifier_redaction_and_cost_boundary", + "rollback_or_noop_plan": "no-op;不提供 runtime escalation。", + "next_gate": "secret_or_paid_provider_gate" + }, + { + "candidate_id": "candidate_destructive_host_or_cluster_action", + "source_category_id": "destructive_host_or_cluster_action", + "display_name": "破壞性主機 / 叢集動作 dry-run", + "risk_tier": "critical", + "permission_lane": "explicitly_blocked", + "owner_agent": "openclaw", + "dry_run_status": "blocked_by_policy", + "dry_run_scope": "只產生 break-glass 前置檢查、維護窗口、rollback owner 與替代 no-op;不 SSH、不 sudo、不刪除、不重啟、不套用叢集變更。", + "input_evidence_hash": "sha256:d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0", + "output_evidence_hash": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "side_effect_count": 0, + "production_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "secret_value_read_count": 0, + "destructive_action_count": 0, + "blocked_actions": ["ssh write", "sudo", "delete", "reboot", "kubectl apply"], + "required_human_decision": "需 owner approval、maintenance window、rollback owner、blast radius 與 post-check;P2-102 不允許開啟任何 destructive gate。", + "verifier_plan_id": "verifier_destructive_boundary_preflight", + "rollback_or_noop_plan": "no-op;破壞性動作一律留在 break-glass 批准包外,不由 AI 自行執行。", + "next_gate": "break_glass_or_destructive_action_gate" + } + ], + "verifier_plans": [ + { + "plan_id": "verifier_redacted_evidence_hash", + "display_name": "redacted evidence hash verifier", + "owner_agent": "hermes", + "verifier_scope": "比對 input / output hash、source refs 與 redaction 欄位。", + "expected_signal": "hash 存在、source ref 可追溯、未顯示 raw payload。", + "failure_lane": "退回 evidence collection,不進 queue 或 runtime。", + "live_readback_enabled": false, + "writes_result": false, + "requires_secret_value": false, + "evidence_hash": "sha256:c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1" + }, + { + "plan_id": "verifier_gateway_queue_preview", + "display_name": "Gateway queue preview verifier", + "owner_agent": "hermes", + "verifier_scope": "驗證 SRE 戰情室 route、dedupe key、receipt plan 與 failure-only 門檻。", + "expected_signal": "preview 可讀、route 為 AwoooI SRE 戰情室、queue write count 仍為 0。", + "failure_lane": "標記 route gap 或 redaction gap,退回 P2-403K / P2-403L。", + "live_readback_enabled": false, + "writes_result": false, + "requires_secret_value": false, + "evidence_hash": "sha256:c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2" + }, + { + "plan_id": "verifier_shadow_replay_fixture", + "display_name": "shadow replay fixture verifier", + "owner_agent": "nemotron", + "verifier_scope": "檢查 no-write replay、promotion hash、side-effect count 與 model comparison output。", + "expected_signal": "side-effect count 為 0,replay output 可被人工審查。", + "failure_lane": "標記 replay degraded,回到 shadow candidate。", + "live_readback_enabled": false, + "writes_result": false, + "requires_secret_value": false, + "evidence_hash": "sha256:c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3" + }, + { + "plan_id": "verifier_repair_candidate_consistency", + "display_name": "repair candidate consistency verifier", + "owner_agent": "openclaw", + "verifier_scope": "比對 MCP evidence、PlayBook trust、rollback owner、blast radius 與 dry-run output。", + "expected_signal": "修復候選具備 evidence、rollback 與明確人工下一步。", + "failure_lane": "退回人工 SOP 或補 MCP evidence。", + "live_readback_enabled": false, + "writes_result": false, + "requires_secret_value": false, + "evidence_hash": "sha256:c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4" + }, + { + "plan_id": "verifier_live_readback_allowlist", + "display_name": "live readback allow-list verifier", + "owner_agent": "nemotron", + "verifier_scope": "先審查允許讀回的欄位、redaction 與 failure receipt template。", + "expected_signal": "allow-list 完整、secret / raw payload / private reasoning 都不顯示。", + "failure_lane": "阻擋 live readback,產生人工檢查清單。", + "live_readback_enabled": false, + "writes_result": false, + "requires_secret_value": false, + "evidence_hash": "sha256:c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5" + }, + { + "plan_id": "verifier_destructive_boundary_preflight", + "display_name": "destructive boundary preflight verifier", + "owner_agent": "openclaw", + "verifier_scope": "確認破壞性候選仍停留在 break-glass 文件與 no-op 替代方案,沒有任何 host / cluster action。", + "expected_signal": "destructive action count 為 0,且 next gate 明確指向 break-glass 或人工批准包。", + "failure_lane": "阻擋候選,回退到人工 SOP 與風險盤點。", + "live_readback_enabled": false, + "writes_result": false, + "requires_secret_value": false, + "evidence_hash": "sha256:c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6c6" + } + ], + "gate_evidence_requirements": [ + { + "gate_id": "p2_102_dry_run_evidence_gate", + "display_name": "P2-102 dry-run evidence gate", + "required_evidence": ["dry-run input hash", "dry-run output hash", "side-effect count", "verifier plan"], + "missing_or_blocked": [], + "opens_live_execution": false + }, + { + "gate_id": "gateway_queue_write_permission_gate", + "display_name": "Gateway queue write permission gate", + "required_evidence": ["AwoooI SRE 戰情室 route", "dedupe key", "delivery receipt plan", "failure-only policy"], + "missing_or_blocked": ["queue write approval", "delivery receipt E2E"], + "opens_live_execution": false + }, + { + "gate_id": "medium_low_auto_worker_permission_gate", + "display_name": "中低風險 auto worker permission gate", + "required_evidence": ["owner approval", "no-op / rollback plan", "post-action verifier", "blast radius"], + "missing_or_blocked": ["runtime worker approval", "live verifier approval"], + "opens_live_execution": false + }, + { + "gate_id": "post_action_verifier_live_gate", + "display_name": "post-action verifier live gate", + "required_evidence": ["readback allow-list", "redaction plan", "failure lane", "result write boundary"], + "missing_or_blocked": ["live readback allow-list approval"], + "opens_live_execution": false + }, + { + "gate_id": "production_write_permission_gate", + "display_name": "production write permission gate", + "required_evidence": ["source-of-truth diff", "rollback owner", "maintenance window", "post-check plan"], + "missing_or_blocked": ["owner approval", "runtime write gate"], + "opens_live_execution": false + }, + { + "gate_id": "secret_or_paid_provider_gate", + "display_name": "secret / paid provider gate", + "required_evidence": ["metadata-only scope", "cost approval", "redaction policy", "owner response"], + "missing_or_blocked": ["secret value read is prohibited", "paid provider expansion approval"], + "opens_live_execution": false + }, + { + "gate_id": "break_glass_or_destructive_action_gate", + "display_name": "break-glass / destructive action gate", + "required_evidence": ["owner approval", "maintenance window", "rollback owner", "blast radius", "post-check plan"], + "missing_or_blocked": ["break-glass approval", "runtime destructive action approval"], + "opens_live_execution": false + } + ], + "operator_handoffs": [ + { + "handoff_id": "handoff_collect_missing_evidence", + "display_name": "補證據", + "owner_agent": "hermes", + "human_instruction": "補 source ref、MCP evidence、incident / run 關聯或 redaction hash;不要執行修復。", + "creates_runtime_action": false, + "requires_human_review": true + }, + { + "handoff_id": "handoff_review_repair_candidate", + "display_name": "審修復候選", + "owner_agent": "openclaw", + "human_instruction": "檢查 PlayBook trust、MCP evidence、dry-run hash、rollback owner、blast radius;缺一項就退回補證。", + "creates_runtime_action": false, + "requires_human_review": true + }, + { + "handoff_id": "handoff_review_sre_queue_preview", + "display_name": "審戰情室 queue preview", + "owner_agent": "hermes", + "human_instruction": "確認 route 只指向 AwoooI SRE 戰情室、dedupe / receipt / failure-only 門檻完整後,才可另行批准 queue write。", + "creates_runtime_action": false, + "requires_human_review": true + }, + { + "handoff_id": "handoff_review_verifier_allowlist", + "display_name": "審 verifier allow-list", + "owner_agent": "nemotron", + "human_instruction": "確認 readback 欄位、redaction、failure receipt 與 result write 邊界;未通過不得 live readback。", + "creates_runtime_action": false, + "requires_human_review": true + }, + { + "handoff_id": "handoff_escalate_blocked_operation", + "display_name": "升級阻擋操作", + "owner_agent": "openclaw", + "human_instruction": "對 production write、secret / paid provider、host / cluster destructive action 只建立批准包,不在 P2-102 開 gate。", + "creates_runtime_action": false, + "requires_human_review": true + } + ], + "display_redaction_contract": { + "redaction_required": true, + "raw_prompt_display_allowed": false, + "private_reasoning_display_allowed": false, + "secret_value_display_allowed": false, + "raw_telegram_payload_display_allowed": false, + "work_window_transcript_display_allowed": false, + "allowed_display_fields": [ + "candidate_id", + "source_category_id", + "display_name", + "risk_tier", + "permission_lane", + "owner_agent", + "dry_run_status", + "dry_run_scope", + "input_evidence_hash", + "output_evidence_hash", + "side_effect_count", + "blocked_actions", + "required_human_decision", + "verifier_plan_id", + "rollback_or_noop_plan", + "next_gate" + ], + "blocked_display_fields": [ + "raw_prompt", + "chain_of_thought", + "telegram_chat_id", + "telegram_message_id", + "bot_token", + "authorization_header", + "secret_value", + "raw_tool_output", + "work_window_transcript" + ] + }, + "rollups": { + "candidate_operation_count": 13, + "candidate_with_dry_run_evidence_count": 13, + "passed_no_write_count": 4, + "needs_owner_review_count": 3, + "blocked_until_allowlist_count": 2, + "blocked_by_policy_count": 4, + "verifier_plan_count": 6, + "gate_evidence_requirement_count": 7, + "operator_handoff_count": 5, + "side_effect_count": 0, + "runtime_execution_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "production_write_count": 0, + "secret_value_read_count": 0, + "destructive_operation_count": 0 + } +} diff --git a/docs/schemas/ai_agent_candidate_operation_dry_run_evidence_v1.schema.json b/docs/schemas/ai_agent_candidate_operation_dry_run_evidence_v1.schema.json new file mode 100644 index 000000000..2e31523fa --- /dev/null +++ b/docs/schemas/ai_agent_candidate_operation_dry_run_evidence_v1.schema.json @@ -0,0 +1,336 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://awoooi.wooo.work/schemas/ai_agent_candidate_operation_dry_run_evidence_v1.schema.json", + "title": "AI Agent Candidate Operation Dry-Run Evidence v1", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "program_status", + "source_refs", + "dry_run_truth", + "candidate_operations", + "verifier_plans", + "gate_evidence_requirements", + "operator_handoffs", + "display_redaction_contract", + "rollups" + ], + "properties": { + "schema_version": { "const": "ai_agent_candidate_operation_dry_run_evidence_v1" }, + "generated_at": { "type": "string" }, + "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", "minimum": 0, "maximum": 100 }, + "current_priority": { "enum": ["P0", "P1", "P2", "P3"] }, + "current_task_id": { "const": "P2-102" }, + "next_task_id": { "const": "P2-103" }, + "read_only_mode": { "const": true }, + "runtime_authority": { "const": "candidate_operation_dry_run_evidence_only_no_live_execution_or_send" }, + "status_note": { "type": "string" } + }, + "additionalProperties": false + }, + "source_refs": { "type": "array", "items": { "type": "string" }, "minItems": 1 }, + "dry_run_truth": { + "type": "object", + "required": [ + "p2_101_permission_model_loaded", + "dry_run_evidence_gate_ready", + "all_candidate_operations_have_dry_run_evidence", + "side_effect_counter_ready", + "verifier_plan_ready", + "rollback_or_noop_plan_ready", + "owner_review_packet_ready", + "runtime_execution_enabled", + "gateway_queue_write_enabled", + "telegram_send_enabled", + "telegram_bot_api_call_enabled", + "delivery_receipt_write_enabled", + "ai_runtime_worker_enabled", + "medium_low_auto_worker_enabled", + "post_action_verifier_live_readback_enabled", + "production_write_enabled", + "secret_value_read_enabled", + "paid_provider_call_enabled", + "host_or_cluster_command_enabled", + "destructive_operation_enabled", + "work_window_transcript_display_allowed", + "runtime_execution_count_24h", + "gateway_queue_write_count_24h", + "telegram_send_count_24h", + "telegram_bot_api_call_count_24h", + "delivery_receipt_write_count_24h", + "ai_runtime_worker_run_count_24h", + "medium_low_auto_execution_count_24h", + "post_action_verifier_live_readback_count_24h", + "production_write_count_24h", + "secret_value_read_count_24h", + "paid_provider_call_count_24h", + "host_or_cluster_command_count_24h", + "destructive_operation_count_24h", + "truth_note" + ], + "properties": { + "p2_101_permission_model_loaded": { "const": true }, + "dry_run_evidence_gate_ready": { "const": true }, + "all_candidate_operations_have_dry_run_evidence": { "const": true }, + "side_effect_counter_ready": { "const": true }, + "verifier_plan_ready": { "const": true }, + "rollback_or_noop_plan_ready": { "const": true }, + "owner_review_packet_ready": { "const": true }, + "runtime_execution_enabled": { "const": false }, + "gateway_queue_write_enabled": { "const": false }, + "telegram_send_enabled": { "const": false }, + "telegram_bot_api_call_enabled": { "const": false }, + "delivery_receipt_write_enabled": { "const": false }, + "ai_runtime_worker_enabled": { "const": false }, + "medium_low_auto_worker_enabled": { "const": false }, + "post_action_verifier_live_readback_enabled": { "const": false }, + "production_write_enabled": { "const": false }, + "secret_value_read_enabled": { "const": false }, + "paid_provider_call_enabled": { "const": false }, + "host_or_cluster_command_enabled": { "const": false }, + "destructive_operation_enabled": { "const": false }, + "work_window_transcript_display_allowed": { "const": false }, + "runtime_execution_count_24h": { "const": 0 }, + "gateway_queue_write_count_24h": { "const": 0 }, + "telegram_send_count_24h": { "const": 0 }, + "telegram_bot_api_call_count_24h": { "const": 0 }, + "delivery_receipt_write_count_24h": { "const": 0 }, + "ai_runtime_worker_run_count_24h": { "const": 0 }, + "medium_low_auto_execution_count_24h": { "const": 0 }, + "post_action_verifier_live_readback_count_24h": { "const": 0 }, + "production_write_count_24h": { "const": 0 }, + "secret_value_read_count_24h": { "const": 0 }, + "paid_provider_call_count_24h": { "const": 0 }, + "host_or_cluster_command_count_24h": { "const": 0 }, + "destructive_operation_count_24h": { "const": 0 }, + "truth_note": { "type": "string" } + }, + "additionalProperties": false + }, + "candidate_operations": { + "type": "array", + "minItems": 13, + "items": { + "type": "object", + "required": [ + "candidate_id", + "source_category_id", + "display_name", + "risk_tier", + "permission_lane", + "owner_agent", + "dry_run_status", + "dry_run_scope", + "input_evidence_hash", + "output_evidence_hash", + "side_effect_count", + "production_write_count", + "gateway_queue_write_count", + "telegram_send_count", + "secret_value_read_count", + "destructive_action_count", + "blocked_actions", + "required_human_decision", + "verifier_plan_id", + "rollback_or_noop_plan", + "next_gate" + ], + "properties": { + "candidate_id": { "type": "string" }, + "source_category_id": { "type": "string" }, + "display_name": { "type": "string" }, + "risk_tier": { "enum": ["low", "medium", "high", "critical"] }, + "permission_lane": { + "enum": [ + "observe_only", + "no_write_replay_allowed", + "proposal_only", + "human_approval_required", + "explicitly_blocked" + ] + }, + "owner_agent": { "enum": ["openclaw", "hermes", "nemotron"] }, + "dry_run_status": { + "enum": [ + "passed_no_write", + "needs_owner_review", + "blocked_until_allowlist", + "blocked_by_policy" + ] + }, + "dry_run_scope": { "type": "string" }, + "input_evidence_hash": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" }, + "output_evidence_hash": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" }, + "side_effect_count": { "const": 0 }, + "production_write_count": { "const": 0 }, + "gateway_queue_write_count": { "const": 0 }, + "telegram_send_count": { "const": 0 }, + "secret_value_read_count": { "const": 0 }, + "destructive_action_count": { "const": 0 }, + "blocked_actions": { "type": "array", "items": { "type": "string" } }, + "required_human_decision": { "type": "string" }, + "verifier_plan_id": { "type": "string" }, + "rollback_or_noop_plan": { "type": "string" }, + "next_gate": { "type": "string" } + }, + "additionalProperties": false + } + }, + "verifier_plans": { + "type": "array", + "minItems": 5, + "items": { + "type": "object", + "required": [ + "plan_id", + "display_name", + "owner_agent", + "verifier_scope", + "expected_signal", + "failure_lane", + "live_readback_enabled", + "writes_result", + "requires_secret_value", + "evidence_hash" + ], + "properties": { + "plan_id": { "type": "string" }, + "display_name": { "type": "string" }, + "owner_agent": { "enum": ["openclaw", "hermes", "nemotron"] }, + "verifier_scope": { "type": "string" }, + "expected_signal": { "type": "string" }, + "failure_lane": { "type": "string" }, + "live_readback_enabled": { "const": false }, + "writes_result": { "const": false }, + "requires_secret_value": { "const": false }, + "evidence_hash": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" } + }, + "additionalProperties": false + } + }, + "gate_evidence_requirements": { + "type": "array", + "minItems": 6, + "items": { + "type": "object", + "required": [ + "gate_id", + "display_name", + "required_evidence", + "missing_or_blocked", + "opens_live_execution" + ], + "properties": { + "gate_id": { "type": "string" }, + "display_name": { "type": "string" }, + "required_evidence": { "type": "array", "items": { "type": "string" } }, + "missing_or_blocked": { "type": "array", "items": { "type": "string" } }, + "opens_live_execution": { "const": false } + }, + "additionalProperties": false + } + }, + "operator_handoffs": { + "type": "array", + "minItems": 5, + "items": { + "type": "object", + "required": [ + "handoff_id", + "display_name", + "owner_agent", + "human_instruction", + "creates_runtime_action", + "requires_human_review" + ], + "properties": { + "handoff_id": { "type": "string" }, + "display_name": { "type": "string" }, + "owner_agent": { "enum": ["openclaw", "hermes", "nemotron"] }, + "human_instruction": { "type": "string" }, + "creates_runtime_action": { "const": false }, + "requires_human_review": { "const": true } + }, + "additionalProperties": false + } + }, + "display_redaction_contract": { + "type": "object", + "required": [ + "redaction_required", + "raw_prompt_display_allowed", + "private_reasoning_display_allowed", + "secret_value_display_allowed", + "raw_telegram_payload_display_allowed", + "work_window_transcript_display_allowed", + "allowed_display_fields", + "blocked_display_fields" + ], + "properties": { + "redaction_required": { "const": true }, + "raw_prompt_display_allowed": { "const": false }, + "private_reasoning_display_allowed": { "const": false }, + "secret_value_display_allowed": { "const": false }, + "raw_telegram_payload_display_allowed": { "const": false }, + "work_window_transcript_display_allowed": { "const": false }, + "allowed_display_fields": { "type": "array", "items": { "type": "string" } }, + "blocked_display_fields": { "type": "array", "items": { "type": "string" } } + }, + "additionalProperties": false + }, + "rollups": { + "type": "object", + "required": [ + "candidate_operation_count", + "candidate_with_dry_run_evidence_count", + "passed_no_write_count", + "needs_owner_review_count", + "blocked_until_allowlist_count", + "blocked_by_policy_count", + "verifier_plan_count", + "gate_evidence_requirement_count", + "operator_handoff_count", + "side_effect_count", + "runtime_execution_count", + "gateway_queue_write_count", + "telegram_send_count", + "production_write_count", + "secret_value_read_count", + "destructive_operation_count" + ], + "properties": { + "candidate_operation_count": { "type": "integer" }, + "candidate_with_dry_run_evidence_count": { "type": "integer" }, + "passed_no_write_count": { "type": "integer" }, + "needs_owner_review_count": { "type": "integer" }, + "blocked_until_allowlist_count": { "type": "integer" }, + "blocked_by_policy_count": { "type": "integer" }, + "verifier_plan_count": { "type": "integer" }, + "gate_evidence_requirement_count": { "type": "integer" }, + "operator_handoff_count": { "type": "integer" }, + "side_effect_count": { "const": 0 }, + "runtime_execution_count": { "const": 0 }, + "gateway_queue_write_count": { "const": 0 }, + "telegram_send_count": { "const": 0 }, + "production_write_count": { "const": 0 }, + "secret_value_read_count": { "const": 0 }, + "destructive_operation_count": { "const": 0 } + }, + "additionalProperties": false + } + }, + "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 4e3ea91d7..ca54eb67a 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 @@ -634,11 +634,12 @@ Alert / Sentry / SigNoz / Gitea / Market Watch / Operator | `docs/evaluations/ai_agent_communication_learning_contract_2026-06-11.json` | 2026-06-11 committed snapshot;完成度 `35%`,runtime worker / DB migration / Telegram direct send 全部 false | | `apps/api/src/services/ai_agent_communication_learning_contract.py` | 只讀 loader;強制驗證 runtime / migration / Telegram / SDK / route 權限都未開 | | `GET /api/v1/agents/agent-communication-learning-contract` | 治理 API;只回傳 committed contract,不啟動 worker、不碰 DB/Redis、不呼叫外部服務 | -| `docs/evaluations/ai_agent_interaction_learning_proof_2026-06-11.json` + `GET /api/v1/agents/agent-interaction-learning-proof` | P2-403A / P2-403B / P2-403C / P2-403D / P2-403E / P2-403F / P2-403G / P2-403H / P2-403I / P2-403J / P2-403L / P2-403M / P2-403N / P2-404 互動、接手、學習、成長、read model gate、Redis dry-run gate、learning writeback approval package、Telegram receipt approval package、owner-approved learning dry-run、fixture dry-run、runtime write gate review、post-write verifier package、runtime verifier evidence review、報表真相、日週月報、Agent 工作量、圖表化報告、風險自動化政策、報表 runtime readiness、no-write dry-run、fixture/readback/verifier dry-run 與 shadow/no-write execution 證據面;目前 live session、message、handoff、learning write、Gateway queue、Telegram send、report delivery、auto optimization、shadow worker、verifier execution 全部 `0`,由 P2-101 操作類別權限模型承接下一步 | +| `docs/evaluations/ai_agent_interaction_learning_proof_2026-06-11.json` + `GET /api/v1/agents/agent-interaction-learning-proof` | P2-403A / P2-403B / P2-403C / P2-403D / P2-403E / P2-403F / P2-403G / P2-403H / P2-403I / P2-403J / P2-403L / P2-403M / P2-403N / P2-404 互動、接手、學習、成長、read model gate、Redis dry-run gate、learning writeback approval package、Telegram receipt approval package、owner-approved learning dry-run、fixture dry-run、runtime write gate review、post-write verifier package、runtime verifier evidence review、報表真相、日週月報、Agent 工作量、圖表化報告、風險自動化政策、報表 runtime readiness、no-write dry-run、fixture/readback/verifier dry-run 與 shadow/no-write execution 證據面;目前 live session、message、handoff、learning write、Gateway queue、Telegram send、report delivery、auto optimization、shadow worker、verifier execution 全部 `0`,由 P2-101 / P2-102 權限模型與候選 dry-run 證據承接下一步 | | `docs/evaluations/ai_agent_report_runtime_dry_run_2026-06-12.json` + `GET /api/v1/agents/agent-report-runtime-dry-run` | P2-403M 報表 runtime no-write dry-run 證據包;建立 5 個 dry-run artifact、3 個 SRE 戰情室 queue digest 草案、4 個 readback verifier case、3 個 Agent dry-run role 與 6 個 operator checkpoint;不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 delivery receipt、不啟動 worker、不跑 verifier live readback、不讀 secret,已由 P2-403N fixture readback 承接 | | `docs/evaluations/ai_agent_report_runtime_fixture_readback_2026-06-12.json` + `GET /api/v1/agents/agent-report-runtime-fixture-readback` | P2-403N fixture smoke / queue preview readback / verifier dry-run 證據包;建立 5 個 fixture smoke、3 個 SRE 戰情室 queue preview readback、4 個 verifier dry-run case、3 個 Agent fixture role 與 5 個 operator checkpoint;不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 delivery receipt、不啟動 worker、不跑 verifier live readback、不讀 secret,下一步 P2-404 | | `docs/evaluations/ai_agent_runtime_worker_shadow_gate_2026-06-12.json` + `GET /api/v1/agents/agent-runtime-worker-shadow-gate` | P2-404 runtime worker shadow / no-write execution evidence gate;建立 5 個 shadow candidate、4 個 no-write replay、4 個 verifier shadow case、3 個 Agent shadow role 與 6 個 operator checkpoint;shadow live worker、Gateway queue write、Telegram send、Bot API、delivery receipt、auto worker、verifier live readback、production write 與 secret read 全部 `0 / false`,下一步 P2-101 | -| `docs/evaluations/ai_agent_operation_permission_model_2026-06-12.json` + `GET /api/v1/agents/agent-operation-permission-model` | P2-101 操作類別權限模型;建立 5 條 permission lane、13 類 operation category、3 個 Agent permission role、8 個 gate transition 與 5 個 operator decision template;runtime execution、Gateway queue write、Telegram send、Bot API、delivery receipt、auto worker、verifier live readback、production write、secret / paid provider、host command 與 destructive action 全部 `0 / false`,下一步 P2-102 | +| `docs/evaluations/ai_agent_operation_permission_model_2026-06-12.json` + `GET /api/v1/agents/agent-operation-permission-model` | P2-101 操作類別權限模型;建立 5 條 permission lane、13 類 operation category、3 個 Agent permission role、8 個 gate transition 與 5 個 operator decision template;runtime execution、Gateway queue write、Telegram send、Bot API、delivery receipt、auto worker、verifier live readback、production write、secret / paid provider、host command 與 destructive action 全部 `0 / false`,已由 P2-102 承接 | +| `docs/evaluations/ai_agent_candidate_operation_dry_run_evidence_2026-06-12.json` + `GET /api/v1/agents/agent-candidate-operation-dry-run-evidence` | P2-102 候選操作 dry-run 證據;13 類候選操作全部具備 input / output evidence hash、side-effect count、verifier plan、rollback/no-op plan 與人工 handoff;6 個 verifier plan、7 個 gate evidence requirement、5 個 operator handoff;runtime、Gateway queue、Telegram、production write、secret / paid provider 與 destructive action 全部 `0 / false`,下一步 P2-103 | | `docs/evaluations/ai_agent_live_read_model_gate_2026-06-11.json` + `GET /api/v1/agents/agent-live-read-model-gate` | P2-403B AgentSession / Redis Streams live read model gate;定義 safe fields、Redis envelope、worker gate、rollback plan 與 no-write smoke,不連 DB、不讀寫 Redis、不啟動 worker | #### 3.2.1c 2026-06-11 AI Agent 主動營運委派與版本生命週期契約 @@ -728,7 +729,8 @@ Repo / registry / release notes / K8s / host / observability / backup evidence 19. 建立報表 runtime no-write dry-run、SRE 戰情室 Gateway queue 草案與 readback verifier 草案。✅ P2-403M 已完成;Gateway queue write、Telegram send、Bot API、delivery receipt、AI runtime worker、中低風險 auto worker、verifier live readback、production write 與 secret value read 仍為 `0 / false`。 20. 建立 fixture smoke、queue preview readback 與 verifier dry-run 證據包。✅ P2-403N 已完成;fixture smoke `5`、queue preview readback `3`、verifier dry-run case `4`,Gateway queue write、Telegram send、Bot API、delivery receipt、AI runtime worker、中低風險 auto worker、verifier live readback、production write 與 secret value read 仍為 `0 / false`。 21. 建立 runtime worker shadow / no-write execution evidence gate。✅ P2-404 已完成;shadow candidate `5`、no-write replay `4`、verifier shadow case `4`、Agent shadow role `3`、operator checkpoint `6`,shadow live worker、Gateway queue write、Telegram send、Bot API、delivery receipt、AI runtime worker、中低風險 auto worker、verifier live readback、production write 與 secret value read 仍為 `0 / false`。已由 P2-101 承接。 -22. 定義操作類別權限模型。✅ P2-101 已完成;permission lane `5`、operation category `13`、Agent permission role `3`、gate transition `8`、operator decision template `5`,runtime execution、Gateway queue write、Telegram send、Bot API、delivery receipt、AI runtime worker、中低風險 auto worker、verifier live readback、production write、secret / paid provider、host command 與 destructive action 仍為 `0 / false`。下一步 P2-102 候選操作 dry-run 證據。 +22. 定義操作類別權限模型。✅ P2-101 已完成;permission lane `5`、operation category `13`、Agent permission role `3`、gate transition `8`、operator decision template `5`,runtime execution、Gateway queue write、Telegram send、Bot API、delivery receipt、AI runtime worker、中低風險 auto worker、verifier live readback、production write、secret / paid provider、host command 與 destructive action 仍為 `0 / false`。已由 P2-102 承接。 +23. 建立候選操作 dry-run 證據。✅ P2-102 已完成;candidate operation `13`、dry-run evidence `13`、verifier plan `6`、gate evidence requirement `7`、operator handoff `5`,side-effect、runtime execution、Gateway queue write、Telegram send、production write、secret value read、paid provider call、host / cluster command 與 destructive action 仍為 `0 / false`。下一步 P2-103 把任務結果接回 KM / LOGBOOK / 稽核軌跡。 #### 3.2.1d 2026-06-11 Agent 互動、學習與成長證據面 @@ -759,6 +761,7 @@ Repo / registry / release notes / K8s / host / observability / backup evidence | `docs/evaluations/ai_agent_report_runtime_fixture_readback_2026-06-12.json` + `GET /api/v1/agents/agent-report-runtime-fixture-readback` | P2-403N fixture smoke / queue preview readback / verifier dry-run 證據包;5 個 fixture smoke、3 個 queue preview readback、4 個 verifier dry-run case、3 個 Agent fixture role、5 個 operator checkpoint;不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 delivery receipt、不啟動 worker、不跑 verifier live readback | | `docs/evaluations/ai_agent_runtime_worker_shadow_gate_2026-06-12.json` + `GET /api/v1/agents/agent-runtime-worker-shadow-gate` | P2-404 runtime worker shadow / no-write execution evidence gate;5 個 shadow candidate、4 個 no-write replay、4 個 verifier shadow case、3 個 Agent shadow role、6 個 operator checkpoint;不啟動 live worker、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 production target | | `docs/evaluations/ai_agent_operation_permission_model_2026-06-12.json` + `GET /api/v1/agents/agent-operation-permission-model` | P2-101 操作類別權限模型;5 條 permission lane、13 類 operation category、3 個 Agent permission role、8 個 gate transition、5 個 operator decision template;不啟動 runtime worker、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 production target、不讀 secret | +| `docs/evaluations/ai_agent_candidate_operation_dry_run_evidence_2026-06-12.json` + `GET /api/v1/agents/agent-candidate-operation-dry-run-evidence` | P2-102 候選操作 dry-run 證據;13 類候選操作、13 組 dry-run evidence、6 個 verifier plan、7 個 gate evidence requirement、5 個 operator handoff;不啟動 runtime worker、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 production target、不讀 secret、不執行 destructive action | | `apps/api/src/services/ai_agent_interaction_learning_proof.py` | 只讀 loader;強制 live flags / DB / Redis / Telegram / transcript / 私有推理全部關閉 | | `GET /api/v1/agents/agent-interaction-learning-proof` | 治理 API;只回傳證據面,不啟動 worker、不碰 live DB/Redis、不發 Telegram | | `docs/schemas/ai_agent_live_read_model_gate_v1.schema.json` | P2-403B live read model gate schema;強制 DB / Redis / worker / Telegram / learning writeback 仍需批准 | @@ -1919,6 +1922,13 @@ Phase 6 完成後 - 政策裁決:P2-101 只允許操作分類、風險分層、Agent 責任、gate transition 與人工下一步模板;任何 runtime execution、Gateway queue write、Telegram send、Bot API、delivery receipt、AI runtime worker、中低風險 auto worker、verifier live readback、production write、secret / paid provider、host command 或 destructive action 都仍為 `0 / false`。 - 本波仍不送 Telegram、不寫 Gateway queue、不呼叫 Bot API、不寫 delivery receipt、不啟動 live runtime worker、不跑 verifier live readback、不讀 secret、不回傳工作視窗對話內容;下一步 P2-102 才要求每個候選操作具備 dry-run 證據。 +### 2026-06-12 16:20 (台北) — §3.2 / §5 — 完成 P2-102 候選操作 dry-run 證據 — 把每類候選動作固定成 no-write evidence / verifier plan + +- 新增 `ai_agent_candidate_operation_dry_run_evidence_v1` schema / committed snapshot / loader / API / 測試,定義 13 類 candidate operation、13 組 dry-run input / output evidence hash、side-effect count、rollback/no-op plan 與人工 handoff。 +- `apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx` 接入 `GET /api/v1/agents/agent-candidate-operation-dry-run-evidence`,治理頁顯示候選操作、dry-run 證據、verifier plan、gate evidence requirement、operator handoff 與不可誤讀合約。 +- 政策裁決:P2-102 只允許 no-write dry-run 證據、queue preview、verifier fixture 與人工下一步;任何 runtime execution、Gateway queue write、Telegram send、Bot API、delivery receipt、AI runtime worker、中低風險 auto worker、verifier live readback、production write、secret / paid provider、host command 或 destructive action 都仍為 `0 / false`。 +- 本波仍不送 Telegram、不寫 Gateway queue、不呼叫 Bot API、不寫 delivery receipt、不啟動 live runtime worker、不跑 verifier live readback、不讀 secret、不呼叫付費 provider、不執行主機或叢集命令、不回傳內部協作內容;下一步 P2-103 才把結果接回 KM / LOGBOOK / 稽核軌跡。 + ### 2026-06-12 11:55 (台北) — §3.2 / §5 — 完成 P2-403M 報表 runtime no-write dry-run 證據包 — 把 queue / verifier 草案固定成可審核證據 - 新增 `ai_agent_report_runtime_dry_run_v1` schema / committed snapshot / loader / API / 測試,定義 report_run snapshot preview、Telegram digest payload preview、AI post-report analysis packet、中低風險 no-op plan、post-action verifier readback plan。