From 17e017f5a39a7fd0e684799db21e750e4913e858 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 13 Jun 2026 12:52:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(governance):=20=E6=96=B0=E5=A2=9E=20owner?= =?UTF-8?q?=20approved=20result=20capture=20dry=20run?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/api/v1/agents.py | 34 ++ ...t_owner_approved_result_capture_dry_run.py | 354 ++++++++++++++ ...t_owner_approved_result_capture_dry_run.py | 117 +++++ ...ner_approved_result_capture_dry_run_api.py | 31 ++ apps/web/messages/zh-TW.json | 78 +++- .../tabs/automation-inventory-tab.tsx | 249 +++++++++- apps/web/src/lib/api-client.ts | 149 ++++++ docs/LOGBOOK.md | 23 + ...AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md | 10 +- ...ved_result_capture_dry_run_2026-06-13.json | 442 ++++++++++++++++++ ...oved_result_capture_dry_run_v1.schema.json | 151 ++++++ ...-04-15-MASTER-ai-autonomous-flywheel-v2.md | 15 +- 12 files changed, 1641 insertions(+), 12 deletions(-) create mode 100644 apps/api/src/services/ai_agent_owner_approved_result_capture_dry_run.py create mode 100644 apps/api/tests/test_ai_agent_owner_approved_result_capture_dry_run.py create mode 100644 apps/api/tests/test_ai_agent_owner_approved_result_capture_dry_run_api.py create mode 100644 docs/evaluations/ai_agent_owner_approved_result_capture_dry_run_2026-06-13.json create mode 100644 docs/schemas/ai_agent_owner_approved_result_capture_dry_run_v1.schema.json diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index bf747cc87..af9d7689a 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -85,6 +85,9 @@ from src.services.ai_agent_owner_approved_fixture_dry_run import ( from src.services.ai_agent_owner_approved_learning_dry_run import ( load_latest_ai_agent_owner_approved_learning_dry_run, ) +from src.services.ai_agent_owner_approved_result_capture_dry_run import ( + load_latest_ai_agent_owner_approved_result_capture_dry_run, +) from src.services.ai_agent_operation_permission_model import ( load_latest_ai_agent_operation_permission_model, ) @@ -1193,6 +1196,37 @@ async def get_agent_critic_reviewer_result_capture() -> dict[str, Any]: ) from exc +@router.get( + "/agent-owner-approved-result-capture-dry-run", + response_model=dict[str, Any], + summary="取得 AI Agent owner-approved result capture dry-run", + description=( + "讀取最新已提交的 P2-106 owner-approved result capture dry-run;" + "此端點只回傳 owner approval packet、no-write dry-run template、score fixture、" + "dry-run gate 與 operator action," + "不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 KM、" + "不寫 audit DB、不寫 timeline、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、" + "不寫 production target、不讀 secret。" + ), +) +async def get_agent_owner_approved_result_capture_dry_run() -> dict[str, Any]: + """Return the latest read-only owner-approved result capture dry-run contract.""" + try: + payload = await asyncio.to_thread(load_latest_ai_agent_owner_approved_result_capture_dry_run) + return redact_public_lan_topology(payload) + except FileNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(exc), + ) from exc + except (json.JSONDecodeError, ValueError) as exc: + logger.error("ai_agent_owner_approved_result_capture_dry_run_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="AI Agent owner-approved result capture 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_owner_approved_result_capture_dry_run.py b/apps/api/src/services/ai_agent_owner_approved_result_capture_dry_run.py new file mode 100644 index 000000000..d2fe348a3 --- /dev/null +++ b/apps/api/src/services/ai_agent_owner_approved_result_capture_dry_run.py @@ -0,0 +1,354 @@ +""" +AI Agent owner-approved result capture dry-run snapshot. + +Loads the latest committed P2-106 owner-approved result capture dry-run +contract. This module validates repo-committed evidence only; it never writes +scores, result capture rows, learning state, PlayBook trust, KM, audit, +timeline, Gateway queues, Telegram messages, production targets, or secrets. +""" + +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_owner_approved_result_capture_dry_run_*.json" +_SCHEMA_VERSION = "ai_agent_owner_approved_result_capture_dry_run_v1" +_RUNTIME_AUTHORITY = "owner_approved_result_capture_dry_run_only_no_live_write" + + +def load_latest_ai_agent_owner_approved_result_capture_dry_run( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed owner-approved result capture dry-run contract.""" + directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR + candidates = sorted(directory.glob(_SNAPSHOT_PATTERN)) + if not candidates: + raise FileNotFoundError( + f"no AI Agent owner-approved result capture dry-run 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_prior_contract(payload, str(latest)) + _require_dry_run_truth(payload, str(latest)) + _require_approval_packet(payload, str(latest)) + _require_result_capture_templates(payload, str(latest)) + _require_score_fixtures(payload, str(latest)) + _require_dry_run_gates(payload, str(latest)) + _require_operator_actions(payload, str(latest)) + _require_display_redaction(payload, str(latest)) + _require_no_forbidden_display_terms(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-106": + raise ValueError(f"{label}: current_task_id must be P2-106") + if status.get("next_task_id") != "P2-107": + raise ValueError(f"{label}: next_task_id must be P2-107") + + +def _require_prior_contract(payload: dict[str, Any], label: str) -> None: + prior = payload.get("prior_contract_readback") or {} + if prior.get("source_schema_version") != "ai_agent_critic_reviewer_result_capture_v1": + raise ValueError(f"{label}: prior_contract_readback must chain from P2-105") + required_counts = { + "scorecard_count": 5, + "result_capture_contract_count": 5, + "promotion_gate_count": 6, + "candidate_route_count": 4, + "approved_without_execution_meta_24h": 63, + "execution_failed_with_matched_24h": 1, + "result_capture_runtime_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "telegram_send_count": 0, + } + mismatches = { + key: {"expected": expected, "actual": prior.get(key)} + for key, expected in required_counts.items() + if prior.get(key) != expected + } + if mismatches: + raise ValueError(f"{label}: P2-105 prior contract counts mismatch: {mismatches}") + + +def _require_dry_run_truth(payload: dict[str, Any], label: str) -> None: + truth = payload.get("dry_run_truth") or {} + required_true = { + "p2_105_contract_loaded", + "owner_approval_required", + "dry_run_preview_allowed", + "result_capture_payload_template_ready", + "critic_reviewer_score_fixture_ready", + "post_write_verifier_fixture_required", + "redacted_operator_digest_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_result_capture_write_enabled", + "runtime_score_write_enabled", + "runtime_learning_write_enabled", + "playbook_trust_write_enabled", + "gateway_queue_write_enabled", + "telegram_send_enabled", + "production_write_enabled", + "secret_read_enabled", + "destructive_operation_enabled", + } + unsafe = sorted(field for field in required_false if truth.get(field) is not False) + if unsafe: + raise ValueError(f"{label}: runtime write/send flags must remain false: {unsafe}") + + zero_counts = { + "owner_approval_received_count", + "dry_run_preview_generated_count", + "result_capture_write_count_24h", + "score_write_count_24h", + "learning_write_count_24h", + "playbook_trust_write_count_24h", + "gateway_queue_write_count_24h", + "telegram_send_count_24h", + "production_write_count_24h", + "secret_read_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}: dry-run live counters must remain zero: {non_zero}") + + +def _require_approval_packet(payload: dict[str, Any], label: str) -> None: + packet = payload.get("approval_packet") or {} + required_fields = set(packet.get("required_owner_fields") or []) + required_minimum = { + "owner_approval_id", + "owner_role", + "approval_scope", + "approved_result_capture_contract_ids", + "redacted_evidence_refs", + "dry_run_plan_fingerprint", + "rollback_plan_ref", + "post_write_verifier_plan_ref", + } + missing = sorted(required_minimum - required_fields) + if missing: + raise ValueError(f"{label}: approval packet missing owner fields: {missing}") + if not packet.get("operator_meaning"): + raise ValueError(f"{label}: approval packet must include operator_meaning") + if not _is_redacted_sha256(packet.get("dry_run_plan_fingerprint")): + raise ValueError(f"{label}: approval packet must expose dry_run_plan_fingerprint") + + +def _require_result_capture_templates(payload: dict[str, Any], label: str) -> None: + templates = payload.get("result_capture_dry_run_templates") or [] + template_ids = {template.get("template_id") for template in templates} + required = { + "dry_run_capture_approved_execution_result", + "dry_run_capture_execution_failed_candidate", + "dry_run_capture_pending_human_gate", + "dry_run_capture_manual_or_noop", + "dry_run_capture_post_write_verifier_receipt", + } + if template_ids != required: + raise ValueError(f"{label}: result capture dry-run templates must match {sorted(required)}") + valid_statuses = {"ready_for_dry_run", "approval_required", "blocked_by_policy"} + for template in templates: + template_id = template.get("template_id") + if template.get("status") not in valid_statuses: + raise ValueError(f"{label}: template {template_id} status is invalid") + if template.get("write_enabled") is not False: + raise ValueError(f"{label}: template {template_id} write_enabled must remain false") + if template.get("runtime_writer_enabled") is not False: + raise ValueError(f"{label}: template {template_id} runtime_writer_enabled must remain false") + if not template.get("required_inputs") or not template.get("preview_outputs"): + raise ValueError(f"{label}: template {template_id} must list required inputs and preview outputs") + if not _is_redacted_sha256(template.get("no_write_evidence_hash")): + raise ValueError(f"{label}: template {template_id} must expose no_write_evidence_hash") + + +def _require_score_fixtures(payload: dict[str, Any], label: str) -> None: + fixtures = payload.get("critic_reviewer_score_fixtures") or [] + fixture_ids = {fixture.get("fixture_id") for fixture in fixtures} + required = { + "fixture_openclaw_critic_decision_quality", + "fixture_openclaw_reviewer_safety_verdict", + "fixture_hermes_redaction_operator_report", + "fixture_nemotron_failure_candidate_verifier", + "fixture_coordinator_disagreement_gate", + } + if fixture_ids != required: + raise ValueError(f"{label}: critic reviewer score fixtures must match {sorted(required)}") + for fixture in fixtures: + fixture_id = fixture.get("fixture_id") + if fixture.get("fixture_only") is not True: + raise ValueError(f"{label}: fixture {fixture_id} fixture_only must remain true") + if fixture.get("runtime_score_write_enabled") is not False: + raise ValueError(f"{label}: fixture {fixture_id} runtime_score_write_enabled must remain false") + if not isinstance(fixture.get("minimum_score"), int): + raise ValueError(f"{label}: fixture {fixture_id} minimum_score must be integer") + if not fixture.get("required_inputs") or not fixture.get("failure_if_missing"): + raise ValueError(f"{label}: fixture {fixture_id} must list required inputs and failure text") + if not _is_redacted_sha256(fixture.get("evidence_hash")): + raise ValueError(f"{label}: fixture {fixture_id} must expose evidence_hash") + + +def _require_dry_run_gates(payload: dict[str, Any], label: str) -> None: + gates = payload.get("dry_run_gates") or [] + gate_ids = {gate.get("gate_id") for gate in gates} + required = { + "gate_owner_approval_packet_complete", + "gate_critic_reviewer_score_fixture_complete", + "gate_result_capture_payload_preview_complete", + "gate_redaction_public_display_safe", + "gate_no_live_write_enforced", + "gate_post_write_verifier_fixture_ready", + "gate_operator_digest_preview_only", + } + if gate_ids != required: + raise ValueError(f"{label}: dry-run gates must match {sorted(required)}") + for gate in gates: + gate_id = gate.get("gate_id") + if gate.get("status") not in {"ready", "approval_required", "blocked_by_policy"}: + raise ValueError(f"{label}: gate {gate_id} status is invalid") + if gate.get("creates_runtime_write") is not False: + raise ValueError(f"{label}: gate {gate_id} creates_runtime_write must remain false") + if not gate.get("required_evidence") or not gate.get("blocked_write_action"): + raise ValueError(f"{label}: gate {gate_id} must list evidence and blocked write action") + + +def _require_operator_actions(payload: dict[str, Any], label: str) -> None: + actions = payload.get("operator_actions") or [] + action_types = {action.get("action_type") for action in actions} + required = {"review", "collect_evidence", "approve_dry_run", "reject_or_rework", "promote_to_next_gate"} + if not required.issubset(action_types): + raise ValueError(f"{label}: operator actions must cover {sorted(required)}") + for action in actions: + if action.get("runtime_write_allowed") is not False: + raise ValueError(f"{label}: operator action {action.get('action_id')} must not allow runtime write") + if not action.get("operator_instruction"): + raise ValueError(f"{label}: operator action {action.get('action_id')} must include instruction") + + +def _require_display_redaction(payload: dict[str, Any], label: str) -> None: + contract = payload.get("display_redaction_contract") or {} + if contract.get("redaction_required") is not True: + raise ValueError(f"{label}: display redaction must remain required") + required_false = { + "raw_prompt_display_allowed", + "private_reasoning_display_allowed", + "secret_value_display_allowed", + "raw_telegram_payload_display_allowed", + "work_window_transcript_display_allowed", + } + 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_no_forbidden_display_terms(payload: dict[str, Any], label: str) -> None: + forbidden_terms = { + "工作視窗", + "對話內容", + "批准!繼續", + "In app browser", + "My request for Codex", + "browser_context", + "codex_user_message", + "prompt_text", + "raw prompt", + "private reasoning", + "chain of thought", + "private_reasoning", + "chain_of_thought", + "authorization_header", + "work window transcript", + "internal collaboration transcript", + } + hits: list[str] = [] + + def walk(value: Any, path: str) -> None: + if isinstance(value, dict): + for key, nested in value.items(): + walk(nested, f"{path}.{key}" if path else str(key)) + return + if isinstance(value, list): + for index, nested in enumerate(value): + walk(nested, f"{path}[{index}]") + return + if isinstance(value, str): + matched = sorted(term for term in forbidden_terms if term in value) + if matched: + hits.append(f"{path}: {', '.join(matched)}") + + walk(payload, "") + if hits: + raise ValueError(f"{label}: forbidden display terms found: {hits}") + + +def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None: + rollups = payload.get("rollups") or {} + truth = payload.get("dry_run_truth") or {} + prior = payload.get("prior_contract_readback") or {} + templates = payload.get("result_capture_dry_run_templates") or [] + fixtures = payload.get("critic_reviewer_score_fixtures") or [] + gates = payload.get("dry_run_gates") or [] + actions = payload.get("operator_actions") or [] + expected = { + "result_capture_template_count": len(templates), + "score_fixture_count": len(fixtures), + "dry_run_gate_count": len(gates), + "operator_action_count": len(actions), + "approval_required_gate_count": sum(1 for gate in gates if gate.get("status") == "approval_required"), + "blocked_gate_count": sum(1 for gate in gates if gate.get("status") == "blocked_by_policy"), + "approval_24h_total": prior.get("approval_24h_total"), + "approved_without_execution_meta_24h": prior.get("approved_without_execution_meta_24h"), + "execution_failed_with_matched_24h": prior.get("execution_failed_with_matched_24h"), + "owner_approval_received_count": truth.get("owner_approval_received_count"), + "dry_run_preview_generated_count": truth.get("dry_run_preview_generated_count"), + "result_capture_write_count": truth.get("result_capture_write_count_24h"), + "score_write_count": truth.get("score_write_count_24h"), + "learning_write_count": truth.get("learning_write_count_24h"), + "playbook_trust_write_count": truth.get("playbook_trust_write_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_read_count": truth.get("secret_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_owner_approved_result_capture_dry_run.py b/apps/api/tests/test_ai_agent_owner_approved_result_capture_dry_run.py new file mode 100644 index 000000000..b833b8c77 --- /dev/null +++ b/apps/api/tests/test_ai_agent_owner_approved_result_capture_dry_run.py @@ -0,0 +1,117 @@ +import copy +import json + +import pytest + +from src.services.ai_agent_owner_approved_result_capture_dry_run import ( + load_latest_ai_agent_owner_approved_result_capture_dry_run, +) + + +def _write_snapshot(tmp_path, payload): + path = tmp_path / "ai_agent_owner_approved_result_capture_dry_run_2026-06-13.json" + path.write_text(json.dumps(payload), encoding="utf-8") + return path + + +def test_load_latest_ai_agent_owner_approved_result_capture_dry_run(): + data = load_latest_ai_agent_owner_approved_result_capture_dry_run() + + assert data["schema_version"] == "ai_agent_owner_approved_result_capture_dry_run_v1" + assert data["program_status"]["current_task_id"] == "P2-106" + assert data["program_status"]["next_task_id"] == "P2-107" + assert data["program_status"]["overall_completion_percent"] == 100 + assert data["prior_contract_readback"]["source_schema_version"] == "ai_agent_critic_reviewer_result_capture_v1" + assert data["prior_contract_readback"]["approved_without_execution_meta_24h"] == 63 + assert data["dry_run_truth"]["owner_approval_required"] is True + assert data["dry_run_truth"]["dry_run_preview_allowed"] is True + assert data["dry_run_truth"]["owner_approval_received_count"] == 0 + assert data["dry_run_truth"]["dry_run_preview_generated_count"] == 0 + assert data["dry_run_truth"]["runtime_result_capture_write_enabled"] is False + assert data["dry_run_truth"]["telegram_send_enabled"] is False + assert data["rollups"]["result_capture_template_count"] == 5 + assert data["rollups"]["score_fixture_count"] == 5 + assert data["rollups"]["dry_run_gate_count"] == 7 + assert data["rollups"]["operator_action_count"] == 5 + assert data["rollups"]["result_capture_write_count"] == 0 + assert data["rollups"]["score_write_count"] == 0 + assert data["rollups"]["learning_write_count"] == 0 + assert data["rollups"]["playbook_trust_write_count"] == 0 + assert data["rollups"]["telegram_send_count"] == 0 + + +def test_rejects_owner_approval_received_count(tmp_path): + data = load_latest_ai_agent_owner_approved_result_capture_dry_run() + bad = copy.deepcopy(data) + bad["dry_run_truth"]["owner_approval_received_count"] = 1 + bad["rollups"]["owner_approval_received_count"] = 1 + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="live counters"): + load_latest_ai_agent_owner_approved_result_capture_dry_run(tmp_path) + + +def test_rejects_runtime_result_capture_write_enabled(tmp_path): + data = load_latest_ai_agent_owner_approved_result_capture_dry_run() + bad = copy.deepcopy(data) + bad["dry_run_truth"]["runtime_result_capture_write_enabled"] = True + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="runtime write/send flags"): + load_latest_ai_agent_owner_approved_result_capture_dry_run(tmp_path) + + +def test_rejects_template_write_enabled(tmp_path): + data = load_latest_ai_agent_owner_approved_result_capture_dry_run() + bad = copy.deepcopy(data) + bad["result_capture_dry_run_templates"][0]["write_enabled"] = True + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="write_enabled"): + load_latest_ai_agent_owner_approved_result_capture_dry_run(tmp_path) + + +def test_rejects_score_fixture_runtime_write(tmp_path): + data = load_latest_ai_agent_owner_approved_result_capture_dry_run() + bad = copy.deepcopy(data) + bad["critic_reviewer_score_fixtures"][0]["runtime_score_write_enabled"] = True + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="runtime_score_write_enabled"): + load_latest_ai_agent_owner_approved_result_capture_dry_run(tmp_path) + + +def test_rejects_missing_dry_run_gate(tmp_path): + data = load_latest_ai_agent_owner_approved_result_capture_dry_run() + bad = copy.deepcopy(data) + bad["dry_run_gates"] = [ + gate for gate in bad["dry_run_gates"] if gate["gate_id"] != "gate_no_live_write_enforced" + ] + bad["rollups"]["dry_run_gate_count"] = len(bad["dry_run_gates"]) + bad["rollups"]["blocked_gate_count"] = sum( + 1 for gate in bad["dry_run_gates"] if gate["status"] == "blocked_by_policy" + ) + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="dry-run gates"): + load_latest_ai_agent_owner_approved_result_capture_dry_run(tmp_path) + + +def test_rejects_forbidden_display_terms(tmp_path): + data = load_latest_ai_agent_owner_approved_result_capture_dry_run() + bad = copy.deepcopy(data) + bad["operator_actions"][0]["operator_instruction"] = "不得顯示工作視窗內容" + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="forbidden display terms"): + load_latest_ai_agent_owner_approved_result_capture_dry_run(tmp_path) + + +def test_rejects_rollup_mismatch(tmp_path): + data = load_latest_ai_agent_owner_approved_result_capture_dry_run() + bad = copy.deepcopy(data) + bad["rollups"]["result_capture_template_count"] = 999 + _write_snapshot(tmp_path, bad) + + with pytest.raises(ValueError, match="rollup counts"): + load_latest_ai_agent_owner_approved_result_capture_dry_run(tmp_path) diff --git a/apps/api/tests/test_ai_agent_owner_approved_result_capture_dry_run_api.py b/apps/api/tests/test_ai_agent_owner_approved_result_capture_dry_run_api.py new file mode 100644 index 000000000..f0bb04bba --- /dev/null +++ b/apps/api/tests/test_ai_agent_owner_approved_result_capture_dry_run_api.py @@ -0,0 +1,31 @@ +from fastapi.testclient import TestClient + +from src.main import app + + +def test_get_ai_agent_owner_approved_result_capture_dry_run_api(): + client = TestClient(app) + response = client.get("/api/v1/agents/agent-owner-approved-result-capture-dry-run") + + assert response.status_code == 200 + data = response.json() + assert data["schema_version"] == "ai_agent_owner_approved_result_capture_dry_run_v1" + assert data["program_status"]["current_task_id"] == "P2-106" + assert data["program_status"]["next_task_id"] == "P2-107" + assert data["program_status"]["overall_completion_percent"] == 100 + assert data["prior_contract_readback"]["approved_without_execution_meta_24h"] == 63 + assert data["dry_run_truth"]["owner_approval_required"] is True + assert data["dry_run_truth"]["owner_approval_received_count"] == 0 + assert data["dry_run_truth"]["dry_run_preview_generated_count"] == 0 + assert data["dry_run_truth"]["runtime_result_capture_write_enabled"] is False + assert data["dry_run_truth"]["runtime_score_write_enabled"] is False + assert data["dry_run_truth"]["telegram_send_enabled"] is False + assert data["rollups"]["result_capture_template_count"] == 5 + assert data["rollups"]["score_fixture_count"] == 5 + assert data["rollups"]["dry_run_gate_count"] == 7 + assert data["rollups"]["operator_action_count"] == 5 + assert data["rollups"]["result_capture_write_count"] == 0 + assert data["rollups"]["score_write_count"] == 0 + assert data["rollups"]["learning_write_count"] == 0 + assert data["rollups"]["playbook_trust_write_count"] == 0 + assert data["rollups"]["telegram_send_count"] == 0 diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 96fc9e11f..9146f5c4d 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -4141,9 +4141,12 @@ "agents": "Agent 數", "workload": "工作量", "done": "已完成", + "waitingApproval": "待審核", "recommendations": "AI 建議", "approval": "需審核", - "autoEnabled": "自動執行" + "autoEnabled": "自動執行", + "liveDelivery": "實發報告", + "liveOptimization": "自動優化" }, "flags": { "daily": "日報: {value}", @@ -4158,12 +4161,15 @@ "labels": { "sections": "章節 {count}", "liveDelivery": "實發 {count}", - "workUnits": "work units {count}", + "workUnits": "工作單位 {count}", "doneRatio": "完成比例", "doneDetail": "{done}/{total} 已完成;{approval} 待審核", - "targets": "佈建 {count}", - "capabilities": "能力 {count}", - "liveRuntime": "live runtime {count}", + "targets": "佈建目標 {count}", + "capabilities": "可委派能力 {count}", + "reportSections": "報告章節 {count}", + "ownedRecommendations": "分析建議 {count}", + "waitingApproval": "待審核 {count}", + "liveRuntime": "24h runtime 作業 {count}", "approvalRequired": "需審核: {value}" }, "riskTiers": { @@ -4722,6 +4728,68 @@ "high": "高風險", "critical": "關鍵阻擋" } + }, + "ownerApprovedResultCaptureDryRun": { + "title": "P2-106 統帥批准後結果捕捉 dry-run", + "source": "{generated} · {current} → {next}", + "packetTitle": "批准包", + "truthTitle": "no-write dry-run 真相", + "metrics": { + "overall": "P2-106 進度", + "templates": "捕捉模板", + "scoreFixtures": "評分 fixture", + "gates": "dry-run 閘門", + "actions": "操作選項", + "approvals": "需批准", + "blocked": "阻擋閘門", + "approvedGap": "批准缺口", + "failedCandidates": "失敗候選", + "ownerApprovals": "已收批准", + "previewGenerated": "預覽產出", + "liveWrites": "live 寫入" + }, + "flags": { + "contractLoaded": "P2-105 契約: {value}", + "ownerApproval": "需統帥批准: {value}", + "previewAllowed": "允許預覽: {value}", + "resultWrite": "result write: {value}", + "scoreWrite": "score write: {value}", + "learningWrite": "learning write: {value}", + "telegramSend": "Telegram send: {value}", + "secretRead": "secret read: {value}" + }, + "labels": { + "ownerFields": "批准欄位 {count}", + "forbiddenInputs": "禁止輸入 {count}", + "fingerprint": "指紋 {value}", + "candidateCount": "24h 候選 {value}", + "requiredInputs": "必填輸入 {count}", + "previewOutputs": "預覽輸出 {count}", + "writeEnabled": "write enabled: {value}", + "runtimeWriter": "runtime writer: {value}", + "minimumScore": "最低分 {value}", + "fixtureOnly": "fixture only: {value}", + "runtimeScoreWrite": "runtime score write: {value}", + "evidenceHash": "evidence: {value}", + "runtimeWrite": "runtime write: {value}" + }, + "statuses": { + "ready_for_dry_run": "可 dry-run", + "approval_required": "需批准", + "blocked_by_policy": "政策阻擋" + }, + "gateStatuses": { + "ready": "可審查", + "approval_required": "需批准", + "blocked_by_policy": "政策阻擋" + }, + "actionTypes": { + "review": "審查", + "collect_evidence": "收集證據", + "approve_dry_run": "批准 dry-run", + "reject_or_rework": "退回重整", + "promote_to_next_gate": "推進下一閘門" + } } } }, diff --git a/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx b/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx index b23910937..be4873121 100644 --- a/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx +++ b/apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx @@ -49,6 +49,7 @@ import { type AiAgentPostWriteVerifierPackageSnapshot, type AiAgentProactiveOperationsContractSnapshot, type AiAgentRedisDryRunGateSnapshot, + type AiAgentOwnerApprovedResultCaptureDryRunSnapshot, type AiAgentReportAutomationReviewSnapshot, type AiAgentReportRuntimeDryRunSnapshot, type AiAgentReportRuntimeFixtureReadbackSnapshot, @@ -428,6 +429,7 @@ export function AutomationInventoryTab() { const [taskResultAuditTrail, setTaskResultAuditTrail] = useState(null) const [matchedPlaybookLearningGap, setMatchedPlaybookLearningGap] = useState(null) const [criticReviewerResultCapture, setCriticReviewerResultCapture] = useState(null) + const [ownerApprovedResultCaptureDryRun, setOwnerApprovedResultCaptureDryRun] = useState(null) const [reportTruthActionabilityReview, setReportTruthActionabilityReview] = useState(null) const [ownerDryRunPackage, setOwnerDryRunPackage] = useState(null) const [hostStatefulInventory, setHostStatefulInventory] = useState(null) @@ -470,6 +472,7 @@ export function AutomationInventoryTab() { apiClient.getAiAgentTaskResultAuditTrail(), apiClient.getAiAgentMatchedPlaybookLearningGap(), apiClient.getAiAgentCriticReviewerResultCapture(), + apiClient.getAiAgentOwnerApprovedResultCaptureDryRun(), apiClient.getAiAgentReportTruthActionabilityReview(), apiClient.getAiAgentOwnerApprovedFixtureDryRun(), apiClient.getAiAgentHostStatefulVersionInventory(), @@ -511,6 +514,7 @@ export function AutomationInventoryTab() { taskResultAuditTrailResult, matchedPlaybookLearningGapResult, criticReviewerResultCaptureResult, + ownerApprovedResultCaptureDryRunResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -549,6 +553,7 @@ export function AutomationInventoryTab() { setTaskResultAuditTrail(taskResultAuditTrailResult.status === 'fulfilled' ? taskResultAuditTrailResult.value : null) setMatchedPlaybookLearningGap(matchedPlaybookLearningGapResult.status === 'fulfilled' ? matchedPlaybookLearningGapResult.value : null) setCriticReviewerResultCapture(criticReviewerResultCaptureResult.status === 'fulfilled' ? criticReviewerResultCaptureResult.value : null) + setOwnerApprovedResultCaptureDryRun(ownerApprovedResultCaptureDryRunResult.status === 'fulfilled' ? ownerApprovedResultCaptureDryRunResult.value : null) setReportTruthActionabilityReview(reportTruthActionabilityReviewResult.status === 'fulfilled' ? reportTruthActionabilityReviewResult.value : null) setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null) setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null) @@ -585,6 +590,7 @@ export function AutomationInventoryTab() { taskResultAuditTrailResult, matchedPlaybookLearningGapResult, criticReviewerResultCaptureResult, + ownerApprovedResultCaptureDryRunResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -1356,6 +1362,55 @@ export function AutomationInventoryTab() { .slice(0, 4) }, [criticReviewerResultCapture]) + const visibleOwnerResultDryRunTemplates = useMemo(() => { + if (!ownerApprovedResultCaptureDryRun) return [] + const statusPriority = { blocked_by_policy: 0, approval_required: 1, ready_for_dry_run: 2 } as Record + return [...ownerApprovedResultCaptureDryRun.result_capture_dry_run_templates] + .sort((a, b) => { + const left = statusPriority[a.status] ?? 3 + const right = statusPriority[b.status] ?? 3 + if (left !== right) return left - right + return b.candidate_count_24h - a.candidate_count_24h + }) + .slice(0, 5) + }, [ownerApprovedResultCaptureDryRun]) + + const visibleOwnerResultDryRunScoreFixtures = useMemo(() => { + if (!ownerApprovedResultCaptureDryRun) return [] + return [...ownerApprovedResultCaptureDryRun.critic_reviewer_score_fixtures] + .sort((a, b) => { + if (a.minimum_score !== b.minimum_score) return b.minimum_score - a.minimum_score + return a.fixture_id.localeCompare(b.fixture_id) + }) + .slice(0, 5) + }, [ownerApprovedResultCaptureDryRun]) + + const visibleOwnerResultDryRunGates = useMemo(() => { + if (!ownerApprovedResultCaptureDryRun) return [] + const statusPriority = { blocked_by_policy: 0, approval_required: 1, ready: 2 } as Record + return [...ownerApprovedResultCaptureDryRun.dry_run_gates] + .sort((a, b) => { + const left = statusPriority[a.status] ?? 3 + const right = statusPriority[b.status] ?? 3 + if (left !== right) return left - right + return a.gate_id.localeCompare(b.gate_id) + }) + .slice(0, 7) + }, [ownerApprovedResultCaptureDryRun]) + + const visibleOwnerResultDryRunActions = useMemo(() => { + if (!ownerApprovedResultCaptureDryRun) return [] + const actionPriority = { review: 0, collect_evidence: 1, approve_dry_run: 2, reject_or_rework: 3, promote_to_next_gate: 4 } as Record + return [...ownerApprovedResultCaptureDryRun.operator_actions] + .sort((a, b) => { + const left = actionPriority[a.action_type] ?? 5 + const right = actionPriority[b.action_type] ?? 5 + if (left !== right) return left - right + return a.action_id.localeCompare(b.action_id) + }) + .slice(0, 5) + }, [ownerApprovedResultCaptureDryRun]) + const visibleReportTruthFindings = useMemo(() => { if (!reportTruthActionabilityReview) return [] const priority = { critical: 0, high: 1, medium: 2, low: 3 } as Record @@ -1575,7 +1630,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 || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !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 || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { return (
@@ -1719,6 +1774,7 @@ export function AutomationInventoryTab() { const reportAgentCount = reportAutomationReview.rollups.agent_count const reportWorkloadTotal = reportAutomationReview.rollups.workload_unit_total const reportWorkloadDone = reportAutomationReview.rollups.workload_done_total + const reportWorkloadWaitingApproval = reportAutomationReview.rollups.workload_waiting_approval_total const reportRecommendations = reportAutomationReview.rollups.recommendation_count const reportApprovalRecommendations = reportAutomationReview.rollups.approval_required_recommendation_ids.length const reportAutoEnabled = reportAutomationReview.rollups.current_auto_execution_enabled_count @@ -1840,6 +1896,26 @@ export function AutomationInventoryTab() { const criticReviewerLearningWrites = criticReviewerResultCapture.rollups.learning_write_count const criticReviewerTrustWrites = criticReviewerResultCapture.rollups.playbook_trust_write_count const criticReviewerTelegramSends = criticReviewerResultCapture.rollups.telegram_send_count + const ownerResultDryRunOverall = ownerApprovedResultCaptureDryRun.program_status.overall_completion_percent + const ownerResultDryRunTemplates = ownerApprovedResultCaptureDryRun.rollups.result_capture_template_count + const ownerResultDryRunScoreFixtures = ownerApprovedResultCaptureDryRun.rollups.score_fixture_count + const ownerResultDryRunGates = ownerApprovedResultCaptureDryRun.rollups.dry_run_gate_count + const ownerResultDryRunActions = ownerApprovedResultCaptureDryRun.rollups.operator_action_count + const ownerResultDryRunApprovals = ownerApprovedResultCaptureDryRun.rollups.approval_required_gate_count + const ownerResultDryRunBlocked = ownerApprovedResultCaptureDryRun.rollups.blocked_gate_count + const ownerResultDryRunApprovedGap = ownerApprovedResultCaptureDryRun.rollups.approved_without_execution_meta_24h + const ownerResultDryRunFailedCandidates = ownerApprovedResultCaptureDryRun.rollups.execution_failed_with_matched_24h + const ownerResultDryRunOwnerApprovals = ownerApprovedResultCaptureDryRun.rollups.owner_approval_received_count + const ownerResultDryRunPreviewGenerated = ownerApprovedResultCaptureDryRun.rollups.dry_run_preview_generated_count + const ownerResultDryRunWrites = ( + ownerApprovedResultCaptureDryRun.rollups.result_capture_write_count + + ownerApprovedResultCaptureDryRun.rollups.score_write_count + + ownerApprovedResultCaptureDryRun.rollups.learning_write_count + + ownerApprovedResultCaptureDryRun.rollups.playbook_trust_write_count + + ownerApprovedResultCaptureDryRun.rollups.gateway_queue_write_count + + ownerApprovedResultCaptureDryRun.rollups.telegram_send_count + + ownerApprovedResultCaptureDryRun.rollups.production_write_count + ) const reportTruthOverall = reportTruthActionabilityReview.program_status.overall_completion_percent const reportTruthFindings = reportTruthActionabilityReview.rollups.zero_signal_finding_count const reportTruthCritical = reportTruthActionabilityReview.rollups.critical_finding_count @@ -2458,9 +2534,12 @@ export function AutomationInventoryTab() { } /> } /> } /> + 0 ? 'danger' : 'ok'} icon={} /> } /> 0 ? 'danger' : 'ok'} icon={} /> } /> + } /> + } />
@@ -2524,6 +2603,9 @@ export function AutomationInventoryTab() {
+ + {agent.primary_role} + + + + + + {agent.workload_note} + ) })} @@ -4076,6 +4164,165 @@ export function AutomationInventoryTab() { +
+
+
+ + + {t('ownerApprovedResultCaptureDryRun.title')} + +
+ +
+ +
+ } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> +
+ +
+
+ {t('ownerApprovedResultCaptureDryRun.packetTitle')} + + {ownerApprovedResultCaptureDryRun.approval_packet.operator_meaning} + +
+ + + + +
+
+ +
+ {t('ownerApprovedResultCaptureDryRun.truthTitle')} + + {ownerApprovedResultCaptureDryRun.dry_run_truth.truth_note} + +
+ + + + + + + + +
+
+
+ +
+ {visibleOwnerResultDryRunTemplates.map(template => ( +
+
+ + {template.display_name} + + +
+
+ + + +
+ + {template.blocked_write_action} + +
+ + + + +
+
+ ))} +
+ +
+ {visibleOwnerResultDryRunScoreFixtures.map(fixture => ( +
+
+ + {fixture.fixture_id} + + +
+ + {fixture.failure_if_missing} + +
+ + + + + +
+
+ ))} +
+ +
+ {visibleOwnerResultDryRunGates.map(gate => ( +
+
+ + {gate.display_name} + + +
+ + {gate.required_evidence} + + + {gate.blocked_write_action} + +
+ + +
+
+ ))} +
+ +
+ {visibleOwnerResultDryRunActions.map(action => ( +
+
+ + {action.display_name} + + +
+ + {action.operator_instruction} + +
+ + +
+
+ ))} +
+
+
diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index c4458f43d..7a89fb42e 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -425,6 +425,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentOwnerApprovedResultCaptureDryRun() { + const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-result-capture-dry-run`) + return handleResponse(res) + }, + async getAiAgentOwnerApprovedFixtureDryRun() { const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-fixture-dry-run`) return handleResponse(res) @@ -3372,6 +3377,150 @@ export interface AiAgentCriticReviewerResultCaptureSnapshot { } } +export interface AiAgentOwnerApprovedResultCaptureDryRunSnapshot { + schema_version: 'ai_agent_owner_approved_result_capture_dry_run_v1' + generated_at: string + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: 'P2-106' + next_task_id: 'P2-107' + read_only_mode: true + runtime_authority: 'owner_approved_result_capture_dry_run_only_no_live_write' + status_note: string + } + source_refs: string[] + prior_contract_readback: { + source_schema_version: 'ai_agent_critic_reviewer_result_capture_v1' + readback_at: string + approval_24h_total: number + scorecard_count: number + result_capture_contract_count: number + promotion_gate_count: number + candidate_route_count: number + approved_without_execution_meta_24h: number + execution_failed_with_matched_24h: number + pending_with_matched_24h: number + result_capture_runtime_write_count: number + learning_write_count: number + playbook_trust_write_count: number + telegram_send_count: number + readback_note: string + } + dry_run_truth: { + p2_105_contract_loaded: true + owner_approval_required: true + dry_run_preview_allowed: true + result_capture_payload_template_ready: true + critic_reviewer_score_fixture_ready: true + post_write_verifier_fixture_required: true + redacted_operator_digest_ready: true + runtime_result_capture_write_enabled: false + runtime_score_write_enabled: false + runtime_learning_write_enabled: false + playbook_trust_write_enabled: false + gateway_queue_write_enabled: false + telegram_send_enabled: false + production_write_enabled: false + secret_read_enabled: false + destructive_operation_enabled: false + owner_approval_received_count: number + dry_run_preview_generated_count: number + result_capture_write_count_24h: number + score_write_count_24h: number + learning_write_count_24h: number + playbook_trust_write_count_24h: number + gateway_queue_write_count_24h: number + telegram_send_count_24h: number + production_write_count_24h: number + secret_read_count_24h: number + destructive_operation_count_24h: number + truth_note: string + } + approval_packet: { + packet_id: string + display_name: string + required_owner_fields: string[] + forbidden_inputs: string[] + operator_meaning: string + dry_run_plan_fingerprint: string + } + result_capture_dry_run_templates: Array<{ + template_id: string + display_name: string + source_contract_id: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + result_state: string + status: 'ready_for_dry_run' | 'approval_required' | 'blocked_by_policy' + candidate_count_24h: number + required_inputs: string[] + preview_outputs: string[] + write_enabled: false + runtime_writer_enabled: false + blocked_write_action: string + no_write_evidence_hash: string + }> + critic_reviewer_score_fixtures: Array<{ + fixture_id: string + source_scorecard_id: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + minimum_score: number + fixture_only: true + runtime_score_write_enabled: false + required_inputs: string[] + failure_if_missing: string + evidence_hash: string + }> + dry_run_gates: Array<{ + gate_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: 'ready' | 'approval_required' | 'blocked_by_policy' + required_evidence: string + blocked_write_action: string + creates_runtime_write: false + }> + operator_actions: Array<{ + action_id: string + action_type: 'review' | 'collect_evidence' | 'approve_dry_run' | 'reject_or_rework' | 'promote_to_next_gate' + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + operator_instruction: string + runtime_write_allowed: false + }> + display_redaction_contract: { + redaction_required: true + frontend_display_policy: string + 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 + } + rollups: { + result_capture_template_count: number + score_fixture_count: number + dry_run_gate_count: number + operator_action_count: number + approval_required_gate_count: number + blocked_gate_count: number + approval_24h_total: number + approved_without_execution_meta_24h: number + execution_failed_with_matched_24h: number + owner_approval_received_count: number + dry_run_preview_generated_count: number + result_capture_write_count: number + score_write_count: number + learning_write_count: number + playbook_trust_write_count: number + gateway_queue_write_count: number + telegram_send_count: number + production_write_count: number + secret_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 3d731c2b7..cc9d2758f 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,26 @@ +## 2026-06-13|P2-106 Owner-approved result capture dry-run 本地完成 + +**背景**:P2-105 已把 approved 後缺口轉成 critic / reviewer scorecard、result capture contract、promotion gate 與 candidate route;但統帥批准後仍不能直接寫 score、result capture、learning、PlayBook trust 或 Telegram。本段把「批准後可以先產生什麼 no-write preview」固定成只讀契約。 + +**修正內容:** +- 新增 `ai_agent_owner_approved_result_capture_dry_run_v1` schema、committed snapshot 與 backend loader,固定 5 個 result capture dry-run template、5 個 critic / reviewer score fixture、7 個 dry-run gate 與 5 個 operator action。 +- 新增 `GET /api/v1/agents/agent-owner-approved-result-capture-dry-run` 只讀 API;API 只回傳批准包、no-write template、score fixture、gate、operator action 與 display redaction contract。 +- Governance automation inventory 頁新增 P2-106 區塊,顯示 P2-106 進度 `100%`、捕捉模板 `5`、評分 fixture `5`、dry-run gate `7`、操作選項 `5`、批准缺口 `63`、失敗候選 `1`、owner approval received `0`、dry-run preview generated `0` 與 live writes `0`。 +- P2-403J「每個 Agent 工作量」區塊強化為工作狀態報告:OpenClaw / Hermes / NemoTron 顯示角色、工作單位、完成比例、待審核數、負責報告章節、分析建議數、24h runtime 作業數與狀態說明。 +- 更新 MASTER §3.2 / §5 與 `docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md`,將 P2-106 標記為完成,下一步改為 `P2-107`。 + +**本地驗證:** +- JSON parse:P2-106 snapshot / schema、`zh-TW.json` 通過。 +- API/service pytest:P2-106 result capture dry-run、P2-403J report automation review、public redaction 目標測試 `23 passed`。 +- `pnpm --filter @awoooi/web typecheck` 通過。 +- `git diff --check` 通過。 +- Production readback(部署前現況):`GET /api/v1/agents/agent-report-automation-review` 回 P2-403J `100%`,日報 / 週報 / 月報 ready 皆為 `true`;OpenClaw `38/32/6`、Hermes `45/42/3`、NemoTron `8/5/3`;`report_delivery_enabled=false`、live delivery `0`、live optimization `0`。 + +**邊界:** +- 本段不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 KM、不 runtime append LOGBOOK、不寫 audit DB、不寫 timeline、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不啟動 live AI runtime worker、不啟動中低風險 auto worker、不跑 verifier live readback、不讀 secret、不呼叫付費 provider、不執行 host / cluster / destructive action、不提供前端批准 / 執行 / 發送按鈕。 +- 日報 / 週報 / 月報目前是可見契約與治理頁報告;正式定時派送、AI 讀報後 runtime analysis、中低風險自動優化與 Telegram receipt 仍須後續 runtime gate 通過。 +- 正式站 P2-106 API / UI / browser smoke 待本段推版後補記。 + ## 2026-06-13|P0-PUBLICENV public host alias redaction 正式收斂 **正式部署錨點:** 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 1bbcf96b4..b8982953e 100644 --- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md +++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md @@ -12,17 +12,17 @@ | 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;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 證據;P2-103 已完成任務結果稽核軌跡;P2-104 已完成 matched PlayBook 學習缺口回查;P2-105 已完成 critic / reviewer 評分與 result capture 契約,固定 5 張 scorecard、5 個 result capture contract、6 個 promotion gate 與 4 條 candidate route。runtime worker、DB migration、production Redis consumer group、runtime score、result capture write、Telegram 實發、delivery receipt E2E、report delivery、Gateway queue write、AI analysis runtime、中低風險 auto worker、KM / LOGBOOK / audit DB / timeline / PlayBook trust 寫入、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`、`ai_agent_task_result_audit_trail_v1`、`ai_agent_matched_playbook_learning_gap_v1`、`ai_agent_critic_reviewer_result_capture_v1`、`GET /api/v1/agents/agent-operation-permission-model`、`GET /api/v1/agents/agent-candidate-operation-dry-run-evidence`、`GET /api/v1/agents/agent-task-result-audit-trail`、`GET /api/v1/agents/agent-matched-playbook-learning-gap`、`GET /api/v1/agents/agent-critic-reviewer-result-capture`、`/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 證據;P2-103 已完成任務結果稽核軌跡;P2-104 已完成 matched PlayBook 學習缺口回查;P2-105 已完成 critic / reviewer 評分與 result capture 契約;P2-106 已完成 owner-approved result capture dry-run,固定 5 個 no-write template、5 個 score fixture、7 個 dry-run gate 與 5 個 operator action。runtime worker、DB migration、production Redis consumer group、runtime score、result capture write、Telegram 實發、delivery receipt E2E、report delivery、Gateway queue write、AI analysis runtime、中低風險 auto worker、KM / LOGBOOK / audit DB / timeline / PlayBook trust 寫入、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`、`ai_agent_task_result_audit_trail_v1`、`ai_agent_matched_playbook_learning_gap_v1`、`ai_agent_critic_reviewer_result_capture_v1`、`ai_agent_owner_approved_result_capture_dry_run_v1`、`GET /api/v1/agents/agent-operation-permission-model`、`GET /api/v1/agents/agent-candidate-operation-dry-run-evidence`、`GET /api/v1/agents/agent-task-result-audit-trail`、`GET /api/v1/agents/agent-matched-playbook-learning-gap`、`GET /api/v1/agents/agent-critic-reviewer-result-capture`、`GET /api/v1/agents/agent-owner-approved-result-capture-dry-run`、`/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 文件 | -AI Agent 自動化工作包目前完成度:**92%**。本工作清單文件本身完成度:**100%**。 +AI Agent 自動化工作包目前完成度:**93%**。本工作清單文件本身完成度:**100%**。 三 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、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 證據、P2-103 任務結果稽核軌跡、P2-104 matched PlayBook 學習缺口回查,以及 P2-105 critic / reviewer 評分與 result capture 契約;目前 live AgentSession、Agent message、handoff、runtime score、result capture write、learning write、Telegram receipt、Gateway queue write、runtime verifier execution、report delivery、AI analysis runtime、中低風險 auto worker、Telegram 實發、shadow worker live、delivery receipt E2E、KM / LOGBOOK / audit DB / timeline / PlayBook trust runtime 寫入仍全部為 `0`。P2-104 證實 `matched_playbook_id` 近 24h 已 `66/66`;P2-105 已固定 5 張 scorecard、5 個 result capture contract、6 個 promotion gate 與 4 條 candidate route。真正下一步是 `P2-106`:owner-approved result capture dry-run。 +三 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 證據、P2-103 任務結果稽核軌跡、P2-104 matched PlayBook 學習缺口回查、P2-105 critic / reviewer 評分與 result capture 契約,以及 P2-106 owner-approved result capture dry-run;目前 live AgentSession、Agent message、handoff、runtime score、result capture write、learning write、Telegram receipt、Gateway queue write、runtime verifier execution、report delivery、AI analysis runtime、中低風險 auto worker、Telegram 實發、shadow worker live、delivery receipt E2E、KM / LOGBOOK / audit DB / timeline / PlayBook trust runtime 寫入仍全部為 `0`。P2-106 已固定 5 個 no-write result capture template、5 個 score fixture、7 個 dry-run gate 與 5 個 operator action;真正下一步是 `P2-107`。 -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-105` 已補互動、學習證據面、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 證據、任務結果稽核軌跡、matched PlayBook 學習缺口與 critic / reviewer result capture。下一步是 `P2-106` owner-approved result capture 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-106` 已補互動、學習證據面、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 證據、任務結果稽核軌跡、matched PlayBook 學習缺口、critic / reviewer result capture 與 owner-approved result capture dry-run。下一步是 `P2-107`;外部 registry / package source / host probe / SSH / kubectl / 工具安裝 / CI 變更 / 實際 PR creation / Telegram 實發與 learning write 仍需 gate。 完成度計算模型: @@ -978,7 +978,7 @@ UI: | P2-103 | 完成 | 99 | Hermes + OpenClaw | 把任務結果接回 KM / LOGBOOK / 稽核軌跡 | `ai_agent_task_result_audit_trail_v1` / snapshot / 只讀 API / governance UI;8 條 result route、6 個 writeback contract、7 個 audit checkpoint、5 個 operator handoff;KM / LOGBOOK / audit DB / timeline / PlayBook trust / queue / Telegram 寫入全為 `0` | 已由 P2-104 承接;不寫 KM、不 runtime append LOGBOOK、不寫 audit DB、不送 Telegram | | P2-104 | 完成 | 100 | OpenClaw + Hermes + NemoTron | 修復 `matched_playbook_id` 學習缺口 | `ai_agent_matched_playbook_learning_gap_v1` / snapshot / 只讀 API / governance UI;正式 DB 只讀回查確認 24h approval `66`、matched `66`、matched rate `100%`、approved without execution meta `63`、pending matched `2`、execution failed matched `1`、PlayBook updated_24h `0`;5 條 gap lane、5 個 learning gate、4 個 writeback candidate | 已由 P2-105 承接;不寫 learning、不更新 PlayBook trust、不送 Telegram、不寫 Gateway queue、不讀 secret | | P2-105 | 完成 | 100 | OpenClaw + Hermes + NemoTron | 批准前加入 critic / reviewer 評分 | `ai_agent_critic_reviewer_result_capture_v1` / snapshot / 只讀 API / governance UI;5 張 Agent scorecard、5 個 result capture contract、6 個 promotion gate、4 條 candidate route;approved gap `63`、failed candidate `1`;runtime score / result capture write / learning / trust / queue / Telegram 全為 `0` | 已由 P2-106 承接;不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不送 Telegram | -| P2-106 | 待辦 | 0 | OpenClaw + Hermes + NemoTron | owner-approved result capture dry-run | no-write payload、owner review packet、post-write verifier gate | 不啟動 live write、不送 Telegram、不更新 PlayBook trust | +| P2-106 | 完成 | 100 | OpenClaw + Hermes + NemoTron | owner-approved result capture dry-run | `ai_agent_owner_approved_result_capture_dry_run_v1` / schema / snapshot / 只讀 API / governance UI;5 個 no-write result capture template、5 個 score fixture、7 個 dry-run gate、5 個 operator action;owner approval received、preview generated、score / result capture / learning / trust / queue / Telegram 全部 `0` | 已由 P2-107 承接;不啟動 live write、不送 Telegram、不更新 PlayBook trust | ### P3 - 候選 Agent 擴展 diff --git a/docs/evaluations/ai_agent_owner_approved_result_capture_dry_run_2026-06-13.json b/docs/evaluations/ai_agent_owner_approved_result_capture_dry_run_2026-06-13.json new file mode 100644 index 000000000..dcd714a80 --- /dev/null +++ b/docs/evaluations/ai_agent_owner_approved_result_capture_dry_run_2026-06-13.json @@ -0,0 +1,442 @@ +{ + "schema_version": "ai_agent_owner_approved_result_capture_dry_run_v1", + "generated_at": "2026-06-13T12:05:00+08:00", + "program_status": { + "overall_completion_percent": 100, + "current_priority": "P2", + "current_task_id": "P2-106", + "next_task_id": "P2-107", + "read_only_mode": true, + "runtime_authority": "owner_approved_result_capture_dry_run_only_no_live_write", + "status_note": "P2-106 將 P2-105 的 scorecard、result capture contract 與 promotion gate 收斂成 owner-approved no-write dry-run;本快照只定義批准包、預覽模板、評分 fixture 與 verifier fixture,不寫 runtime 結果。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_critic_reviewer_result_capture_2026-06-13.json", + "apps/api/src/services/ai_agent_critic_reviewer_result_capture.py", + "apps/api/src/services/learning_service.py", + "apps/api/src/services/approval_execution.py", + "apps/api/src/services/post_execution_verifier.py", + "apps/api/src/services/telegram_gateway.py", + "docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md" + ], + "prior_contract_readback": { + "source_schema_version": "ai_agent_critic_reviewer_result_capture_v1", + "readback_at": "2026-06-13T01:46:00+08:00", + "approval_24h_total": 66, + "scorecard_count": 5, + "result_capture_contract_count": 5, + "promotion_gate_count": 6, + "candidate_route_count": 4, + "approved_without_execution_meta_24h": 63, + "execution_failed_with_matched_24h": 1, + "pending_with_matched_24h": 2, + "result_capture_runtime_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "telegram_send_count": 0, + "readback_note": "P2-105 已固定 score/result-capture 欄位;P2-106 只建立 owner approval 後的 dry-run preview,不產生 live write。" + }, + "dry_run_truth": { + "p2_105_contract_loaded": true, + "owner_approval_required": true, + "dry_run_preview_allowed": true, + "result_capture_payload_template_ready": true, + "critic_reviewer_score_fixture_ready": true, + "post_write_verifier_fixture_required": true, + "redacted_operator_digest_ready": true, + "runtime_result_capture_write_enabled": false, + "runtime_score_write_enabled": false, + "runtime_learning_write_enabled": false, + "playbook_trust_write_enabled": false, + "gateway_queue_write_enabled": false, + "telegram_send_enabled": false, + "production_write_enabled": false, + "secret_read_enabled": false, + "destructive_operation_enabled": false, + "owner_approval_received_count": 0, + "dry_run_preview_generated_count": 0, + "result_capture_write_count_24h": 0, + "score_write_count_24h": 0, + "learning_write_count_24h": 0, + "playbook_trust_write_count_24h": 0, + "gateway_queue_write_count_24h": 0, + "telegram_send_count_24h": 0, + "production_write_count_24h": 0, + "secret_read_count_24h": 0, + "destructive_operation_count_24h": 0, + "truth_note": "統帥批准後也只允許產生 no-write preview 與 verifier fixture;任何 score、result capture、learning、trust、Gateway queue、Telegram 與 production write 仍為 0。" + }, + "approval_packet": { + "packet_id": "owner_packet_p2_106_result_capture_dry_run", + "display_name": "P2-106 owner-approved result capture dry-run 批准包", + "required_owner_fields": [ + "owner_approval_id", + "owner_role", + "approval_scope", + "approved_result_capture_contract_ids", + "redacted_evidence_refs", + "dry_run_plan_fingerprint", + "rollback_plan_ref", + "post_write_verifier_plan_ref" + ], + "forbidden_inputs": [ + "未脫敏提示內容", + "私有推理內容", + "機密明文", + "未脫敏工具輸出", + "未脫敏瀏覽器狀態" + ], + "operator_meaning": "統帥批准時只批准產生 dry-run preview,不批准正式寫入 result capture、score、learning 或 Telegram 實發。", + "dry_run_plan_fingerprint": "sha256:1111111111111111111111111111111111111111111111111111111111111111" + }, + "result_capture_dry_run_templates": [ + { + "template_id": "dry_run_capture_approved_execution_result", + "display_name": "APPROVED result capture dry-run", + "source_contract_id": "capture_approved_execution_result", + "owner_agent": "openclaw", + "result_state": "approved_without_execution_meta", + "status": "approval_required", + "candidate_count_24h": 63, + "required_inputs": [ + "owner_approval_id", + "approval_id", + "incident_id", + "matched_playbook_id", + "operator_outcome", + "verifier_plan_ref" + ], + "preview_outputs": [ + "redacted_result_capture_payload", + "post_write_verifier_fixture", + "rollback_or_noop_plan", + "operator_digest_preview" + ], + "write_enabled": false, + "runtime_writer_enabled": false, + "blocked_write_action": "result_capture_table_insert", + "no_write_evidence_hash": "sha256:1212121212121212121212121212121212121212121212121212121212121212" + }, + { + "template_id": "dry_run_capture_execution_failed_candidate", + "display_name": "EXECUTION_FAILED negative-learning candidate dry-run", + "source_contract_id": "capture_execution_failed_candidate", + "owner_agent": "nemotron", + "result_state": "execution_failed", + "status": "approval_required", + "candidate_count_24h": 1, + "required_inputs": [ + "owner_approval_id", + "approval_id", + "matched_playbook_id", + "failure_reason_ref", + "negative_learning_weight_preview", + "verifier_plan_ref" + ], + "preview_outputs": [ + "failure_candidate_payload_preview", + "negative_learning_preview", + "trust_update_hold_reason", + "operator_digest_preview" + ], + "write_enabled": false, + "runtime_writer_enabled": false, + "blocked_write_action": "negative_learning_insert", + "no_write_evidence_hash": "sha256:2323232323232323232323232323232323232323232323232323232323232323" + }, + { + "template_id": "dry_run_capture_pending_human_gate", + "display_name": "PENDING human gate dry-run", + "source_contract_id": "capture_pending_human_gate", + "owner_agent": "hermes", + "result_state": "pending_human_gate", + "status": "blocked_by_policy", + "candidate_count_24h": 2, + "required_inputs": [ + "approval_id", + "matched_playbook_id", + "pending_reason", + "human_decision_required", + "expires_at" + ], + "preview_outputs": [ + "hold_reason_preview", + "owner_followup_packet", + "no_learning_write_notice" + ], + "write_enabled": false, + "runtime_writer_enabled": false, + "blocked_write_action": "pending_to_learning_promotion", + "no_write_evidence_hash": "sha256:3434343434343434343434343434343434343434343434343434343434343434" + }, + { + "template_id": "dry_run_capture_manual_or_noop", + "display_name": "manual / no-op result dry-run", + "source_contract_id": "capture_noop_manual_resolution", + "owner_agent": "hermes", + "result_state": "manual_or_noop", + "status": "ready_for_dry_run", + "candidate_count_24h": 0, + "required_inputs": [ + "incident_id", + "approval_id", + "manual_resolution_note", + "noop_reason", + "operator_verified" + ], + "preview_outputs": [ + "manual_result_payload_preview", + "noop_classification_preview", + "learning_exclusion_reason" + ], + "write_enabled": false, + "runtime_writer_enabled": false, + "blocked_write_action": "manual_result_capture_write", + "no_write_evidence_hash": "sha256:4545454545454545454545454545454545454545454545454545454545454545" + }, + { + "template_id": "dry_run_capture_post_write_verifier_receipt", + "display_name": "post-write verifier receipt dry-run", + "source_contract_id": "capture_post_write_verifier_receipt", + "owner_agent": "nemotron", + "result_state": "verifier_receipt", + "status": "blocked_by_policy", + "candidate_count_24h": 0, + "required_inputs": [ + "verifier_plan_ref", + "expected_result_hash", + "rollback_plan_ref", + "operator_ack_required" + ], + "preview_outputs": [ + "verifier_receipt_fixture", + "rollback_trigger_preview", + "operator_digest_preview" + ], + "write_enabled": false, + "runtime_writer_enabled": false, + "blocked_write_action": "verifier_receipt_write", + "no_write_evidence_hash": "sha256:5656565656565656565656565656565656565656565656565656565656565656" + } + ], + "critic_reviewer_score_fixtures": [ + { + "fixture_id": "fixture_openclaw_critic_decision_quality", + "source_scorecard_id": "scorecard_openclaw_critic_decision_quality", + "owner_agent": "openclaw", + "minimum_score": 80, + "fixture_only": true, + "runtime_score_write_enabled": false, + "required_inputs": [ + "hypothesis_count", + "challenge_count", + "unsafe_action_detected", + "verdict" + ], + "failure_if_missing": "缺 Critic 反方挑戰時,dry-run preview 必須停在 rework。", + "evidence_hash": "sha256:6767676767676767676767676767676767676767676767676767676767676767" + }, + { + "fixture_id": "fixture_openclaw_reviewer_safety_verdict", + "source_scorecard_id": "scorecard_openclaw_reviewer_safety_verdict", + "owner_agent": "openclaw", + "minimum_score": 90, + "fixture_only": true, + "runtime_score_write_enabled": false, + "required_inputs": [ + "risk_level", + "hard_rules_hit", + "dry_run_evidence_hash", + "rollback_owner" + ], + "failure_if_missing": "缺 Reviewer 安全裁決時,不得產生 owner-approved dry-run preview。", + "evidence_hash": "sha256:7878787878787878787878787878787878787878787878787878787878787878" + }, + { + "fixture_id": "fixture_hermes_redaction_operator_report", + "source_scorecard_id": "scorecard_hermes_redaction_operator_report", + "owner_agent": "hermes", + "minimum_score": 85, + "fixture_only": true, + "runtime_score_write_enabled": false, + "required_inputs": [ + "redacted_summary", + "blocked_fields", + "allowed_display_fields", + "operator_next_action" + ], + "failure_if_missing": "缺公開遮罩報告時,治理頁與摘要不得顯示 dry-run preview。", + "evidence_hash": "sha256:8989898989898989898989898989898989898989898989898989898989898989" + }, + { + "fixture_id": "fixture_nemotron_failure_candidate_verifier", + "source_scorecard_id": "scorecard_nemotron_failure_candidate_verifier", + "owner_agent": "nemotron", + "minimum_score": 80, + "fixture_only": true, + "runtime_score_write_enabled": false, + "required_inputs": [ + "failure_reason", + "result_state", + "verifier_plan", + "no_write_replay_hash" + ], + "failure_if_missing": "NemoTron 僅能驗證 failure candidate dry-run,不得直接寫負向 learning。", + "evidence_hash": "sha256:9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a" + }, + { + "fixture_id": "fixture_coordinator_disagreement_gate", + "source_scorecard_id": "scorecard_coordinator_disagreement_gate", + "owner_agent": "openclaw", + "minimum_score": 95, + "fixture_only": true, + "runtime_score_write_enabled": false, + "required_inputs": [ + "critic_verdict", + "reviewer_verdict", + "disagreement_reason", + "human_gate_required" + ], + "failure_if_missing": "Critic / Reviewer 不一致時,dry-run preview 必須停在 owner hold。", + "evidence_hash": "sha256:abababababababababababababababababababababababababababababababab" + } + ], + "dry_run_gates": [ + { + "gate_id": "gate_owner_approval_packet_complete", + "display_name": "Owner approval packet complete", + "owner_agent": "openclaw", + "status": "approval_required", + "required_evidence": "必須有 owner role、scope、approved contract ids、公開遮罩證據與 plan fingerprint。", + "blocked_write_action": "result_capture_write_without_owner_packet", + "creates_runtime_write": false + }, + { + "gate_id": "gate_critic_reviewer_score_fixture_complete", + "display_name": "Critic / Reviewer score fixture complete", + "owner_agent": "openclaw", + "status": "ready", + "required_evidence": "5 張 score fixture 必須齊備,且 runtime score write 維持 false。", + "blocked_write_action": "score_table_insert", + "creates_runtime_write": false + }, + { + "gate_id": "gate_result_capture_payload_preview_complete", + "display_name": "Result capture payload preview complete", + "owner_agent": "hermes", + "status": "ready", + "required_evidence": "5 組 result capture dry-run template 必須輸出公開遮罩 preview。", + "blocked_write_action": "result_capture_table_insert", + "creates_runtime_write": false + }, + { + "gate_id": "gate_redaction_public_display_safe", + "display_name": "Public display redaction safe", + "owner_agent": "hermes", + "status": "ready", + "required_evidence": "治理頁只能顯示公開遮罩 summary、hash 與 operator instruction。", + "blocked_write_action": "unredacted_display", + "creates_runtime_write": false + }, + { + "gate_id": "gate_no_live_write_enforced", + "display_name": "No live write enforced", + "owner_agent": "openclaw", + "status": "blocked_by_policy", + "required_evidence": "P2-106 不得寫 score/result/learning/trust/Gateway/Telegram/production target。", + "blocked_write_action": "any_live_write", + "creates_runtime_write": false + }, + { + "gate_id": "gate_post_write_verifier_fixture_ready", + "display_name": "Post-write verifier fixture ready", + "owner_agent": "nemotron", + "status": "approval_required", + "required_evidence": "post-write verifier 只能產生 fixture 與 rollback preview,不讀 canonical target。", + "blocked_write_action": "post_write_verifier_execution", + "creates_runtime_write": false + }, + { + "gate_id": "gate_operator_digest_preview_only", + "display_name": "Operator digest preview only", + "owner_agent": "hermes", + "status": "ready", + "required_evidence": "Telegram 只允許摘要預覽,不呼叫 Bot API、不寫 Gateway queue。", + "blocked_write_action": "telegram_or_gateway_send", + "creates_runtime_write": false + } + ], + "operator_actions": [ + { + "action_id": "op_review_owner_packet", + "action_type": "review", + "display_name": "審查 owner approval packet", + "owner_agent": "openclaw", + "operator_instruction": "檢查批准範圍與 contract ids 是否只涵蓋 dry-run preview。", + "runtime_write_allowed": false + }, + { + "action_id": "op_collect_redacted_evidence", + "action_type": "collect_evidence", + "display_name": "收集公開遮罩證據", + "owner_agent": "hermes", + "operator_instruction": "只收公開遮罩 refs、hash 與 rollback / verifier refs。", + "runtime_write_allowed": false + }, + { + "action_id": "op_approve_no_write_dry_run", + "action_type": "approve_dry_run", + "display_name": "批准 no-write dry-run", + "owner_agent": "openclaw", + "operator_instruction": "批准後仍只產生 preview artifact,不寫任何 runtime target。", + "runtime_write_allowed": false + }, + { + "action_id": "op_reject_or_rework_packet", + "action_type": "reject_or_rework", + "display_name": "退回或補件", + "owner_agent": "hermes", + "operator_instruction": "缺任一 owner 欄位、遮罩證據或 verifier plan 時,退回補件。", + "runtime_write_allowed": false + }, + { + "action_id": "op_promote_to_p2_107_gate", + "action_type": "promote_to_next_gate", + "display_name": "推進 P2-107 verifier gate", + "owner_agent": "nemotron", + "operator_instruction": "僅在 dry-run preview 與 verifier fixture 都通過後,提出下一關審核。", + "runtime_write_allowed": false + } + ], + "display_redaction_contract": { + "redaction_required": true, + "frontend_display_policy": "治理頁只顯示公開遮罩 summary、hash、gate、fixture 與 operator action;不得顯示內部協作逐字稿、未脫敏提示內容、私有推理內容、未脫敏工具輸出或機密明文。", + "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 + }, + "rollups": { + "result_capture_template_count": 5, + "score_fixture_count": 5, + "dry_run_gate_count": 7, + "operator_action_count": 5, + "approval_required_gate_count": 2, + "blocked_gate_count": 1, + "approval_24h_total": 66, + "approved_without_execution_meta_24h": 63, + "execution_failed_with_matched_24h": 1, + "owner_approval_received_count": 0, + "dry_run_preview_generated_count": 0, + "result_capture_write_count": 0, + "score_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "destructive_operation_count": 0 + } +} diff --git a/docs/schemas/ai_agent_owner_approved_result_capture_dry_run_v1.schema.json b/docs/schemas/ai_agent_owner_approved_result_capture_dry_run_v1.schema.json new file mode 100644 index 000000000..46f762d1c --- /dev/null +++ b/docs/schemas/ai_agent_owner_approved_result_capture_dry_run_v1.schema.json @@ -0,0 +1,151 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://awoooi.wooo.work/schemas/ai_agent_owner_approved_result_capture_dry_run_v1.schema.json", + "title": "AI Agent Owner Approved Result Capture Dry Run v1", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "program_status", + "source_refs", + "prior_contract_readback", + "dry_run_truth", + "approval_packet", + "result_capture_dry_run_templates", + "critic_reviewer_score_fixtures", + "dry_run_gates", + "operator_actions", + "display_redaction_contract", + "rollups" + ], + "properties": { + "schema_version": { "const": "ai_agent_owner_approved_result_capture_dry_run_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-106" }, + "next_task_id": { "const": "P2-107" }, + "read_only_mode": { "const": true }, + "runtime_authority": { "const": "owner_approved_result_capture_dry_run_only_no_live_write" }, + "status_note": { "type": "string" } + }, + "additionalProperties": false + }, + "source_refs": { "type": "array", "items": { "type": "string" }, "minItems": 1 }, + "prior_contract_readback": { + "type": "object", + "required": [ + "source_schema_version", + "readback_at", + "approval_24h_total", + "scorecard_count", + "result_capture_contract_count", + "promotion_gate_count", + "candidate_route_count", + "approved_without_execution_meta_24h", + "execution_failed_with_matched_24h", + "pending_with_matched_24h", + "result_capture_runtime_write_count", + "learning_write_count", + "playbook_trust_write_count", + "telegram_send_count", + "readback_note" + ], + "properties": { + "source_schema_version": { "const": "ai_agent_critic_reviewer_result_capture_v1" }, + "readback_at": { "type": "string" }, + "approval_24h_total": { "type": "integer", "minimum": 0 }, + "scorecard_count": { "const": 5 }, + "result_capture_contract_count": { "const": 5 }, + "promotion_gate_count": { "const": 6 }, + "candidate_route_count": { "const": 4 }, + "approved_without_execution_meta_24h": { "type": "integer", "minimum": 0 }, + "execution_failed_with_matched_24h": { "type": "integer", "minimum": 0 }, + "pending_with_matched_24h": { "type": "integer", "minimum": 0 }, + "result_capture_runtime_write_count": { "const": 0 }, + "learning_write_count": { "const": 0 }, + "playbook_trust_write_count": { "const": 0 }, + "telegram_send_count": { "const": 0 }, + "readback_note": { "type": "string" } + }, + "additionalProperties": false + }, + "dry_run_truth": { "type": "object" }, + "approval_packet": { + "type": "object", + "required": [ + "packet_id", + "display_name", + "required_owner_fields", + "forbidden_inputs", + "operator_meaning", + "dry_run_plan_fingerprint" + ], + "properties": { + "packet_id": { "type": "string" }, + "display_name": { "type": "string" }, + "required_owner_fields": { "type": "array", "items": { "type": "string" }, "minItems": 1 }, + "forbidden_inputs": { "type": "array", "items": { "type": "string" } }, + "operator_meaning": { "type": "string" }, + "dry_run_plan_fingerprint": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" } + }, + "additionalProperties": false + }, + "result_capture_dry_run_templates": { + "type": "array", + "minItems": 5, + "items": { + "type": "object", + "required": [ + "template_id", + "display_name", + "source_contract_id", + "owner_agent", + "result_state", + "status", + "candidate_count_24h", + "required_inputs", + "preview_outputs", + "write_enabled", + "runtime_writer_enabled", + "blocked_write_action", + "no_write_evidence_hash" + ], + "properties": { + "template_id": { "type": "string" }, + "display_name": { "type": "string" }, + "source_contract_id": { "type": "string" }, + "owner_agent": { "enum": ["openclaw", "hermes", "nemotron"] }, + "result_state": { "type": "string" }, + "status": { "enum": ["ready_for_dry_run", "approval_required", "blocked_by_policy"] }, + "candidate_count_24h": { "type": "integer", "minimum": 0 }, + "required_inputs": { "type": "array", "items": { "type": "string" }, "minItems": 1 }, + "preview_outputs": { "type": "array", "items": { "type": "string" }, "minItems": 1 }, + "write_enabled": { "const": false }, + "runtime_writer_enabled": { "const": false }, + "blocked_write_action": { "type": "string" }, + "no_write_evidence_hash": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" } + }, + "additionalProperties": false + } + }, + "critic_reviewer_score_fixtures": { "type": "array", "minItems": 5 }, + "dry_run_gates": { "type": "array", "minItems": 7 }, + "operator_actions": { "type": "array", "minItems": 5 }, + "display_redaction_contract": { "type": "object" }, + "rollups": { "type": "object" } + }, + "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 e8f879f55..10ef8f108 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 @@ -643,6 +643,7 @@ Alert / Sentry / SigNoz / Gitea / Market Watch / Operator | `docs/evaluations/ai_agent_task_result_audit_trail_2026-06-13.json` + `GET /api/v1/agents/agent-task-result-audit-trail` | P2-103 任務結果稽核軌跡;8 條 result route、6 個 writeback contract、7 個 audit checkpoint、5 個 operator handoff;把 diagnostic-only、repair candidate、execution failed、provider unmatched、report zero-signal 等結果固定到 KM 草稿、LOGBOOK 證據、audit trail、timeline 與人工下一步;KM / LOGBOOK / audit DB / timeline / PlayBook trust / Gateway queue / Telegram 寫入全為 `0 / false`,已由 P2-104 承接 | | `docs/evaluations/ai_agent_matched_playbook_learning_gap_2026-06-13.json` + `GET /api/v1/agents/agent-matched-playbook-learning-gap` | P2-104 matched PlayBook 學習缺口;正式 DB 只讀回查確認 24h approval `66`、matched `66`、matched rate `100%`,active gap 是 APPROVED `63` 筆缺 execution learning、PlayBook updated_24h `0`;5 條 gap lane、5 個 learning gate、4 個 writeback candidate;learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 全部 `0 / false`,已由 P2-105 承接 | | `docs/evaluations/ai_agent_critic_reviewer_result_capture_2026-06-13.json` + `GET /api/v1/agents/agent-critic-reviewer-result-capture` | P2-105 critic / reviewer 評分與 result capture 契約;承接 P2-104 approved gap `63` 與 failed candidate `1`,建立 5 張 scorecard、5 個 result capture contract、6 個 promotion gate、4 條 candidate route;runtime Critic score、Reviewer score、result capture write、learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 全部 `0 / false`,下一步 P2-106 owner-approved result capture dry-run | +| `docs/evaluations/ai_agent_owner_approved_result_capture_dry_run_2026-06-13.json` + `GET /api/v1/agents/agent-owner-approved-result-capture-dry-run` | P2-106 owner-approved result capture dry-run;承接 P2-105 scorecard / result capture contract / promotion gate,建立 5 個 result capture dry-run template、5 個 critic / reviewer score fixture、7 個 dry-run gate 與 5 個 operator action;owner approval received、dry-run preview generated、score write、result capture write、learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 全部 `0 / false`,下一步 P2-107 | | `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 主動營運委派與版本生命週期契約 @@ -736,7 +737,8 @@ Repo / registry / release notes / K8s / host / observability / backup evidence 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 承接。 24. 把任務結果接回 KM / LOGBOOK / 稽核軌跡。✅ P2-103 已完成;result route `8`、writeback contract `6`、audit checkpoint `7`、operator handoff `5`,KM write、LOGBOOK runtime append、audit DB write、timeline write、PlayBook trust write、Gateway queue write、Telegram send 與 production write 仍為 `0 / false`。已由 P2-104 承接。 25. 修復 matched PlayBook 學習缺口。✅ P2-104 已完成;正式 DB 只讀回查確認 24h approval `66`、matched `66`、matched rate `100%`,真正缺口是 APPROVED `63` 筆缺 execution learning、PlayBook updated_24h `0`;learning write、PlayBook trust write、Gateway queue write、Telegram send 與 production write 仍為 `0 / false`。已由 P2-105 承接。 -26. 批准前加入 critic / reviewer 評分與 result capture。✅ P2-105 已完成;scorecard `5`、result capture contract `5`、promotion gate `6`、candidate route `4`,approved gap `63`、failed candidate `1`;runtime score、result capture write、learning write、PlayBook trust write、Gateway queue write、Telegram send 與 production write 仍為 `0 / false`。下一步 P2-106 owner-approved result capture dry-run。 +26. 批准前加入 critic / reviewer 評分與 result capture。✅ P2-105 已完成;scorecard `5`、result capture contract `5`、promotion gate `6`、candidate route `4`,approved gap `63`、failed candidate `1`;runtime score、result capture write、learning write、PlayBook trust write、Gateway queue write、Telegram send 與 production write 仍為 `0 / false`。已由 P2-106 承接。 +27. 建立 owner-approved result capture dry-run。✅ P2-106 已完成;result capture dry-run template `5`、score fixture `5`、dry-run gate `7`、operator action `5`,owner approval received、dry-run preview generated、score write、result capture write、learning write、PlayBook trust write、Gateway queue write、Telegram send 與 production write 仍為 `0 / false`。下一步 P2-107。 #### 3.2.1d 2026-06-11 Agent 互動、學習與成長證據面 @@ -770,6 +772,9 @@ Repo / registry / release notes / K8s / host / observability / backup evidence | `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 | | `docs/evaluations/ai_agent_task_result_audit_trail_2026-06-13.json` + `GET /api/v1/agents/agent-task-result-audit-trail` | P2-103 任務結果稽核軌跡;8 條 result route、6 個 writeback contract、7 個 audit checkpoint、5 個 operator handoff;不寫 KM、不 runtime append LOGBOOK、不寫 audit DB、不寫 timeline、不更新 PlayBook trust、不寫 Gateway queue、不送 Telegram | | `docs/evaluations/ai_agent_matched_playbook_learning_gap_2026-06-13.json` + `GET /api/v1/agents/agent-matched-playbook-learning-gap` | P2-104 matched PlayBook 學習缺口;24h approval `66`、matched `66`、approved without execution meta `63`、PlayBook updated_24h `0`;不寫 learning、不更新 PlayBook trust、不寫 Gateway queue、不送 Telegram、不讀 secret | +| `docs/evaluations/ai_agent_critic_reviewer_result_capture_2026-06-13.json` + `GET /api/v1/agents/agent-critic-reviewer-result-capture` | P2-105 critic / reviewer 評分與 result capture;5 張 scorecard、5 個 result capture contract、6 個 promotion gate、4 條 candidate route;不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 Gateway queue、不送 Telegram | +| `docs/schemas/ai_agent_owner_approved_result_capture_dry_run_v1.schema.json` | P2-106 owner-approved result capture dry-run schema;強制 owner approval received、dry-run preview generated、score write、result capture write、learning write、PlayBook trust write、Gateway queue、Telegram、production write、secret read 與 destructive action 全部維持 `0 / false` | +| `docs/evaluations/ai_agent_owner_approved_result_capture_dry_run_2026-06-13.json` + `GET /api/v1/agents/agent-owner-approved-result-capture-dry-run` | P2-106 committed snapshot;5 個 no-write result capture template、5 個 critic / reviewer score fixture、7 個 dry-run gate、5 個 operator action;不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不送 Telegram、不讀 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 仍需批准 | @@ -1958,6 +1963,14 @@ Phase 6 完成後 - 政策裁決:P2-105 只定義批准前互判與批准後結果捕捉欄位,讓 OpenClaw / Hermes / NemoTron 的接手與成長具備可審核欄位;不得把 scorecard 可見解讀成 runtime score 已寫入。 - 本波仍不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 KM、不 runtime append LOGBOOK、不寫 audit DB、不寫 timeline、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不啟動 runtime worker、不讀 secret、不回傳內部協作內容;下一步 P2-106 owner-approved result capture dry-run。 +### 2026-06-13 12:49 (台北) — §3.2 / §5 — 完成 P2-106 owner-approved result capture dry-run — 把統帥批准後可做的 no-write preview 固定成可審核契約 + +- 新增 `ai_agent_owner_approved_result_capture_dry_run_v1` schema / committed snapshot / loader / API / 測試,承接 P2-105 的 scorecard、result capture contract 與 promotion gate,定義 5 個 result capture dry-run template、5 個 critic / reviewer score fixture、7 個 dry-run gate 與 5 個 operator action。 +- `apps/web/src/app/[locale]/governance/tabs/automation-inventory-tab.tsx` 接入 `GET /api/v1/agents/agent-owner-approved-result-capture-dry-run`,治理頁顯示 P2-106 進度 `100%`、捕捉模板 `5`、評分 fixture `5`、dry-run gate `7`、操作選項 `5`、批准缺口 `63`、失敗候選 `1`、owner approval received `0`、dry-run preview generated `0` 與 live writes `0`。 +- 同步強化 P2-403J 每個 Agent 工作狀態報告:OpenClaw / Hermes / NemoTron 卡片顯示角色、工作單位、完成比例、待審核數、負責報告章節、分析建議數與 24h runtime 作業數;正式 runtime delivery 與 auto optimization 仍不得因 UI 可見而視為啟用。 +- 政策裁決:P2-106 只允許在統帥批准後產生 no-write preview 與 verifier fixture;不得把批准包、score fixture 或 dry-run template 解讀成 runtime score / result capture / learning 已寫入。 +- 本波仍不寫 score、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 KM、不 runtime append LOGBOOK、不寫 audit DB、不寫 timeline、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不啟動 runtime worker、不讀 secret、不執行 destructive action、不回傳內部協作內容;下一步 P2-107。 + ### 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。