diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index d77047c61..64ada8db9 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -145,6 +145,9 @@ from src.services.ai_agent_result_capture_owner_release_approval_gate import ( from src.services.ai_agent_result_capture_final_release_candidate_readback import ( load_latest_ai_agent_result_capture_final_release_candidate_readback, ) +from src.services.ai_agent_result_capture_release_authorization_hold import ( + load_latest_ai_agent_result_capture_release_authorization_hold, +) from src.services.ai_agent_result_capture_post_release_verifier_rollback_gate import ( load_latest_ai_agent_result_capture_post_release_verifier_rollback_gate, ) @@ -2119,6 +2122,37 @@ async def get_agent_result_capture_final_release_candidate_readback() -> dict[st ) from exc +@router.get( + "/agent-result-capture-release-authorization-hold", + response_model=dict[str, Any], + summary="取得 AI Agent result capture release authorization hold", + description=( + "讀取最新已提交的 P2-134 release authorization hold;" + "此端點只回傳 release authorization hold、rollback authorization hold、release window hold、" + "live apply authorization hold、blocked authorization transition 與 operator handoff," + "不授權 owner release、不批准 maintenance window、不確認 rollback owner、不通過 release authorization、" + "不釋放 live apply、不套用 writer、不寫 receipt、不寫 result capture、learning、PlayBook trust、" + "reviewer queue、Gateway queue,不送 Telegram、不呼叫 Bot API、不讀 secret。" + ), +) +async def get_agent_result_capture_release_authorization_hold() -> dict[str, Any]: + """Return the latest read-only release authorization hold.""" + try: + payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_release_authorization_hold) + return redact_public_lan_topology(payload) + except FileNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(exc), + ) from exc + except (json.JSONDecodeError, ValueError) as exc: + logger.error("ai_agent_result_capture_release_authorization_hold_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="AI Agent result capture release authorization hold 無效", + ) from exc + + @router.get( "/agent-owner-approved-fixture-dry-run", response_model=dict[str, Any], diff --git a/apps/api/src/services/ai_agent_result_capture_release_authorization_hold.py b/apps/api/src/services/ai_agent_result_capture_release_authorization_hold.py new file mode 100644 index 000000000..1212c7d3c --- /dev/null +++ b/apps/api/src/services/ai_agent_result_capture_release_authorization_hold.py @@ -0,0 +1,433 @@ +""" +AI Agent result capture release authorization hold snapshot. + +Loads the latest committed P2-134 release authorization hold. +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_authorization_hold_*.json" +_SCHEMA_VERSION = "ai_agent_result_capture_release_authorization_hold_v1" +_RUNTIME_AUTHORITY = "result_capture_release_authorization_hold_only_no_live_write" + + +def load_latest_ai_agent_result_capture_release_authorization_hold( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed release authorization hold.""" + directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR + candidates = sorted(directory.glob(_SNAPSHOT_PATTERN)) + if not candidates: + raise FileNotFoundError(f"no AI Agent result capture release authorization hold snapshots found in {directory}") + + latest = candidates[-1] + with latest.open(encoding="utf-8") as handle: + payload = json.load(handle) + + if not isinstance(payload, dict): + raise ValueError(f"{latest}: expected JSON object") + + label = str(latest) + _require_schema(payload, label) + _require_prior(payload, label) + _require_truth(payload, label) + _require_authorization_holds(payload, label) + _require_rollback_holds(payload, label) + _require_release_window_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-134", + "next_task_id": "P2-135", + "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_final_release_candidate_readback") or {} + expected = { + "schema_version": "ai_agent_result_capture_final_release_candidate_readback_v1", + "final_release_candidate_readback_count": 5, + "rollback_candidate_readback_count": 5, + "candidate_acceptance_hold_count": 5, + "live_apply_candidate_hold_count": 5, + "blocked_final_candidate_transition_count": 6, + "operator_action_count": 5, + "approval_required_total": 8, + "blocked_total": 9, + "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, + "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_final_release_candidate_readback mismatch: {mismatches}") + if not prior.get("readiness_note"): + raise ValueError(f"{label}: prior_final_release_candidate_readback.readiness_note is required") + + +def _require_truth(payload: dict[str, Any], label: str) -> None: + truth = payload.get("release_authorization_truth") or {} + required_true = { + "p2_133_final_candidate_loaded", + "release_authorization_hold_ready", + "rollback_authorization_hold_ready", + "release_window_hold_active", + "live_apply_authorization_hold_active", + "owner_authorization_review_still_required", + "maintenance_window_review_still_required", + "rollback_owner_review_required", + "redaction_review_required", + "release_authorization_hold_only", + } + missing = sorted(field for field in required_true if truth.get(field) is not True) + if missing: + raise ValueError(f"{label}: release authorization hold 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 authorization 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 authorization live counters must remain zero: {non_zero}") + if not truth.get("truth_note"): + raise ValueError(f"{label}: release_authorization_truth.truth_note is required") + + +def _require_authorization_holds(payload: dict[str, Any], label: str) -> None: + items = payload.get("release_authorization_holds") or [] + _require_ids( + items, + "hold_id", + { + "release_authorization_result_capture", + "release_authorization_learning", + "release_authorization_playbook_trust", + "release_authorization_reviewer_queue", + "release_authorization_gateway_queue", + }, + label, + "release authorization holds", + ) + for item in items: + item_id = item.get("hold_id") + if item.get("hold_mode") != "release_authorization_hold": + raise ValueError(f"{label}: release authorization hold {item_id} mode is invalid") + if item.get("owner_release_authorized") is not False: + raise ValueError(f"{label}: release authorization hold {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 authorization hold {item_id} must stay ungranted and unpassed") + _require_valid_status(item, label, f"release authorization hold {item_id}") + if not item.get("authorization_summary") or not _is_redacted_sha256(item.get("evidence_hash")): + raise ValueError(f"{label}: release authorization hold {item_id} must include summary and redacted evidence_hash") + + +def _require_rollback_holds(payload: dict[str, Any], label: str) -> None: + items = payload.get("rollback_authorization_holds") or [] + _require_count(items, 5, label, "rollback authorization holds") + for item in items: + item_id = item.get("hold_id") + if item.get("hold_mode") != "rollback_authorization_hold": + raise ValueError(f"{label}: rollback authorization hold {item_id} mode is invalid") + if item.get("rollback_owner_required") is not True: + raise ValueError(f"{label}: rollback authorization hold {item_id} must require rollback owner") + if item.get("rollback_owner_confirmed") is not False or item.get("rollback_release_enabled") is not False: + raise ValueError(f"{label}: rollback authorization hold {item_id} must stay unconfirmed and disabled") + _require_valid_status(item, label, f"rollback authorization hold {item_id}") + if not item.get("rollback_summary"): + raise ValueError(f"{label}: rollback authorization hold {item_id}.rollback_summary is required") + + +def _require_release_window_holds(payload: dict[str, Any], label: str) -> None: + items = payload.get("release_window_holds") or [] + _require_count(items, 5, label, "release window holds") + for item in items: + item_id = item.get("hold_id") + if item.get("hold_mode") != "release_window_hold": + raise ValueError(f"{label}: release window 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}: release window hold {item_id} must keep owner release and maintenance unapproved") + if item.get("final_release_candidate_passed") is not False: + raise ValueError(f"{label}: release window hold {item_id} must keep final candidate unpassed") + _require_valid_status(item, label, f"release window hold {item_id}") + if not item.get("hold_reason"): + raise ValueError(f"{label}: release window hold {item_id}.hold_reason is required") + + +def _require_live_apply_holds(payload: dict[str, Any], label: str) -> None: + items = payload.get("live_apply_authorization_holds") or [] + _require_count(items, 5, label, "live apply authorization holds") + for item in items: + item_id = item.get("hold_id") + if item.get("hold_mode") != "live_apply_authorization_hold": + raise ValueError(f"{label}: live apply authorization hold {item_id} mode is invalid") + if item.get("release_authorization_granted") is not False or item.get("live_apply_release_enabled") is not False: + raise ValueError(f"{label}: live apply authorization hold {item_id} must stay ungranted and disabled") + _require_valid_status(item, label, f"live apply authorization hold {item_id}") + if not item.get("release_condition"): + raise ValueError(f"{label}: live apply authorization hold {item_id}.release_condition is required") + + +def _require_blocked_transitions(payload: dict[str, Any], label: str) -> None: + items = payload.get("blocked_authorization_transitions") or [] + _require_count(items, 6, label, "blocked authorization 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 authorization 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 authorization transition {item_id} must include blocked action, until, and evidence hash") + if critical_count != 5: + raise ValueError(f"{label}: expected 5 critical authorization 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_authorization_holds": payload.get("release_authorization_holds"), + "rollback_authorization_holds": payload.get("rollback_authorization_holds"), + "release_window_holds": payload.get("release_window_holds"), + "live_apply_authorization_holds": payload.get("live_apply_authorization_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_authorization_hold_count": 5, + "rollback_authorization_hold_count": 5, + "release_window_hold_count": 5, + "live_apply_authorization_hold_count": 5, + "blocked_authorization_transition_count": 6, + "operator_action_count": 5, + "approval_required_authorization_count": 2, + "blocked_authorization_count": 1, + "approval_required_rollback_count": 2, + "blocked_rollback_count": 1, + "approval_required_release_window_count": 2, + "blocked_release_window_count": 1, + "approval_required_live_apply_count": 2, + "blocked_live_apply_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_authorization_hold.py b/apps/api/tests/test_ai_agent_result_capture_release_authorization_hold.py new file mode 100644 index 000000000..df464a6c7 --- /dev/null +++ b/apps/api/tests/test_ai_agent_result_capture_release_authorization_hold.py @@ -0,0 +1,124 @@ +from __future__ import annotations + +import copy +import json +from pathlib import Path + +import pytest + +from src.services.ai_agent_result_capture_release_authorization_hold import ( + load_latest_ai_agent_result_capture_release_authorization_hold, +) + + +def test_load_latest_release_authorization_hold_snapshot() -> None: + snapshot = load_latest_ai_agent_result_capture_release_authorization_hold() + + assert snapshot["schema_version"] == "ai_agent_result_capture_release_authorization_hold_v1" + assert snapshot["program_status"]["current_task_id"] == "P2-134" + assert snapshot["program_status"]["next_task_id"] == "P2-135" + assert snapshot["program_status"]["overall_completion_percent"] == 100 + assert snapshot["program_status"]["runtime_authority"] == "result_capture_release_authorization_hold_only_no_live_write" + + rollups = snapshot["rollups"] + assert rollups["release_authorization_hold_count"] == 5 + assert rollups["rollback_authorization_hold_count"] == 5 + assert rollups["release_window_hold_count"] == 5 + assert rollups["live_apply_authorization_hold_count"] == 5 + assert rollups["blocked_authorization_transition_count"] == 6 + assert rollups["operator_action_count"] == 5 + assert ( + rollups["approval_required_authorization_count"] + + rollups["approval_required_rollback_count"] + + rollups["approval_required_release_window_count"] + + rollups["approval_required_live_apply_count"] + ) == 8 + assert ( + rollups["blocked_authorization_count"] + + rollups["blocked_rollback_count"] + + rollups["blocked_release_window_count"] + + rollups["blocked_live_apply_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["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_authorization_truth_keeps_all_live_paths_closed() -> None: + snapshot = load_latest_ai_agent_result_capture_release_authorization_hold() + truth = snapshot["release_authorization_truth"] + + assert truth["p2_133_final_candidate_loaded"] is True + assert truth["release_authorization_hold_ready"] is True + assert truth["live_apply_authorization_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["final_release_candidate_passed"] is False + assert truth["release_authorization_granted"] is False + assert truth["release_authorization_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_authorization_hold()) + snapshot["release_authorization_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_authorization_hold(tmp_path) + + +def test_rejects_live_apply_release_enabled(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_authorization_hold()) + snapshot["live_apply_authorization_holds"][0]["live_apply_release_enabled"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="must stay ungranted and disabled"): + load_latest_ai_agent_result_capture_release_authorization_hold(tmp_path) + + +def test_rejects_rollup_drift(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_authorization_hold()) + snapshot["rollups"]["release_authorization_hold_count"] = 4 + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="rollups mismatch"): + load_latest_ai_agent_result_capture_release_authorization_hold(tmp_path) + + +def test_rejects_forbidden_display_terms(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_release_authorization_hold()) + 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_authorization_hold(tmp_path) + + +def _write_snapshot(directory: Path, payload: dict) -> None: + path = directory / "ai_agent_result_capture_release_authorization_hold_2099-01-01.json" + path.write_text(json.dumps(payload, ensure_ascii=False), encoding="utf-8") diff --git a/apps/api/tests/test_ai_agent_result_capture_release_authorization_hold_api.py b/apps/api/tests/test_ai_agent_result_capture_release_authorization_hold_api.py new file mode 100644 index 000000000..fad071b80 --- /dev/null +++ b/apps/api/tests/test_ai_agent_result_capture_release_authorization_hold_api.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from fastapi.testclient import TestClient + +from src.main import app + + +def test_release_authorization_hold_endpoint() -> None: + client = TestClient(app) + + response = client.get("/api/v1/agents/agent-result-capture-release-authorization-hold") + + assert response.status_code == 200 + payload = response.json() + assert payload["schema_version"] == "ai_agent_result_capture_release_authorization_hold_v1" + assert payload["program_status"]["current_task_id"] == "P2-134" + assert payload["program_status"]["next_task_id"] == "P2-135" + assert payload["program_status"]["overall_completion_percent"] == 100 + assert payload["program_status"]["runtime_authority"] == "result_capture_release_authorization_hold_only_no_live_write" + assert payload["rollups"]["release_authorization_hold_count"] == 5 + assert payload["rollups"]["rollback_authorization_hold_count"] == 5 + assert payload["rollups"]["release_window_hold_count"] == 5 + assert payload["rollups"]["live_apply_authorization_hold_count"] == 5 + assert payload["rollups"]["blocked_authorization_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"]["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 add257bfa..bb95dc641 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -6045,6 +6045,42 @@ "runtimeWriteAllowed": "runtime write allowed={value}" } }, + "resultCaptureReleaseAuthorizationHold": { + "title": "P2-134 釋出授權保留", + "source": "產生 {generated};目前 {current};下一步 {next}", + "priorTitle": "前一關最終釋出候選讀回", + "truthTitle": "釋出授權保留真相", + "metrics": { + "overall": "完成度", + "authorizationHolds": "授權保留", + "rollbackHolds": "回滾保留", + "releaseWindowHolds": "釋出窗口保留", + "liveApplyHolds": "正式套用保留", + "blockers": "已阻擋授權", + "actions": "操作選項", + "approvalRequired": "需批准", + "blocked": "阻擋", + "liveWrites": "正式寫入 / 發送" + }, + "flags": { + "p2Loaded": "已載入 P2-133={value}", + "authorizationHoldReady": "釋出授權保留已就緒={value}", + "liveApplyHoldActive": "正式套用授權保留啟用={value}" + }, + "labels": { + "ownerReleaseAuthorized": "負責人釋出授權={value}", + "maintenanceApproved": "維護窗口批准={value}", + "candidatePassed": "最終候選通過={value}", + "authorizationGranted": "釋出授權已核發={value}", + "authorizationPassed": "釋出授權通過={value}", + "gatewayWrites": "Gateway 寫入={value}", + "rollbackRequired": "需要回滾 owner={value}", + "rollbackConfirmed": "回滾 owner 已確認={value}", + "rollbackEnabled": "回滾釋出啟用={value}", + "liveApplyReleaseEnabled": "正式套用釋出啟用={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 add257bfa..bb95dc641 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -6045,6 +6045,42 @@ "runtimeWriteAllowed": "runtime write allowed={value}" } }, + "resultCaptureReleaseAuthorizationHold": { + "title": "P2-134 釋出授權保留", + "source": "產生 {generated};目前 {current};下一步 {next}", + "priorTitle": "前一關最終釋出候選讀回", + "truthTitle": "釋出授權保留真相", + "metrics": { + "overall": "完成度", + "authorizationHolds": "授權保留", + "rollbackHolds": "回滾保留", + "releaseWindowHolds": "釋出窗口保留", + "liveApplyHolds": "正式套用保留", + "blockers": "已阻擋授權", + "actions": "操作選項", + "approvalRequired": "需批准", + "blocked": "阻擋", + "liveWrites": "正式寫入 / 發送" + }, + "flags": { + "p2Loaded": "已載入 P2-133={value}", + "authorizationHoldReady": "釋出授權保留已就緒={value}", + "liveApplyHoldActive": "正式套用授權保留啟用={value}" + }, + "labels": { + "ownerReleaseAuthorized": "負責人釋出授權={value}", + "maintenanceApproved": "維護窗口批准={value}", + "candidatePassed": "最終候選通過={value}", + "authorizationGranted": "釋出授權已核發={value}", + "authorizationPassed": "釋出授權通過={value}", + "gatewayWrites": "Gateway 寫入={value}", + "rollbackRequired": "需要回滾 owner={value}", + "rollbackConfirmed": "回滾 owner 已確認={value}", + "rollbackEnabled": "回滾釋出啟用={value}", + "liveApplyReleaseEnabled": "正式套用釋出啟用={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 3e86788b1..e46cfb47c 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 @@ -81,6 +81,7 @@ import { type AiAgentResultCaptureOwnerReleaseApprovalGateSnapshot, type AiAgentResultCapturePostReleaseVerifierRollbackGateSnapshot, type AiAgentResultCaptureFinalReleaseCandidateReadbackSnapshot, + type AiAgentResultCaptureReleaseAuthorizationHoldSnapshot, type AiAgentResultCaptureWriterImplementationReviewSnapshot, type AiAgentOwnerApprovedFixturePromotionGateSnapshot, type AiAgentRuntimeWorkerShadowGateSnapshot, @@ -485,6 +486,7 @@ export function AutomationInventoryTab() { const [resultCaptureOwnerReleaseApprovalGate, setResultCaptureOwnerReleaseApprovalGate] = useState(null) const [resultCapturePostReleaseVerifierRollbackGate, setResultCapturePostReleaseVerifierRollbackGate] = useState(null) const [resultCaptureFinalReleaseCandidateReadback, setResultCaptureFinalReleaseCandidateReadback] = useState(null) + const [resultCaptureReleaseAuthorizationHold, setResultCaptureReleaseAuthorizationHold] = useState(null) const [reportTruthActionabilityReview, setReportTruthActionabilityReview] = useState(null) const [ownerDryRunPackage, setOwnerDryRunPackage] = useState(null) const [hostStatefulInventory, setHostStatefulInventory] = useState(null) @@ -555,6 +557,7 @@ export function AutomationInventoryTab() { apiClient.getAiAgentResultCaptureOwnerReleaseApprovalGate(), apiClient.getAiAgentResultCapturePostReleaseVerifierRollbackGate(), apiClient.getAiAgentResultCaptureFinalReleaseCandidateReadback(), + apiClient.getAiAgentResultCaptureReleaseAuthorizationHold(), apiClient.getAiAgentReportTruthActionabilityReview(), apiClient.getAiAgentOwnerApprovedFixtureDryRun(), apiClient.getAiAgentHostStatefulVersionInventory(), @@ -624,6 +627,7 @@ export function AutomationInventoryTab() { resultCaptureOwnerReleaseApprovalGateResult, resultCapturePostReleaseVerifierRollbackGateResult, resultCaptureFinalReleaseCandidateReadbackResult, + resultCaptureReleaseAuthorizationHoldResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -690,6 +694,7 @@ export function AutomationInventoryTab() { setResultCaptureOwnerReleaseApprovalGate(resultCaptureOwnerReleaseApprovalGateResult.status === 'fulfilled' ? resultCaptureOwnerReleaseApprovalGateResult.value : null) setResultCapturePostReleaseVerifierRollbackGate(resultCapturePostReleaseVerifierRollbackGateResult.status === 'fulfilled' ? resultCapturePostReleaseVerifierRollbackGateResult.value : null) setResultCaptureFinalReleaseCandidateReadback(resultCaptureFinalReleaseCandidateReadbackResult.status === 'fulfilled' ? resultCaptureFinalReleaseCandidateReadbackResult.value : null) + setResultCaptureReleaseAuthorizationHold(resultCaptureReleaseAuthorizationHoldResult.status === 'fulfilled' ? resultCaptureReleaseAuthorizationHoldResult.value : null) setReportTruthActionabilityReview(reportTruthActionabilityReviewResult.status === 'fulfilled' ? reportTruthActionabilityReviewResult.value : null) setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null) setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null) @@ -754,6 +759,7 @@ export function AutomationInventoryTab() { resultCaptureOwnerReleaseApprovalGateResult, resultCapturePostReleaseVerifierRollbackGateResult, resultCaptureFinalReleaseCandidateReadbackResult, + resultCaptureReleaseAuthorizationHoldResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -1996,7 +2002,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 || !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 || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { return (
@@ -3142,6 +3148,40 @@ export function AutomationInventoryTab() { + resultCaptureFinalReleaseCandidateReadback.rollups.playbook_trust_write_count + resultCaptureFinalReleaseCandidateReadback.rollups.production_write_count ) + const resultCaptureReleaseAuthorizationOverall = resultCaptureReleaseAuthorizationHold.program_status.overall_completion_percent + const resultCaptureReleaseAuthorizationHolds = resultCaptureReleaseAuthorizationHold.rollups.release_authorization_hold_count + const resultCaptureReleaseAuthorizationRollbackHolds = resultCaptureReleaseAuthorizationHold.rollups.rollback_authorization_hold_count + const resultCaptureReleaseAuthorizationWindowHolds = resultCaptureReleaseAuthorizationHold.rollups.release_window_hold_count + const resultCaptureReleaseAuthorizationLiveApplyHolds = resultCaptureReleaseAuthorizationHold.rollups.live_apply_authorization_hold_count + const resultCaptureReleaseAuthorizationBlockedTransitions = resultCaptureReleaseAuthorizationHold.rollups.blocked_authorization_transition_count + const resultCaptureReleaseAuthorizationActions = resultCaptureReleaseAuthorizationHold.rollups.operator_action_count + const resultCaptureReleaseAuthorizationApprovalRequired = ( + resultCaptureReleaseAuthorizationHold.rollups.approval_required_authorization_count + + resultCaptureReleaseAuthorizationHold.rollups.approval_required_rollback_count + + resultCaptureReleaseAuthorizationHold.rollups.approval_required_release_window_count + + resultCaptureReleaseAuthorizationHold.rollups.approval_required_live_apply_count + ) + const resultCaptureReleaseAuthorizationBlocked = ( + resultCaptureReleaseAuthorizationHold.rollups.blocked_authorization_count + + resultCaptureReleaseAuthorizationHold.rollups.blocked_rollback_count + + resultCaptureReleaseAuthorizationHold.rollups.blocked_release_window_count + + resultCaptureReleaseAuthorizationHold.rollups.blocked_live_apply_count + + resultCaptureReleaseAuthorizationHold.rollups.critical_blocker_count + ) + const resultCaptureReleaseAuthorizationLiveWrites = ( + resultCaptureReleaseAuthorizationHold.rollups.writer_apply_count + + resultCaptureReleaseAuthorizationHold.rollups.execution_apply_count + + resultCaptureReleaseAuthorizationHold.rollups.receipt_write_count + + resultCaptureReleaseAuthorizationHold.rollups.reviewer_queue_write_count + + resultCaptureReleaseAuthorizationHold.rollups.gateway_queue_write_count + + resultCaptureReleaseAuthorizationHold.rollups.telegram_send_count + + resultCaptureReleaseAuthorizationHold.rollups.bot_api_call_count + + resultCaptureReleaseAuthorizationHold.rollups.report_receipt_write_count + + resultCaptureReleaseAuthorizationHold.rollups.result_capture_write_count + + resultCaptureReleaseAuthorizationHold.rollups.learning_write_count + + resultCaptureReleaseAuthorizationHold.rollups.playbook_trust_write_count + + resultCaptureReleaseAuthorizationHold.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 @@ -7853,6 +7893,169 @@ export function AutomationInventoryTab() {
+
+
+
+ + + {t('resultCaptureReleaseAuthorizationHold.title')} + +
+ +
+ +
+ } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> +
+ +
+
+ {t('resultCaptureReleaseAuthorizationHold.priorTitle')} +

+ {resultCaptureReleaseAuthorizationHold.prior_final_release_candidate_readback.readiness_note} +

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

+ {resultCaptureReleaseAuthorizationHold.release_authorization_truth.truth_note} +

+
+ + + + + + +
+
+
+ +
+ {resultCaptureReleaseAuthorizationHold.release_authorization_holds.slice(0, 5).map(hold => ( +
+
+ + {hold.display_name} + + +
+ + {hold.authorization_summary} + +
+ + + + + +
+
+ ))} +
+ +
+ {resultCaptureReleaseAuthorizationHold.rollback_authorization_holds.slice(0, 5).map(hold => ( +
+
+ + {hold.display_name} + + +
+ + {hold.rollback_summary} + +
+ + + +
+
+ ))} +
+ +
+ {resultCaptureReleaseAuthorizationHold.release_window_holds.slice(0, 5).map(hold => ( +
+
+ + {hold.display_name} + + +
+ + {hold.hold_reason} + +
+ + + +
+
+ ))} +
+ +
+ {resultCaptureReleaseAuthorizationHold.live_apply_authorization_holds.slice(0, 5).map(hold => ( +
+
+ + {hold.display_name} + + +
+ + {hold.release_condition} + + +
+ ))} +
+ +
+ {resultCaptureReleaseAuthorizationHold.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 0e50ae959..8282eccb8 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -565,6 +565,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentResultCaptureReleaseAuthorizationHold() { + const res = await fetch(`${API_BASE_URL}/agents/agent-result-capture-release-authorization-hold`) + return handleResponse(res) + }, + async getAiAgentOwnerApprovedFixtureDryRun() { const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-fixture-dry-run`) return handleResponse(res) @@ -8172,6 +8177,227 @@ export interface AiAgentResultCaptureFinalReleaseCandidateReadbackSnapshot { } } +export interface AiAgentResultCaptureReleaseAuthorizationHoldSnapshot { + schema_version: 'ai_agent_result_capture_release_authorization_hold_v1' + generated_at: string + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: 'P2-134' + next_task_id: 'P2-135' + read_only_mode: true + runtime_authority: 'result_capture_release_authorization_hold_only_no_live_write' + status_note: string + } + source_refs: string[] + prior_final_release_candidate_readback: { + schema_version: string + final_release_candidate_readback_count: number + rollback_candidate_readback_count: number + candidate_acceptance_hold_count: number + live_apply_candidate_hold_count: number + blocked_final_candidate_transition_count: number + operator_action_count: number + approval_required_total: number + blocked_total: 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 + 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_authorization_truth: { + p2_133_final_candidate_loaded: boolean + release_authorization_hold_ready: boolean + rollback_authorization_hold_ready: boolean + release_window_hold_active: boolean + live_apply_authorization_hold_active: boolean + owner_authorization_review_still_required: boolean + maintenance_window_review_still_required: boolean + rollback_owner_review_required: boolean + redaction_review_required: boolean + release_authorization_hold_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_authorization_holds: Array<{ + hold_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + source_final_candidate: string + status: 'ready_for_owner_review' | 'approval_required' | 'blocked_by_policy' + hold_mode: 'release_authorization_hold' + owner_release_authorized: false + release_authorization_granted: false + release_authorization_passed: false + authorization_summary: string + evidence_hash: string + }> + rollback_authorization_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: 'rollback_authorization_hold' + rollback_owner_required: true + rollback_owner_confirmed: false + rollback_release_enabled: false + rollback_summary: string + }> + release_window_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: 'release_window_hold' + owner_release_authorized: false + maintenance_window_approved: false + final_release_candidate_passed: false + hold_reason: string + }> + live_apply_authorization_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_authorization_hold' + release_authorization_granted: false + live_apply_release_enabled: false + release_condition: string + }> + blocked_authorization_transitions: Array<{ + blocker_id: string + display_name: string + severity: 'high' | 'critical' + status: 'approval_required' | 'blocked_by_policy' + 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_authorization_hold_count: number + rollback_authorization_hold_count: number + release_window_hold_count: number + live_apply_authorization_hold_count: number + blocked_authorization_transition_count: number + operator_action_count: number + approval_required_authorization_count: number + blocked_authorization_count: number + approval_required_rollback_count: number + blocked_rollback_count: number + approval_required_release_window_count: number + blocked_release_window_count: number + approval_required_live_apply_count: number + blocked_live_apply_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 9a51f3475..5bfbbcd4a 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -19,6 +19,33 @@ - 這是 root-cause candidate 修正且已部署,不是完成證明;仍必須等下一次官方 03:00 `km-vectorize` 成功更新 `lastSuccessfulTime`,或失敗時留下 Pod/log 證據再繼續修。 - 不手動建立 Job、不 patch live、不刪 failed Job、不偽造 credential escrow evidence。 +## 2026-06-14|P2-134 釋出授權保留本地完成 + +**背景**:P2-133 已把 final release candidate readback 正式驗證完成;但候選讀回仍不得被誤讀成 owner release authorized、maintenance window approved、rollback owner confirmed、release authorization granted / passed 或 live apply 可執行。P2-134 因此只建立 release authorization hold,把 final release candidate readback、rollback candidate readback、candidate acceptance hold、live-apply candidate hold 與 blocked final candidate transition 轉成釋出授權前的可審核保留狀態,不接受口頭批准、不核發 release authorization、不釋放 live apply、不套用 writer、不寫 receipt、不寫 result capture / learning / PlayBook trust / reviewer queue / Gateway queue,也不送 Telegram 或呼叫 Bot API。 + +**完成內容**: +- 新增 `ai_agent_result_capture_release_authorization_hold_v1` schema、committed snapshot、loader 與 API endpoint `GET /api/v1/agents/agent-result-capture-release-authorization-hold`。 +- P2-134 snapshot 固定 5 個 release authorization hold、5 個 rollback authorization hold、5 個 release window hold、5 個 live-apply authorization hold、6 個 blocked authorization transition 與 5 個 operator action。 +- Governance automation inventory 頁新增 P2-134 區塊,顯示 P2-134 進度 `100%`、授權保留 `5`、回滾保留 `5`、釋出窗口保留 `5`、正式套用保留 `5`、已阻擋授權 `6`、operator action `5`、需批准 `8`、阻擋 `9`、正式寫入 / 發送 `0`。 +- Release authorization truth 固定為 `result_capture_release_authorization_hold_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-134 schema / snapshot、`zh-TW.json`、`en.json` 通過。 +- Python 編譯:P2-134 loader 與 `agents.py` 通過。 +- API/service pytest:P2-134 + P2-133 regression `14 passed`。 +- Web typecheck:`pnpm --filter @awoooi/web typecheck` 通過。 +- Web production build:`NEXT_PUBLIC_API_URL=https://awoooi.wooo.work pnpm --filter @awoooi/web build` 通過;僅既有 Sentry setup / deprecation warning,governance First Load JS `435 kB`。 + +**CD 狀態同步**: +- P2-134 目前仍是本地完成,尚未宣稱正式部署完成。 +- 下一步會先跑 `git diff --check`、文件 secrets sanity、owner-response guard 與 security mirror guard,再推送 Gitea main、等待 CD,最後以正式 API 與 Browser desktop / mobile smoke 驗證。 + +**安全邊界**: +- P2-134 仍是 release authorization hold;不接受口頭批准、不把 authorization hold 解讀成 owner release authorized、不批准維護窗口、不確認 rollback owner、不通過 final candidate、不核發 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、不回傳內部協作內容。 + +**下一步**: +- `P2-135`:必須等 P2-134 正式部署與 production smoke 完成後,才可承接 release authorization hold 後續關卡;仍不得直接開啟 result capture writer、learning writer、PlayBook trust writer、reviewer queue write、Gateway queue write、Telegram send、Bot API call 或 production write。 + ## 2026-06-14|P2-133 Final release candidate readback 完成與正式驗證 **背景**:P2-132 已把 post-release verifier / rollback gate 正式驗證完成;但 verifier gate 仍不得被誤讀成 post-release verifier ready、release verification passed、rollback release passed、live apply release passed 或 final candidate approved。P2-133 因此只建立 final release candidate readback,把 post-release verifier gate、rollback release gate、release verification hold、live-apply post-release gate 與 blocked post-release transition 讀回成 release candidate 可審核狀態,不批准 owner release、不批准 maintenance window、不確認 rollback owner、不通過 final candidate、不釋放 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 4ef5e36e8..845248a26 100644 --- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md +++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md @@ -16,7 +16,15 @@ | AI Agent 主動營運委派與版本生命週期 | 100% | P2-402A / P2-402B / P2-402C / P2-402D / P2-402E / P2-402F / P2-402G 已完成;已建立 repo-only 版本新鮮度快照、工具採用批准包、Telegram action-required digest policy、Gitea PR 草案 lane、host / K3s / stateful 版本只讀盤點、API 與 governance UI。定期排程、外部版本查詢、工具安裝、CI 變更、套件升級、主機更新、container pull、實際 PR creation、auto merge、Telegram 實發、SSH、kubectl、重啟仍未開 gate | `ai_agent_proactive_operations_contract_v1`、`ai_agent_version_freshness_snapshot_v1`、`ai_agent_tool_adoption_approval_package_v1`、`ai_agent_telegram_action_required_digest_policy_v1`、`ai_agent_gitea_pr_draft_lane_v1`、`ai_agent_host_stateful_version_inventory_v1`、`GET /api/v1/agents/agent-proactive-operations-contract`、`GET /api/v1/agents/agent-version-freshness-snapshot`、`GET /api/v1/agents/agent-tool-adoption-approval-package`、`GET /api/v1/agents/agent-telegram-action-required-digest-policy`、`GET /api/v1/agents/agent-gitea-pr-draft-lane`、`GET /api/v1/agents/agent-host-stateful-version-inventory`、`/zh-TW/governance?tab=automation-inventory`、MASTER §3.2.1c | | 本工作清單與分析報告 | 100% | 已完成 | 本 MD 文件 | -AI Agent 自動化工作包目前完成度:**99.7%**。本工作清單文件本身完成度:**100%**。 +### 2026-06-14 08:20 狀態同步 + +- `P2-134` release authorization hold 已本地完成:新增 `ai_agent_result_capture_release_authorization_hold_v1`、`GET /api/v1/agents/agent-result-capture-release-authorization-hold`、治理頁 P2-134 區塊與繁中 UI 文案。 +- 本地證據:P2-134 + P2-133 API/service regression `14 passed`、JSON parse、Python compile、Web typecheck、`NEXT_PUBLIC_API_URL=https://awoooi.wooo.work pnpm --filter @awoooi/web build` 均通過;governance First Load JS `435 kB`。 +- P2-134 固定 5 個 release authorization hold、5 個 rollback authorization hold、5 個 release window hold、5 個 live-apply authorization hold、6 個 blocked authorization transition、5 個 operator action、需批准 `8`、阻擋 `9`、正式寫入 / 發送 `0`。 +- 邊界仍維持:owner release authorized、maintenance window approved、rollback owner confirmed、release authorization granted / 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`。 +- 正式部署、production API readback、desktop / mobile Browser smoke 尚未完成;不得把本地完成解讀成正式環境已運作。下一步為推 Gitea main、等待 CD、正式驗證後才可交給 `P2-135`。 + +AI Agent 自動化工作包目前完成度:**99.8%**。本工作清單文件本身完成度:**100%**。 三 Agent 佈建布局目前完成度:**45%**。第一波已完成只讀 schema / snapshot / API / 測試 / 報告,第二波已接入治理頁自動化盤點 UI;正式 runtime 佈署、Telegram E2E 發送與 AgentSession 工作流仍需逐項 gate。 diff --git a/docs/evaluations/ai_agent_result_capture_release_authorization_hold_2026-06-14.json b/docs/evaluations/ai_agent_result_capture_release_authorization_hold_2026-06-14.json new file mode 100644 index 000000000..e31de3e89 --- /dev/null +++ b/docs/evaluations/ai_agent_result_capture_release_authorization_hold_2026-06-14.json @@ -0,0 +1,489 @@ +{ + "schema_version": "ai_agent_result_capture_release_authorization_hold_v1", + "generated_at": "2026-06-14T08:07:02+08:00", + "program_status": { + "overall_completion_percent": 100, + "current_priority": "P2", + "current_task_id": "P2-134", + "next_task_id": "P2-135", + "read_only_mode": true, + "runtime_authority": "result_capture_release_authorization_hold_only_no_live_write", + "status_note": "P2-134 只把 P2-133 final release candidate readback 轉成 release authorization hold;不得視為 owner release authorized,不得批准 maintenance window,不得確認 rollback owner,不得通過 release authorization,不得釋放 live apply、套用 writer、寫 receipt、寫 result capture、寫 learning、寫 queue、送 Telegram 或寫入 production。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_result_capture_final_release_candidate_readback_2026-06-14.json", + "docs/evaluations/ai_agent_result_capture_post_release_verifier_rollback_gate_2026-06-14.json", + "docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md#54-建立-final-release-candidate-readback" + ], + "prior_final_release_candidate_readback": { + "schema_version": "ai_agent_result_capture_final_release_candidate_readback_v1", + "final_release_candidate_readback_count": 5, + "rollback_candidate_readback_count": 5, + "candidate_acceptance_hold_count": 5, + "live_apply_candidate_hold_count": 5, + "blocked_final_candidate_transition_count": 6, + "operator_action_count": 5, + "approval_required_total": 8, + "blocked_total": 9, + "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, + "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-133 已把 final release candidate readback、rollback candidate readback、candidate acceptance hold、live-apply candidate hold 與 blocked final candidate transition 固定為只讀 candidate evidence;P2-134 只建立 release authorization hold,不視為 owner release authorized。" + }, + "release_authorization_truth": { + "p2_133_final_candidate_loaded": true, + "release_authorization_hold_ready": true, + "rollback_authorization_hold_ready": true, + "release_window_hold_active": true, + "live_apply_authorization_hold_active": true, + "owner_authorization_review_still_required": true, + "maintenance_window_review_still_required": true, + "rollback_owner_review_required": true, + "redaction_review_required": true, + "release_authorization_hold_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-134 只建立 release authorization hold;authorization hold 可見不代表 owner release authorized、maintenance window approved、rollback owner confirmed、release authorization passed 或 live apply 可執行。" + }, + "release_authorization_holds": [ + { + "hold_id": "release_authorization_result_capture", + "display_name": "Result capture release authorization hold", + "owner_agent": "openclaw", + "source_final_candidate": "final_candidate_result_capture", + "status": "ready_for_owner_review", + "hold_mode": "release_authorization_hold", + "owner_release_authorized": false, + "release_authorization_granted": false, + "release_authorization_passed": false, + "authorization_summary": "建立 Result capture release authorization hold;未取得 owner authorization、maintenance window、rollback owner 與 final verifier pass 前不得寫入或發送。", + "evidence_hash": "sha256:7111111111111111111111111111111111111111111111111111111111111111" + }, + { + "hold_id": "release_authorization_learning", + "display_name": "Learning release authorization hold", + "owner_agent": "hermes", + "source_final_candidate": "final_candidate_learning", + "status": "approval_required", + "hold_mode": "release_authorization_hold", + "owner_release_authorized": false, + "release_authorization_granted": false, + "release_authorization_passed": false, + "authorization_summary": "建立 Learning release authorization hold;未取得 owner authorization、maintenance window、rollback owner 與 final verifier pass 前不得寫入或發送。", + "evidence_hash": "sha256:7222222222222222222222222222222222222222222222222222222222222222" + }, + { + "hold_id": "release_authorization_playbook_trust", + "display_name": "PlayBook trust release authorization hold", + "owner_agent": "nemotron", + "source_final_candidate": "final_candidate_playbook_trust", + "status": "blocked_by_policy", + "hold_mode": "release_authorization_hold", + "owner_release_authorized": false, + "release_authorization_granted": false, + "release_authorization_passed": false, + "authorization_summary": "建立 PlayBook trust release authorization hold;未取得 owner authorization、maintenance window、rollback owner 與 final verifier pass 前不得寫入或發送。", + "evidence_hash": "sha256:7333333333333333333333333333333333333333333333333333333333333333" + }, + { + "hold_id": "release_authorization_reviewer_queue", + "display_name": "Reviewer queue release authorization hold", + "owner_agent": "openclaw", + "source_final_candidate": "final_candidate_reviewer_queue", + "status": "approval_required", + "hold_mode": "release_authorization_hold", + "owner_release_authorized": false, + "release_authorization_granted": false, + "release_authorization_passed": false, + "authorization_summary": "建立 Reviewer queue release authorization hold;未取得 owner authorization、maintenance window、rollback owner 與 final verifier pass 前不得寫入或發送。", + "evidence_hash": "sha256:7444444444444444444444444444444444444444444444444444444444444444" + }, + { + "hold_id": "release_authorization_gateway_queue", + "display_name": "Gateway queue release authorization hold", + "owner_agent": "nemotron", + "source_final_candidate": "final_candidate_gateway_queue", + "status": "ready_for_owner_review", + "hold_mode": "release_authorization_hold", + "owner_release_authorized": false, + "release_authorization_granted": false, + "release_authorization_passed": false, + "authorization_summary": "建立 Gateway queue release authorization hold;未取得 owner authorization、maintenance window、rollback owner 與 final verifier pass 前不得寫入或發送。", + "evidence_hash": "sha256:7555555555555555555555555555555555555555555555555555555555555555" + } + ], + "rollback_authorization_holds": [ + { + "hold_id": "rollback_authorization_result_capture", + "display_name": "Result capture rollback authorization hold", + "owner_agent": "openclaw", + "status": "ready_for_owner_review", + "hold_mode": "rollback_authorization_hold", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_enabled": false, + "rollback_summary": "建立 Result capture rollback authorization hold;rollback owner 未 confirmed 前不得釋放 rollback release。" + }, + { + "hold_id": "rollback_authorization_learning", + "display_name": "Learning rollback authorization hold", + "owner_agent": "hermes", + "status": "approval_required", + "hold_mode": "rollback_authorization_hold", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_enabled": false, + "rollback_summary": "建立 Learning rollback authorization hold;rollback owner 未 confirmed 前不得釋放 rollback release。" + }, + { + "hold_id": "rollback_authorization_playbook_trust", + "display_name": "PlayBook trust rollback authorization hold", + "owner_agent": "nemotron", + "status": "blocked_by_policy", + "hold_mode": "rollback_authorization_hold", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_enabled": false, + "rollback_summary": "建立 PlayBook trust rollback authorization hold;rollback owner 未 confirmed 前不得釋放 rollback release。" + }, + { + "hold_id": "rollback_authorization_reviewer_queue", + "display_name": "Reviewer queue rollback authorization hold", + "owner_agent": "openclaw", + "status": "approval_required", + "hold_mode": "rollback_authorization_hold", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_enabled": false, + "rollback_summary": "建立 Reviewer queue rollback authorization hold;rollback owner 未 confirmed 前不得釋放 rollback release。" + }, + { + "hold_id": "rollback_authorization_gateway_queue", + "display_name": "Gateway queue rollback authorization hold", + "owner_agent": "nemotron", + "status": "ready_for_owner_review", + "hold_mode": "rollback_authorization_hold", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_enabled": false, + "rollback_summary": "建立 Gateway queue rollback authorization hold;rollback owner 未 confirmed 前不得釋放 rollback release。" + } + ], + "release_window_holds": [ + { + "hold_id": "release_window_result_capture", + "display_name": "Result capture release window hold", + "owner_agent": "openclaw", + "status": "ready_for_owner_review", + "hold_mode": "release_window_hold", + "owner_release_authorized": false, + "maintenance_window_approved": false, + "final_release_candidate_passed": false, + "hold_reason": "Result capture release window 仍需 owner authorization、maintenance window 與 final candidate pass;不得把 P2-134 hold 當成正式批准。" + }, + { + "hold_id": "release_window_learning", + "display_name": "Learning release window hold", + "owner_agent": "hermes", + "status": "approval_required", + "hold_mode": "release_window_hold", + "owner_release_authorized": false, + "maintenance_window_approved": false, + "final_release_candidate_passed": false, + "hold_reason": "Learning release window 仍需 owner authorization、maintenance window 與 final candidate pass;不得把 P2-134 hold 當成正式批准。" + }, + { + "hold_id": "release_window_playbook_trust", + "display_name": "PlayBook trust release window hold", + "owner_agent": "nemotron", + "status": "blocked_by_policy", + "hold_mode": "release_window_hold", + "owner_release_authorized": false, + "maintenance_window_approved": false, + "final_release_candidate_passed": false, + "hold_reason": "PlayBook trust release window 仍需 owner authorization、maintenance window 與 final candidate pass;不得把 P2-134 hold 當成正式批准。" + }, + { + "hold_id": "release_window_reviewer_queue", + "display_name": "Reviewer queue release window hold", + "owner_agent": "openclaw", + "status": "approval_required", + "hold_mode": "release_window_hold", + "owner_release_authorized": false, + "maintenance_window_approved": false, + "final_release_candidate_passed": false, + "hold_reason": "Reviewer queue release window 仍需 owner authorization、maintenance window 與 final candidate pass;不得把 P2-134 hold 當成正式批准。" + }, + { + "hold_id": "release_window_gateway_queue", + "display_name": "Gateway queue release window hold", + "owner_agent": "nemotron", + "status": "ready_for_owner_review", + "hold_mode": "release_window_hold", + "owner_release_authorized": false, + "maintenance_window_approved": false, + "final_release_candidate_passed": false, + "hold_reason": "Gateway queue release window 仍需 owner authorization、maintenance window 與 final candidate pass;不得把 P2-134 hold 當成正式批准。" + } + ], + "live_apply_authorization_holds": [ + { + "hold_id": "live_apply_authorization_result_capture", + "display_name": "Result capture live apply authorization hold", + "owner_agent": "openclaw", + "status": "ready_for_owner_review", + "hold_mode": "live_apply_authorization_hold", + "release_authorization_granted": false, + "live_apply_release_enabled": false, + "release_condition": "P2-135 才能承接 authorization readback;P2-134 不得直接 writer apply 或 live apply。" + }, + { + "hold_id": "live_apply_authorization_learning", + "display_name": "Learning live apply authorization hold", + "owner_agent": "hermes", + "status": "approval_required", + "hold_mode": "live_apply_authorization_hold", + "release_authorization_granted": false, + "live_apply_release_enabled": false, + "release_condition": "P2-135 才能承接 authorization readback;P2-134 不得直接 writer apply 或 live apply。" + }, + { + "hold_id": "live_apply_authorization_playbook_trust", + "display_name": "PlayBook trust live apply authorization hold", + "owner_agent": "nemotron", + "status": "blocked_by_policy", + "hold_mode": "live_apply_authorization_hold", + "release_authorization_granted": false, + "live_apply_release_enabled": false, + "release_condition": "P2-135 才能承接 authorization readback;P2-134 不得直接 writer apply 或 live apply。" + }, + { + "hold_id": "live_apply_authorization_reviewer_queue", + "display_name": "Reviewer queue live apply authorization hold", + "owner_agent": "openclaw", + "status": "approval_required", + "hold_mode": "live_apply_authorization_hold", + "release_authorization_granted": false, + "live_apply_release_enabled": false, + "release_condition": "P2-135 才能承接 authorization readback;P2-134 不得直接 writer apply 或 live apply。" + }, + { + "hold_id": "live_apply_authorization_gateway_queue", + "display_name": "Gateway queue live apply authorization hold", + "owner_agent": "nemotron", + "status": "ready_for_owner_review", + "hold_mode": "live_apply_authorization_hold", + "release_authorization_granted": false, + "live_apply_release_enabled": false, + "release_condition": "P2-135 才能承接 authorization readback;P2-134 不得直接 writer apply 或 live apply。" + } + ], + "blocked_authorization_transitions": [ + { + "blocker_id": "blocked_writer_apply_release_authorization", + "display_name": "Writer apply release authorization blocked", + "severity": "critical", + "status": "blocked_by_policy", + "blocked_action": "writer_apply_after_release_authorization_hold", + "blocked_until": "owner authorization + maintenance window + release authorization pass + rollback owner confirmed", + "evidence_hash": "sha256:61a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1" + }, + { + "blocker_id": "blocked_execution_apply_release_authorization", + "display_name": "Execution apply release authorization blocked", + "severity": "critical", + "status": "blocked_by_policy", + "blocked_action": "execution_apply_after_release_authorization_hold", + "blocked_until": "maintenance window approved + rollback release gate passed", + "evidence_hash": "sha256:62b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2" + }, + { + "blocker_id": "blocked_receipt_write_release_authorization", + "display_name": "Receipt write release authorization blocked", + "severity": "critical", + "status": "approval_required", + "blocked_action": "receipt_write_after_release_authorization_hold", + "blocked_until": "release authorization granted + release verification passed", + "evidence_hash": "sha256:63c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3" + }, + { + "blocker_id": "blocked_result_capture_write_release_authorization", + "display_name": "Result capture write release authorization blocked", + "severity": "critical", + "status": "blocked_by_policy", + "blocked_action": "result_capture_write_after_release_authorization_hold", + "blocked_until": "owner authorization + release authorization pass + rollback owner confirmed", + "evidence_hash": "sha256:64d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4" + }, + { + "blocker_id": "blocked_gateway_queue_write_release_authorization", + "display_name": "Gateway queue write release authorization blocked", + "severity": "critical", + "status": "approval_required", + "blocked_action": "gateway_queue_write_after_release_authorization_hold", + "blocked_until": "SRE route lock + Telegram receipt owner approved + release authorization pass", + "evidence_hash": "sha256:65e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5" + }, + { + "blocker_id": "blocked_telegram_send_release_authorization", + "display_name": "Telegram send release authorization blocked", + "severity": "high", + "status": "approval_required", + "blocked_action": "telegram_send_after_release_authorization_hold", + "blocked_until": "Gateway queue release approved + Telegram receipt E2E approved + rollback release gate passed", + "evidence_hash": "sha256:66f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6" + } + ], + "operator_actions": [ + { + "action_id": "review_release_authorization_holds", + "owner_agent": "openclaw", + "status": "ready_for_operator_review", + "operator_instruction": "審核 5 個 release authorization hold 是否仍符合 P2-133 final candidate 邊界;不要把 hold 視為正式授權。", + "runtime_write_allowed": false + }, + { + "action_id": "verify_rollback_authorization_hold", + "owner_agent": "hermes", + "status": "ready_for_operator_review", + "operator_instruction": "確認 rollback authorization hold 仍全部未 confirmed,且 rollback_release_enabled 仍為 false。", + "runtime_write_allowed": false + }, + { + "action_id": "verify_release_window_hold", + "owner_agent": "nemotron", + "status": "ready_for_operator_review", + "operator_instruction": "確認 release window hold 已讀回但仍 hold;不可批准 maintenance window 或套用 writer。", + "runtime_write_allowed": false + }, + { + "action_id": "verify_live_apply_authorization_hold", + "owner_agent": "openclaw", + "status": "approval_required", + "operator_instruction": "確認 live apply authorization hold 仍未 enabled,且 release_authorization_granted 仍為 false。", + "runtime_write_allowed": false + }, + { + "action_id": "prepare_p2_135_release_authorization_readback", + "owner_agent": "hermes", + "status": "ready_for_operator_review", + "operator_instruction": "下一關只可建立 P2-135 release authorization readback,不可放行 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 authorization hold 摘要、狀態、數量、redacted hash 與 0 / false 邊界;不得顯示內部協作環境對話、原始提示詞、私有推理、secret、授權標頭或原始 runtime payload。" + }, + "rollups": { + "release_authorization_hold_count": 5, + "rollback_authorization_hold_count": 5, + "release_window_hold_count": 5, + "live_apply_authorization_hold_count": 5, + "blocked_authorization_transition_count": 6, + "operator_action_count": 5, + "approval_required_authorization_count": 2, + "blocked_authorization_count": 1, + "approval_required_rollback_count": 2, + "blocked_rollback_count": 1, + "approval_required_release_window_count": 2, + "blocked_release_window_count": 1, + "approval_required_live_apply_count": 2, + "blocked_live_apply_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_authorization_hold_v1.schema.json b/docs/schemas/ai_agent_result_capture_release_authorization_hold_v1.schema.json new file mode 100644 index 000000000..ad6893472 --- /dev/null +++ b/docs/schemas/ai_agent_result_capture_release_authorization_hold_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_authorization_hold_v1.schema.json", + "title": "AI Agent result capture release authorization hold v1", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "program_status", + "source_refs", + "prior_final_release_candidate_readback", + "release_authorization_truth", + "release_authorization_holds", + "rollback_authorization_holds", + "release_window_holds", + "live_apply_authorization_holds", + "blocked_authorization_transitions", + "operator_actions", + "display_redaction_contract", + "rollups" + ], + "properties": { + "schema_version": { + "const": "ai_agent_result_capture_release_authorization_hold_v1" + }, + "generated_at": { + "type": "string" + }, + "program_status": { + "type": "object" + }, + "source_refs": { + "type": "array", + "items": { + "type": "string" + } + }, + "prior_final_release_candidate_readback": { + "type": "object" + }, + "release_authorization_truth": { + "type": "object" + }, + "release_authorization_holds": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "rollback_authorization_holds": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "release_window_holds": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "live_apply_authorization_holds": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "blocked_authorization_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 a9098e64e..6c6dd9762 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 @@ -671,6 +671,7 @@ Alert / Sentry / SigNoz / Gitea / Market Watch / Operator | `docs/evaluations/ai_agent_result_capture_owner_release_approval_gate_2026-06-14.json` + `GET /api/v1/agents/agent-result-capture-owner-release-approval-gate` | P2-131 owner release approval gate;承接 P2-130 owner-approved release readiness readback,建立 5 個 owner release approval packet、5 個 maintenance window approval gate、5 個 live-apply release approval gate、5 個 rollback owner approval check、6 個 blocked approval transition 與 5 個 operator action;runtime authority 固定 `result_capture_owner_release_approval_gate_only_no_live_write`;owner release approved、maintenance window approved、rollback owner confirmed、post-release verifier ready、release approval passed、live apply release passed、writer apply、execution apply、receipt write、canonical runtime target read、live query、reviewer queue write、Gateway queue write、Telegram send、Bot API call、report receipt write、result capture write、learning write、PlayBook trust write、production write、secret read 與 destructive action 全部 `0 / false`;deploy marker `03617db7` 已正式驗證,已由 P2-132 承接 | | `docs/evaluations/ai_agent_result_capture_post_release_verifier_rollback_gate_2026-06-14.json` + `GET /api/v1/agents/agent-result-capture-post-release-verifier-rollback-gate` | P2-132 post-release verifier / rollback gate;承接 P2-131 owner release approval gate,建立 5 個 post-release verifier gate、5 個 rollback release gate、5 個 release verification hold、5 個 live-apply post-release gate、6 個 blocked post-release transition 與 5 個 operator action;runtime authority 固定 `result_capture_post_release_verifier_rollback_gate_only_no_live_write`;owner release approved、maintenance window approved、rollback owner confirmed、post-release verifier ready、release verification passed、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、canonical runtime target read、live query、reviewer queue write、Gateway queue write、Telegram send、Bot API call、report receipt write、result capture write、learning write、PlayBook trust write、production write、secret read 與 destructive action 全部 `0 / false`;deploy marker `934af770` 已正式驗證,下一步 P2-133 | | `docs/evaluations/ai_agent_result_capture_final_release_candidate_readback_2026-06-14.json` + `GET /api/v1/agents/agent-result-capture-final-release-candidate-readback` | P2-133 final release candidate readback;承接 P2-132 post-release verifier / rollback gate,建立 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、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、canonical runtime target read、live query、reviewer queue write、Gateway queue write、Telegram send、Bot API call、report receipt write、result capture write、learning write、PlayBook trust write、production write、secret read 與 destructive action 全部 `0 / false`;deploy marker `8be5ddab` 已正式驗證,下一步 P2-134 | +| `docs/evaluations/ai_agent_result_capture_release_authorization_hold_2026-06-14.json` + `GET /api/v1/agents/agent-result-capture-release-authorization-hold` | P2-134 release authorization hold;承接 P2-133 final release candidate readback,建立 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、owner release approved、maintenance window approved、rollback owner confirmed、post-release verifier ready、final release candidate approved、final release candidate passed、release authorization granted、release authorization passed、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、canonical runtime target read、live query、reviewer queue write、Gateway queue write、Telegram send、Bot API call、report receipt write、result capture write、learning write、PlayBook trust write、production write、secret read 與 destructive action 全部 `0 / false`;本地 API / typecheck / build 已通過,正式部署與 Browser smoke 待推進 | | `docs/evaluations/ai_agent_live_read_model_gate_2026-06-11.json` + `GET /api/v1/agents/agent-live-read-model-gate` | P2-403B AgentSession / Redis Streams live read model gate;定義 safe fields、Redis envelope、worker gate、rollback plan 與 no-write smoke,不連 DB、不讀寫 Redis、不啟動 worker | #### 3.2.1c 2026-06-11 AI Agent 主動營運委派與版本生命週期契約 @@ -792,7 +793,8 @@ Repo / registry / release notes / K8s / host / observability / backup evidence 51. 建立 owner-approved release readiness readback。✅ P2-130 已完成並正式驗證;release readiness readback `5`、owner release readiness check `5`、live-apply readiness gate `5`、rollback readiness check `5`、blocked readiness transition `6`、operator action `5`,approval-required readback / owner check / readiness gate / rollback `2 / 2 / 2 / 2`、blocked readback / owner check / readiness gate / rollback `1 / 1 / 1 / 1`、critical blocker `5`;owner release approved、maintenance window approved、rollback owner confirmed、post-release verifier ready、release readiness 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 `6fcf7241`、正式 API readback 與 desktop / mobile smoke 已完成。已由 P2-131 承接。 52. 建立 owner release approval gate。✅ P2-131 已完成並正式驗證;owner release approval packet `5`、maintenance window approval gate `5`、live-apply release approval gate `5`、rollback owner approval check `5`、blocked approval transition `6`、operator action `5`,approval-required packet / maintenance / live apply / rollback `2 / 2 / 2 / 2`、blocked packet / maintenance / live apply / rollback `1 / 1 / 1 / 1`、critical blocker `5`;owner release approved、maintenance window approved、rollback owner confirmed、post-release verifier ready、release approval 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 `03617db7`、正式 API readback 與 desktop / mobile smoke 已完成。已由 P2-132 承接。 53. 建立 post-release verifier / rollback gate。✅ P2-132 已完成並正式驗證;post-release verifier gate `5`、rollback release gate `5`、release verification hold `5`、live-apply post-release gate `5`、blocked post-release transition `6`、operator action `5`,approval-required verifier / rollback / verification / live apply `2 / 2 / 2 / 2`、blocked verifier / rollback / verification / live apply `1 / 1 / 1 / 1`、critical blocker `5`;owner release approved、maintenance window approved、rollback owner confirmed、post-release verifier ready、release verification 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 `934af770`、正式 API readback 與 desktop / mobile smoke 已完成。已由 P2-133 承接。 -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。 +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 本地完成,正式部署與 production smoke 待推進;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`;本地 API/service regression `14 passed`、Web typecheck / production build 通過。正式驗證後才可由 P2-135 承接。 #### 3.2.1d 2026-06-11 Agent 互動、學習與成長證據面 @@ -1879,6 +1881,15 @@ Phase 6 完成後 --- +### 2026-06-14 08:20 (台北) — §3.2 / §5 — 本地完成 P2-134 release authorization hold — 把 final candidate 讀回轉成釋出授權保留 + +- 新增 `ai_agent_result_capture_release_authorization_hold_v1` schema / committed snapshot / loader / API / 測試,承接 P2-133 final release candidate readback,定義 5 個 release authorization hold、5 個 rollback authorization hold、5 個 release window hold、5 個 live-apply authorization hold、6 個 blocked authorization transition 與 5 個 operator action。 +- Governance automation inventory 新增 P2-134 區塊,將授權保留、回滾保留、釋出窗口保留、正式套用保留、blocked authorization transition 與 operator action 全部以只讀方式呈現,並維持前端 redaction,不回傳內部協作內容。 +- 本地 API/service regression:P2-134 + P2-133 `14 passed`;JSON parse、Python 3.11 py_compile、`pnpm --filter @awoooi/web typecheck` 與 production build 通過。 +- 本地 snapshot 固定 `schema_version=ai_agent_result_capture_release_authorization_hold_v1`、current `P2-134`、next `P2-135`、completion `100`;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`。 +- 本地 0 / 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、execution apply、receipt write、reviewer queue write、Gateway queue write、Telegram send、Bot API call、report receipt write、result capture write、learning write、PlayBook trust write、production write、secret read 與 destructive operation 均為 `0 / false`。 +- 本波仍不讀 canonical runtime target、不做 live query、不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 report receipt、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 production target、不讀 secret、不執行 destructive action、不回傳內部協作內容;正式推版與 production smoke 通過後才可由 P2-135 承接。 + ### 2026-06-11 20:40 (台北) — §3.2 / §3.4 / §5 — 補 OpenClaw / Hermes / NemoTron 主動溝通與學習契約 — 回應統帥要求讓 Agent 可主動溝通、主動學習、記錄且不洩漏工作視窗對話 - 新增 §3.2.1b,定義三 Agent 主動溝通資料面、角色主動權、前端 redaction 與 committed contract/API。