From 913d7f683ba97756cc4a5a82d677a2df6e7d3c66 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 14 Jun 2026 09:28:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(governance):=20=E6=96=B0=E5=A2=9E=20releas?= =?UTF-8?q?e=20verifier=20preflight=20gate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/api/v1/agents.py | 34 ++ ...capture_release_verifier_preflight_gate.py | 438 +++++++++++++++ ...capture_release_verifier_preflight_gate.py | 126 +++++ ...ure_release_verifier_preflight_gate_api.py | 31 ++ apps/web/messages/en.json | 30 ++ apps/web/messages/zh-TW.json | 30 ++ .../tabs/automation-inventory-tab.tsx | 166 +++++- apps/web/src/lib/api-client.ts | 232 ++++++++ docs/LOGBOOK.md | 23 + ...AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md | 10 + ...se_verifier_preflight_gate_2026-06-14.json | 507 ++++++++++++++++++ ...ase_verifier_preflight_gate_v1.schema.json | 100 ++++ ...-04-15-MASTER-ai-autonomous-flywheel-v2.md | 8 + 13 files changed, 1734 insertions(+), 1 deletion(-) create mode 100644 apps/api/src/services/ai_agent_result_capture_release_verifier_preflight_gate.py create mode 100644 apps/api/tests/test_ai_agent_result_capture_release_verifier_preflight_gate.py create mode 100644 apps/api/tests/test_ai_agent_result_capture_release_verifier_preflight_gate_api.py create mode 100644 docs/evaluations/ai_agent_result_capture_release_verifier_preflight_gate_2026-06-14.json create mode 100644 docs/schemas/ai_agent_result_capture_release_verifier_preflight_gate_v1.schema.json diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index 0fdcaf4d6..6296c24ea 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -151,6 +151,9 @@ from src.services.ai_agent_result_capture_release_authorization_hold import ( from src.services.ai_agent_result_capture_release_authorization_readback_gate import ( load_latest_ai_agent_result_capture_release_authorization_readback_gate, ) +from src.services.ai_agent_result_capture_release_verifier_preflight_gate import ( + load_latest_ai_agent_result_capture_release_verifier_preflight_gate, +) from src.services.ai_agent_result_capture_post_release_verifier_rollback_gate import ( load_latest_ai_agent_result_capture_post_release_verifier_rollback_gate, ) @@ -2187,6 +2190,37 @@ async def get_agent_result_capture_release_authorization_readback_gate() -> dict ) from exc +@router.get( + "/agent-result-capture-release-verifier-preflight-gate", + response_model=dict[str, Any], + summary="取得 AI Agent result capture release verifier preflight gate", + description=( + "讀取最新已提交的 P2-136 release verifier preflight gate;" + "此端點只回傳 release verifier preflight、rollback verifier preflight、maintenance window verifier hold、" + "live apply verifier hold、blocked verifier preflight transition 與 operator handoff," + "不授權 owner release、不批准 maintenance window、不確認 rollback owner、不通過 release authorization、" + "不釋放 rollback release 或 live apply、不套用 writer、不寫 receipt、不寫 result capture、learning、PlayBook trust、" + "reviewer queue、Gateway queue,不送 Telegram、不呼叫 Bot API、不讀 secret。" + ), +) +async def get_agent_result_capture_release_verifier_preflight_gate() -> dict[str, Any]: + """Return the latest read-only release verifier preflight gate.""" + try: + payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_release_verifier_preflight_gate) + 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_verifier_preflight_gate_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="AI Agent result capture release verifier preflight gate 無效", + ) 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_verifier_preflight_gate.py b/apps/api/src/services/ai_agent_result_capture_release_verifier_preflight_gate.py new file mode 100644 index 000000000..6881e4390 --- /dev/null +++ b/apps/api/src/services/ai_agent_result_capture_release_verifier_preflight_gate.py @@ -0,0 +1,438 @@ +""" +AI Agent result capture release verifier preflight gate snapshot. + +Loads the latest committed P2-136 release verifier preflight gate. +This module validates committed evidence only; it never authorizes owner +release, approves maintenance windows, confirms rollback owners, passes +release authorization, releases live apply, applies writers, writes receipts, +writes result captures, writes learning records, updates PlayBook trust, writes +reviewer / Gateway queues, sends Telegram messages, reads secrets, 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_verifier_preflight_gate_*.json" +_SCHEMA_VERSION = "ai_agent_result_capture_release_verifier_preflight_gate_v1" +_RUNTIME_AUTHORITY = "result_capture_release_verifier_preflight_gate_only_no_live_write" + + +def load_latest_ai_agent_result_capture_release_verifier_preflight_gate( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed release verifier preflight gate.""" + 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 verifier preflight gate 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_authorization_readbacks(payload, label) + _require_rollback_readbacks(payload, label) + _require_maintenance_holds(payload, label) + _require_live_apply_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-136", + "next_task_id": "P2-137", + "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_authorization_readback_gate") or {} + expected = { + "schema_version": "ai_agent_result_capture_release_authorization_readback_gate_v1", + "release_authorization_readback_count": 5, + "rollback_release_readback_count": 5, + "maintenance_window_readback_hold_count": 5, + "live_apply_release_readback_hold_count": 5, + "blocked_release_readback_transition_count": 6, + "operator_action_count": 5, + "approval_required_total": 8, + "blocked_total": 9, + "owner_release_authorized_count": 0, + "owner_release_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_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, + "result_capture_write_count": 0, + "learning_write_count": 0, + "playbook_trust_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, + } + mismatches = _mismatches(prior, expected) + if mismatches: + raise ValueError(f"{label}: prior_release_authorization_readback_gate mismatch: {mismatches}") + if not prior.get("readiness_note"): + raise ValueError(f"{label}: prior_release_authorization_readback_gate.readiness_note is required") + + +def _require_truth(payload: dict[str, Any], label: str) -> None: + truth = payload.get("release_verifier_preflight_truth") or {} + required_true = { + "p2_135_release_authorization_readback_gate_loaded", + "release_verifier_preflight_ready", + "rollback_verifier_preflight_ready", + "maintenance_window_verifier_hold_active", + "live_apply_verifier_hold_active", + "owner_authorization_review_still_required", + "release_verifier_review_still_required", + "rollback_owner_review_required", + "redaction_review_required", + "release_verifier_preflight_only", + } + missing = sorted(field for field in required_true if truth.get(field) is not True) + if missing: + raise ValueError(f"{label}: release verifier preflight flags must remain true: {missing}") + + required_false = { + "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_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 verifier preflight live/send/write flags must remain false: {unsafe}") + + zero_counts = { + "owner_release_authorized_count", + "owner_release_approved_count", + "maintenance_window_approved_count", + "rollback_owner_confirmed_count", + "post_release_verifier_ready_count", + "final_release_candidate_approved_count", + "final_release_candidate_pass_count", + "release_authorization_granted_count", + "release_authorization_pass_count", + "rollback_release_pass_count", + "live_apply_release_pass_count", + "writer_apply_count", + "execution_apply_count", + "receipt_write_count", + "reviewer_queue_write_count", + "gateway_queue_write_count", + "telegram_send_count", + "bot_api_call_count", + "report_receipt_write_count", + "result_capture_write_count", + "learning_write_count", + "playbook_trust_write_count", + "production_write_count", + "secret_read_count", + "destructive_operation_count", + } + non_zero = sorted(field for field in zero_counts if truth.get(field) != 0) + if non_zero: + raise ValueError(f"{label}: release verifier preflight live counters must remain zero: {non_zero}") + if not truth.get("truth_note"): + raise ValueError(f"{label}: release_verifier_preflight_truth.truth_note is required") + + +def _require_authorization_readbacks(payload: dict[str, Any], label: str) -> None: + items = payload.get("release_verifier_preflight_gates") or [] + _require_ids( + items, + "readback_id", + { + "release_verifier_preflight_result_capture", + "release_verifier_preflight_learning", + "release_verifier_preflight_playbook_trust", + "release_verifier_preflight_reviewer_queue", + "release_verifier_preflight_gateway_queue", + }, + label, + "release verifier preflights", + ) + for item in items: + item_id = item.get("readback_id") + if item.get("readback_mode") != "release_verifier_preflight_gate": + raise ValueError(f"{label}: release verifier preflight {item_id} mode is invalid") + if item.get("owner_release_authorized") is not False: + raise ValueError(f"{label}: release verifier preflight {item_id} must stay unauthorized") + if item.get("release_authorization_granted") is not False or item.get("release_authorization_passed") is not False: + raise ValueError(f"{label}: release verifier preflight {item_id} must stay ungranted and unpassed") + _require_valid_status(item, label, f"release verifier preflight {item_id}") + if not item.get("readback_summary") or not _is_redacted_sha256(item.get("evidence_hash")): + raise ValueError(f"{label}: release verifier preflight {item_id} must include summary and redacted evidence_hash") + + +def _require_rollback_readbacks(payload: dict[str, Any], label: str) -> None: + items = payload.get("rollback_verifier_preflight_gates") or [] + _require_count(items, 5, label, "rollback release readbacks") + for item in items: + item_id = item.get("readback_id") + if item.get("readback_mode") != "rollback_verifier_preflight_gate": + raise ValueError(f"{label}: rollback release readback {item_id} mode is invalid") + if item.get("rollback_owner_required") is not True: + raise ValueError(f"{label}: rollback release readback {item_id} must require rollback owner") + if item.get("rollback_owner_confirmed") is not False or item.get("rollback_release_passed") is not False or item.get("rollback_release_enabled") is not False: + raise ValueError(f"{label}: rollback release readback {item_id} must stay unconfirmed, unpassed, and disabled") + _require_valid_status(item, label, f"rollback release readback {item_id}") + if not item.get("readback_summary"): + raise ValueError(f"{label}: rollback release readback {item_id}.readback_summary is required") + + +def _require_maintenance_holds(payload: dict[str, Any], label: str) -> None: + items = payload.get("maintenance_window_verifier_holds") or [] + _require_count(items, 5, label, "maintenance window readback holds") + for item in items: + item_id = item.get("hold_id") + if item.get("hold_mode") != "maintenance_window_verifier_hold": + raise ValueError(f"{label}: maintenance window readback hold {item_id} mode is invalid") + if item.get("owner_release_authorized") is not False or item.get("maintenance_window_approved") is not False: + raise ValueError(f"{label}: maintenance window readback hold {item_id} must keep owner release and maintenance unapproved") + if item.get("final_release_candidate_passed") is not False: + raise ValueError(f"{label}: maintenance window readback hold {item_id} must keep final candidate unpassed") + _require_valid_status(item, label, f"maintenance window readback hold {item_id}") + if not item.get("hold_reason"): + raise ValueError(f"{label}: maintenance window readback hold {item_id}.hold_reason is required") + + +def _require_live_apply_holds(payload: dict[str, Any], label: str) -> None: + items = payload.get("live_apply_verifier_holds") or [] + _require_count(items, 5, label, "live apply release readback holds") + for item in items: + item_id = item.get("hold_id") + if item.get("hold_mode") != "live_apply_verifier_hold": + raise ValueError(f"{label}: live apply release readback hold {item_id} mode is invalid") + if item.get("release_authorization_granted") is not False or item.get("release_authorization_passed") is not False: + raise ValueError(f"{label}: live apply release readback hold {item_id} must stay ungranted and unpassed") + if 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 release readback hold {item_id} must stay unpassed and disabled") + _require_valid_status(item, label, f"live apply release readback hold {item_id}") + if not item.get("release_condition"): + raise ValueError(f"{label}: live apply release readback hold {item_id}.release_condition is required") + + +def _require_blocked_transitions(payload: dict[str, Any], label: str) -> None: + items = payload.get("blocked_verifier_preflight_transitions") or [] + _require_count(items, 6, label, "blocked release readback transitions") + critical_count = 0 + for item in items: + item_id = item.get("blocker_id") + if item.get("severity") == "critical": + critical_count += 1 + if item.get("status") not in {"approval_required", "blocked_by_policy"}: + raise ValueError(f"{label}: blocked release readback transition {item_id} status is invalid") + if not item.get("blocked_action") or not item.get("blocked_until") or not _is_redacted_sha256(item.get("evidence_hash")): + raise ValueError(f"{label}: blocked release readback transition {item_id} must include blocked action, until, and evidence hash") + if critical_count != 5: + raise ValueError(f"{label}: expected 5 critical release readback blockers, got {critical_count}") + + +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 not allow runtime writes") + 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_no_forbidden_display_terms(payload: dict[str, Any], label: str) -> None: + forbidden_terms = { + "work_window_transcript", + "session_id", + "browser_context", + "authorization_header", + "raw Telegram payload", + "private reasoning", + "raw prompt", + "chain-of-thought", + "批准!繼續", + "My request for Codex", + "In app browser", + } + display_blob = json.dumps( + { + "program_status": payload.get("program_status"), + "release_verifier_preflight_gates": payload.get("release_verifier_preflight_gates"), + "rollback_verifier_preflight_gates": payload.get("rollback_verifier_preflight_gates"), + "maintenance_window_verifier_holds": payload.get("maintenance_window_verifier_holds"), + "live_apply_verifier_holds": payload.get("live_apply_verifier_holds"), + "operator_actions": payload.get("operator_actions"), + "display_redaction_contract": payload.get("display_redaction_contract"), + }, + ensure_ascii=False, + ) + leaked = sorted(term for term in forbidden_terms if term in display_blob) + if leaked: + raise ValueError(f"{label}: forbidden display terms leaked: {leaked}") + + +def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None: + rollups = payload.get("rollups") or {} + expected = { + "release_verifier_preflight_count": 5, + "rollback_verifier_preflight_count": 5, + "maintenance_window_verifier_hold_count": 5, + "live_apply_verifier_hold_count": 5, + "blocked_verifier_preflight_transition_count": 6, + "operator_action_count": 5, + "approval_required_authorization_readback_count": 2, + "blocked_authorization_readback_count": 1, + "approval_required_rollback_readback_count": 2, + "blocked_rollback_readback_count": 1, + "approval_required_maintenance_window_count": 2, + "blocked_maintenance_window_count": 1, + "approval_required_live_apply_readback_count": 2, + "blocked_live_apply_readback_count": 1, + "critical_blocker_count": 5, + "owner_release_authorized_count": 0, + "owner_release_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_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, + } + mismatches = _mismatches(rollups, expected) + if mismatches: + raise ValueError(f"{label}: rollups mismatch: {mismatches}") + + +def _require_valid_status(item: dict[str, Any], label: str, item_label: str) -> None: + if item.get("status") not in {"ready_for_owner_review", "approval_required", "blocked_by_policy"}: + raise ValueError(f"{label}: {item_label} status is invalid") + + +def _require_ids( + items: list[dict[str, Any]], + id_field: str, + expected_ids: set[str], + label: str, + item_label: str, +) -> None: + actual_ids = {str(item.get(id_field)) for item in items} + if actual_ids != expected_ids: + raise ValueError(f"{label}: {item_label} ids mismatch: expected={sorted(expected_ids)} actual={sorted(actual_ids)}") + + +def _require_count(items: list[dict[str, Any]], expected_count: int, label: str, item_label: str) -> None: + if len(items) != expected_count: + raise ValueError(f"{label}: expected {expected_count} {item_label}, got {len(items)}") + + +def _mismatches(data: dict[str, Any], expected: dict[str, Any]) -> dict[str, dict[str, Any]]: + mismatches: dict[str, dict[str, Any]] = {} + for key, expected_value in expected.items(): + actual_value = data.get(key) + if actual_value != expected_value: + mismatches[key] = {"expected": expected_value, "actual": actual_value} + return mismatches + + +def _is_redacted_sha256(value: object) -> bool: + return isinstance(value, str) and re.fullmatch(r"sha256:[0-9a-f]{64}", value) is not None diff --git a/apps/api/tests/test_ai_agent_result_capture_release_verifier_preflight_gate.py b/apps/api/tests/test_ai_agent_result_capture_release_verifier_preflight_gate.py new file mode 100644 index 000000000..799954613 --- /dev/null +++ b/apps/api/tests/test_ai_agent_result_capture_release_verifier_preflight_gate.py @@ -0,0 +1,126 @@ +from __future__ import annotations + +import copy +import json +from pathlib import Path + +import pytest + +from src.services.ai_agent_result_capture_release_verifier_preflight_gate import ( + load_latest_ai_agent_result_capture_release_verifier_preflight_gate, +) + + +def test_load_latest_release_verifier_preflight_gate_snapshot() -> None: + snapshot = load_latest_ai_agent_result_capture_release_verifier_preflight_gate() + + assert snapshot["schema_version"] == "ai_agent_result_capture_release_verifier_preflight_gate_v1" + assert snapshot["program_status"]["current_task_id"] == "P2-136" + assert snapshot["program_status"]["next_task_id"] == "P2-137" + assert snapshot["program_status"]["overall_completion_percent"] == 100 + assert snapshot["program_status"]["runtime_authority"] == "result_capture_release_verifier_preflight_gate_only_no_live_write" + + rollups = snapshot["rollups"] + assert rollups["release_verifier_preflight_count"] == 5 + assert rollups["rollback_verifier_preflight_count"] == 5 + assert rollups["maintenance_window_verifier_hold_count"] == 5 + assert rollups["live_apply_verifier_hold_count"] == 5 + assert rollups["blocked_verifier_preflight_transition_count"] == 6 + assert rollups["operator_action_count"] == 5 + assert ( + rollups["approval_required_authorization_readback_count"] + + rollups["approval_required_rollback_readback_count"] + + rollups["approval_required_maintenance_window_count"] + + rollups["approval_required_live_apply_readback_count"] + ) == 8 + assert ( + rollups["blocked_authorization_readback_count"] + + rollups["blocked_rollback_readback_count"] + + rollups["blocked_maintenance_window_count"] + + rollups["blocked_live_apply_readback_count"] + + rollups["critical_blocker_count"] + ) == 9 + + assert rollups["owner_release_authorized_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["execution_apply_count"] == 0 + assert rollups["receipt_write_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["report_receipt_write_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_verifier_preflight_truth_keeps_all_live_paths_closed() -> None: + snapshot = load_latest_ai_agent_result_capture_release_verifier_preflight_gate() + truth = snapshot["release_verifier_preflight_truth"] + + assert truth["p2_135_release_authorization_readback_gate_loaded"] is True + assert truth["release_verifier_preflight_ready"] is True + assert truth["live_apply_verifier_hold_active"] is True + assert truth["owner_release_authorized"] is False + assert truth["owner_release_approved"] is False + assert truth["maintenance_window_approved"] is False + assert truth["rollback_owner_confirmed"] is False + assert truth["post_release_verifier_ready"] is False + assert truth["final_release_candidate_approved"] 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["writer_apply_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_granted_release_authorization(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_verifier_preflight_gate()) + snapshot["release_verifier_preflight_truth"]["release_authorization_granted"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="must remain false"): + load_latest_ai_agent_result_capture_release_verifier_preflight_gate(tmp_path) + + +def test_rejects_live_apply_release_enabled(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_verifier_preflight_gate()) + snapshot["live_apply_verifier_holds"][0]["live_apply_release_enabled"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="must stay unpassed and disabled"): + load_latest_ai_agent_result_capture_release_verifier_preflight_gate(tmp_path) + + +def test_rejects_rollup_drift(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_verifier_preflight_gate()) + snapshot["rollups"]["release_verifier_preflight_count"] = 4 + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="rollups mismatch"): + load_latest_ai_agent_result_capture_release_verifier_preflight_gate(tmp_path) + + +def test_rejects_forbidden_display_terms(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_verifier_preflight_gate()) + snapshot["operator_actions"][0]["operator_instruction"] = "不要顯示 work_window_transcript" + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="forbidden display terms leaked"): + load_latest_ai_agent_result_capture_release_verifier_preflight_gate(tmp_path) + + +def _write_snapshot(directory: Path, payload: dict) -> None: + path = directory / "ai_agent_result_capture_release_verifier_preflight_gate_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_verifier_preflight_gate_api.py b/apps/api/tests/test_ai_agent_result_capture_release_verifier_preflight_gate_api.py new file mode 100644 index 000000000..94fc66cd0 --- /dev/null +++ b/apps/api/tests/test_ai_agent_result_capture_release_verifier_preflight_gate_api.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +from fastapi.testclient import TestClient + +from src.main import app + + +def test_release_verifier_preflight_gate_endpoint() -> None: + client = TestClient(app) + + response = client.get("/api/v1/agents/agent-result-capture-release-verifier-preflight-gate") + + assert response.status_code == 200 + payload = response.json() + assert payload["schema_version"] == "ai_agent_result_capture_release_verifier_preflight_gate_v1" + assert payload["program_status"]["current_task_id"] == "P2-136" + assert payload["program_status"]["next_task_id"] == "P2-137" + assert payload["program_status"]["overall_completion_percent"] == 100 + assert payload["program_status"]["runtime_authority"] == "result_capture_release_verifier_preflight_gate_only_no_live_write" + assert payload["rollups"]["release_verifier_preflight_count"] == 5 + assert payload["rollups"]["rollback_verifier_preflight_count"] == 5 + assert payload["rollups"]["maintenance_window_verifier_hold_count"] == 5 + assert payload["rollups"]["live_apply_verifier_hold_count"] == 5 + assert payload["rollups"]["blocked_verifier_preflight_transition_count"] == 6 + assert payload["rollups"]["operator_action_count"] == 5 + assert payload["rollups"]["owner_release_authorized_count"] == 0 + assert payload["rollups"]["release_authorization_granted_count"] == 0 + assert payload["rollups"]["release_authorization_pass_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 52a6609de..dd7af3522 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -6119,6 +6119,36 @@ "runtimeWriteAllowed": "runtime 寫入允許={value}" } }, + "resultCaptureReleaseVerifierPreflightGate": { + "title": "P2-136 釋出驗證器預檢關卡", + "source": "產生 {generated};目前 {current};下一步 {next}", + "priorTitle": "前一關釋出授權讀回", + "truthTitle": "釋出驗證器預檢真相", + "metrics": { + "overall": "完成度", + "preflights": "驗證預檢", + "rollbackPreflights": "回滾預檢", + "maintenanceHolds": "維護窗口保留", + "liveApplyHolds": "正式套用保留", + "blockers": "已阻擋預檢", + "actions": "操作選項", + "approvalRequired": "需批准", + "blocked": "阻擋", + "liveWrites": "正式寫入 / 發送" + }, + "flags": { + "p2Loaded": "已載入 P2-135={value}", + "preflightReady": "釋出驗證器預檢已就緒={value}", + "liveApplyHoldActive": "正式套用預檢保留啟用={value}" + }, + "labels": { + "ownerReleaseAuthorized": "負責人釋出授權={value}", + "authorizationGranted": "釋出授權已核發={value}", + "authorizationPassed": "釋出授權通過={value}", + "gatewayWrites": "Gateway 寫入={value}", + "runtimeWriteAllowed": "runtime 寫入允許={value}" + } + }, "resultCaptureOwnerReleaseApprovalGate": { "title": "P2-131 owner release approval gate", "source": "產生 {generated};目前 {current};下一步 {next}", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 52a6609de..dd7af3522 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -6119,6 +6119,36 @@ "runtimeWriteAllowed": "runtime 寫入允許={value}" } }, + "resultCaptureReleaseVerifierPreflightGate": { + "title": "P2-136 釋出驗證器預檢關卡", + "source": "產生 {generated};目前 {current};下一步 {next}", + "priorTitle": "前一關釋出授權讀回", + "truthTitle": "釋出驗證器預檢真相", + "metrics": { + "overall": "完成度", + "preflights": "驗證預檢", + "rollbackPreflights": "回滾預檢", + "maintenanceHolds": "維護窗口保留", + "liveApplyHolds": "正式套用保留", + "blockers": "已阻擋預檢", + "actions": "操作選項", + "approvalRequired": "需批准", + "blocked": "阻擋", + "liveWrites": "正式寫入 / 發送" + }, + "flags": { + "p2Loaded": "已載入 P2-135={value}", + "preflightReady": "釋出驗證器預檢已就緒={value}", + "liveApplyHoldActive": "正式套用預檢保留啟用={value}" + }, + "labels": { + "ownerReleaseAuthorized": "負責人釋出授權={value}", + "authorizationGranted": "釋出授權已核發={value}", + "authorizationPassed": "釋出授權通過={value}", + "gatewayWrites": "Gateway 寫入={value}", + "runtimeWriteAllowed": "runtime 寫入允許={value}" + } + }, "resultCaptureOwnerReleaseApprovalGate": { "title": "P2-131 owner release approval gate", "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 3e2d9f865..7d6dd5a5c 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 @@ -83,6 +83,7 @@ import { type AiAgentResultCaptureFinalReleaseCandidateReadbackSnapshot, type AiAgentResultCaptureReleaseAuthorizationHoldSnapshot, type AiAgentResultCaptureReleaseAuthorizationReadbackGateSnapshot, + type AiAgentResultCaptureReleaseVerifierPreflightGateSnapshot, type AiAgentResultCaptureWriterImplementationReviewSnapshot, type AiAgentOwnerApprovedFixturePromotionGateSnapshot, type AiAgentRuntimeWorkerShadowGateSnapshot, @@ -489,6 +490,7 @@ export function AutomationInventoryTab() { const [resultCaptureFinalReleaseCandidateReadback, setResultCaptureFinalReleaseCandidateReadback] = useState(null) const [resultCaptureReleaseAuthorizationHold, setResultCaptureReleaseAuthorizationHold] = useState(null) const [resultCaptureReleaseAuthorizationReadbackGate, setResultCaptureReleaseAuthorizationReadbackGate] = useState(null) + const [resultCaptureReleaseVerifierPreflightGate, setResultCaptureReleaseVerifierPreflightGate] = useState(null) const [reportTruthActionabilityReview, setReportTruthActionabilityReview] = useState(null) const [ownerDryRunPackage, setOwnerDryRunPackage] = useState(null) const [hostStatefulInventory, setHostStatefulInventory] = useState(null) @@ -561,6 +563,7 @@ export function AutomationInventoryTab() { apiClient.getAiAgentResultCaptureFinalReleaseCandidateReadback(), apiClient.getAiAgentResultCaptureReleaseAuthorizationHold(), apiClient.getAiAgentResultCaptureReleaseAuthorizationReadbackGate(), + apiClient.getAiAgentResultCaptureReleaseVerifierPreflightGate(), apiClient.getAiAgentReportTruthActionabilityReview(), apiClient.getAiAgentOwnerApprovedFixtureDryRun(), apiClient.getAiAgentHostStatefulVersionInventory(), @@ -632,6 +635,7 @@ export function AutomationInventoryTab() { resultCaptureFinalReleaseCandidateReadbackResult, resultCaptureReleaseAuthorizationHoldResult, resultCaptureReleaseAuthorizationReadbackGateResult, + resultCaptureReleaseVerifierPreflightGateResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -700,6 +704,7 @@ export function AutomationInventoryTab() { setResultCaptureFinalReleaseCandidateReadback(resultCaptureFinalReleaseCandidateReadbackResult.status === 'fulfilled' ? resultCaptureFinalReleaseCandidateReadbackResult.value : null) setResultCaptureReleaseAuthorizationHold(resultCaptureReleaseAuthorizationHoldResult.status === 'fulfilled' ? resultCaptureReleaseAuthorizationHoldResult.value : null) setResultCaptureReleaseAuthorizationReadbackGate(resultCaptureReleaseAuthorizationReadbackGateResult.status === 'fulfilled' ? resultCaptureReleaseAuthorizationReadbackGateResult.value : null) + setResultCaptureReleaseVerifierPreflightGate(resultCaptureReleaseVerifierPreflightGateResult.status === 'fulfilled' ? resultCaptureReleaseVerifierPreflightGateResult.value : null) setReportTruthActionabilityReview(reportTruthActionabilityReviewResult.status === 'fulfilled' ? reportTruthActionabilityReviewResult.value : null) setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null) setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null) @@ -766,6 +771,7 @@ export function AutomationInventoryTab() { resultCaptureFinalReleaseCandidateReadbackResult, resultCaptureReleaseAuthorizationHoldResult, resultCaptureReleaseAuthorizationReadbackGateResult, + resultCaptureReleaseVerifierPreflightGateResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -2008,7 +2014,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 || !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 || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { return (
@@ -3222,6 +3228,40 @@ export function AutomationInventoryTab() { + resultCaptureReleaseAuthorizationReadbackGate.rollups.playbook_trust_write_count + resultCaptureReleaseAuthorizationReadbackGate.rollups.production_write_count ) + const resultCaptureReleaseVerifierPreflightOverall = resultCaptureReleaseVerifierPreflightGate.program_status.overall_completion_percent + const resultCaptureReleaseVerifierPreflights = resultCaptureReleaseVerifierPreflightGate.rollups.release_verifier_preflight_count + const resultCaptureReleaseVerifierRollbacks = resultCaptureReleaseVerifierPreflightGate.rollups.rollback_verifier_preflight_count + const resultCaptureReleaseVerifierMaintenanceHolds = resultCaptureReleaseVerifierPreflightGate.rollups.maintenance_window_verifier_hold_count + const resultCaptureReleaseVerifierLiveApplyHolds = resultCaptureReleaseVerifierPreflightGate.rollups.live_apply_verifier_hold_count + const resultCaptureReleaseVerifierBlockedTransitions = resultCaptureReleaseVerifierPreflightGate.rollups.blocked_verifier_preflight_transition_count + const resultCaptureReleaseVerifierActions = resultCaptureReleaseVerifierPreflightGate.rollups.operator_action_count + const resultCaptureReleaseVerifierApprovalRequired = ( + resultCaptureReleaseVerifierPreflightGate.rollups.approval_required_authorization_readback_count + + resultCaptureReleaseVerifierPreflightGate.rollups.approval_required_rollback_readback_count + + resultCaptureReleaseVerifierPreflightGate.rollups.approval_required_maintenance_window_count + + resultCaptureReleaseVerifierPreflightGate.rollups.approval_required_live_apply_readback_count + ) + const resultCaptureReleaseVerifierBlocked = ( + resultCaptureReleaseVerifierPreflightGate.rollups.blocked_authorization_readback_count + + resultCaptureReleaseVerifierPreflightGate.rollups.blocked_rollback_readback_count + + resultCaptureReleaseVerifierPreflightGate.rollups.blocked_maintenance_window_count + + resultCaptureReleaseVerifierPreflightGate.rollups.blocked_live_apply_readback_count + + resultCaptureReleaseVerifierPreflightGate.rollups.critical_blocker_count + ) + const resultCaptureReleaseVerifierLiveWrites = ( + resultCaptureReleaseVerifierPreflightGate.rollups.writer_apply_count + + resultCaptureReleaseVerifierPreflightGate.rollups.execution_apply_count + + resultCaptureReleaseVerifierPreflightGate.rollups.receipt_write_count + + resultCaptureReleaseVerifierPreflightGate.rollups.reviewer_queue_write_count + + resultCaptureReleaseVerifierPreflightGate.rollups.gateway_queue_write_count + + resultCaptureReleaseVerifierPreflightGate.rollups.telegram_send_count + + resultCaptureReleaseVerifierPreflightGate.rollups.bot_api_call_count + + resultCaptureReleaseVerifierPreflightGate.rollups.report_receipt_write_count + + resultCaptureReleaseVerifierPreflightGate.rollups.result_capture_write_count + + resultCaptureReleaseVerifierPreflightGate.rollups.learning_write_count + + resultCaptureReleaseVerifierPreflightGate.rollups.playbook_trust_write_count + + resultCaptureReleaseVerifierPreflightGate.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 @@ -7933,6 +7973,130 @@ export function AutomationInventoryTab() {
+
+
+
+ + + {t('resultCaptureReleaseVerifierPreflightGate.title')} + +
+ +
+ +
+ } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> +
+ +
+
+ {t('resultCaptureReleaseVerifierPreflightGate.priorTitle')} +

+ {resultCaptureReleaseVerifierPreflightGate.prior_release_authorization_readback_gate.readiness_note} +

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

+ {resultCaptureReleaseVerifierPreflightGate.release_verifier_preflight_truth.truth_note} +

+
+ + + + + + +
+
+
+ +
+ {resultCaptureReleaseVerifierPreflightGate.release_verifier_preflight_gates.slice(0, 5).map(readback => ( +
+
+ + {readback.display_name} + + +
+ + {readback.readback_summary} + +
+ + + + + +
+
+ ))} +
+ +
+ {resultCaptureReleaseVerifierPreflightGate.blocked_verifier_preflight_transitions.slice(0, 6).map(blocker => ( +
+
+ + {blocker.display_name} + + +
+ + {blocker.blocked_action} + +
+ + +
+
+ ))} +
+ +
+ {resultCaptureReleaseVerifierPreflightGate.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 e1de7e8d1..98f8c79a1 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -575,6 +575,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentResultCaptureReleaseVerifierPreflightGate() { + const res = await fetch(`${API_BASE_URL}/agents/agent-result-capture-release-verifier-preflight-gate`) + return handleResponse(res) + }, + async getAiAgentOwnerApprovedFixtureDryRun() { const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-fixture-dry-run`) return handleResponse(res) @@ -8621,6 +8626,233 @@ export interface AiAgentResultCaptureReleaseAuthorizationReadbackGateSnapshot { } } +export interface AiAgentResultCaptureReleaseVerifierPreflightGateSnapshot { + schema_version: 'ai_agent_result_capture_release_verifier_preflight_gate_v1' + generated_at: string + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: 'P2-136' + next_task_id: 'P2-137' + read_only_mode: true + runtime_authority: 'result_capture_release_verifier_preflight_gate_only_no_live_write' + status_note: string + } + source_refs: string[] + prior_release_authorization_readback_gate: { + schema_version: string + release_authorization_readback_count: number + rollback_release_readback_count: number + maintenance_window_readback_hold_count: number + live_apply_release_readback_hold_count: number + blocked_release_readback_transition_count: number + operator_action_count: number + approval_required_total: number + blocked_total: number + owner_release_authorized_count: number + owner_release_approved_count: number + maintenance_window_approved_count: number + rollback_owner_confirmed_count: number + post_release_verifier_ready_count: number + final_release_candidate_approved_count: number + final_release_candidate_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 + result_capture_write_count: number + learning_write_count: number + playbook_trust_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 + readiness_note: string + } + release_verifier_preflight_truth: { + p2_135_release_authorization_readback_gate_loaded: boolean + release_verifier_preflight_ready: boolean + rollback_verifier_preflight_ready: boolean + maintenance_window_verifier_hold_active: boolean + live_apply_verifier_hold_active: boolean + owner_authorization_review_still_required: boolean + release_verifier_review_still_required: boolean + rollback_owner_review_required: boolean + redaction_review_required: boolean + release_verifier_preflight_only: boolean + owner_release_authorized: boolean + owner_release_approved: boolean + maintenance_window_approved: boolean + rollback_owner_confirmed: boolean + post_release_verifier_ready: boolean + final_release_candidate_approved: boolean + final_release_candidate_passed: boolean + release_authorization_granted: boolean + release_authorization_passed: boolean + rollback_release_passed: boolean + live_apply_release_passed: boolean + writer_apply_enabled: boolean + execution_apply_enabled: boolean + receipt_write_enabled: boolean + reviewer_queue_write_enabled: boolean + gateway_queue_write_enabled: boolean + telegram_send_enabled: boolean + bot_api_call_enabled: boolean + report_receipt_write_enabled: boolean + result_capture_write_enabled: boolean + learning_write_enabled: boolean + playbook_trust_write_enabled: boolean + production_write_enabled: boolean + secret_read_enabled: boolean + destructive_operation_enabled: boolean + owner_release_authorized_count: number + owner_release_approved_count: number + maintenance_window_approved_count: number + rollback_owner_confirmed_count: number + post_release_verifier_ready_count: number + final_release_candidate_approved_count: number + final_release_candidate_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 + secret_read_count: number + destructive_operation_count: number + truth_note: string + } + release_verifier_preflight_gates: Array<{ + readback_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + source_authorization_hold: string + status: 'ready_for_owner_review' | 'approval_required' | 'blocked_by_policy' + readback_mode: 'release_verifier_preflight_gate' + owner_release_authorized: false + release_authorization_granted: false + release_authorization_passed: false + readback_summary: string + evidence_hash: string + }> + rollback_verifier_preflight_gates: Array<{ + readback_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: 'ready_for_owner_review' | 'approval_required' | 'blocked_by_policy' + readback_mode: 'rollback_verifier_preflight_gate' + rollback_owner_required: true + rollback_owner_confirmed: false + rollback_release_passed: false + rollback_release_enabled: false + readback_summary: string + }> + maintenance_window_verifier_holds: Array<{ + hold_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: 'ready_for_owner_review' | 'approval_required' | 'blocked_by_policy' + hold_mode: 'maintenance_window_verifier_hold' + owner_release_authorized: false + maintenance_window_approved: false + final_release_candidate_passed: false + hold_reason: string + }> + live_apply_verifier_holds: Array<{ + hold_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: 'ready_for_owner_review' | 'approval_required' | 'blocked_by_policy' + hold_mode: 'live_apply_verifier_hold' + release_authorization_granted: false + release_authorization_passed: false + live_apply_release_passed: false + live_apply_release_enabled: false + release_condition: string + }> + blocked_verifier_preflight_transitions: Array<{ + blocker_id: string + display_name: string + severity: 'critical' | 'high' | 'medium' | 'low' + status: 'blocked_by_policy' | 'approval_required' + blocked_action: string + blocked_until: string + evidence_hash: string + }> + operator_actions: Array<{ + action_id: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: 'ready_for_operator_review' | 'approval_required' + operator_instruction: string + 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: string + } + rollups: { + release_verifier_preflight_count: number + rollback_verifier_preflight_count: number + maintenance_window_verifier_hold_count: number + live_apply_verifier_hold_count: number + blocked_verifier_preflight_transition_count: number + operator_action_count: number + approval_required_authorization_readback_count: number + blocked_authorization_readback_count: number + approval_required_rollback_readback_count: number + blocked_rollback_readback_count: number + approval_required_maintenance_window_count: number + blocked_maintenance_window_count: number + approval_required_live_apply_readback_count: number + blocked_live_apply_readback_count: number + critical_blocker_count: number + owner_release_authorized_count: number + owner_release_approved_count: number + maintenance_window_approved_count: number + rollback_owner_confirmed_count: number + post_release_verifier_ready_count: number + final_release_candidate_approved_count: number + final_release_candidate_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 + secret_read_count: number + destructive_operation_count: number + } +} + export interface AiAgentOwnerApprovedFixtureDryRunSnapshot { schema_version: 'ai_agent_owner_approved_fixture_dry_run_v1' generated_at: string diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 88b789f79..af61d1530 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,26 @@ +## 2026-06-14|P2-136 釋出驗證器預檢關卡本地完成 + +**背景**:P2-135 已把 release authorization readback gate 正式驗證完成;但授權讀回仍不得被誤讀成 post-release verifier ready、release authorization granted / passed、rollback release passed 或 live apply release passed。P2-136 因此只建立 release verifier preflight gate,把 release authorization readback、rollback release readback、maintenance window readback hold、live-apply release readback hold 與 blocked release readback transition 轉成釋出驗證器預檢視圖,供 operator / owner 後續審核。 + +**完成內容**: +- 新增 `ai_agent_result_capture_release_verifier_preflight_gate_v1` schema、committed snapshot、loader 與 API endpoint `GET /api/v1/agents/agent-result-capture-release-verifier-preflight-gate`。 +- P2-136 snapshot 固定 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。 +- Governance automation inventory 頁新增 P2-136 區塊,顯示 P2-136 進度 `100%`、驗證預檢 `5`、回滾預檢 `5`、維護窗口保留 `5`、正式套用保留 `5`、已阻擋預檢 `6`、operator action `5`、需批准 `8`、阻擋 `9`、正式寫入 / 發送 `0`。 +- Release verifier preflight truth 固定為 `result_capture_release_verifier_preflight_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、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-136 schema / snapshot、`zh-TW.json`、`en.json` 通過。 +- i18n mirror:`zh-TW.json` / `en.json` key diff `0 / 0`。 +- Python 編譯:P2-136 loader 與 `agents.py` 通過。 +- API/service pytest:P2-136 + P2-135 regression `14 passed`。 +- Web typecheck:本臨時 worktree 缺少 `node_modules`,`pnpm --filter @awoooi/web typecheck` 停在 `tsc: command not found`;未做新的依賴安裝,等待 Gitea CD 在乾淨環境跑完整前端驗證。 + +**安全邊界**: +- P2-136 仍是 release verifier preflight gate;不接受口頭批准、不把 verifier preflight 解讀成 post-release verifier ready、不批准維護窗口、不確認 rollback owner、不核發 release authorization、不通過 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、不回傳內部協作內容。 + +**下一步**: +- 推送 Gitea main 後等待 CD;取得 deploy marker 後再做 production API readback 與 `/zh-TW/governance?tab=automation-inventory` desktop / mobile smoke,確認 P2-136 區塊、正式寫入 / 發送 `0`、水平溢位 `0`、危險控制 `0`。 + ## 2026-06-14|P2-135 釋出授權讀回關卡完成與正式驗證 **背景**:P2-134 已把 release authorization hold 正式驗證完成;但授權保留仍不得被誤讀成 owner release authorized、release authorization granted / passed、rollback release passed 或 live apply release passed。P2-135 因此只建立 release authorization readback gate,把 release authorization hold、rollback authorization hold、release window hold、live-apply authorization hold 與 blocked authorization transition 讀回成下一關可審核狀態,不接受口頭批准、不核發 release authorization、不釋放 rollback release 或 live apply、不套用 writer、不寫 receipt、不寫 result capture / learning / PlayBook trust / reviewer queue / Gateway queue,也不送 Telegram 或呼叫 Bot API。 diff --git a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md index 6045de5c1..56ccc0fb5 100644 --- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md +++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md @@ -32,6 +32,15 @@ - 內部工作視窗防洩漏已驗證:`批准!繼續`、`My request for Codex`、`In app browser`、`work_window_transcript`、`raw prompt`、`private reasoning`、`chain-of-thought` 均未出現在 production DOM。 - P2-135 已由 `P2-136` 承接;仍不得直接開啟 result capture writer、learning writer、PlayBook trust writer、reviewer queue write、Gateway queue write、Telegram send、Bot API call 或 production write。 +### 2026-06-14 09:18 狀態同步 + +- `P2-136` release verifier preflight gate 已本地完成:新增 `ai_agent_result_capture_release_verifier_preflight_gate_v1`、`GET /api/v1/agents/agent-result-capture-release-verifier-preflight-gate`、治理頁 P2-136 區塊與繁中 UI 文案。 +- 本地證據:P2-136 + P2-135 API/service regression `14 passed`、JSON parse、Python compile、i18n key diff `0 / 0` 通過。 +- P2-136 固定 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`、阻擋 `9`、正式寫入 / 發送 `0`。 +- Web typecheck 在本臨時 worktree 因缺少 `node_modules` 停在 `tsc: command not found`;未安裝依賴,等待 Gitea CD 在乾淨環境跑完整前端驗證。 +- 邊界仍維持:post-release verifier ready、release authorization granted / passed、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、result capture write、learning write、PlayBook trust write、reviewer queue write、Gateway queue write、Telegram send、Bot API、production write、secret read、destructive operation 全部 `0 / false`。 +- P2-136 推送與正式驗證後才可交給 `P2-137`;仍不得直接開啟 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。 @@ -1024,6 +1033,7 @@ UI: | P2-133 | 完成 | 100 | OpenClaw + Hermes + NemoTron | final release candidate readback | `ai_agent_result_capture_final_release_candidate_readback_v1` / schema / snapshot / 只讀 API / governance UI;正式站 deploy marker `8be5ddab` 已驗證;5 個 final release candidate readback、5 個 rollback candidate readback、5 個 candidate acceptance hold、5 個 live-apply candidate hold、6 個 blocked final candidate transition、5 個 operator action;runtime authority 固定 `result_capture_final_release_candidate_readback_only_no_live_write`;owner release approved / maintenance window approved / rollback owner confirmed / final candidate passed / live apply release passed / writer apply / execution apply / receipt write / result capture / learning / PlayBook trust / reviewer queue / Gateway / Telegram / Bot API / report receipt / production write 全為 `0`;desktop / mobile smoke 禁用內部協作片語 `0`、水平溢位 `0`、P2-133 高風險控制 `0`、console error `0` | 已由 P2-134 承接;不把 candidate readback 視為 owner release approved、不釋放 live apply、不套用 writer、不寫 receipt、不寫 result capture、不寫 learning、不送 Telegram、不寫 production | | P2-134 | 完成 | 100 | OpenClaw + Hermes + NemoTron | release authorization hold | `ai_agent_result_capture_release_authorization_hold_v1` / schema / snapshot / 只讀 API / governance UI;正式站 deploy marker `18b867c3` 已驗證;5 個 release authorization hold、5 個 rollback authorization hold、5 個 release window hold、5 個 live-apply authorization hold、6 個 blocked authorization transition、5 個 operator action;runtime authority 固定 `result_capture_release_authorization_hold_only_no_live_write`;owner release authorized / maintenance window approved / rollback owner confirmed / release authorization granted / release authorization passed / live apply release passed / writer apply / execution apply / receipt write / result capture / learning / PlayBook trust / reviewer queue / Gateway / Telegram / Bot API / report receipt / production write / secret read / destructive operation 全為 `0`;desktop / mobile smoke 禁用內部協作片語 `0`、水平溢位 `0`、P2-134 區塊危險操作入口 `0`、console error `0`、critical network issue `0` | 已由 P2-135 承接;不把 authorization hold 視為正式授權、不釋放 live apply、不套用 writer、不寫 receipt、不寫 result capture、不寫 learning、不送 Telegram、不寫 production | | 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;本地 P2-136 + P2-135 pytest `14 passed`、JSON parse、Python compile、i18n key diff `0 / 0` 通過;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、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` | 待 Gitea CD 與 production smoke;不把 verifier preflight 視為 post-release verifier ready、不核發 release authorization、不釋放 live apply、不套用 writer | ### P3 - 候選 Agent 擴展 diff --git a/docs/evaluations/ai_agent_result_capture_release_verifier_preflight_gate_2026-06-14.json b/docs/evaluations/ai_agent_result_capture_release_verifier_preflight_gate_2026-06-14.json new file mode 100644 index 000000000..2edfd501b --- /dev/null +++ b/docs/evaluations/ai_agent_result_capture_release_verifier_preflight_gate_2026-06-14.json @@ -0,0 +1,507 @@ +{ + "schema_version": "ai_agent_result_capture_release_verifier_preflight_gate_v1", + "generated_at": "2026-06-14T09:16:38+08:00", + "program_status": { + "overall_completion_percent": 100, + "current_priority": "P2", + "current_task_id": "P2-136", + "next_task_id": "P2-137", + "read_only_mode": true, + "runtime_authority": "result_capture_release_verifier_preflight_gate_only_no_live_write", + "status_note": "P2-136 只把 P2-135 release authorization readback gate 轉成 release verifier preflight gate;不得視為 owner release authorized,不得核發 release authorization,不得啟用 post-release verifier live read,不得通過 rollback release 或 live apply,不得套用 writer、寫 receipt、寫 result capture、寫 learning、寫 queue、送 Telegram 或寫入 production。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_result_capture_release_authorization_readback_gate_2026-06-14.json", + "docs/evaluations/ai_agent_result_capture_release_authorization_hold_2026-06-14.json", + "docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md#57-建立-release-verifier-preflight-gate" + ], + "prior_release_authorization_readback_gate": { + "schema_version": "ai_agent_result_capture_release_authorization_readback_gate_v1", + "release_authorization_readback_count": 5, + "rollback_release_readback_count": 5, + "maintenance_window_readback_hold_count": 5, + "live_apply_release_readback_hold_count": 5, + "blocked_release_readback_transition_count": 6, + "operator_action_count": 5, + "approval_required_total": 8, + "blocked_total": 9, + "owner_release_authorized_count": 0, + "owner_release_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_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, + "result_capture_write_count": 0, + "learning_write_count": 0, + "playbook_trust_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, + "readiness_note": "P2-135 已把 release authorization readback、rollback release readback、maintenance window readback hold、live-apply release readback hold 與 blocked release readback transition 固定為只讀 gate;P2-136 只建立 verifier preflight gate,不啟用 verifier live read 或 release authorization。" + }, + "release_verifier_preflight_truth": { + "p2_135_release_authorization_readback_gate_loaded": true, + "release_verifier_preflight_ready": true, + "rollback_verifier_preflight_ready": true, + "maintenance_window_verifier_hold_active": true, + "live_apply_verifier_hold_active": true, + "owner_authorization_review_still_required": true, + "release_verifier_review_still_required": true, + "rollback_owner_review_required": true, + "redaction_review_required": true, + "release_verifier_preflight_only": true, + "owner_release_authorized": false, + "owner_release_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_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, + "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_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-136 只建立 release verifier preflight gate;preflight 可見不代表 owner release authorized、post-release verifier ready、release authorization granted / passed、rollback release passed 或 live apply release passed。" + }, + "release_verifier_preflight_gates": [ + { + "readback_id": "release_verifier_preflight_result_capture", + "display_name": "Result capture release verifier preflight", + "owner_agent": "openclaw", + "source_authorization_hold": "release_authorization_result_capture", + "status": "ready_for_owner_review", + "readback_mode": "release_verifier_preflight_gate", + "owner_release_authorized": false, + "release_authorization_granted": false, + "release_authorization_passed": false, + "readback_summary": "讀回 Result capture release authorization hold;仍未取得 owner authorization、maintenance window、rollback owner 與 release authorization pass,不得寫入或發送。", + "evidence_hash": "sha256:8111111111111111111111111111111111111111111111111111111111111111" + }, + { + "readback_id": "release_verifier_preflight_learning", + "display_name": "Learning release verifier preflight", + "owner_agent": "hermes", + "source_authorization_hold": "release_authorization_learning", + "status": "approval_required", + "readback_mode": "release_verifier_preflight_gate", + "owner_release_authorized": false, + "release_authorization_granted": false, + "release_authorization_passed": false, + "readback_summary": "讀回 Learning release authorization hold;仍未取得 owner authorization、maintenance window、rollback owner 與 release authorization pass,不得寫入或發送。", + "evidence_hash": "sha256:8122222222222222222222222222222222222222222222222222222222222222" + }, + { + "readback_id": "release_verifier_preflight_playbook_trust", + "display_name": "PlayBook trust release verifier preflight", + "owner_agent": "nemotron", + "source_authorization_hold": "release_authorization_playbook_trust", + "status": "blocked_by_policy", + "readback_mode": "release_verifier_preflight_gate", + "owner_release_authorized": false, + "release_authorization_granted": false, + "release_authorization_passed": false, + "readback_summary": "讀回 PlayBook trust release authorization hold;仍未取得 owner authorization、maintenance window、rollback owner 與 release authorization pass,不得寫入或發送。", + "evidence_hash": "sha256:8133333333333333333333333333333333333333333333333333333333333333" + }, + { + "readback_id": "release_verifier_preflight_reviewer_queue", + "display_name": "Reviewer queue release verifier preflight", + "owner_agent": "openclaw", + "source_authorization_hold": "release_authorization_reviewer_queue", + "status": "approval_required", + "readback_mode": "release_verifier_preflight_gate", + "owner_release_authorized": false, + "release_authorization_granted": false, + "release_authorization_passed": false, + "readback_summary": "讀回 Reviewer queue release authorization hold;仍未取得 owner authorization、maintenance window、rollback owner 與 release authorization pass,不得寫入或發送。", + "evidence_hash": "sha256:8144444444444444444444444444444444444444444444444444444444444444" + }, + { + "readback_id": "release_verifier_preflight_gateway_queue", + "display_name": "Gateway queue release verifier preflight", + "owner_agent": "nemotron", + "source_authorization_hold": "release_authorization_gateway_queue", + "status": "ready_for_owner_review", + "readback_mode": "release_verifier_preflight_gate", + "owner_release_authorized": false, + "release_authorization_granted": false, + "release_authorization_passed": false, + "readback_summary": "讀回 Gateway queue release authorization hold;仍未取得 owner authorization、maintenance window、rollback owner 與 release authorization pass,不得寫入或發送。", + "evidence_hash": "sha256:8155555555555555555555555555555555555555555555555555555555555555" + } + ], + "rollback_verifier_preflight_gates": [ + { + "readback_id": "rollback_verifier_preflight_result_capture", + "display_name": "Result capture rollback release readback", + "owner_agent": "openclaw", + "status": "ready_for_owner_review", + "readback_mode": "rollback_verifier_preflight_gate", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_passed": false, + "rollback_release_enabled": false, + "readback_summary": "讀回 Result capture rollback release gate;rollback owner 未 confirmed 前不得通過 rollback release。" + }, + { + "readback_id": "rollback_verifier_preflight_learning", + "display_name": "Learning rollback release readback", + "owner_agent": "hermes", + "status": "approval_required", + "readback_mode": "rollback_verifier_preflight_gate", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_passed": false, + "rollback_release_enabled": false, + "readback_summary": "讀回 Learning rollback release gate;rollback owner 未 confirmed 前不得通過 rollback release。" + }, + { + "readback_id": "rollback_verifier_preflight_playbook_trust", + "display_name": "PlayBook trust rollback release readback", + "owner_agent": "nemotron", + "status": "blocked_by_policy", + "readback_mode": "rollback_verifier_preflight_gate", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_passed": false, + "rollback_release_enabled": false, + "readback_summary": "讀回 PlayBook trust rollback release gate;rollback owner 未 confirmed 前不得通過 rollback release。" + }, + { + "readback_id": "rollback_verifier_preflight_reviewer_queue", + "display_name": "Reviewer queue rollback release readback", + "owner_agent": "openclaw", + "status": "approval_required", + "readback_mode": "rollback_verifier_preflight_gate", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_passed": false, + "rollback_release_enabled": false, + "readback_summary": "讀回 Reviewer queue rollback release gate;rollback owner 未 confirmed 前不得通過 rollback release。" + }, + { + "readback_id": "rollback_verifier_preflight_gateway_queue", + "display_name": "Gateway queue rollback release readback", + "owner_agent": "nemotron", + "status": "ready_for_owner_review", + "readback_mode": "rollback_verifier_preflight_gate", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_passed": false, + "rollback_release_enabled": false, + "readback_summary": "讀回 Gateway queue rollback release gate;rollback owner 未 confirmed 前不得通過 rollback release。" + } + ], + "maintenance_window_verifier_holds": [ + { + "hold_id": "maintenance_window_readback_result_capture", + "display_name": "Result capture maintenance window readback hold", + "owner_agent": "openclaw", + "status": "ready_for_owner_review", + "hold_mode": "maintenance_window_verifier_hold", + "owner_release_authorized": false, + "maintenance_window_approved": false, + "final_release_candidate_passed": false, + "hold_reason": "Result capture maintenance window 只讀回 gate;仍需 owner authorization、maintenance approval 與 final verifier pass。" + }, + { + "hold_id": "maintenance_window_readback_learning", + "display_name": "Learning maintenance window readback hold", + "owner_agent": "hermes", + "status": "approval_required", + "hold_mode": "maintenance_window_verifier_hold", + "owner_release_authorized": false, + "maintenance_window_approved": false, + "final_release_candidate_passed": false, + "hold_reason": "Learning maintenance window 只讀回 gate;仍需 owner authorization、maintenance approval 與 final verifier pass。" + }, + { + "hold_id": "maintenance_window_readback_playbook_trust", + "display_name": "PlayBook trust maintenance window readback hold", + "owner_agent": "nemotron", + "status": "blocked_by_policy", + "hold_mode": "maintenance_window_verifier_hold", + "owner_release_authorized": false, + "maintenance_window_approved": false, + "final_release_candidate_passed": false, + "hold_reason": "PlayBook trust maintenance window 只讀回 gate;仍需 owner authorization、maintenance approval 與 final verifier pass。" + }, + { + "hold_id": "maintenance_window_readback_reviewer_queue", + "display_name": "Reviewer queue maintenance window readback hold", + "owner_agent": "openclaw", + "status": "approval_required", + "hold_mode": "maintenance_window_verifier_hold", + "owner_release_authorized": false, + "maintenance_window_approved": false, + "final_release_candidate_passed": false, + "hold_reason": "Reviewer queue maintenance window 只讀回 gate;仍需 owner authorization、maintenance approval 與 final verifier pass。" + }, + { + "hold_id": "maintenance_window_readback_gateway_queue", + "display_name": "Gateway queue maintenance window readback hold", + "owner_agent": "nemotron", + "status": "ready_for_owner_review", + "hold_mode": "maintenance_window_verifier_hold", + "owner_release_authorized": false, + "maintenance_window_approved": false, + "final_release_candidate_passed": false, + "hold_reason": "Gateway queue maintenance window 只讀回 gate;仍需 owner authorization、maintenance approval 與 final verifier pass。" + } + ], + "live_apply_verifier_holds": [ + { + "hold_id": "live_apply_release_readback_result_capture", + "display_name": "Result capture live apply release readback hold", + "owner_agent": "openclaw", + "status": "ready_for_owner_review", + "hold_mode": "live_apply_verifier_hold", + "release_authorization_granted": false, + "release_authorization_passed": false, + "live_apply_release_passed": false, + "live_apply_release_enabled": false, + "release_condition": "P2-136 只讀回 release authorization gate;不得直接 writer apply、live apply 或 Telegram 實發。" + }, + { + "hold_id": "live_apply_release_readback_learning", + "display_name": "Learning live apply release readback hold", + "owner_agent": "hermes", + "status": "approval_required", + "hold_mode": "live_apply_verifier_hold", + "release_authorization_granted": false, + "release_authorization_passed": false, + "live_apply_release_passed": false, + "live_apply_release_enabled": false, + "release_condition": "P2-136 只讀回 release authorization gate;不得直接 writer apply、live apply 或 Telegram 實發。" + }, + { + "hold_id": "live_apply_release_readback_playbook_trust", + "display_name": "PlayBook trust live apply release readback hold", + "owner_agent": "nemotron", + "status": "blocked_by_policy", + "hold_mode": "live_apply_verifier_hold", + "release_authorization_granted": false, + "release_authorization_passed": false, + "live_apply_release_passed": false, + "live_apply_release_enabled": false, + "release_condition": "P2-136 只讀回 release authorization gate;不得直接 writer apply、live apply 或 Telegram 實發。" + }, + { + "hold_id": "live_apply_release_readback_reviewer_queue", + "display_name": "Reviewer queue live apply release readback hold", + "owner_agent": "openclaw", + "status": "approval_required", + "hold_mode": "live_apply_verifier_hold", + "release_authorization_granted": false, + "release_authorization_passed": false, + "live_apply_release_passed": false, + "live_apply_release_enabled": false, + "release_condition": "P2-136 只讀回 release authorization gate;不得直接 writer apply、live apply 或 Telegram 實發。" + }, + { + "hold_id": "live_apply_release_readback_gateway_queue", + "display_name": "Gateway queue live apply release readback hold", + "owner_agent": "nemotron", + "status": "ready_for_owner_review", + "hold_mode": "live_apply_verifier_hold", + "release_authorization_granted": false, + "release_authorization_passed": false, + "live_apply_release_passed": false, + "live_apply_release_enabled": false, + "release_condition": "P2-136 只讀回 release authorization gate;不得直接 writer apply、live apply 或 Telegram 實發。" + } + ], + "blocked_verifier_preflight_transitions": [ + { + "blocker_id": "blocked_writer_apply_release_readback", + "display_name": "Writer apply release readback blocked", + "severity": "critical", + "status": "blocked_by_policy", + "blocked_action": "writer_apply_after_release_verifier_preflight", + "blocked_until": "owner authorization + maintenance window + release authorization pass + rollback owner confirmed", + "evidence_hash": "sha256:9122222222222222222222222222222222222222222222222222222222222222" + }, + { + "blocker_id": "blocked_execution_apply_release_readback", + "display_name": "Execution apply release readback blocked", + "severity": "critical", + "status": "blocked_by_policy", + "blocked_action": "execution_apply_after_release_verifier_preflight", + "blocked_until": "release authorization passed + rollback release passed + post-release verifier ready", + "evidence_hash": "sha256:9233333333333333333333333333333333333333333333333333333333333333" + }, + { + "blocker_id": "blocked_receipt_write_release_readback", + "display_name": "Receipt write release readback blocked", + "severity": "critical", + "status": "approval_required", + "blocked_action": "receipt_write_after_release_verifier_preflight", + "blocked_until": "release authorization granted + release verification passed", + "evidence_hash": "sha256:9344444444444444444444444444444444444444444444444444444444444444" + }, + { + "blocker_id": "blocked_result_capture_write_release_readback", + "display_name": "Result capture write release readback blocked", + "severity": "critical", + "status": "blocked_by_policy", + "blocked_action": "result_capture_write_after_release_verifier_preflight", + "blocked_until": "owner authorization + release authorization pass + rollback owner confirmed", + "evidence_hash": "sha256:9455555555555555555555555555555555555555555555555555555555555555" + }, + { + "blocker_id": "blocked_gateway_queue_write_release_readback", + "display_name": "Gateway queue write release readback blocked", + "severity": "critical", + "status": "approval_required", + "blocked_action": "gateway_queue_write_after_release_verifier_preflight", + "blocked_until": "SRE route lock + Telegram receipt owner approved + release authorization pass", + "evidence_hash": "sha256:9566666666666666666666666666666666666666666666666666666666666666" + }, + { + "blocker_id": "blocked_telegram_send_release_readback", + "display_name": "Telegram send release readback blocked", + "severity": "high", + "status": "approval_required", + "blocked_action": "telegram_send_after_release_verifier_preflight", + "blocked_until": "Gateway queue release approved + Telegram receipt E2E approved + rollback release gate passed", + "evidence_hash": "sha256:9677777777777777777777777777777777777777777777777777777777777777" + } + ], + "operator_actions": [ + { + "action_id": "review_release_verifier_preflight_gates", + "owner_agent": "openclaw", + "status": "ready_for_operator_review", + "operator_instruction": "審核 5 個 release verifier preflight 是否仍符合 P2-135 hold 邊界;不要把 readback 視為正式授權。", + "runtime_write_allowed": false + }, + { + "action_id": "verify_rollback_verifier_preflight", + "owner_agent": "hermes", + "status": "ready_for_operator_review", + "operator_instruction": "確認 rollback release readback 仍全部未 passed,且 rollback_release_enabled 仍為 false。", + "runtime_write_allowed": false + }, + { + "action_id": "verify_maintenance_window_verifier_hold", + "owner_agent": "nemotron", + "status": "ready_for_operator_review", + "operator_instruction": "確認 maintenance window readback 仍為 hold;不可批准 maintenance window 或套用 writer。", + "runtime_write_allowed": false + }, + { + "action_id": "verify_live_apply_verifier_hold", + "owner_agent": "openclaw", + "status": "approval_required", + "operator_instruction": "確認 live apply release readback 仍未 enabled,且 release_authorization_passed 仍為 false。", + "runtime_write_allowed": false + }, + { + "action_id": "prepare_p2_136_release_authorization_review_packet", + "owner_agent": "hermes", + "status": "ready_for_operator_review", + "operator_instruction": "下一關只可建立 P2-137 release authorization review packet,不可放行 writer 或正式寫入。", + "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": "前端只能顯示 release verifier preflight 摘要、狀態、數量、redacted hash 與 0 / false 邊界;不得顯示內部協作環境對話、原始提示詞、私有推理、secret、授權標頭或原始 runtime payload。" + }, + "rollups": { + "release_verifier_preflight_count": 5, + "rollback_verifier_preflight_count": 5, + "maintenance_window_verifier_hold_count": 5, + "live_apply_verifier_hold_count": 5, + "blocked_verifier_preflight_transition_count": 6, + "operator_action_count": 5, + "approval_required_authorization_readback_count": 2, + "blocked_authorization_readback_count": 1, + "approval_required_rollback_readback_count": 2, + "blocked_rollback_readback_count": 1, + "approval_required_maintenance_window_count": 2, + "blocked_maintenance_window_count": 1, + "approval_required_live_apply_readback_count": 2, + "blocked_live_apply_readback_count": 1, + "critical_blocker_count": 5, + "owner_release_authorized_count": 0, + "owner_release_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_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_verifier_preflight_gate_v1.schema.json b/docs/schemas/ai_agent_result_capture_release_verifier_preflight_gate_v1.schema.json new file mode 100644 index 000000000..f383a424c --- /dev/null +++ b/docs/schemas/ai_agent_result_capture_release_verifier_preflight_gate_v1.schema.json @@ -0,0 +1,100 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://awoooi.wooo.work/schemas/ai_agent_result_capture_release_verifier_preflight_gate_v1.schema.json", + "title": "AI Agent result capture release verifier preflight gate v1", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "program_status", + "source_refs", + "prior_release_authorization_readback_gate", + "release_verifier_preflight_truth", + "release_verifier_preflight_gates", + "rollback_verifier_preflight_gates", + "maintenance_window_verifier_holds", + "live_apply_verifier_holds", + "blocked_verifier_preflight_transitions", + "operator_actions", + "display_redaction_contract", + "rollups" + ], + "properties": { + "schema_version": { + "const": "ai_agent_result_capture_release_verifier_preflight_gate_v1" + }, + "generated_at": { + "type": "string" + }, + "program_status": { + "type": "object" + }, + "source_refs": { + "type": "array", + "items": { + "type": "string" + } + }, + "prior_release_authorization_readback_gate": { + "type": "object" + }, + "release_verifier_preflight_truth": { + "type": "object" + }, + "release_verifier_preflight_gates": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "rollback_verifier_preflight_gates": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "maintenance_window_verifier_holds": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "live_apply_verifier_holds": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "blocked_verifier_preflight_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 37812f804..7c7ea61e0 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 @@ -797,6 +797,7 @@ Repo / registry / release notes / K8s / host / observability / backup evidence 54. 建立 final release candidate readback。✅ P2-133 已完成並正式驗證;final release candidate readback `5`、rollback candidate readback `5`、candidate acceptance hold `5`、live-apply candidate hold `5`、blocked final candidate transition `6`、operator action `5`,approval-required candidate / rollback / acceptance / live apply `2 / 2 / 2 / 2`、blocked candidate / rollback / acceptance / live apply `1 / 1 / 1 / 1`、critical blocker `5`;owner release approved、maintenance window approved、rollback owner confirmed、post-release verifier ready、final release candidate approved、final release candidate 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 `8be5ddab`、正式 API readback 與 desktop / mobile smoke 已完成。已由 P2-134 正式承接。 55. 建立 release authorization hold。✅ P2-134 已完成並正式驗證;release authorization hold `5`、rollback authorization hold `5`、release window hold `5`、live-apply authorization hold `5`、blocked authorization transition `6`、operator action `5`,approval-required authorization / rollback / release window / live apply `2 / 2 / 2 / 2`、blocked authorization / rollback / release window / 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 `18b867c3`、正式 API readback 與 desktop / mobile smoke 已完成。下一步 P2-135。 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`;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 仍為 `0 / false`。正式站 deploy marker 與 desktop / mobile smoke 完成後才可由 P2-137 承接。 #### 3.2.1d 2026-06-11 Agent 互動、學習與成長證據面 @@ -1907,6 +1908,13 @@ Phase 6 完成後 - 內部工作視窗防洩漏已驗證:`批准!繼續`、`My request for Codex`、`In app browser`、`work_window_transcript`、`raw prompt`、`private reasoning`、`chain-of-thought` 均未出現在 production DOM。 - 本波仍不讀 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-136 承接。 +### 2026-06-14 09:18 (台北) — §3.2 / §5 — 新增 P2-136 release verifier preflight gate — 本地完成釋出驗證器預檢只讀關卡 + +- 新增 `ai_agent_result_capture_release_verifier_preflight_gate_v1` schema、snapshot、loader、API route、governance UI 與繁中訊息;承接 P2-135 release authorization readback gate,但不把 readback 解讀成 release authorization granted / passed 或 post-release verifier ready。 +- 本地 rollup: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`、正式寫入 / 發送 `0`。 +- 本地驗證:JSON parse、Python compile、P2-136 + P2-135 API/service regression `14 passed`、i18n key diff `0 / 0` 通過;本臨時 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-137 承接。 + ### 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`。