From 655df33d39102ec21652df2536e4c2e7e24b1075 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 14 Jun 2026 10:56:52 +0800 Subject: [PATCH] =?UTF-8?q?feat(governance):=20=E6=96=B0=E5=A2=9E=20releas?= =?UTF-8?q?e=20decision=20hold?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/api/v1/agents.py | 35 ++ ...nt_result_capture_release_decision_hold.py | 399 +++++++++++++ ...nt_result_capture_release_decision_hold.py | 129 ++++ ...esult_capture_release_decision_hold_api.py | 36 ++ apps/web/messages/en.json | 34 ++ apps/web/messages/zh-TW.json | 34 ++ .../tabs/automation-inventory-tab.tsx | 191 +++++- apps/web/src/lib/api-client.ts | 190 ++++++ docs/LOGBOOK.md | 24 + ...AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md | 16 +- ...ture_release_decision_hold_2026-06-14.json | 563 ++++++++++++++++++ ...pture_release_decision_hold_v1.schema.json | 109 ++++ ...-04-15-MASTER-ai-autonomous-flywheel-v2.md | 11 +- 13 files changed, 1766 insertions(+), 5 deletions(-) create mode 100644 apps/api/src/services/ai_agent_result_capture_release_decision_hold.py create mode 100644 apps/api/tests/test_ai_agent_result_capture_release_decision_hold.py create mode 100644 apps/api/tests/test_ai_agent_result_capture_release_decision_hold_api.py create mode 100644 docs/evaluations/ai_agent_result_capture_release_decision_hold_2026-06-14.json create mode 100644 docs/schemas/ai_agent_result_capture_release_decision_hold_v1.schema.json diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index c169d7617..9e1bb31fa 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -121,6 +121,9 @@ from src.services.ai_agent_reviewer_queue_no_write_readback import ( from src.services.ai_agent_result_capture_no_write_readback import ( load_latest_ai_agent_result_capture_no_write_readback, ) +from src.services.ai_agent_result_capture_release_decision_hold import ( + load_latest_ai_agent_result_capture_release_decision_hold, +) from src.services.ai_agent_result_capture_owner_promotion_review import ( load_latest_ai_agent_result_capture_owner_promotion_review, ) @@ -2256,6 +2259,38 @@ async def get_agent_result_capture_release_verifier_owner_review_packet() -> dic ) from exc +@router.get( + "/agent-result-capture-release-decision-hold", + response_model=dict[str, Any], + summary="取得 AI Agent result capture release decision hold", + description=( + "讀取最新已提交的 P2-138 release decision hold;" + "此端點只回傳 release decision hold、owner / verifier / rollback / live apply decision hold、" + "blocked decision transition 與 operator action," + "不把 owner review packet 視為決策通過、不授權 owner release、不通過 release decision、" + "不核發或通過 release authorization、不釋放 rollback release 或 live apply、不套用 writer、" + "不寫 reviewer queue、Gateway queue、receipt、result capture、learning 或 PlayBook trust," + "不送 Telegram、不呼叫 Bot API、不讀 secret、不執行 production 寫入。" + ), +) +async def get_agent_result_capture_release_decision_hold() -> dict[str, Any]: + """Return the latest read-only release decision hold.""" + try: + payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_release_decision_hold) + 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_result_capture_release_decision_hold_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="AI Agent result capture release decision hold 無效", + ) from exc + + @router.get( "/agent-owner-approved-fixture-dry-run", response_model=dict[str, Any], diff --git a/apps/api/src/services/ai_agent_result_capture_release_decision_hold.py b/apps/api/src/services/ai_agent_result_capture_release_decision_hold.py new file mode 100644 index 000000000..4ef57e97f --- /dev/null +++ b/apps/api/src/services/ai_agent_result_capture_release_decision_hold.py @@ -0,0 +1,399 @@ +""" +AI Agent result capture release decision hold snapshot. + +Loads the latest committed P2-138 release decision hold. This module validates +committed evidence only; it never treats owner review packets as decisions, +authorizes owner release, grants release authorization, writes reviewer / +Gateway queues, sends Telegram messages, writes result capture, writes learning +records, updates PlayBook trust, reads secrets, writes production targets, or +performs destructive operations. +""" + +from __future__ import annotations + +import json +import re +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_result_capture_release_decision_hold_*.json" +_SCHEMA_VERSION = "ai_agent_result_capture_release_decision_hold_v1" +_RUNTIME_AUTHORITY = "result_capture_release_decision_hold_only_no_live_write" + + +def load_latest_ai_agent_result_capture_release_decision_hold( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed release decision hold.""" + directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR + candidates = sorted(directory.glob(_SNAPSHOT_PATTERN)) + if not candidates: + raise FileNotFoundError(f"no AI Agent result capture release decision hold snapshots found in {directory}") + + latest = candidates[-1] + with latest.open(encoding="utf-8") as handle: + payload = json.load(handle) + + if not isinstance(payload, dict): + raise ValueError(f"{latest}: expected JSON object") + + label = str(latest) + _require_schema(payload, label) + _require_prior(payload, label) + _require_truth(payload, label) + _require_release_decision_holds(payload, label) + _require_owner_decision_holds(payload, label) + _require_verifier_decision_holds(payload, label) + _require_rollback_decision_holds(payload, label) + _require_live_apply_decision_holds(payload, label) + _require_blocked_transitions(payload, label) + _require_actions(payload, label) + _require_display_redaction(payload, label) + _require_no_forbidden_display_terms(payload, label) + _require_rollup_consistency(payload, label) + return payload + + +def _require_schema(payload: dict[str, Any], label: str) -> None: + if payload.get("schema_version") != _SCHEMA_VERSION: + raise ValueError(f"{label}: expected schema_version={_SCHEMA_VERSION}") + status = payload.get("program_status") or {} + expected = { + "current_priority": "P2", + "current_task_id": "P2-138", + "next_task_id": "P2-139", + "read_only_mode": True, + "runtime_authority": _RUNTIME_AUTHORITY, + "overall_completion_percent": 100, + } + mismatches = _mismatches(status, expected) + if mismatches: + raise ValueError(f"{label}: program_status mismatch: {mismatches}") + if not status.get("status_note"): + raise ValueError(f"{label}: program_status.status_note is required") + + +def _require_prior(payload: dict[str, Any], label: str) -> None: + prior = payload.get("prior_release_verifier_owner_review_packet") or {} + expected = { + "schema_version": "ai_agent_result_capture_release_verifier_owner_review_packet_v1", + "owner_review_packet_count": 5, + "verifier_review_packet_count": 5, + "rollback_owner_review_packet_count": 5, + "maintenance_window_review_hold_count": 5, + "live_apply_owner_review_hold_count": 5, + "blocked_owner_review_transition_count": 6, + "operator_action_count": 5, + "approval_required_total": 10, + "blocked_total": 10, + } + expected.update(_zero_count_expectations()) + mismatches = _mismatches(prior, expected) + if mismatches: + raise ValueError(f"{label}: prior_release_verifier_owner_review_packet mismatch: {mismatches}") + if not prior.get("readiness_note"): + raise ValueError(f"{label}: prior_release_verifier_owner_review_packet.readiness_note is required") + + +def _require_truth(payload: dict[str, Any], label: str) -> None: + truth = payload.get("release_decision_hold_truth") or {} + required_true = { + "p2_137_owner_review_packet_loaded", + "release_decision_hold_ready", + "owner_decision_required", + "verifier_decision_required", + "rollback_decision_required", + "maintenance_window_decision_required", + "live_apply_decision_required", + "redaction_review_required", + "release_decision_hold_only", + } + missing = sorted(field for field in required_true if truth.get(field) is not True) + if missing: + raise ValueError(f"{label}: release decision hold flags must remain true: {missing}") + + required_false = { + "owner_release_authorized", + "owner_release_approved", + "owner_review_approved", + "owner_decision_approved", + "verifier_decision_approved", + "maintenance_window_approved", + "rollback_owner_confirmed", + "post_release_verifier_ready", + "final_release_candidate_approved", + "final_release_candidate_passed", + "release_decision_passed", + "release_authorization_granted", + "release_authorization_passed", + "rollback_release_passed", + "live_apply_release_passed", + "writer_apply_enabled", + "execution_apply_enabled", + "receipt_write_enabled", + "reviewer_queue_write_enabled", + "gateway_queue_write_enabled", + "telegram_send_enabled", + "bot_api_call_enabled", + "report_receipt_write_enabled", + "result_capture_write_enabled", + "learning_write_enabled", + "playbook_trust_write_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}: release decision hold live/send/write flags must remain false: {unsafe}") + + non_zero = sorted(field for field in _zero_count_expectations() if truth.get(field) != 0) + if non_zero: + raise ValueError(f"{label}: release decision hold live counters must remain zero: {non_zero}") + if not truth.get("truth_note"): + raise ValueError(f"{label}: release_decision_hold_truth.truth_note is required") + + +def _require_release_decision_holds(payload: dict[str, Any], label: str) -> None: + items = payload.get("release_decision_holds") or [] + _require_ids(items, "hold_id", _expected_ids("release_decision_hold"), label, "release decision holds") + for item in items: + item_id = item.get("hold_id") + if item.get("hold_mode") != "release_decision_hold": + raise ValueError(f"{label}: release decision hold {item_id} mode is invalid") + if item.get("decision_required") is not True: + raise ValueError(f"{label}: release decision hold {item_id} must require decision") + if ( + item.get("decision_approved") is not False + or item.get("release_authorization_granted") is not False + or item.get("release_decision_passed") is not False + ): + raise ValueError(f"{label}: release decision hold {item_id} must stay unapproved, ungranted, and unpassed") + _require_valid_status(item, label, f"release decision hold {item_id}") + if not item.get("decision_summary") or not _is_redacted_sha256(item.get("evidence_hash")): + raise ValueError(f"{label}: release decision hold {item_id} must include decision_summary and redacted evidence_hash") + + +def _require_owner_decision_holds(payload: dict[str, Any], label: str) -> None: + items = payload.get("owner_decision_holds") or [] + _require_ids(items, "hold_id", _expected_ids("owner_decision_hold"), label, "owner decision holds") + for item in items: + item_id = item.get("hold_id") + if item.get("hold_mode") != "owner_decision_hold": + raise ValueError(f"{label}: owner decision hold {item_id} mode is invalid") + if item.get("owner_release_authorized") is not False or item.get("owner_decision_approved") is not False: + raise ValueError(f"{label}: owner decision hold {item_id} must stay unauthorized and unapproved") + _require_valid_status(item, label, f"owner decision hold {item_id}") + if not item.get("decision_summary"): + raise ValueError(f"{label}: owner decision hold {item_id}.decision_summary is required") + + +def _require_verifier_decision_holds(payload: dict[str, Any], label: str) -> None: + items = payload.get("verifier_decision_holds") or [] + _require_ids(items, "hold_id", _expected_ids("verifier_decision_hold"), label, "verifier decision holds") + for item in items: + item_id = item.get("hold_id") + if item.get("hold_mode") != "verifier_decision_hold": + raise ValueError(f"{label}: verifier decision hold {item_id} mode is invalid") + if item.get("post_release_verifier_required") is not True: + raise ValueError(f"{label}: verifier decision hold {item_id} must require post-release verifier") + if item.get("verifier_decision_approved") is not False or item.get("release_authorization_passed") is not False: + raise ValueError(f"{label}: verifier decision hold {item_id} must stay unapproved and unpassed") + _require_valid_status(item, label, f"verifier decision hold {item_id}") + + +def _require_rollback_decision_holds(payload: dict[str, Any], label: str) -> None: + items = payload.get("rollback_decision_holds") or [] + _require_ids(items, "hold_id", _expected_ids("rollback_decision_hold"), label, "rollback decision holds") + for item in items: + item_id = item.get("hold_id") + if item.get("hold_mode") != "rollback_decision_hold": + raise ValueError(f"{label}: rollback decision hold {item_id} mode is invalid") + if item.get("rollback_owner_required") is not True: + raise ValueError(f"{label}: rollback decision hold {item_id} must require rollback owner") + if item.get("rollback_owner_confirmed") is not False or item.get("rollback_release_passed") is not False: + raise ValueError(f"{label}: rollback decision hold {item_id} must stay unconfirmed and unpassed") + _require_valid_status(item, label, f"rollback decision hold {item_id}") + + +def _require_live_apply_decision_holds(payload: dict[str, Any], label: str) -> None: + items = payload.get("live_apply_decision_holds") or [] + _require_ids(items, "hold_id", _expected_ids("live_apply_decision_hold"), label, "live apply decision holds") + for item in items: + item_id = item.get("hold_id") + if item.get("hold_mode") != "live_apply_decision_hold": + raise ValueError(f"{label}: live apply decision hold {item_id} mode is invalid") + if ( + item.get("release_authorization_granted") is not False + or item.get("release_authorization_passed") is not False + or item.get("live_apply_release_passed") is not False + or item.get("live_apply_release_enabled") is not False + ): + raise ValueError(f"{label}: live apply decision hold {item_id} must stay ungranted, unpassed, and disabled") + _require_valid_status(item, label, f"live apply decision hold {item_id}") + + +def _require_blocked_transitions(payload: dict[str, Any], label: str) -> None: + items = payload.get("blocked_decision_transitions") or [] + _require_count(items, 6, label, "blocked decision transitions") + for item in items: + item_id = item.get("transition_id") + if item.get("runtime_write_allowed") is not False: + raise ValueError(f"{label}: blocked decision transition {item_id} must keep runtime_write_allowed=false") + if item.get("status") not in {"blocked_by_policy", "approval_required"}: + raise ValueError(f"{label}: blocked decision transition {item_id} status is invalid") + if not item.get("reason"): + raise ValueError(f"{label}: blocked decision transition {item_id}.reason is required") + + +def _require_actions(payload: dict[str, Any], label: str) -> None: + items = payload.get("operator_actions") or [] + _require_count(items, 5, label, "operator actions") + for item in items: + item_id = item.get("action_id") + if item.get("runtime_write_allowed") is not False: + raise ValueError(f"{label}: operator action {item_id} must keep runtime_write_allowed=false") + if item.get("status") not in {"ready_for_operator_review", "approval_required"}: + raise ValueError(f"{label}: operator action {item_id} status is invalid") + if not item.get("operator_instruction"): + raise ValueError(f"{label}: operator action {item_id}.operator_instruction is required") + + +def _require_display_redaction(payload: dict[str, Any], label: str) -> None: + contract = payload.get("display_redaction_contract") or {} + expected = { + "redaction_required": True, + "raw_prompt_display_allowed": False, + "private_reasoning_display_allowed": False, + "secret_value_display_allowed": False, + "raw_runtime_payload_display_allowed": False, + "internal_collaboration_content_display_allowed": False, + } + mismatches = _mismatches(contract, expected) + if mismatches: + raise ValueError(f"{label}: display_redaction_contract mismatch: {mismatches}") + if not contract.get("frontend_display_policy"): + raise ValueError(f"{label}: display_redaction_contract.frontend_display_policy is required") + + +def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None: + rollups = payload.get("rollups") or {} + expected = { + "release_decision_hold_count": len(payload.get("release_decision_holds") or []), + "owner_decision_hold_count": len(payload.get("owner_decision_holds") or []), + "verifier_decision_hold_count": len(payload.get("verifier_decision_holds") or []), + "rollback_decision_hold_count": len(payload.get("rollback_decision_holds") or []), + "live_apply_decision_hold_count": len(payload.get("live_apply_decision_holds") or []), + "blocked_decision_transition_count": len(payload.get("blocked_decision_transitions") or []), + "operator_action_count": len(payload.get("operator_actions") or []), + } + expected.update(_zero_count_expectations()) + mismatches = _mismatches(rollups, expected) + if mismatches: + raise ValueError(f"{label}: rollups mismatch: {mismatches}") + + +def _require_no_forbidden_display_terms(payload: Any, label: str) -> None: + forbidden = { + "work_window_transcript", + "internal_collaboration_transcript", + "raw_prompt", + "private_reasoning", + "chain_of_thought", + "authorization_header", + "secret_value", + } + found = sorted(term for term in forbidden if _contains_term(payload, term)) + if found: + raise ValueError(f"{label}: forbidden display terms leaked: {found}") + + +def _contains_term(value: Any, term: str) -> bool: + if isinstance(value, str): + return term.lower() in value.lower() + if isinstance(value, dict): + return any(_contains_term(child, term) for child in value.values()) + if isinstance(value, list): + return any(_contains_term(child, term) for child in value) + return False + + +def _zero_count_expectations() -> dict[str, int]: + return { + "owner_release_authorized_count": 0, + "owner_release_approved_count": 0, + "owner_review_approved_count": 0, + "owner_decision_approved_count": 0, + "verifier_decision_approved_count": 0, + "maintenance_window_approved_count": 0, + "rollback_owner_confirmed_count": 0, + "post_release_verifier_ready_count": 0, + "final_release_candidate_approved_count": 0, + "final_release_candidate_pass_count": 0, + "release_decision_pass_count": 0, + "release_authorization_granted_count": 0, + "release_authorization_pass_count": 0, + "rollback_release_pass_count": 0, + "live_apply_release_pass_count": 0, + "writer_apply_count": 0, + "execution_apply_count": 0, + "receipt_write_count": 0, + "reviewer_queue_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "bot_api_call_count": 0, + "report_receipt_write_count": 0, + "result_capture_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "destructive_operation_count": 0, + } + + +def _expected_ids(prefix: str) -> set[str]: + return { + f"{prefix}_result_capture", + f"{prefix}_learning", + f"{prefix}_playbook_trust", + f"{prefix}_reviewer_queue", + f"{prefix}_gateway_queue", + } + + +def _require_ids( + items: list[dict[str, Any]], + field: str, + expected_ids: set[str], + label: str, + group_name: str, +) -> None: + actual_ids = {str(item.get(field)) for item in items} + if actual_ids != expected_ids: + raise ValueError(f"{label}: {group_name} ids mismatch: expected={sorted(expected_ids)} actual={sorted(actual_ids)}") + + +def _require_count(items: list[Any], expected: int, label: str, group_name: str) -> None: + if len(items) != expected: + raise ValueError(f"{label}: expected {expected} {group_name}, got {len(items)}") + + +def _require_valid_status(item: dict[str, Any], label: str, item_label: str) -> None: + if item.get("status") not in {"ready_for_decision_review", "approval_required", "blocked_by_policy"}: + raise ValueError(f"{label}: {item_label} status is invalid") + + +def _is_redacted_sha256(value: Any) -> bool: + return isinstance(value, str) and re.fullmatch(r"sha256:[0-9a-f]{64}", value) is not None + + +def _mismatches(payload: dict[str, Any], expected: dict[str, Any]) -> dict[str, dict[str, Any]]: + return { + key: {"expected": expected_value, "actual": payload.get(key)} + for key, expected_value in expected.items() + if payload.get(key) != expected_value + } diff --git a/apps/api/tests/test_ai_agent_result_capture_release_decision_hold.py b/apps/api/tests/test_ai_agent_result_capture_release_decision_hold.py new file mode 100644 index 000000000..4aa89a369 --- /dev/null +++ b/apps/api/tests/test_ai_agent_result_capture_release_decision_hold.py @@ -0,0 +1,129 @@ +from __future__ import annotations + +import copy +import json +from pathlib import Path + +import pytest + +from src.services.ai_agent_result_capture_release_decision_hold import ( + load_latest_ai_agent_result_capture_release_decision_hold, +) + + +def test_load_latest_release_decision_hold_snapshot() -> None: + snapshot = load_latest_ai_agent_result_capture_release_decision_hold() + + assert snapshot["schema_version"] == "ai_agent_result_capture_release_decision_hold_v1" + assert snapshot["program_status"]["current_task_id"] == "P2-138" + assert snapshot["program_status"]["next_task_id"] == "P2-139" + assert snapshot["program_status"]["overall_completion_percent"] == 100 + assert snapshot["program_status"]["runtime_authority"] == "result_capture_release_decision_hold_only_no_live_write" + + rollups = snapshot["rollups"] + assert rollups["release_decision_hold_count"] == 5 + assert rollups["owner_decision_hold_count"] == 5 + assert rollups["verifier_decision_hold_count"] == 5 + assert rollups["rollback_decision_hold_count"] == 5 + assert rollups["live_apply_decision_hold_count"] == 5 + assert rollups["blocked_decision_transition_count"] == 6 + assert rollups["operator_action_count"] == 5 + assert ( + rollups["approval_required_release_decision_count"] + + rollups["approval_required_owner_decision_count"] + + rollups["approval_required_verifier_decision_count"] + + rollups["approval_required_rollback_decision_count"] + + rollups["approval_required_live_apply_decision_count"] + ) == 10 + assert ( + rollups["blocked_release_decision_count"] + + rollups["blocked_owner_decision_count"] + + rollups["blocked_verifier_decision_count"] + + rollups["blocked_rollback_decision_count"] + + rollups["blocked_live_apply_decision_count"] + + rollups["critical_blocker_count"] + ) == 10 + + assert rollups["owner_release_authorized_count"] == 0 + assert rollups["owner_review_approved_count"] == 0 + assert rollups["owner_decision_approved_count"] == 0 + assert rollups["verifier_decision_approved_count"] == 0 + assert rollups["release_decision_pass_count"] == 0 + assert rollups["release_authorization_granted_count"] == 0 + assert rollups["release_authorization_pass_count"] == 0 + assert rollups["rollback_release_pass_count"] == 0 + assert rollups["live_apply_release_pass_count"] == 0 + assert rollups["writer_apply_count"] == 0 + assert rollups["reviewer_queue_write_count"] == 0 + assert rollups["gateway_queue_write_count"] == 0 + assert rollups["telegram_send_count"] == 0 + assert rollups["bot_api_call_count"] == 0 + assert rollups["result_capture_write_count"] == 0 + assert rollups["learning_write_count"] == 0 + assert rollups["playbook_trust_write_count"] == 0 + assert rollups["production_write_count"] == 0 + + +def test_release_decision_hold_truth_keeps_all_live_paths_closed() -> None: + snapshot = load_latest_ai_agent_result_capture_release_decision_hold() + truth = snapshot["release_decision_hold_truth"] + + assert truth["p2_137_owner_review_packet_loaded"] is True + assert truth["release_decision_hold_ready"] is True + assert truth["owner_decision_required"] is True + assert truth["owner_release_authorized"] is False + assert truth["owner_review_approved"] is False + assert truth["owner_decision_approved"] is False + assert truth["verifier_decision_approved"] is False + assert truth["release_decision_passed"] is False + assert truth["release_authorization_granted"] is False + assert truth["release_authorization_passed"] is False + assert truth["rollback_release_passed"] is False + assert truth["live_apply_release_passed"] is False + assert truth["reviewer_queue_write_enabled"] is False + assert truth["gateway_queue_write_enabled"] is False + assert truth["telegram_send_enabled"] is False + assert truth["production_write_enabled"] is False + assert truth["secret_read_enabled"] is False + assert truth["destructive_operation_enabled"] is False + + +def test_rejects_release_decision_passed(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_decision_hold()) + snapshot["release_decision_holds"][0]["release_decision_passed"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="must stay unapproved, ungranted, and unpassed"): + load_latest_ai_agent_result_capture_release_decision_hold(tmp_path) + + +def test_rejects_owner_decision_approved(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_decision_hold()) + snapshot["owner_decision_holds"][0]["owner_decision_approved"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="must stay unauthorized and unapproved"): + load_latest_ai_agent_result_capture_release_decision_hold(tmp_path) + + +def test_rejects_queue_write_drift(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_decision_hold()) + snapshot["rollups"]["gateway_queue_write_count"] = 1 + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="rollups mismatch"): + load_latest_ai_agent_result_capture_release_decision_hold(tmp_path) + + +def test_rejects_forbidden_display_terms(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_decision_hold()) + snapshot["operator_actions"][0]["operator_instruction"] = "不要顯示 raw_prompt" + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="forbidden display terms leaked"): + load_latest_ai_agent_result_capture_release_decision_hold(tmp_path) + + +def _write_snapshot(directory: Path, payload: dict) -> None: + path = directory / "ai_agent_result_capture_release_decision_hold_2099-01-01.json" + path.write_text(json.dumps(payload, ensure_ascii=False), encoding="utf-8") diff --git a/apps/api/tests/test_ai_agent_result_capture_release_decision_hold_api.py b/apps/api/tests/test_ai_agent_result_capture_release_decision_hold_api.py new file mode 100644 index 000000000..714fa50e2 --- /dev/null +++ b/apps/api/tests/test_ai_agent_result_capture_release_decision_hold_api.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +from fastapi.testclient import TestClient + +from src.main import app + + +def test_release_decision_hold_endpoint() -> None: + client = TestClient(app) + + response = client.get("/api/v1/agents/agent-result-capture-release-decision-hold") + + assert response.status_code == 200 + payload = response.json() + assert payload["schema_version"] == "ai_agent_result_capture_release_decision_hold_v1" + assert payload["program_status"]["current_task_id"] == "P2-138" + assert payload["program_status"]["next_task_id"] == "P2-139" + assert payload["program_status"]["overall_completion_percent"] == 100 + assert payload["program_status"]["runtime_authority"] == "result_capture_release_decision_hold_only_no_live_write" + assert payload["rollups"]["release_decision_hold_count"] == 5 + assert payload["rollups"]["owner_decision_hold_count"] == 5 + assert payload["rollups"]["verifier_decision_hold_count"] == 5 + assert payload["rollups"]["rollback_decision_hold_count"] == 5 + assert payload["rollups"]["live_apply_decision_hold_count"] == 5 + assert payload["rollups"]["blocked_decision_transition_count"] == 6 + assert payload["rollups"]["operator_action_count"] == 5 + assert payload["rollups"]["owner_release_authorized_count"] == 0 + assert payload["rollups"]["owner_review_approved_count"] == 0 + assert payload["rollups"]["owner_decision_approved_count"] == 0 + assert payload["rollups"]["release_decision_pass_count"] == 0 + assert payload["rollups"]["release_authorization_granted_count"] == 0 + assert payload["rollups"]["release_authorization_pass_count"] == 0 + assert payload["rollups"]["reviewer_queue_write_count"] == 0 + assert payload["rollups"]["gateway_queue_write_count"] == 0 + assert payload["rollups"]["telegram_send_count"] == 0 + assert payload["rollups"]["production_write_count"] == 0 diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 90a6d7dfe..9ce74b767 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -6143,6 +6143,40 @@ "runtimeWriteAllowed": "runtime 寫入允許={value}" } }, + "resultCaptureReleaseDecisionHold": { + "title": "P2-138 釋出決策保留關卡", + "source": "產生 {generated};目前 {current};下一步 {next}", + "priorTitle": "前一關負責人審查包", + "truthTitle": "釋出決策保留真相", + "metrics": { + "overall": "完成度", + "releaseHolds": "釋出決策保留", + "ownerHolds": "負責人決策保留", + "verifierHolds": "驗證器決策保留", + "rollbackHolds": "回滾決策保留", + "liveApplyHolds": "正式套用保留", + "blockers": "已阻擋轉換", + "actions": "操作選項", + "approvalRequired": "需批准", + "blocked": "阻擋", + "liveWrites": "正式寫入 / 發送" + }, + "flags": { + "p2Loaded": "已載入 P2-137={value}", + "decisionHoldReady": "釋出決策保留已就緒={value}", + "liveApplyDecisionRequired": "正式套用決策必要={value}" + }, + "labels": { + "ownerReleaseAuthorized": "負責人釋出授權={value}", + "ownerDecisionApproved": "負責人決策通過={value}", + "decisionApproved": "決策通過={value}", + "authorizationGranted": "釋出授權已核發={value}", + "decisionPassed": "釋出決策通過={value}", + "authorizationPassed": "釋出授權通過={value}", + "gatewayWrites": "Gateway 寫入={value}", + "runtimeWriteAllowed": "runtime 寫入允許={value}" + } + }, "resultCaptureReleaseVerifierOwnerReviewPacket": { "title": "P2-137 釋出驗證器負責人審查包", "source": "產生 {generated};目前 {current};下一步 {next}", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 90a6d7dfe..9ce74b767 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -6143,6 +6143,40 @@ "runtimeWriteAllowed": "runtime 寫入允許={value}" } }, + "resultCaptureReleaseDecisionHold": { + "title": "P2-138 釋出決策保留關卡", + "source": "產生 {generated};目前 {current};下一步 {next}", + "priorTitle": "前一關負責人審查包", + "truthTitle": "釋出決策保留真相", + "metrics": { + "overall": "完成度", + "releaseHolds": "釋出決策保留", + "ownerHolds": "負責人決策保留", + "verifierHolds": "驗證器決策保留", + "rollbackHolds": "回滾決策保留", + "liveApplyHolds": "正式套用保留", + "blockers": "已阻擋轉換", + "actions": "操作選項", + "approvalRequired": "需批准", + "blocked": "阻擋", + "liveWrites": "正式寫入 / 發送" + }, + "flags": { + "p2Loaded": "已載入 P2-137={value}", + "decisionHoldReady": "釋出決策保留已就緒={value}", + "liveApplyDecisionRequired": "正式套用決策必要={value}" + }, + "labels": { + "ownerReleaseAuthorized": "負責人釋出授權={value}", + "ownerDecisionApproved": "負責人決策通過={value}", + "decisionApproved": "決策通過={value}", + "authorizationGranted": "釋出授權已核發={value}", + "decisionPassed": "釋出決策通過={value}", + "authorizationPassed": "釋出授權通過={value}", + "gatewayWrites": "Gateway 寫入={value}", + "runtimeWriteAllowed": "runtime 寫入允許={value}" + } + }, "resultCaptureReleaseVerifierOwnerReviewPacket": { "title": "P2-137 釋出驗證器負責人審查包", "source": "產生 {generated};目前 {current};下一步 {next}", 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 ebf899ece..ea25c17ff 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 @@ -84,6 +84,7 @@ import { type AiAgentResultCaptureFinalReleaseCandidateReadbackSnapshot, type AiAgentResultCaptureReleaseAuthorizationHoldSnapshot, type AiAgentResultCaptureReleaseAuthorizationReadbackGateSnapshot, + type AiAgentResultCaptureReleaseDecisionHoldSnapshot, type AiAgentResultCaptureReleaseVerifierPreflightGateSnapshot, type AiAgentResultCaptureReleaseVerifierOwnerReviewPacketSnapshot, type AiAgentResultCaptureWriterImplementationReviewSnapshot, @@ -494,6 +495,7 @@ export function AutomationInventoryTab() { const [resultCaptureReleaseAuthorizationReadbackGate, setResultCaptureReleaseAuthorizationReadbackGate] = useState(null) const [resultCaptureReleaseVerifierPreflightGate, setResultCaptureReleaseVerifierPreflightGate] = useState(null) const [resultCaptureReleaseVerifierOwnerReviewPacket, setResultCaptureReleaseVerifierOwnerReviewPacket] = useState(null) + const [resultCaptureReleaseDecisionHold, setResultCaptureReleaseDecisionHold] = useState(null) const [reportTruthActionabilityReview, setReportTruthActionabilityReview] = useState(null) const [ownerDryRunPackage, setOwnerDryRunPackage] = useState(null) const [hostStatefulInventory, setHostStatefulInventory] = useState(null) @@ -568,6 +570,7 @@ export function AutomationInventoryTab() { apiClient.getAiAgentResultCaptureReleaseAuthorizationReadbackGate(), apiClient.getAiAgentResultCaptureReleaseVerifierPreflightGate(), apiClient.getAiAgentResultCaptureReleaseVerifierOwnerReviewPacket(), + apiClient.getAiAgentResultCaptureReleaseDecisionHold(), apiClient.getAiAgentReportTruthActionabilityReview(), apiClient.getAiAgentOwnerApprovedFixtureDryRun(), apiClient.getAiAgentHostStatefulVersionInventory(), @@ -641,6 +644,7 @@ export function AutomationInventoryTab() { resultCaptureReleaseAuthorizationReadbackGateResult, resultCaptureReleaseVerifierPreflightGateResult, resultCaptureReleaseVerifierOwnerReviewPacketResult, + resultCaptureReleaseDecisionHoldResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -711,6 +715,7 @@ export function AutomationInventoryTab() { setResultCaptureReleaseAuthorizationReadbackGate(resultCaptureReleaseAuthorizationReadbackGateResult.status === 'fulfilled' ? resultCaptureReleaseAuthorizationReadbackGateResult.value : null) setResultCaptureReleaseVerifierPreflightGate(resultCaptureReleaseVerifierPreflightGateResult.status === 'fulfilled' ? resultCaptureReleaseVerifierPreflightGateResult.value : null) setResultCaptureReleaseVerifierOwnerReviewPacket(resultCaptureReleaseVerifierOwnerReviewPacketResult.status === 'fulfilled' ? resultCaptureReleaseVerifierOwnerReviewPacketResult.value : null) + setResultCaptureReleaseDecisionHold(resultCaptureReleaseDecisionHoldResult.status === 'fulfilled' ? resultCaptureReleaseDecisionHoldResult.value : null) setReportTruthActionabilityReview(reportTruthActionabilityReviewResult.status === 'fulfilled' ? reportTruthActionabilityReviewResult.value : null) setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null) setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null) @@ -779,6 +784,7 @@ export function AutomationInventoryTab() { resultCaptureReleaseAuthorizationReadbackGateResult, resultCaptureReleaseVerifierPreflightGateResult, resultCaptureReleaseVerifierOwnerReviewPacketResult, + resultCaptureReleaseDecisionHoldResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -2021,7 +2027,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 || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !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 || !reportStatusBoard || !reportRuntimeReadiness || !reportRuntimeDryRun || !reportRuntimeFixtureReadback || !runtimeWorkerShadowGate || !operationPermissionModel || !candidateOperationDryRunEvidence || !taskResultAuditTrail || !matchedPlaybookLearningGap || !criticReviewerResultCapture || !ownerApprovedResultCaptureDryRun || !ownerApprovedResultCaptureReadback || !runtimeReadbackApprovalPackage || !runtimeReadbackImplementationReview || !reportLiveDeliveryApprovalPackage || !runtimeReadbackFixtureApproval || !runtimeReadbackPromotionGate || !ownerApprovedFixturePromotionGate || !canonicalRuntimeReadbackOwnerAcceptance || !failureReceiptNoSendReplay || !reviewerQueueNoWriteReadback || !resultCaptureNoWriteReadback || !resultCapturePromotionApprovalGate || !ownerApprovedResultCapturePromotionDryRun || !resultCaptureWriteGateReview || !resultCaptureWriterImplementationReview || !resultCaptureWriterDryRunFixture || !resultCaptureWriterDryRunReadback || !resultCaptureOwnerPromotionReview || !resultCaptureOwnerApprovedExecutionRehearsal || !resultCaptureOwnerAcceptanceMaintenanceGate || !resultCaptureOwnerAcceptanceReadbackPreflightHold || !resultCaptureOwnerApprovedPreflightReleasePackage || !resultCaptureOwnerApprovedReleaseReadinessReadback || !resultCaptureOwnerReleaseApprovalGate || !resultCapturePostReleaseVerifierRollbackGate || !resultCaptureFinalReleaseCandidateReadback || !resultCaptureReleaseAuthorizationHold || !resultCaptureReleaseAuthorizationReadbackGate || !resultCaptureReleaseVerifierPreflightGate || !resultCaptureReleaseVerifierOwnerReviewPacket || !resultCaptureReleaseDecisionHold || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { return (
@@ -3306,6 +3312,43 @@ export function AutomationInventoryTab() { + resultCaptureReleaseVerifierOwnerReviewPacket.rollups.playbook_trust_write_count + resultCaptureReleaseVerifierOwnerReviewPacket.rollups.production_write_count ) + const resultCaptureReleaseDecisionHoldOverall = resultCaptureReleaseDecisionHold.program_status.overall_completion_percent + const resultCaptureReleaseDecisionHolds = resultCaptureReleaseDecisionHold.rollups.release_decision_hold_count + const resultCaptureReleaseOwnerDecisionHolds = resultCaptureReleaseDecisionHold.rollups.owner_decision_hold_count + const resultCaptureReleaseVerifierDecisionHolds = resultCaptureReleaseDecisionHold.rollups.verifier_decision_hold_count + const resultCaptureReleaseRollbackDecisionHolds = resultCaptureReleaseDecisionHold.rollups.rollback_decision_hold_count + const resultCaptureReleaseLiveApplyDecisionHolds = resultCaptureReleaseDecisionHold.rollups.live_apply_decision_hold_count + const resultCaptureReleaseDecisionBlockedTransitions = resultCaptureReleaseDecisionHold.rollups.blocked_decision_transition_count + const resultCaptureReleaseDecisionActions = resultCaptureReleaseDecisionHold.rollups.operator_action_count + const resultCaptureReleaseDecisionApprovalRequired = ( + resultCaptureReleaseDecisionHold.rollups.approval_required_release_decision_count + + resultCaptureReleaseDecisionHold.rollups.approval_required_owner_decision_count + + resultCaptureReleaseDecisionHold.rollups.approval_required_verifier_decision_count + + resultCaptureReleaseDecisionHold.rollups.approval_required_rollback_decision_count + + resultCaptureReleaseDecisionHold.rollups.approval_required_live_apply_decision_count + ) + const resultCaptureReleaseDecisionBlocked = ( + resultCaptureReleaseDecisionHold.rollups.blocked_release_decision_count + + resultCaptureReleaseDecisionHold.rollups.blocked_owner_decision_count + + resultCaptureReleaseDecisionHold.rollups.blocked_verifier_decision_count + + resultCaptureReleaseDecisionHold.rollups.blocked_rollback_decision_count + + resultCaptureReleaseDecisionHold.rollups.blocked_live_apply_decision_count + + resultCaptureReleaseDecisionHold.rollups.critical_blocker_count + ) + const resultCaptureReleaseDecisionLiveWrites = ( + resultCaptureReleaseDecisionHold.rollups.writer_apply_count + + resultCaptureReleaseDecisionHold.rollups.execution_apply_count + + resultCaptureReleaseDecisionHold.rollups.receipt_write_count + + resultCaptureReleaseDecisionHold.rollups.reviewer_queue_write_count + + resultCaptureReleaseDecisionHold.rollups.gateway_queue_write_count + + resultCaptureReleaseDecisionHold.rollups.telegram_send_count + + resultCaptureReleaseDecisionHold.rollups.bot_api_call_count + + resultCaptureReleaseDecisionHold.rollups.report_receipt_write_count + + resultCaptureReleaseDecisionHold.rollups.result_capture_write_count + + resultCaptureReleaseDecisionHold.rollups.learning_write_count + + resultCaptureReleaseDecisionHold.rollups.playbook_trust_write_count + + resultCaptureReleaseDecisionHold.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 @@ -7866,6 +7909,152 @@ export function AutomationInventoryTab() {
+
+
+
+ + + {t('resultCaptureReleaseDecisionHold.title')} + +
+ +
+ +
+ } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> +
+ +
+
+ {t('resultCaptureReleaseDecisionHold.priorTitle')} +

+ {resultCaptureReleaseDecisionHold.prior_release_verifier_owner_review_packet.readiness_note} +

+
+ + + +
+
+ +
+ {t('resultCaptureReleaseDecisionHold.truthTitle')} +

+ {resultCaptureReleaseDecisionHold.release_decision_hold_truth.truth_note} +

+
+ + + + + + +
+
+
+ +
+ {resultCaptureReleaseDecisionHold.release_decision_holds.slice(0, 5).map(hold => ( +
+
+ + {hold.display_name} + + +
+ + {hold.decision_summary} + +
+ + + + + +
+
+ ))} +
+ +
+ {resultCaptureReleaseDecisionHold.owner_decision_holds.slice(0, 5).map(hold => ( +
+
+ + {hold.display_name} + + +
+ + {hold.decision_summary} + +
+ + + + +
+
+ ))} +
+ +
+ {resultCaptureReleaseDecisionHold.blocked_decision_transitions.slice(0, 6).map(transition => ( +
+
+ + {transition.transition_id} + + +
+ + {transition.reason} + +
+ +
+
+ ))} +
+ +
+ {resultCaptureReleaseDecisionHold.operator_actions.slice(0, 5).map(action => ( +
+
+ + {action.action_id} + + +
+ + {action.operator_instruction} + +
+ + +
+
+ ))} +
+
+
diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index bc3f65027..b6ed0dd5a 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -585,6 +585,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentResultCaptureReleaseDecisionHold() { + const res = await fetch(`${API_BASE_URL}/agents/agent-result-capture-release-decision-hold`) + return handleResponse(res) + }, + async getAiAgentOwnerApprovedFixtureDryRun() { const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-fixture-dry-run`) return handleResponse(res) @@ -9101,6 +9106,191 @@ export interface AiAgentResultCaptureReleaseVerifierOwnerReviewPacketSnapshot { } } +export interface AiAgentResultCaptureReleaseDecisionHoldSnapshot { + schema_version: 'ai_agent_result_capture_release_decision_hold_v1' + generated_at: string + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: 'P2-138' + next_task_id: 'P2-139' + read_only_mode: true + runtime_authority: 'result_capture_release_decision_hold_only_no_live_write' + status_note: string + } + source_refs: string[] + prior_release_verifier_owner_review_packet: { + schema_version: string + owner_review_packet_count: number + verifier_review_packet_count: number + rollback_owner_review_packet_count: number + maintenance_window_review_hold_count: number + live_apply_owner_review_hold_count: number + blocked_owner_review_transition_count: number + operator_action_count: number + approval_required_total: number + blocked_total: number + owner_release_authorized_count: number + owner_review_approved_count: number + owner_decision_approved_count: number + release_decision_pass_count: number + release_authorization_granted_count: number + release_authorization_pass_count: number + reviewer_queue_write_count: number + gateway_queue_write_count: number + telegram_send_count: number + bot_api_call_count: number + result_capture_write_count: number + learning_write_count: number + playbook_trust_write_count: number + production_write_count: number + readiness_note: string + } + release_decision_hold_truth: { + p2_137_owner_review_packet_loaded: boolean + release_decision_hold_ready: boolean + owner_decision_required: boolean + verifier_decision_required: boolean + rollback_decision_required: boolean + maintenance_window_decision_required: boolean + live_apply_decision_required: boolean + release_decision_hold_only: boolean + owner_release_authorized: boolean + owner_review_approved: boolean + owner_decision_approved: boolean + verifier_decision_approved: boolean + release_decision_passed: boolean + release_authorization_granted: boolean + release_authorization_passed: boolean + reviewer_queue_write_enabled: boolean + gateway_queue_write_enabled: boolean + telegram_send_enabled: boolean + production_write_enabled: boolean + truth_note: string + } + release_decision_holds: { + hold_id: string + display_name: string + owner_agent: string + source_packet_id: string + status: string + hold_mode: string + decision_required: boolean + decision_approved: boolean + release_authorization_granted: boolean + release_decision_passed: boolean + decision_summary: string + evidence_hash: string + }[] + owner_decision_holds: { + hold_id: string + display_name: string + owner_agent: string + status: string + hold_mode: string + owner_release_authorized: boolean + owner_decision_approved: boolean + decision_summary: string + }[] + verifier_decision_holds: { + hold_id: string + display_name: string + owner_agent: string + status: string + hold_mode: string + post_release_verifier_required: boolean + verifier_decision_approved: boolean + release_authorization_passed: boolean + verifier_summary: string + }[] + rollback_decision_holds: { + hold_id: string + display_name: string + owner_agent: string + status: string + hold_mode: string + rollback_owner_required: boolean + rollback_owner_confirmed: boolean + rollback_release_passed: boolean + rollback_summary: string + }[] + live_apply_decision_holds: { + hold_id: string + display_name: string + owner_agent: string + status: string + hold_mode: string + release_authorization_granted: boolean + release_authorization_passed: boolean + live_apply_release_passed: boolean + live_apply_release_enabled: boolean + release_condition: string + }[] + blocked_decision_transitions: { + transition_id: string + status: string + reason: string + runtime_write_allowed: boolean + }[] + operator_actions: { + action_id: string + owner_agent: string + status: string + operator_instruction: string + runtime_write_allowed: boolean + }[] + display_redaction_contract: { + redaction_required: boolean + raw_prompt_display_allowed: boolean + private_reasoning_display_allowed: boolean + secret_value_display_allowed: boolean + raw_runtime_payload_display_allowed: boolean + internal_collaboration_content_display_allowed: boolean + frontend_display_policy: string + } + rollups: { + release_decision_hold_count: number + owner_decision_hold_count: number + verifier_decision_hold_count: number + rollback_decision_hold_count: number + live_apply_decision_hold_count: number + blocked_decision_transition_count: number + operator_action_count: number + approval_required_release_decision_count: number + blocked_release_decision_count: number + approval_required_owner_decision_count: number + blocked_owner_decision_count: number + approval_required_verifier_decision_count: number + blocked_verifier_decision_count: number + approval_required_rollback_decision_count: number + blocked_rollback_decision_count: number + approval_required_live_apply_decision_count: number + blocked_live_apply_decision_count: number + critical_blocker_count: number + owner_release_authorized_count: number + owner_review_approved_count: number + owner_decision_approved_count: number + verifier_decision_approved_count: number + release_decision_pass_count: number + release_authorization_granted_count: number + release_authorization_pass_count: number + rollback_release_pass_count: number + live_apply_release_pass_count: number + writer_apply_count: number + execution_apply_count: number + receipt_write_count: number + reviewer_queue_write_count: number + gateway_queue_write_count: number + telegram_send_count: number + bot_api_call_count: number + report_receipt_write_count: number + result_capture_write_count: number + learning_write_count: number + playbook_trust_write_count: number + production_write_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 40fc9cc53..92c55e327 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,27 @@ +## 2026-06-14|P2-138 釋出決策保留關卡本地完成 + +**背景**:P2-137 release verifier owner review packet 已正式驗證,但 owner review packet 可見仍不得被誤讀成 owner decision approved、release decision passed、release authorization granted / passed、rollback release passed 或 live apply release passed。P2-138 因此只建立 release decision hold,讓後續 readback 能明確看見決策仍被保留。 + +**完成內容**: +- 新增 `ai_agent_result_capture_release_decision_hold_v1` schema、committed snapshot、loader 與 API endpoint `GET /api/v1/agents/agent-result-capture-release-decision-hold`。 +- P2-138 snapshot 固定 5 個 release decision hold、5 個 owner decision hold、5 個 verifier decision hold、5 個 rollback decision hold、5 個 live-apply decision hold、6 個 blocked decision transition 與 5 個 operator action。 +- Governance automation inventory 頁新增 P2-138 區塊,顯示 P2-138 進度 `100%`、釋出決策保留 `5`、負責人決策保留 `5`、驗證器決策保留 `5`、回滾決策保留 `5`、正式套用保留 `5`、已阻擋轉換 `6`、operator action `5`、需批准 `10`、阻擋 `10`、正式寫入 / 發送 `0`。 +- Release decision hold truth 固定為 `result_capture_release_decision_hold_only_no_live_write`;owner release authorized、owner review approved、owner decision approved、verifier decision approved、release decision passed、release authorization granted、release authorization passed、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、reviewer queue write、Gateway queue write、Telegram send、Bot API、report receipt、result capture write、learning write、PlayBook trust write、production write、secret read 與 destructive operation 仍全部維持 `0 / false`。 + +**本地驗證**: +- JSON parse:P2-138 schema / snapshot、`zh-TW.json`、`en.json` 通過。 +- Python 編譯:P2-138 loader 與 `agents.py` 通過。 +- API/service pytest:P2-138 + P2-137 regression `14 passed`。 +- i18n key mirror:`11772` keys,`zh-TW.json` / `en.json` 鏡像差異 `0`。 +- Web typecheck:本臨時 worktree 缺少 `node_modules`,`tsc: command not found`;未安裝依賴,避免增加主機負載,等待 Gitea CD 在乾淨環境補跑。 + +**安全邊界**: +- P2-138 仍是只讀 release decision hold;不接受口頭批准、不把 hold 解讀成 owner release authorized、不把 owner review packet 解讀成決策通過、不核發或通過 release authorization、不通過 rollback release、不釋放 live apply、不套用 writer、不執行正式寫入、不寫 receipt、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 report receipt、不讀 canonical runtime target、不做 live query、不寫 production target、不讀 secret、不執行 destructive action、不回傳內部工作內容。 +- 本段尚未正式部署;需要 feature commit、deploy marker、production API readback 與 desktop / mobile Browser smoke 後,才可記錄正式驗證完成。 + +**下一步**: +- 推送 P2-138 feature commit,等待 Gitea CD / code-review;正式驗證通過後,才可由 `P2-139` release decision readback 承接。 + ## 2026-06-14|P2-137 / CI smoke timeout 修正後 recovery readback 無回歸 **背景**:另一工作視窗已完成 P2-137 正式驗證與 BusyBox `timeout` smoke 修正,最新 `gitea/main=50d4f2ba docs(ai): 記錄 P2-137 正式驗證 [skip ci]`;runtime deploy marker 為 `d023f5d7 chore(cd): deploy f737f27 [skip ci]`,對應 feature commit `f737f278 feat(governance): 新增 release verifier owner review packet`。本輪只從 reboot / cold-start / backup recovery 角度做只讀 readback,不重複修改 P2-137 正式驗證、MASTER 或 AI worklist。 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 ac05fde50..c76ec0713 100644 --- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md +++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md @@ -63,13 +63,22 @@ - Browser / Chrome smoke 已完成:desktop `1440x1000` 與 mobile `390x844` 均確認 P2-137 區塊、負責人審查包 `5`、驗證器審查包 `5`、回滾 owner 審查 `5`、維護窗口保留 `5`、正式套用保留 `5`、已阻擋轉換 `6`、需批准 `10`、阻擋 `10`、正式寫入 / 發送 `0`、Gateway 寫入 `0`、負責人釋出授權 `0` 可見;水平溢位 `0`、P2-137 卡片危險控制 `0`、console error `0`、HTTP 4xx/5xx `0`。 - P2-137 已由 `P2-138` release decision hold 承接;仍不得直接開啟 result capture writer、learning writer、PlayBook trust writer、reviewer queue write、Gateway queue write、Telegram send、Bot API call 或 production write。 +### 2026-06-14 10:58 狀態同步 + +- `P2-138` release decision hold 已本地完成:新增 `ai_agent_result_capture_release_decision_hold_v1`、`GET /api/v1/agents/agent-result-capture-release-decision-hold`、治理頁 P2-138 區塊與繁中 UI 文案。 +- 本地證據:P2-138 + P2-137 API/service regression `14 passed`、JSON parse、Python compile、i18n key mirror `11772` 通過。 +- P2-138 固定 5 個 release decision hold、5 個 owner decision hold、5 個 verifier decision hold、5 個 rollback decision hold、5 個 live-apply decision hold、6 個 blocked decision transition、5 個 operator action、需批准 `10`、阻擋 `10`、正式寫入 / 發送 `0`。 +- Web typecheck 在本臨時 worktree 因缺少 `node_modules` 停在 `tsc: command not found`;未安裝依賴,避免增加主機負載,等待 Gitea CD 在乾淨環境跑完整前端驗證。 +- 邊界仍維持:owner release authorized、owner review approved、owner decision approved、verifier decision approved、release decision passed、release authorization granted / passed、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、reviewer queue write、Gateway queue write、Telegram send、Bot API、report receipt、result capture write、learning write、PlayBook trust write、production write、secret read、destructive operation 全部 `0 / false`。 +- P2-138 推送與正式驗證後才可交給 `P2-139` release decision readback;仍不得直接開啟 result capture writer、learning writer、PlayBook trust writer、reviewer queue write、Gateway queue write、Telegram send、Bot API call 或 production write。 + AI Agent 自動化工作包目前完成度:**99.8%**。本工作清單文件本身完成度:**100%**。 三 Agent 佈建布局目前完成度:**45%**。第一波已完成只讀 schema / snapshot / API / 測試 / 報告,第二波已接入治理頁自動化盤點 UI;正式 runtime 佈署、Telegram E2E 發送與 AgentSession 工作流仍需逐項 gate。 -三 Agent 主動溝通、學習與成長證據目前完成度:**100%**。P2-403A 到 P2-137 已把互動證據、報表治理、runtime readback、reviewer / result capture / writer gates、no-write readback、promotion review、writer implementation review、writer dry-run fixture、writer dry-run readback、owner promotion execution gate、owner-approved execution rehearsal、owner acceptance / maintenance window gate、owner acceptance readback / preflight hold、owner-approved preflight release package、owner-approved release readiness readback、owner release approval gate、post-release verifier / rollback gate、final release candidate readback、release authorization hold、release authorization readback gate、release verifier preflight gate 與 release verifier owner review packet 固定成可驗證證據。P2-137 已正式驗證,deploy marker `d023f5d7`。目前 live AgentSession、Agent message、handoff、canonical runtime readback、live query、runtime score、result capture write、learning write、Telegram receipt、Gateway queue write、reviewer queue write、runtime verifier execution、live report delivery、AI analysis runtime、中低風險 auto worker、Telegram 實發、shadow worker live、delivery receipt E2E、KM / LOGBOOK / audit DB / timeline / PlayBook trust runtime 寫入仍全部為 `0`。真正下一步是 `P2-138`。 +三 Agent 主動溝通、學習與成長證據目前完成度:**100%**。P2-403A 到 P2-138 已把互動證據、報表治理、runtime readback、reviewer / result capture / writer gates、no-write readback、promotion review、writer implementation review、writer dry-run fixture、writer dry-run readback、owner promotion execution gate、owner-approved execution rehearsal、owner acceptance / maintenance window gate、owner acceptance readback / preflight hold、owner-approved preflight release package、owner-approved release readiness readback、owner release approval gate、post-release verifier / rollback gate、final release candidate readback、release authorization hold、release authorization readback gate、release verifier preflight gate、release verifier owner review packet 與 release decision hold 固定成可驗證證據。P2-138 已本地完成,待 Gitea CD 與正式站驗證;P2-137 已正式驗證,deploy marker `d023f5d7`。目前 live AgentSession、Agent message、handoff、canonical runtime readback、live query、runtime score、result capture write、learning write、Telegram receipt、Gateway queue write、reviewer queue write、runtime verifier execution、live report delivery、AI analysis runtime、中低風險 auto worker、Telegram 實發、shadow worker live、delivery receipt E2E、KM / LOGBOOK / audit DB / timeline / PlayBook trust runtime 寫入仍全部為 `0`。真正下一步是 P2-138 正式驗證,通過後才可推 `P2-139`。 -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-137` 已補互動、學習證據面、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 / readback、result capture writer dry-run fixture、writer dry-run readback、owner promotion execution gate、owner-approved execution rehearsal、owner acceptance / maintenance window gate、owner acceptance readback / preflight hold、owner-approved preflight release package、owner-approved release readiness readback、owner release approval gate、post-release verifier / rollback gate、final release candidate readback、release authorization hold、release authorization readback gate、release verifier preflight gate 與 release verifier owner review packet。下一步是 `P2-138` release decision hold;外部 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-138` 已補互動、學習證據面、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 / readback、result capture writer dry-run fixture、writer dry-run readback、owner promotion execution gate、owner-approved execution rehearsal、owner acceptance / maintenance window gate、owner acceptance readback / preflight hold、owner-approved preflight release package、owner-approved release readiness readback、owner release approval gate、post-release verifier / rollback gate、final release candidate readback、release authorization hold、release authorization readback gate、release verifier preflight gate、release verifier owner review packet 與 release decision hold。下一步是 P2-138 正式部署驗證;外部 registry / package source / host probe / SSH / kubectl / 工具安裝 / CI 變更 / 實際 PR creation / Telegram 實發與 learning write 仍需 gate。 完成度計算模型: @@ -1057,7 +1066,8 @@ UI: | P2-135 | 完成 | 100 | OpenClaw + Hermes + NemoTron | release authorization readback gate | `ai_agent_result_capture_release_authorization_readback_gate_v1` / schema / snapshot / 只讀 API / governance UI;本地 pytest `14 passed`、JSON parse、Python compile、Web typecheck 通過;5 個 release authorization readback、5 個 rollback release readback、5 個 maintenance window readback hold、5 個 live-apply release readback hold、6 個 blocked release readback transition、5 個 operator action;runtime authority 固定 `result_capture_release_authorization_readback_gate_only_no_live_write`;owner release authorized、release authorization granted / passed、rollback release、live apply release、writer apply、execution apply、receipt write、result capture、learning、PlayBook trust、Gateway、Telegram、production write 全為 `0` | 正式站 deploy marker `8d575c1a` 已驗證;production API readback、desktop / mobile smoke、禁用內部協作片語 `0`、水平溢位 `0`、高風險控制 `0`、console error `0`;已由 P2-136 承接,不核發 release authorization、不釋放 live apply、不套用 writer | | P2-136 | 完成 | 100 | OpenClaw + Hermes + NemoTron | release verifier preflight gate | `ai_agent_result_capture_release_verifier_preflight_gate_v1` / schema / snapshot / 只讀 API / governance UI;feature commit `913d7f68`、deploy marker `f2fa8454` 已正式驗證;5 個 release verifier preflight、5 個 rollback verifier preflight、5 個 maintenance window verifier hold、5 個 live-apply verifier hold、6 個 blocked verifier preflight transition、5 個 operator action;需批准 `8`、blocked status subtotal `4`、critical blocker `5`、前端顯示阻擋總數 `9`;runtime authority 固定 `result_capture_release_verifier_preflight_gate_only_no_live_write`;post-release verifier ready、release authorization granted / passed、rollback release、live apply release、writer apply、execution apply、receipt write、result capture、learning、PlayBook trust、Gateway、Telegram、production write、secret read、destructive operation 全為 `0` | production API readback、desktop / mobile smoke、禁用內部協作片語 `0`、水平溢位 `0`、危險控制 `0`、console error `0`;已由 P2-137 承接,不把 verifier preflight 視為 post-release verifier ready、不核發 release authorization、不釋放 live apply、不套用 writer | | P2-137 | 完成 | 100 | OpenClaw + Hermes + NemoTron | release verifier owner review packet | `ai_agent_result_capture_release_verifier_owner_review_packet_v1` / schema / snapshot / 只讀 API / governance UI;feature commit `f737f278`、deploy marker `d023f5d7` 已正式驗證;5 個 owner review packet、5 個 verifier review packet、5 個 rollback owner review packet、5 個 maintenance window review hold、5 個 live-apply owner review hold、6 個 blocked owner review transition、5 個 operator action;需批准 `10`、阻擋 `10`;runtime authority 固定 `result_capture_release_verifier_owner_review_packet_only_no_live_write`;owner release authorized / owner review approved / maintenance window approved / rollback owner confirmed / post-release verifier ready / release authorization granted / release authorization passed / rollback release / live apply release / writer apply / execution apply / receipt write / result capture / learning / PlayBook trust / reviewer queue / Gateway / Telegram / Bot API / production write / secret read / destructive operation 全為 `0` | 本地 pytest `7 passed`、JSON parse、Python compile、Web typecheck 通過;production API readback、desktop / mobile smoke、水平溢位 `0`、P2-137 卡片危險控制 `0`、console error `0`、HTTP 4xx/5xx `0`;已由 P2-138 承接 | -| P2-138 | 計畫中 | 0 | OpenClaw + Hermes + NemoTron | release decision hold | 承接 P2-137,只能建立 release decision hold 的只讀證據;不得把 owner review packet 視為授權通過,不得讀 secret、不得寫 reviewer queue、不得寫 Gateway queue、不得送 Telegram、不得呼叫 Bot API、不得 production write | P2-137 已正式驗證,可開始;仍不得把 P2-138 當 runtime gate | +| P2-138 | 本地完成 | 100 | OpenClaw + Hermes + NemoTron | release decision hold | `ai_agent_result_capture_release_decision_hold_v1` / schema / snapshot / 只讀 API / governance UI;承接 P2-137,只建立 5 個 release decision hold、5 個 owner decision hold、5 個 verifier decision hold、5 個 rollback decision hold、5 個 live-apply decision hold、6 個 blocked decision transition、5 個 operator action;需批准 `10`、阻擋 `10`;runtime authority 固定 `result_capture_release_decision_hold_only_no_live_write`;owner release authorized / owner review approved / owner decision approved / verifier decision approved / release decision passed / release authorization granted / passed / rollback release / live apply release / writer apply / execution apply / receipt write / reviewer queue / Gateway / Telegram / Bot API / result capture / learning / PlayBook trust / production write / secret read / destructive operation 全為 `0` | 本地 P2-138 + P2-137 pytest `14 passed`、JSON parse、Python compile、i18n key mirror `11772` 通過;本 worktree 因缺少 `node_modules` 未跑 Web typecheck,待 Gitea CD / production smoke;不得把 P2-138 當 runtime gate | +| P2-139 | 計畫中 | 0 | OpenClaw + Hermes + NemoTron | release decision readback | 承接 P2-138,只能建立 release decision hold readback 的只讀證據;不得把 release decision hold 視為決策通過,不得讀 secret、不得寫 reviewer queue、不得寫 Gateway queue、不得送 Telegram、不得呼叫 Bot API、不得 production write | P2-138 正式部署驗證後才可開始;仍不得把 P2-139 當 runtime gate | ### P3 - 候選 Agent 擴展 diff --git a/docs/evaluations/ai_agent_result_capture_release_decision_hold_2026-06-14.json b/docs/evaluations/ai_agent_result_capture_release_decision_hold_2026-06-14.json new file mode 100644 index 000000000..a7d48fff1 --- /dev/null +++ b/docs/evaluations/ai_agent_result_capture_release_decision_hold_2026-06-14.json @@ -0,0 +1,563 @@ +{ + "schema_version": "ai_agent_result_capture_release_decision_hold_v1", + "generated_at": "2026-06-14T10:48:00+08:00", + "program_status": { + "overall_completion_percent": 100, + "current_priority": "P2", + "current_task_id": "P2-138", + "next_task_id": "P2-139", + "read_only_mode": true, + "runtime_authority": "result_capture_release_decision_hold_only_no_live_write", + "status_note": "P2-138 只把 P2-137 release verifier owner review packet 整理成 release decision hold;不得視為 owner review approved、release decision passed、release authorization granted / passed、rollback release passed 或 live apply passed,不得寫 reviewer / Gateway queue、送 Telegram、呼叫 Bot API、寫 result capture、learning、PlayBook trust 或 production target。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_result_capture_release_verifier_owner_review_packet_2026-06-14.json", + "docs/evaluations/ai_agent_result_capture_release_verifier_preflight_gate_2026-06-14.json", + "docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md#59-建立-release-decision-hold" + ], + "prior_release_verifier_owner_review_packet": { + "schema_version": "ai_agent_result_capture_release_verifier_owner_review_packet_v1", + "owner_review_packet_count": 5, + "verifier_review_packet_count": 5, + "rollback_owner_review_packet_count": 5, + "maintenance_window_review_hold_count": 5, + "live_apply_owner_review_hold_count": 5, + "blocked_owner_review_transition_count": 6, + "operator_action_count": 5, + "approval_required_total": 10, + "blocked_total": 10, + "owner_release_authorized_count": 0, + "owner_release_approved_count": 0, + "owner_review_approved_count": 0, + "owner_decision_approved_count": 0, + "verifier_decision_approved_count": 0, + "maintenance_window_approved_count": 0, + "rollback_owner_confirmed_count": 0, + "post_release_verifier_ready_count": 0, + "final_release_candidate_approved_count": 0, + "final_release_candidate_pass_count": 0, + "release_decision_pass_count": 0, + "release_authorization_granted_count": 0, + "release_authorization_pass_count": 0, + "rollback_release_pass_count": 0, + "live_apply_release_pass_count": 0, + "writer_apply_count": 0, + "execution_apply_count": 0, + "receipt_write_count": 0, + "reviewer_queue_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "bot_api_call_count": 0, + "report_receipt_write_count": 0, + "result_capture_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "destructive_operation_count": 0, + "readiness_note": "P2-137 已把 owner review packet、verifier review packet、rollback owner review、maintenance window review hold、live apply owner review hold 與 blocked owner review transition 固定為只讀;P2-138 只建立 release decision hold,不把 review packet 視為決策通過。" + }, + "release_decision_hold_truth": { + "p2_137_owner_review_packet_loaded": true, + "release_decision_hold_ready": true, + "owner_decision_required": true, + "verifier_decision_required": true, + "rollback_decision_required": true, + "maintenance_window_decision_required": true, + "live_apply_decision_required": true, + "redaction_review_required": true, + "release_decision_hold_only": true, + "owner_release_authorized": false, + "owner_release_approved": false, + "owner_review_approved": false, + "owner_decision_approved": false, + "verifier_decision_approved": false, + "maintenance_window_approved": false, + "rollback_owner_confirmed": false, + "post_release_verifier_ready": false, + "final_release_candidate_approved": false, + "final_release_candidate_passed": false, + "release_decision_passed": false, + "release_authorization_granted": false, + "release_authorization_passed": false, + "rollback_release_passed": false, + "live_apply_release_passed": false, + "writer_apply_enabled": false, + "execution_apply_enabled": false, + "receipt_write_enabled": false, + "reviewer_queue_write_enabled": false, + "gateway_queue_write_enabled": false, + "telegram_send_enabled": false, + "bot_api_call_enabled": false, + "report_receipt_write_enabled": false, + "result_capture_write_enabled": false, + "learning_write_enabled": false, + "playbook_trust_write_enabled": false, + "production_write_enabled": false, + "secret_read_enabled": false, + "destructive_operation_enabled": false, + "owner_release_authorized_count": 0, + "owner_release_approved_count": 0, + "owner_review_approved_count": 0, + "owner_decision_approved_count": 0, + "verifier_decision_approved_count": 0, + "maintenance_window_approved_count": 0, + "rollback_owner_confirmed_count": 0, + "post_release_verifier_ready_count": 0, + "final_release_candidate_approved_count": 0, + "final_release_candidate_pass_count": 0, + "release_decision_pass_count": 0, + "release_authorization_granted_count": 0, + "release_authorization_pass_count": 0, + "rollback_release_pass_count": 0, + "live_apply_release_pass_count": 0, + "writer_apply_count": 0, + "execution_apply_count": 0, + "receipt_write_count": 0, + "reviewer_queue_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "bot_api_call_count": 0, + "report_receipt_write_count": 0, + "result_capture_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "destructive_operation_count": 0, + "truth_note": "P2-138 只建立 release decision hold;hold 可見不代表 owner / verifier / rollback / maintenance / live apply 任何決策通過,也不代表 runtime queue 或 Telegram 可以寫入。" + }, + "release_decision_holds": [ + { + "hold_id": "release_decision_hold_result_capture", + "display_name": "Result capture 釋出決策保留", + "owner_agent": "openclaw", + "source_packet_id": "owner_review_packet_result_capture", + "status": "ready_for_decision_review", + "hold_mode": "release_decision_hold", + "decision_required": true, + "decision_approved": false, + "release_authorization_granted": false, + "release_decision_passed": false, + "decision_summary": "Result capture 僅可進入 release decision hold;owner review packet 未等於決策通過,仍不得寫入 result capture。", + "evidence_hash": "sha256:8311111111111111111111111111111111111111111111111111111111111111" + }, + { + "hold_id": "release_decision_hold_learning", + "display_name": "Learning 釋出決策保留", + "owner_agent": "hermes", + "source_packet_id": "owner_review_packet_learning", + "status": "approval_required", + "hold_mode": "release_decision_hold", + "decision_required": true, + "decision_approved": false, + "release_authorization_granted": false, + "release_decision_passed": false, + "decision_summary": "Learning 僅可進入 release decision hold;owner review packet 未等於決策通過,仍不得寫入 learning。", + "evidence_hash": "sha256:8322222222222222222222222222222222222222222222222222222222222222" + }, + { + "hold_id": "release_decision_hold_playbook_trust", + "display_name": "PlayBook trust 釋出決策保留", + "owner_agent": "nemotron", + "source_packet_id": "owner_review_packet_playbook_trust", + "status": "blocked_by_policy", + "hold_mode": "release_decision_hold", + "decision_required": true, + "decision_approved": false, + "release_authorization_granted": false, + "release_decision_passed": false, + "decision_summary": "PlayBook trust 僅可進入 release decision hold;owner review packet 未等於決策通過,仍不得寫入 trust。", + "evidence_hash": "sha256:8333333333333333333333333333333333333333333333333333333333333333" + }, + { + "hold_id": "release_decision_hold_reviewer_queue", + "display_name": "Reviewer queue 釋出決策保留", + "owner_agent": "openclaw", + "source_packet_id": "owner_review_packet_reviewer_queue", + "status": "approval_required", + "hold_mode": "release_decision_hold", + "decision_required": true, + "decision_approved": false, + "release_authorization_granted": false, + "release_decision_passed": false, + "decision_summary": "Reviewer queue 僅可進入 release decision hold;owner review packet 未等於決策通過,仍不得寫 reviewer queue。", + "evidence_hash": "sha256:8344444444444444444444444444444444444444444444444444444444444444" + }, + { + "hold_id": "release_decision_hold_gateway_queue", + "display_name": "Gateway queue 釋出決策保留", + "owner_agent": "nemotron", + "source_packet_id": "owner_review_packet_gateway_queue", + "status": "ready_for_decision_review", + "hold_mode": "release_decision_hold", + "decision_required": true, + "decision_approved": false, + "release_authorization_granted": false, + "release_decision_passed": false, + "decision_summary": "Gateway queue 僅可進入 release decision hold;owner review packet 未等於決策通過,仍不得寫 Gateway queue。", + "evidence_hash": "sha256:8355555555555555555555555555555555555555555555555555555555555555" + } + ], + "owner_decision_holds": [ + { + "hold_id": "owner_decision_hold_result_capture", + "display_name": "Result capture 負責人決策保留", + "owner_agent": "openclaw", + "status": "ready_for_decision_review", + "hold_mode": "owner_decision_hold", + "owner_release_authorized": false, + "owner_decision_approved": false, + "decision_summary": "負責人尚未核准 Result capture release decision。" + }, + { + "hold_id": "owner_decision_hold_learning", + "display_name": "Learning 負責人決策保留", + "owner_agent": "hermes", + "status": "approval_required", + "hold_mode": "owner_decision_hold", + "owner_release_authorized": false, + "owner_decision_approved": false, + "decision_summary": "負責人尚未核准 Learning release decision。" + }, + { + "hold_id": "owner_decision_hold_playbook_trust", + "display_name": "PlayBook trust 負責人決策保留", + "owner_agent": "nemotron", + "status": "blocked_by_policy", + "hold_mode": "owner_decision_hold", + "owner_release_authorized": false, + "owner_decision_approved": false, + "decision_summary": "負責人尚未核准 PlayBook trust release decision。" + }, + { + "hold_id": "owner_decision_hold_reviewer_queue", + "display_name": "Reviewer queue 負責人決策保留", + "owner_agent": "openclaw", + "status": "approval_required", + "hold_mode": "owner_decision_hold", + "owner_release_authorized": false, + "owner_decision_approved": false, + "decision_summary": "負責人尚未核准 Reviewer queue release decision。" + }, + { + "hold_id": "owner_decision_hold_gateway_queue", + "display_name": "Gateway queue 負責人決策保留", + "owner_agent": "nemotron", + "status": "ready_for_decision_review", + "hold_mode": "owner_decision_hold", + "owner_release_authorized": false, + "owner_decision_approved": false, + "decision_summary": "負責人尚未核准 Gateway queue release decision。" + } + ], + "verifier_decision_holds": [ + { + "hold_id": "verifier_decision_hold_result_capture", + "display_name": "Result capture 驗證器決策保留", + "owner_agent": "openclaw", + "status": "ready_for_decision_review", + "hold_mode": "verifier_decision_hold", + "post_release_verifier_required": true, + "verifier_decision_approved": false, + "release_authorization_passed": false, + "verifier_summary": "post-release verifier 未核准前不得通過 Result capture release authorization。" + }, + { + "hold_id": "verifier_decision_hold_learning", + "display_name": "Learning 驗證器決策保留", + "owner_agent": "hermes", + "status": "approval_required", + "hold_mode": "verifier_decision_hold", + "post_release_verifier_required": true, + "verifier_decision_approved": false, + "release_authorization_passed": false, + "verifier_summary": "post-release verifier 未核准前不得通過 Learning release authorization。" + }, + { + "hold_id": "verifier_decision_hold_playbook_trust", + "display_name": "PlayBook trust 驗證器決策保留", + "owner_agent": "nemotron", + "status": "blocked_by_policy", + "hold_mode": "verifier_decision_hold", + "post_release_verifier_required": true, + "verifier_decision_approved": false, + "release_authorization_passed": false, + "verifier_summary": "post-release verifier 未核准前不得通過 PlayBook trust release authorization。" + }, + { + "hold_id": "verifier_decision_hold_reviewer_queue", + "display_name": "Reviewer queue 驗證器決策保留", + "owner_agent": "openclaw", + "status": "approval_required", + "hold_mode": "verifier_decision_hold", + "post_release_verifier_required": true, + "verifier_decision_approved": false, + "release_authorization_passed": false, + "verifier_summary": "post-release verifier 未核准前不得通過 Reviewer queue release authorization。" + }, + { + "hold_id": "verifier_decision_hold_gateway_queue", + "display_name": "Gateway queue 驗證器決策保留", + "owner_agent": "nemotron", + "status": "ready_for_decision_review", + "hold_mode": "verifier_decision_hold", + "post_release_verifier_required": true, + "verifier_decision_approved": false, + "release_authorization_passed": false, + "verifier_summary": "post-release verifier 未核准前不得通過 Gateway queue release authorization。" + } + ], + "rollback_decision_holds": [ + { + "hold_id": "rollback_decision_hold_result_capture", + "display_name": "Result capture 回滾決策保留", + "owner_agent": "openclaw", + "status": "ready_for_decision_review", + "hold_mode": "rollback_decision_hold", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_passed": false, + "rollback_summary": "rollback owner 未確認前不得通過 Result capture rollback release。" + }, + { + "hold_id": "rollback_decision_hold_learning", + "display_name": "Learning 回滾決策保留", + "owner_agent": "hermes", + "status": "approval_required", + "hold_mode": "rollback_decision_hold", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_passed": false, + "rollback_summary": "rollback owner 未確認前不得通過 Learning rollback release。" + }, + { + "hold_id": "rollback_decision_hold_playbook_trust", + "display_name": "PlayBook trust 回滾決策保留", + "owner_agent": "nemotron", + "status": "blocked_by_policy", + "hold_mode": "rollback_decision_hold", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_passed": false, + "rollback_summary": "rollback owner 未確認前不得通過 PlayBook trust rollback release。" + }, + { + "hold_id": "rollback_decision_hold_reviewer_queue", + "display_name": "Reviewer queue 回滾決策保留", + "owner_agent": "openclaw", + "status": "approval_required", + "hold_mode": "rollback_decision_hold", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_passed": false, + "rollback_summary": "rollback owner 未確認前不得通過 Reviewer queue rollback release。" + }, + { + "hold_id": "rollback_decision_hold_gateway_queue", + "display_name": "Gateway queue 回滾決策保留", + "owner_agent": "nemotron", + "status": "ready_for_decision_review", + "hold_mode": "rollback_decision_hold", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_passed": false, + "rollback_summary": "rollback owner 未確認前不得通過 Gateway queue rollback release。" + } + ], + "live_apply_decision_holds": [ + { + "hold_id": "live_apply_decision_hold_result_capture", + "display_name": "Result capture 正式套用決策保留", + "owner_agent": "openclaw", + "status": "ready_for_decision_review", + "hold_mode": "live_apply_decision_hold", + "release_authorization_granted": false, + "release_authorization_passed": false, + "live_apply_release_passed": false, + "live_apply_release_enabled": false, + "release_condition": "release authorization 未核發與通過前不得正式套用 Result capture。" + }, + { + "hold_id": "live_apply_decision_hold_learning", + "display_name": "Learning 正式套用決策保留", + "owner_agent": "hermes", + "status": "approval_required", + "hold_mode": "live_apply_decision_hold", + "release_authorization_granted": false, + "release_authorization_passed": false, + "live_apply_release_passed": false, + "live_apply_release_enabled": false, + "release_condition": "release authorization 未核發與通過前不得正式套用 Learning。" + }, + { + "hold_id": "live_apply_decision_hold_playbook_trust", + "display_name": "PlayBook trust 正式套用決策保留", + "owner_agent": "nemotron", + "status": "blocked_by_policy", + "hold_mode": "live_apply_decision_hold", + "release_authorization_granted": false, + "release_authorization_passed": false, + "live_apply_release_passed": false, + "live_apply_release_enabled": false, + "release_condition": "release authorization 未核發與通過前不得正式套用 PlayBook trust。" + }, + { + "hold_id": "live_apply_decision_hold_reviewer_queue", + "display_name": "Reviewer queue 正式套用決策保留", + "owner_agent": "openclaw", + "status": "approval_required", + "hold_mode": "live_apply_decision_hold", + "release_authorization_granted": false, + "release_authorization_passed": false, + "live_apply_release_passed": false, + "live_apply_release_enabled": false, + "release_condition": "release authorization 未核發與通過前不得正式套用 Reviewer queue。" + }, + { + "hold_id": "live_apply_decision_hold_gateway_queue", + "display_name": "Gateway queue 正式套用決策保留", + "owner_agent": "nemotron", + "status": "ready_for_decision_review", + "hold_mode": "live_apply_decision_hold", + "release_authorization_granted": false, + "release_authorization_passed": false, + "live_apply_release_passed": false, + "live_apply_release_enabled": false, + "release_condition": "release authorization 未核發與通過前不得正式套用 Gateway queue。" + } + ], + "blocked_decision_transitions": [ + { + "transition_id": "decision_hold_to_release_authorization", + "status": "blocked_by_policy", + "reason": "release decision 尚未通過,不得轉 release authorization granted。", + "runtime_write_allowed": false + }, + { + "transition_id": "decision_hold_to_reviewer_queue", + "status": "approval_required", + "reason": "reviewer queue write 未授權,不得把決策保留寫入 reviewer queue。", + "runtime_write_allowed": false + }, + { + "transition_id": "decision_hold_to_gateway_queue", + "status": "blocked_by_policy", + "reason": "Gateway queue write 未授權,不得把決策保留寫入 Gateway queue。", + "runtime_write_allowed": false + }, + { + "transition_id": "decision_hold_to_telegram_send", + "status": "approval_required", + "reason": "Telegram send 未授權,不得發送 release decision hold 通知。", + "runtime_write_allowed": false + }, + { + "transition_id": "decision_hold_to_writer_apply", + "status": "blocked_by_policy", + "reason": "writer apply 未授權,不得套用 result capture、learning 或 PlayBook trust。", + "runtime_write_allowed": false + }, + { + "transition_id": "decision_hold_to_production_target", + "status": "approval_required", + "reason": "production write 未授權,不得觸及任何 production target。", + "runtime_write_allowed": false + } + ], + "operator_actions": [ + { + "action_id": "review_release_decision_hold_scope", + "owner_agent": "openclaw", + "status": "ready_for_operator_review", + "operator_instruction": "審查 release decision hold 是否只引用 redacted evidence;不得核發 release authorization。", + "runtime_write_allowed": false + }, + { + "action_id": "confirm_owner_decision_inputs", + "owner_agent": "hermes", + "status": "approval_required", + "operator_instruction": "確認 owner decision 所需輸入仍缺 maintenance window、rollback owner 與 verifier decision;不得寫 production。", + "runtime_write_allowed": false + }, + { + "action_id": "verify_queue_write_closed", + "owner_agent": "nemotron", + "status": "ready_for_operator_review", + "operator_instruction": "確認 reviewer queue、Gateway queue 與 Telegram 仍關閉;不得呼叫 Bot API。", + "runtime_write_allowed": false + }, + { + "action_id": "prepare_p2_139_release_decision_readback", + "owner_agent": "openclaw", + "status": "ready_for_operator_review", + "operator_instruction": "下一關只可建立 release decision readback,不得 writer apply、queue write 或 result capture write。", + "runtime_write_allowed": false + }, + { + "action_id": "document_decision_hold_boundary", + "owner_agent": "hermes", + "status": "approval_required", + "operator_instruction": "同步治理文件的 release decision hold 邊界;不得送 Telegram。", + "runtime_write_allowed": false + } + ], + "display_redaction_contract": { + "redaction_required": true, + "raw_prompt_display_allowed": false, + "private_reasoning_display_allowed": false, + "secret_value_display_allowed": false, + "raw_runtime_payload_display_allowed": false, + "internal_collaboration_content_display_allowed": false, + "frontend_display_policy": "只顯示 redacted evidence refs、release decision hold、決策狀態與 no-live-write 邊界;不得顯示未審核原始內容、敏感內容或 runtime payload。" + }, + "rollups": { + "release_decision_hold_count": 5, + "owner_decision_hold_count": 5, + "verifier_decision_hold_count": 5, + "rollback_decision_hold_count": 5, + "live_apply_decision_hold_count": 5, + "blocked_decision_transition_count": 6, + "operator_action_count": 5, + "approval_required_release_decision_count": 2, + "blocked_release_decision_count": 1, + "approval_required_owner_decision_count": 2, + "blocked_owner_decision_count": 1, + "approval_required_verifier_decision_count": 2, + "blocked_verifier_decision_count": 1, + "approval_required_rollback_decision_count": 2, + "blocked_rollback_decision_count": 1, + "approval_required_live_apply_decision_count": 2, + "blocked_live_apply_decision_count": 1, + "critical_blocker_count": 5, + "owner_release_authorized_count": 0, + "owner_release_approved_count": 0, + "owner_review_approved_count": 0, + "owner_decision_approved_count": 0, + "verifier_decision_approved_count": 0, + "maintenance_window_approved_count": 0, + "rollback_owner_confirmed_count": 0, + "post_release_verifier_ready_count": 0, + "final_release_candidate_approved_count": 0, + "final_release_candidate_pass_count": 0, + "release_decision_pass_count": 0, + "release_authorization_granted_count": 0, + "release_authorization_pass_count": 0, + "rollback_release_pass_count": 0, + "live_apply_release_pass_count": 0, + "writer_apply_count": 0, + "execution_apply_count": 0, + "receipt_write_count": 0, + "reviewer_queue_write_count": 0, + "gateway_queue_write_count": 0, + "telegram_send_count": 0, + "bot_api_call_count": 0, + "report_receipt_write_count": 0, + "result_capture_write_count": 0, + "learning_write_count": 0, + "playbook_trust_write_count": 0, + "production_write_count": 0, + "secret_read_count": 0, + "destructive_operation_count": 0 + } +} diff --git a/docs/schemas/ai_agent_result_capture_release_decision_hold_v1.schema.json b/docs/schemas/ai_agent_result_capture_release_decision_hold_v1.schema.json new file mode 100644 index 000000000..abc4916dc --- /dev/null +++ b/docs/schemas/ai_agent_result_capture_release_decision_hold_v1.schema.json @@ -0,0 +1,109 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://awoooi.wooo.work/schemas/ai_agent_result_capture_release_decision_hold_v1.schema.json", + "title": "AI Agent result capture release decision hold v1", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "program_status", + "source_refs", + "prior_release_verifier_owner_review_packet", + "release_decision_hold_truth", + "release_decision_holds", + "owner_decision_holds", + "verifier_decision_holds", + "rollback_decision_holds", + "live_apply_decision_holds", + "blocked_decision_transitions", + "operator_actions", + "display_redaction_contract", + "rollups" + ], + "properties": { + "schema_version": { + "const": "ai_agent_result_capture_release_decision_hold_v1" + }, + "generated_at": { + "type": "string" + }, + "program_status": { + "type": "object" + }, + "source_refs": { + "type": "array", + "items": { + "type": "string" + } + }, + "prior_release_verifier_owner_review_packet": { + "type": "object" + }, + "release_decision_hold_truth": { + "type": "object" + }, + "release_decision_holds": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "owner_decision_holds": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "verifier_decision_holds": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "rollback_decision_holds": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "live_apply_decision_holds": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "blocked_decision_transitions": { + "type": "array", + "minItems": 6, + "maxItems": 6, + "items": { + "type": "object" + } + }, + "operator_actions": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "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 2670cff14..4b60de1dc 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 @@ -675,6 +675,7 @@ Alert / Sentry / SigNoz / Gitea / Market Watch / Operator | `docs/evaluations/ai_agent_result_capture_release_authorization_readback_gate_2026-06-14.json` + `GET /api/v1/agents/agent-result-capture-release-authorization-readback-gate` | P2-135 release authorization readback gate;承接 P2-134 release authorization hold,建立 5 個 release authorization readback、5 個 rollback release readback、5 個 maintenance window readback hold、5 個 live-apply release readback hold、6 個 blocked release readback transition 與 5 個 operator action;runtime authority 固定 `result_capture_release_authorization_readback_gate_only_no_live_write`;owner release authorized、owner release approved、maintenance window approved、rollback owner confirmed、post-release verifier ready、final release candidate approved、final release candidate passed、release authorization granted、release authorization passed、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、canonical runtime target read、live query、reviewer queue write、Gateway queue write、Telegram send、Bot API call、report receipt write、result capture write、learning write、PlayBook trust write、production write、secret read 與 destructive action 全部 `0 / false`;本地 API / typecheck 已通過,deploy marker `8d575c1a` 已正式驗證,正式 API readback 與 Browser desktop / mobile smoke 已完成 | | `docs/evaluations/ai_agent_result_capture_release_verifier_preflight_gate_2026-06-14.json` + `GET /api/v1/agents/agent-result-capture-release-verifier-preflight-gate` | P2-136 release verifier preflight gate;承接 P2-135 release authorization readback gate,建立 5 個 release verifier preflight、5 個 rollback verifier preflight、5 個 maintenance window verifier hold、5 個 live-apply verifier hold、6 個 blocked verifier preflight transition 與 5 個 operator action;runtime authority 固定 `result_capture_release_verifier_preflight_gate_only_no_live_write`;post-release verifier ready、owner release authorized、owner release approved、maintenance window approved、rollback owner confirmed、release authorization granted、release authorization passed、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、canonical runtime target read、live query、reviewer queue write、Gateway queue write、Telegram send、Bot API call、report receipt write、result capture write、learning write、PlayBook trust write、production write、secret read 與 destructive action 全部 `0 / false`;deploy marker `f2fa8454` 已正式驗證,正式 API readback 與 Browser desktop / mobile smoke 已完成,下一步 P2-137 owner / verifier review packet | | `docs/evaluations/ai_agent_result_capture_release_verifier_owner_review_packet_2026-06-14.json` + `GET /api/v1/agents/agent-result-capture-release-verifier-owner-review-packet` | P2-137 release verifier owner review packet;承接 P2-136 release verifier preflight gate,建立 5 個 owner review packet、5 個 verifier review packet、5 個 rollback owner review packet、5 個 maintenance window review hold、5 個 live-apply owner review hold、6 個 blocked owner review transition 與 5 個 operator action;runtime authority 固定 `result_capture_release_verifier_owner_review_packet_only_no_live_write`;owner release authorized、owner review approved、maintenance window approved、rollback owner confirmed、post-release verifier ready、release authorization granted、release authorization passed、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、canonical runtime target read、live query、reviewer queue write、Gateway queue write、Telegram send、Bot API call、report receipt write、result capture write、learning write、PlayBook trust write、production write、secret read 與 destructive action 全部 `0 / false`;deploy marker `d023f5d7` 已正式驗證,正式 API readback 與 desktop / mobile smoke 已完成,下一步 P2-138 release decision hold | +| `docs/evaluations/ai_agent_result_capture_release_decision_hold_2026-06-14.json` + `GET /api/v1/agents/agent-result-capture-release-decision-hold` | P2-138 release decision hold;承接 P2-137 release verifier owner review packet,建立 5 個 release decision hold、5 個 owner decision hold、5 個 verifier decision hold、5 個 rollback decision hold、5 個 live-apply decision hold、6 個 blocked decision transition 與 5 個 operator action;runtime authority 固定 `result_capture_release_decision_hold_only_no_live_write`;owner release authorized、owner review approved、owner decision approved、verifier decision approved、release decision passed、release authorization granted、release authorization passed、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、canonical runtime target read、live query、reviewer queue write、Gateway queue write、Telegram send、Bot API call、report receipt write、result capture write、learning write、PlayBook trust write、production write、secret read 與 destructive action 全部 `0 / false`;本地 P2-138 + P2-137 regression `14 passed`、JSON parse、Python compile 與 i18n key mirror 通過,待 Gitea CD 與 production smoke,下一步 P2-139 release decision readback | | `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 主動營運委派與版本生命週期契約 @@ -801,7 +802,8 @@ Repo / registry / release notes / K8s / host / observability / backup evidence 56. 建立 release authorization readback gate。✅ P2-135 已完成並正式驗證;release authorization readback `5`、rollback release readback `5`、maintenance window readback hold `5`、live-apply release readback hold `5`、blocked release readback transition `6`、operator action `5`,approval-required readback / rollback / maintenance / live apply `2 / 2 / 2 / 2`、blocked readback / rollback / maintenance / live apply `1 / 1 / 1 / 1`、critical blocker `5`;owner release authorized、owner release approved、maintenance window approved、rollback owner confirmed、post-release verifier ready、final release candidate approved、final release candidate passed、release authorization granted、release authorization passed、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、reviewer queue write、Gateway queue write、Telegram send、Bot API call、report receipt write、result capture write、learning write、PlayBook trust write、production write 仍為 `0 / false`;deploy marker `8d575c1a`、正式 API readback 與 desktop / mobile smoke 已完成。已由 P2-136 承接。 57. 建立 release verifier preflight gate。✅ P2-136 已完成並正式驗證;release verifier preflight `5`、rollback verifier preflight `5`、maintenance window verifier hold `5`、live-apply verifier hold `5`、blocked verifier preflight transition `6`、operator action `5`,approval-required preflight / rollback / maintenance / live apply `2 / 2 / 2 / 2`、blocked preflight / rollback / maintenance / live apply `1 / 1 / 1 / 1`、critical blocker `5`,前端顯示阻擋總數 `9`;post-release verifier ready、owner release authorized、owner release approved、maintenance window approved、rollback owner confirmed、final release candidate approved、final release candidate passed、release authorization granted、release authorization passed、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、reviewer queue write、Gateway queue write、Telegram send、Bot API call、report receipt write、result capture write、learning write、PlayBook trust write、production write、secret read、destructive operation 仍為 `0 / false`;deploy marker `f2fa8454`、正式 API readback 與 desktop / mobile smoke 已完成。已由 P2-137 承接。 58. 建立 owner / verifier review packet。✅ P2-137 已完成並正式驗證;owner review packet `5`、verifier review packet `5`、rollback owner review packet `5`、maintenance window review hold `5`、live-apply owner review hold `5`、blocked owner review transition `6`、operator action `5`,approval-required owner / verifier / rollback / maintenance / live apply `2 / 2 / 2 / 2 / 2`、blocked owner / verifier / rollback / maintenance / live apply `1 / 1 / 1 / 1 / 1`、critical blocker `5`;owner release authorized、owner review approved、maintenance window approved、rollback owner confirmed、post-release verifier ready、release authorization granted、release authorization passed、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、reviewer queue write、Gateway queue write、Telegram send、Bot API call、report receipt write、result capture write、learning write、PlayBook trust write、production write、secret read、destructive operation 仍為 `0 / false`;本地 API/service pytest `7 passed`、JSON parse、Python compile、Web typecheck 通過;deploy marker `d023f5d7`、正式 API readback 與 desktop / mobile smoke 已完成。已由 P2-138 承接。 -59. 建立 release decision hold。⬜ P2-138 planned;承接 P2-137,只能整理 release decision hold 的只讀證據,不得把 owner review packet 視為授權通過,不得寫 reviewer queue、Gateway queue、Telegram、Bot API、result capture、learning、PlayBook trust 或 production target。 +59. 建立 release decision hold。✅ P2-138 本地完成;release decision hold `5`、owner decision hold `5`、verifier decision hold `5`、rollback decision hold `5`、live-apply decision hold `5`、blocked decision transition `6`、operator action `5`,approval-required release / owner / verifier / rollback / live apply `2 / 2 / 2 / 2 / 2`、blocked release / owner / verifier / rollback / live apply `1 / 1 / 1 / 1 / 1`、critical blocker `5`;owner release authorized、owner review approved、owner decision approved、verifier decision approved、release decision passed、release authorization granted、release authorization passed、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、reviewer queue write、Gateway queue write、Telegram send、Bot API call、report receipt write、result capture write、learning write、PlayBook trust write、production write、secret read、destructive operation 仍為 `0 / false`;本地 P2-138 + P2-137 regression `14 passed`、JSON parse、Python compile、i18n key mirror 通過;待 Gitea CD 與正式站 smoke。 +60. 建立 release decision readback。⬜ P2-139 planned;承接 P2-138,只能整理 release decision hold readback 的只讀證據,不得把 hold 視為決策通過,不得寫 reviewer queue、Gateway queue、Telegram、Bot API、result capture、learning、PlayBook trust 或 production target。 #### 3.2.1d 2026-06-11 Agent 互動、學習與成長證據面 @@ -1943,6 +1945,13 @@ Phase 6 完成後 - 正式 Browser / Chrome smoke:desktop `1440x1000` 與 mobile `390x844` 均通過;P2-137 區塊、負責人審查包 `5`、驗證器審查包 `5`、回滾 owner 審查 `5`、維護窗口保留 `5`、正式套用保留 `5`、已阻擋轉換 `6`、需批准 `10`、阻擋 `10`、正式寫入 / 發送 `0`、Gateway 寫入 `0`、負責人釋出授權 `0` 可見;console error `0`、HTTP 4xx/5xx `0`、水平溢位 `0`、P2-137 卡片危險控制 `0`、禁用內部協作片語命中 `0`。 - 本波仍不讀 canonical runtime target、不做 live query、不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 report receipt、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 production target、不讀 secret、不執行 destructive action、不回傳內部工作內容;已由 P2-138 release decision hold 承接。 +### 2026-06-14 10:58 (台北) — §3.2 / §5 — 新增 P2-138 release decision hold — 本地完成決策保留只讀關卡 + +- 新增 `ai_agent_result_capture_release_decision_hold_v1` schema、snapshot、service loader、FastAPI endpoint `GET /api/v1/agents/agent-result-capture-release-decision-hold`、governance automation inventory UI 與 i18n 文案。 +- P2-138 承接 P2-137 owner review packet,只建立 5 個 release decision hold、5 個 owner decision hold、5 個 verifier decision hold、5 個 rollback decision hold、5 個 live-apply decision hold、6 個 blocked decision transition 與 5 個 operator action。 +- 本地驗證:P2-138 + P2-137 API/service regression `14 passed`、JSON parse、Python compile、i18n key mirror `11772` 通過;本 worktree 缺少 `node_modules`,Web typecheck 停在 `tsc: command not found`,待 Gitea CD 於乾淨環境補跑。 +- 本波仍不讀 canonical runtime target、不做 live query、不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 report receipt、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 production target、不讀 secret、不執行 destructive action、不回傳內部工作內容;正式驗證後才可由 P2-139 release decision readback 承接。 + ### 2026-06-14 08:36 (台北) — §3.2 / §5 — 正式驗證 P2-134 release authorization hold — 完成釋出授權保留只讀關卡 - Feature commit `e0a6d339` 已由 deploy marker `18b867c3 chore(cd): deploy e0a6d33 [skip ci]` 部署;正式 API health 回 `healthy`、`environment=prod`、`mock_mode=false`。