diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index 6103fcbb4..d77047c61 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -142,6 +142,9 @@ from src.services.ai_agent_result_capture_owner_approved_release_readiness_readb from src.services.ai_agent_result_capture_owner_release_approval_gate import ( load_latest_ai_agent_result_capture_owner_release_approval_gate, ) +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_post_release_verifier_rollback_gate import ( load_latest_ai_agent_result_capture_post_release_verifier_rollback_gate, ) @@ -2085,6 +2088,37 @@ async def get_agent_result_capture_post_release_verifier_rollback_gate() -> dict ) from exc +@router.get( + "/agent-result-capture-final-release-candidate-readback", + response_model=dict[str, Any], + summary="取得 AI Agent result capture final release candidate readback", + description=( + "讀取最新已提交的 P2-133 final release candidate readback;" + "此端點只回傳 final release candidate readback、rollback candidate readback、candidate acceptance hold、" + "live apply candidate hold、blocked final candidate transition 與 operator handoff," + "不批准 owner release、不批准 maintenance window、不確認 rollback owner、不通過 final candidate、" + "不釋放 live apply、不套用 writer、不寫 receipt、不寫 result capture、learning、PlayBook trust、" + "reviewer queue、Gateway queue,不送 Telegram、不呼叫 Bot API、不讀 secret。" + ), +) +async def get_agent_result_capture_final_release_candidate_readback() -> dict[str, Any]: + """Return the latest read-only final release candidate readback.""" + try: + payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_final_release_candidate_readback) + 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_final_release_candidate_readback_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="AI Agent result capture final release candidate readback 無效", + ) 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_final_release_candidate_readback.py b/apps/api/src/services/ai_agent_result_capture_final_release_candidate_readback.py new file mode 100644 index 000000000..464a54b2e --- /dev/null +++ b/apps/api/src/services/ai_agent_result_capture_final_release_candidate_readback.py @@ -0,0 +1,421 @@ +""" +AI Agent result capture final release candidate readback snapshot. + +Loads the latest committed P2-133 final release candidate readback. +This module validates committed evidence only; it never approves owner release, +approves maintenance windows, confirms rollback owners, passes final release +candidates, 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_final_release_candidate_readback_*.json" +_SCHEMA_VERSION = "ai_agent_result_capture_final_release_candidate_readback_v1" +_RUNTIME_AUTHORITY = "result_capture_final_release_candidate_readback_only_no_live_write" + + +def load_latest_ai_agent_result_capture_final_release_candidate_readback( + evaluations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the newest committed final release candidate readback.""" + directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR + candidates = sorted(directory.glob(_SNAPSHOT_PATTERN)) + if not candidates: + raise FileNotFoundError(f"no AI Agent result capture final release candidate readback 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_candidate_readbacks(payload, label) + _require_rollback_readbacks(payload, label) + _require_acceptance_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-133", + "next_task_id": "P2-134", + "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_post_release_verifier_rollback_gate") or {} + expected = { + "schema_version": "ai_agent_result_capture_post_release_verifier_rollback_gate_v1", + "post_release_verifier_gate_count": 5, + "rollback_release_gate_count": 5, + "release_verification_hold_count": 5, + "live_apply_post_release_gate_count": 5, + "blocked_post_release_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, + "release_verification_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_post_release_verifier_rollback_gate mismatch: {mismatches}") + if not prior.get("readiness_note"): + raise ValueError(f"{label}: prior_post_release_verifier_rollback_gate.readiness_note is required") + + +def _require_truth(payload: dict[str, Any], label: str) -> None: + truth = payload.get("final_release_candidate_truth") or {} + required_true = { + "p2_132_post_release_gate_loaded", + "final_release_candidate_readback_ready", + "rollback_candidate_readback_ready", + "candidate_acceptance_hold_active", + "live_apply_candidate_hold_active", + "owner_release_review_still_required", + "maintenance_window_review_still_required", + "rollback_owner_review_required", + "redaction_review_required", + "final_release_candidate_readback_only", + } + missing = sorted(field for field in required_true if truth.get(field) is not True) + if missing: + raise ValueError(f"{label}: final release candidate readback flags must remain true: {missing}") + + required_false = { + "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_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}: final candidate live/send/write flags must remain false: {unsafe}") + + zero_counts = { + "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", + "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}: final candidate live counters must remain zero: {non_zero}") + if not truth.get("truth_note"): + raise ValueError(f"{label}: final_release_candidate_truth.truth_note is required") + + +def _require_candidate_readbacks(payload: dict[str, Any], label: str) -> None: + items = payload.get("final_release_candidate_readbacks") or [] + _require_ids( + items, + "readback_id", + { + "final_candidate_result_capture", + "final_candidate_learning", + "final_candidate_playbook_trust", + "final_candidate_reviewer_queue", + "final_candidate_gateway_queue", + }, + label, + "final release candidate readbacks", + ) + for item in items: + item_id = item.get("readback_id") + if item.get("readback_mode") != "final_release_candidate_readback": + raise ValueError(f"{label}: final candidate readback {item_id} mode is invalid") + if item.get("final_release_candidate_approved") is not False or item.get("final_release_candidate_passed") is not False: + raise ValueError(f"{label}: final candidate readback {item_id} must stay unapproved and unpassed") + _require_valid_status(item, label, f"final candidate readback {item_id}") + if not item.get("candidate_summary") or not _is_redacted_sha256(item.get("evidence_hash")): + raise ValueError(f"{label}: final candidate readback {item_id} must include summary and redacted evidence_hash") + + +def _require_rollback_readbacks(payload: dict[str, Any], label: str) -> None: + items = payload.get("rollback_candidate_readbacks") or [] + _require_count(items, 5, label, "rollback candidate readbacks") + for item in items: + item_id = item.get("readback_id") + if item.get("readback_mode") != "rollback_candidate_readback": + raise ValueError(f"{label}: rollback candidate readback {item_id} mode is invalid") + if item.get("rollback_owner_required") is not True: + raise ValueError(f"{label}: rollback candidate readback {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 candidate readback {item_id} must stay unconfirmed and disabled") + _require_valid_status(item, label, f"rollback candidate readback {item_id}") + if not item.get("rollback_summary"): + raise ValueError(f"{label}: rollback candidate readback {item_id}.rollback_summary is required") + + +def _require_acceptance_holds(payload: dict[str, Any], label: str) -> None: + items = payload.get("candidate_acceptance_holds") or [] + _require_count(items, 5, label, "candidate acceptance holds") + for item in items: + item_id = item.get("hold_id") + if item.get("hold_mode") != "candidate_acceptance_hold": + raise ValueError(f"{label}: candidate acceptance hold {item_id} mode is invalid") + if item.get("owner_release_approved") is not False or item.get("maintenance_window_approved") is not False: + raise ValueError(f"{label}: candidate acceptance hold {item_id} must keep owner release and maintenance unapproved") + if item.get("final_release_candidate_passed") is not False: + raise ValueError(f"{label}: candidate acceptance hold {item_id} must stay unpassed") + _require_valid_status(item, label, f"candidate acceptance hold {item_id}") + if not item.get("hold_reason"): + raise ValueError(f"{label}: candidate acceptance hold {item_id}.hold_reason is required") + + +def _require_live_apply_holds(payload: dict[str, Any], label: str) -> None: + items = payload.get("live_apply_candidate_holds") or [] + _require_count(items, 5, label, "live apply candidate holds") + for item in items: + item_id = item.get("hold_id") + if item.get("hold_mode") != "live_apply_candidate_hold": + raise ValueError(f"{label}: live apply candidate hold {item_id} mode is invalid") + if item.get("final_release_candidate_approved") is not False or item.get("live_apply_release_enabled") is not False: + raise ValueError(f"{label}: live apply candidate hold {item_id} must stay unapproved and disabled") + _require_valid_status(item, label, f"live apply candidate hold {item_id}") + if not item.get("release_condition"): + raise ValueError(f"{label}: live apply candidate hold {item_id}.release_condition is required") + + +def _require_blocked_transitions(payload: dict[str, Any], label: str) -> None: + items = payload.get("blocked_final_candidate_transitions") or [] + _require_count(items, 6, label, "blocked final candidate 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 final candidate 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 final candidate transition {item_id} must include blocked action, until, and evidence hash") + if critical_count != 5: + raise ValueError(f"{label}: expected 5 critical final candidate 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"), + "final_release_candidate_readbacks": payload.get("final_release_candidate_readbacks"), + "rollback_candidate_readbacks": payload.get("rollback_candidate_readbacks"), + "candidate_acceptance_holds": payload.get("candidate_acceptance_holds"), + "live_apply_candidate_holds": payload.get("live_apply_candidate_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 = { + "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_candidate_count": 2, + "blocked_candidate_count": 1, + "approval_required_rollback_count": 2, + "blocked_rollback_count": 1, + "approval_required_acceptance_hold_count": 2, + "blocked_acceptance_hold_count": 1, + "approval_required_live_apply_count": 2, + "blocked_live_apply_count": 1, + "critical_blocker_count": 5, + "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, + "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: Any) -> bool: + return isinstance(value, str) and re.fullmatch(r"sha256:[a-f0-9]{64}", value) is not None diff --git a/apps/api/tests/test_ai_agent_result_capture_final_release_candidate_readback.py b/apps/api/tests/test_ai_agent_result_capture_final_release_candidate_readback.py new file mode 100644 index 000000000..563b47b20 --- /dev/null +++ b/apps/api/tests/test_ai_agent_result_capture_final_release_candidate_readback.py @@ -0,0 +1,118 @@ +from __future__ import annotations + +import copy +import json +from pathlib import Path + +import pytest + +from src.services.ai_agent_result_capture_final_release_candidate_readback import ( + load_latest_ai_agent_result_capture_final_release_candidate_readback, +) + + +def test_load_latest_final_release_candidate_readback_snapshot() -> None: + snapshot = load_latest_ai_agent_result_capture_final_release_candidate_readback() + + assert snapshot["schema_version"] == "ai_agent_result_capture_final_release_candidate_readback_v1" + assert snapshot["program_status"]["current_task_id"] == "P2-133" + assert snapshot["program_status"]["next_task_id"] == "P2-134" + assert snapshot["program_status"]["overall_completion_percent"] == 100 + assert snapshot["program_status"]["runtime_authority"] == "result_capture_final_release_candidate_readback_only_no_live_write" + + rollups = snapshot["rollups"] + assert rollups["final_release_candidate_readback_count"] == 5 + assert rollups["rollback_candidate_readback_count"] == 5 + assert rollups["candidate_acceptance_hold_count"] == 5 + assert rollups["live_apply_candidate_hold_count"] == 5 + assert rollups["blocked_final_candidate_transition_count"] == 6 + assert rollups["operator_action_count"] == 5 + assert ( + rollups["approval_required_candidate_count"] + + rollups["approval_required_rollback_count"] + + rollups["approval_required_acceptance_hold_count"] + + rollups["approval_required_live_apply_count"] + ) == 8 + assert ( + rollups["blocked_candidate_count"] + + rollups["blocked_rollback_count"] + + rollups["blocked_acceptance_hold_count"] + + rollups["blocked_live_apply_count"] + + rollups["critical_blocker_count"] + ) == 9 + + 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_final_release_candidate_truth_keeps_all_live_paths_closed() -> None: + snapshot = load_latest_ai_agent_result_capture_final_release_candidate_readback() + truth = snapshot["final_release_candidate_truth"] + + assert truth["p2_132_post_release_gate_loaded"] is True + assert truth["final_release_candidate_readback_ready"] is True + assert truth["live_apply_candidate_hold_active"] is True + 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["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_approved_final_candidate(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_final_release_candidate_readback()) + snapshot["final_release_candidate_truth"]["final_release_candidate_approved"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="must remain false"): + load_latest_ai_agent_result_capture_final_release_candidate_readback(tmp_path) + + +def test_rejects_live_apply_release_enabled(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_final_release_candidate_readback()) + snapshot["live_apply_candidate_holds"][0]["live_apply_release_enabled"] = True + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="must stay unapproved and disabled"): + load_latest_ai_agent_result_capture_final_release_candidate_readback(tmp_path) + + +def test_rejects_rollup_drift(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_final_release_candidate_readback()) + snapshot["rollups"]["final_release_candidate_readback_count"] = 4 + _write_snapshot(tmp_path, snapshot) + + with pytest.raises(ValueError, match="rollups mismatch"): + load_latest_ai_agent_result_capture_final_release_candidate_readback(tmp_path) + + +def test_rejects_forbidden_display_terms(tmp_path: Path) -> None: + snapshot = copy.deepcopy(load_latest_ai_agent_result_capture_final_release_candidate_readback()) + 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_final_release_candidate_readback(tmp_path) + + +def _write_snapshot(directory: Path, payload: dict) -> None: + path = directory / "ai_agent_result_capture_final_release_candidate_readback_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_final_release_candidate_readback_api.py b/apps/api/tests/test_ai_agent_result_capture_final_release_candidate_readback_api.py new file mode 100644 index 000000000..a852fe3c8 --- /dev/null +++ b/apps/api/tests/test_ai_agent_result_capture_final_release_candidate_readback_api.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from fastapi.testclient import TestClient + +from src.main import app + + +def test_final_release_candidate_readback_endpoint() -> None: + client = TestClient(app) + + response = client.get("/api/v1/agents/agent-result-capture-final-release-candidate-readback") + + assert response.status_code == 200 + payload = response.json() + assert payload["schema_version"] == "ai_agent_result_capture_final_release_candidate_readback_v1" + assert payload["program_status"]["current_task_id"] == "P2-133" + assert payload["program_status"]["next_task_id"] == "P2-134" + assert payload["program_status"]["overall_completion_percent"] == 100 + assert payload["program_status"]["runtime_authority"] == "result_capture_final_release_candidate_readback_only_no_live_write" + assert payload["rollups"]["final_release_candidate_readback_count"] == 5 + assert payload["rollups"]["rollback_candidate_readback_count"] == 5 + assert payload["rollups"]["candidate_acceptance_hold_count"] == 5 + assert payload["rollups"]["live_apply_candidate_hold_count"] == 5 + assert payload["rollups"]["blocked_final_candidate_transition_count"] == 6 + assert payload["rollups"]["operator_action_count"] == 5 + 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 f600bf01a..add257bfa 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -6010,6 +6010,41 @@ "runtimeWriteAllowed": "runtime write allowed={value}" } }, + "resultCaptureFinalReleaseCandidateReadback": { + "title": "P2-133 final release candidate readback", + "source": "產生 {generated};目前 {current};下一步 {next}", + "priorTitle": "前一關 post-release verifier / rollback gate", + "truthTitle": "final release candidate 真相", + "metrics": { + "overall": "完成度", + "candidates": "Final candidates", + "rollbackReadbacks": "Rollback readbacks", + "acceptanceHolds": "Candidate holds", + "liveApplyHolds": "Live-apply holds", + "blockers": "已阻擋 final candidate", + "actions": "操作選項", + "approvalRequired": "需批准", + "blocked": "阻擋", + "liveWrites": "正式寫入 / 發送" + }, + "flags": { + "p2Loaded": "已載入 P2-132={value}", + "candidateReadbackReady": "final candidate readback ready={value}", + "liveApplyHoldActive": "live apply candidate hold active={value}" + }, + "labels": { + "ownerReleaseApproved": "owner release approved={value}", + "maintenanceApproved": "maintenance approved={value}", + "candidatePassed": "final candidate passed={value}", + "gatewayWrites": "Gateway 寫入={value}", + "candidateApproved": "final candidate approved={value}", + "rollbackRequired": "需要 rollback owner={value}", + "rollbackConfirmed": "rollback owner confirmed={value}", + "rollbackEnabled": "rollback release enabled={value}", + "liveApplyReleaseEnabled": "live apply release enabled={value}", + "runtimeWriteAllowed": "runtime write allowed={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 f600bf01a..add257bfa 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -6010,6 +6010,41 @@ "runtimeWriteAllowed": "runtime write allowed={value}" } }, + "resultCaptureFinalReleaseCandidateReadback": { + "title": "P2-133 final release candidate readback", + "source": "產生 {generated};目前 {current};下一步 {next}", + "priorTitle": "前一關 post-release verifier / rollback gate", + "truthTitle": "final release candidate 真相", + "metrics": { + "overall": "完成度", + "candidates": "Final candidates", + "rollbackReadbacks": "Rollback readbacks", + "acceptanceHolds": "Candidate holds", + "liveApplyHolds": "Live-apply holds", + "blockers": "已阻擋 final candidate", + "actions": "操作選項", + "approvalRequired": "需批准", + "blocked": "阻擋", + "liveWrites": "正式寫入 / 發送" + }, + "flags": { + "p2Loaded": "已載入 P2-132={value}", + "candidateReadbackReady": "final candidate readback ready={value}", + "liveApplyHoldActive": "live apply candidate hold active={value}" + }, + "labels": { + "ownerReleaseApproved": "owner release approved={value}", + "maintenanceApproved": "maintenance approved={value}", + "candidatePassed": "final candidate passed={value}", + "gatewayWrites": "Gateway 寫入={value}", + "candidateApproved": "final candidate approved={value}", + "rollbackRequired": "需要 rollback owner={value}", + "rollbackConfirmed": "rollback owner confirmed={value}", + "rollbackEnabled": "rollback release enabled={value}", + "liveApplyReleaseEnabled": "live apply release enabled={value}", + "runtimeWriteAllowed": "runtime write allowed={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 a3a5e3a71..3e86788b1 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 @@ -80,6 +80,7 @@ import { type AiAgentResultCaptureOwnerApprovedReleaseReadinessReadbackSnapshot, type AiAgentResultCaptureOwnerReleaseApprovalGateSnapshot, type AiAgentResultCapturePostReleaseVerifierRollbackGateSnapshot, + type AiAgentResultCaptureFinalReleaseCandidateReadbackSnapshot, type AiAgentResultCaptureWriterImplementationReviewSnapshot, type AiAgentOwnerApprovedFixturePromotionGateSnapshot, type AiAgentRuntimeWorkerShadowGateSnapshot, @@ -483,6 +484,7 @@ export function AutomationInventoryTab() { const [resultCaptureOwnerApprovedReleaseReadinessReadback, setResultCaptureOwnerApprovedReleaseReadinessReadback] = useState(null) const [resultCaptureOwnerReleaseApprovalGate, setResultCaptureOwnerReleaseApprovalGate] = useState(null) const [resultCapturePostReleaseVerifierRollbackGate, setResultCapturePostReleaseVerifierRollbackGate] = useState(null) + const [resultCaptureFinalReleaseCandidateReadback, setResultCaptureFinalReleaseCandidateReadback] = useState(null) const [reportTruthActionabilityReview, setReportTruthActionabilityReview] = useState(null) const [ownerDryRunPackage, setOwnerDryRunPackage] = useState(null) const [hostStatefulInventory, setHostStatefulInventory] = useState(null) @@ -552,6 +554,7 @@ export function AutomationInventoryTab() { apiClient.getAiAgentResultCaptureOwnerApprovedReleaseReadinessReadback(), apiClient.getAiAgentResultCaptureOwnerReleaseApprovalGate(), apiClient.getAiAgentResultCapturePostReleaseVerifierRollbackGate(), + apiClient.getAiAgentResultCaptureFinalReleaseCandidateReadback(), apiClient.getAiAgentReportTruthActionabilityReview(), apiClient.getAiAgentOwnerApprovedFixtureDryRun(), apiClient.getAiAgentHostStatefulVersionInventory(), @@ -620,6 +623,7 @@ export function AutomationInventoryTab() { resultCaptureOwnerApprovedReleaseReadinessReadbackResult, resultCaptureOwnerReleaseApprovalGateResult, resultCapturePostReleaseVerifierRollbackGateResult, + resultCaptureFinalReleaseCandidateReadbackResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -685,6 +689,7 @@ export function AutomationInventoryTab() { setResultCaptureOwnerApprovedReleaseReadinessReadback(resultCaptureOwnerApprovedReleaseReadinessReadbackResult.status === 'fulfilled' ? resultCaptureOwnerApprovedReleaseReadinessReadbackResult.value : null) setResultCaptureOwnerReleaseApprovalGate(resultCaptureOwnerReleaseApprovalGateResult.status === 'fulfilled' ? resultCaptureOwnerReleaseApprovalGateResult.value : null) setResultCapturePostReleaseVerifierRollbackGate(resultCapturePostReleaseVerifierRollbackGateResult.status === 'fulfilled' ? resultCapturePostReleaseVerifierRollbackGateResult.value : null) + setResultCaptureFinalReleaseCandidateReadback(resultCaptureFinalReleaseCandidateReadbackResult.status === 'fulfilled' ? resultCaptureFinalReleaseCandidateReadbackResult.value : null) setReportTruthActionabilityReview(reportTruthActionabilityReviewResult.status === 'fulfilled' ? reportTruthActionabilityReviewResult.value : null) setOwnerDryRunPackage(ownerDryRunPackageResult.status === 'fulfilled' ? ownerDryRunPackageResult.value : null) setHostStatefulInventory(hostStatefulInventoryResult.status === 'fulfilled' ? hostStatefulInventoryResult.value : null) @@ -748,6 +753,7 @@ export function AutomationInventoryTab() { resultCaptureOwnerApprovedReleaseReadinessReadbackResult, resultCaptureOwnerReleaseApprovalGateResult, resultCapturePostReleaseVerifierRollbackGateResult, + resultCaptureFinalReleaseCandidateReadbackResult, reportTruthActionabilityReviewResult, ownerDryRunPackageResult, hostStatefulInventoryResult, @@ -1990,7 +1996,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 || !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 || !reportTruthActionabilityReview || !ownerDryRunPackage || !hostStatefulInventory || !serviceHealthGapMatrix || !serviceHealthNotificationPolicy) { return (
@@ -3102,6 +3108,40 @@ export function AutomationInventoryTab() { + resultCapturePostReleaseVerifierRollbackGate.rollups.playbook_trust_write_count + resultCapturePostReleaseVerifierRollbackGate.rollups.production_write_count ) + const resultCaptureFinalCandidateOverall = resultCaptureFinalReleaseCandidateReadback.program_status.overall_completion_percent + const resultCaptureFinalCandidateReadbacks = resultCaptureFinalReleaseCandidateReadback.rollups.final_release_candidate_readback_count + const resultCaptureFinalCandidateRollbackReadbacks = resultCaptureFinalReleaseCandidateReadback.rollups.rollback_candidate_readback_count + const resultCaptureFinalCandidateAcceptanceHolds = resultCaptureFinalReleaseCandidateReadback.rollups.candidate_acceptance_hold_count + const resultCaptureFinalCandidateLiveApplyHolds = resultCaptureFinalReleaseCandidateReadback.rollups.live_apply_candidate_hold_count + const resultCaptureFinalCandidateBlockedTransitions = resultCaptureFinalReleaseCandidateReadback.rollups.blocked_final_candidate_transition_count + const resultCaptureFinalCandidateActions = resultCaptureFinalReleaseCandidateReadback.rollups.operator_action_count + const resultCaptureFinalCandidateApprovalRequired = ( + resultCaptureFinalReleaseCandidateReadback.rollups.approval_required_candidate_count + + resultCaptureFinalReleaseCandidateReadback.rollups.approval_required_rollback_count + + resultCaptureFinalReleaseCandidateReadback.rollups.approval_required_acceptance_hold_count + + resultCaptureFinalReleaseCandidateReadback.rollups.approval_required_live_apply_count + ) + const resultCaptureFinalCandidateBlocked = ( + resultCaptureFinalReleaseCandidateReadback.rollups.blocked_candidate_count + + resultCaptureFinalReleaseCandidateReadback.rollups.blocked_rollback_count + + resultCaptureFinalReleaseCandidateReadback.rollups.blocked_acceptance_hold_count + + resultCaptureFinalReleaseCandidateReadback.rollups.blocked_live_apply_count + + resultCaptureFinalReleaseCandidateReadback.rollups.critical_blocker_count + ) + const resultCaptureFinalCandidateLiveWrites = ( + resultCaptureFinalReleaseCandidateReadback.rollups.writer_apply_count + + resultCaptureFinalReleaseCandidateReadback.rollups.execution_apply_count + + resultCaptureFinalReleaseCandidateReadback.rollups.receipt_write_count + + resultCaptureFinalReleaseCandidateReadback.rollups.reviewer_queue_write_count + + resultCaptureFinalReleaseCandidateReadback.rollups.gateway_queue_write_count + + resultCaptureFinalReleaseCandidateReadback.rollups.telegram_send_count + + resultCaptureFinalReleaseCandidateReadback.rollups.bot_api_call_count + + resultCaptureFinalReleaseCandidateReadback.rollups.report_receipt_write_count + + resultCaptureFinalReleaseCandidateReadback.rollups.result_capture_write_count + + resultCaptureFinalReleaseCandidateReadback.rollups.learning_write_count + + resultCaptureFinalReleaseCandidateReadback.rollups.playbook_trust_write_count + + resultCaptureFinalReleaseCandidateReadback.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 @@ -7651,6 +7691,168 @@ export function AutomationInventoryTab() {
+
+
+
+ + + {t('resultCaptureFinalReleaseCandidateReadback.title')} + +
+ +
+ +
+ } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> +
+ +
+
+ {t('resultCaptureFinalReleaseCandidateReadback.priorTitle')} +

+ {resultCaptureFinalReleaseCandidateReadback.prior_post_release_verifier_rollback_gate.readiness_note} +

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

+ {resultCaptureFinalReleaseCandidateReadback.final_release_candidate_truth.truth_note} +

+
+ + + + + + +
+
+
+ +
+ {resultCaptureFinalReleaseCandidateReadback.final_release_candidate_readbacks.slice(0, 5).map(readback => ( +
+
+ + {readback.display_name} + + +
+ + {readback.candidate_summary} + +
+ + + + +
+
+ ))} +
+ +
+ {resultCaptureFinalReleaseCandidateReadback.rollback_candidate_readbacks.slice(0, 5).map(readback => ( +
+
+ + {readback.display_name} + + +
+ + {readback.rollback_summary} + +
+ + + +
+
+ ))} +
+ +
+ {resultCaptureFinalReleaseCandidateReadback.candidate_acceptance_holds.slice(0, 5).map(hold => ( +
+
+ + {hold.display_name} + + +
+ + {hold.hold_reason} + +
+ + + +
+
+ ))} +
+ +
+ {resultCaptureFinalReleaseCandidateReadback.live_apply_candidate_holds.slice(0, 5).map(hold => ( +
+
+ + {hold.display_name} + + +
+ + {hold.release_condition} + + +
+ ))} +
+ +
+ {resultCaptureFinalReleaseCandidateReadback.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 5bfb41ce6..0e50ae959 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -560,6 +560,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiAgentResultCaptureFinalReleaseCandidateReadback() { + const res = await fetch(`${API_BASE_URL}/agents/agent-result-capture-final-release-candidate-readback`) + return handleResponse(res) + }, + async getAiAgentOwnerApprovedFixtureDryRun() { const res = await fetch(`${API_BASE_URL}/agents/agent-owner-approved-fixture-dry-run`) return handleResponse(res) @@ -7957,6 +7962,216 @@ export interface AiAgentResultCapturePostReleaseVerifierRollbackGateSnapshot { } } +export interface AiAgentResultCaptureFinalReleaseCandidateReadbackSnapshot { + schema_version: 'ai_agent_result_capture_final_release_candidate_readback_v1' + generated_at: string + program_status: { + overall_completion_percent: number + current_priority: 'P0' | 'P1' | 'P2' | 'P3' + current_task_id: 'P2-133' + next_task_id: 'P2-134' + read_only_mode: true + runtime_authority: 'result_capture_final_release_candidate_readback_only_no_live_write' + status_note: string + } + source_refs: string[] + prior_post_release_verifier_rollback_gate: { + schema_version: string + post_release_verifier_gate_count: number + rollback_release_gate_count: number + release_verification_hold_count: number + live_apply_post_release_gate_count: number + blocked_post_release_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 + release_verification_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 + } + final_release_candidate_truth: { + p2_132_post_release_gate_loaded: boolean + final_release_candidate_readback_ready: boolean + rollback_candidate_readback_ready: boolean + candidate_acceptance_hold_active: boolean + live_apply_candidate_hold_active: boolean + owner_release_review_still_required: boolean + maintenance_window_review_still_required: boolean + rollback_owner_review_required: boolean + redaction_review_required: boolean + final_release_candidate_readback_only: 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 + 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_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 + 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 + } + final_release_candidate_readbacks: Array<{ + readback_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + source_post_release_gate: string + status: 'ready_for_owner_review' | 'approval_required' | 'blocked_by_policy' + readback_mode: 'final_release_candidate_readback' + final_release_candidate_approved: false + final_release_candidate_passed: false + candidate_summary: string + evidence_hash: string + }> + rollback_candidate_readbacks: Array<{ + readback_id: string + display_name: string + owner_agent: 'openclaw' | 'hermes' | 'nemotron' + status: 'ready_for_owner_review' | 'approval_required' | 'blocked_by_policy' + readback_mode: 'rollback_candidate_readback' + rollback_owner_required: true + rollback_owner_confirmed: false + rollback_release_enabled: false + rollback_summary: string + }> + candidate_acceptance_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: 'candidate_acceptance_hold' + owner_release_approved: false + maintenance_window_approved: false + final_release_candidate_passed: false + hold_reason: string + }> + live_apply_candidate_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_candidate_hold' + final_release_candidate_approved: false + live_apply_release_enabled: false + release_condition: string + }> + blocked_final_candidate_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: { + 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_candidate_count: number + blocked_candidate_count: number + approval_required_rollback_count: number + blocked_rollback_count: number + approval_required_acceptance_hold_count: number + blocked_acceptance_hold_count: number + approval_required_live_apply_count: number + blocked_live_apply_count: number + critical_blocker_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 + 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 4b765b8a1..a81ebad59 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,32 @@ +## 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。 + +**完成內容**: +- 新增 `ai_agent_result_capture_final_release_candidate_readback_v1` schema、committed snapshot、loader 與 API endpoint `GET /api/v1/agents/agent-result-capture-final-release-candidate-readback`。 +- P2-133 snapshot 固定 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。 +- Governance automation inventory 頁新增 P2-133 區塊,顯示 P2-133 進度 `100%`、final candidate `5`、rollback readback `5`、candidate hold `5`、live-apply hold `5`、blocked final candidate `6`、operator action `5`、需批准 `8`、阻擋 `9`、正式寫入 / 發送 `0`,並補齊 candidate acceptance hold 明細。 +- Final release candidate truth 固定為 `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、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-133 schema / snapshot、`zh-TW.json`、`en.json` 通過。 +- Python 編譯:P2-133 loader 與 `agents.py` 通過。 +- API/service pytest:P2-133 + P2-132 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`。 +- `git diff --check`、`doc-secrets-sanity-check.py docs .gitea`、`source-control-owner-response-guard.py --root .`、`security-mirror-progress-guard.py --root .` 通過。 + +**安全邊界**: +- P2-133 仍是 final release candidate readback;不接受口頭批准、不把 candidate readback 解讀成 owner release approved、不批准維護窗口、不確認 rollback owner、不啟用 post-release verifier live read、不通過 final candidate、不釋放 rollback release、不釋放 live apply、不套用 writer、不執行正式寫入、不寫 receipt、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 report receipt、不讀 canonical runtime target、不做 live query、不寫 production target、不讀 secret、不執行 destructive action、不回傳內部協作內容。 + +**正式驗證待辦**: +- 推送 feature commit 到 `gitea/main` 後,必須等待 Gitea Actions Code Review、CD tests、build-and-deploy、post-deploy-checks 全部成功。 +- 正式 API 必須回 `schema_version=ai_agent_result_capture_final_release_candidate_readback_v1`、current `P2-133`、next `P2-134`、completion `100`、runtime authority `result_capture_final_release_candidate_readback_only_no_live_write`。 +- 正式 desktop / mobile smoke 必須確認 P2-133 區塊可見、必要數字可見、禁用內部協作片語命中 `0`、水平溢位 `0`、P2-133 高風險控制 `0`、console error `0`、API bad response `0`、actionable request failure `0`。 + +**下一步**: +- `P2-134`:release authorization hold;只有 P2-133 正式驗證後才可把 final release candidate readback、rollback candidate readback、candidate acceptance hold、live-apply candidate hold 與 blocked final candidate transition 轉成 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-132 Post-release verifier / rollback gate 完成與正式驗證 **背景**:P2-131 已把 owner release approval gate 正式驗證完成;但 approval gate 仍不得被誤讀成 owner release approved、maintenance window approved、rollback owner confirmed、post-release verifier ready、release verification passed 或 live apply release passed。P2-132 因此只建立 post-release verifier / rollback gate,把批准門檻轉成正式釋出後驗證與 rollback release 的可審核 hold,不開啟 verifier live read、不確認 rollback owner、不釋放 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 a090f195d..f5dbf1c56 100644 --- a/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md +++ b/docs/ai/AI_AGENT_AUTOMATION_WORKLIST_2026-06-04.md @@ -12,17 +12,17 @@ | Nemotron 實際整合應用 | 30% | 完整回放前仍被關卡擋下 | `blocked_needs_evidence`,下一關是 `refresh_source_evidence_then_5_record_smoke_only` | | 工具 / 服務 / 套件 AI 自動化 | 92% | P0 已完成;P1 服務 / runtime / 監控 / provider / service health / 備份 / DR / 套件與供應鏈只讀基線已完成;P1-007 失敗限定通知合約與前端 redaction 合約已完成;下一主線是 P2-004 依賴 / 供應鏈漂移監控 | 狀態分類、盤點 schema、權限矩陣、靜態盤點種子、只讀 API、UI 骨架、驗證、自動化待辦 schema / 快照 / API / 分組 UI、Backup / DR 目標盤點、準備度矩陣、備份通知政策、Backup / DR 證據 UI、復原演練批准包模板、異地 / escrow 準備度狀態、任務批准邊界、確定性進度彙總、Python 套件 / 供應鏈只讀基線、JS pnpm/npm 只讀基線、Docker build surface 只讀基線、CVE / license / drift 嚴重度政策、定期依賴漂移與外部資料來源檢查設計、依賴升級批准包模板、runtime_surface_inventory_v1 schema / snapshot / API / UI、gitea_workflow_runner_health_v1 schema / snapshot / API / UI、observability_contract_matrix_v1 schema / snapshot / API / UI、ai_provider_route_matrix_v1 schema / snapshot / API / UI、service_health_gap_matrix_v1 schema / snapshot / API / UI、service health evidence cards UI、service_health_failure_notification_policy_v1 schema / snapshot / API / UI 已完成 | | OpenClaw / Hermes / NemoTron 佈建布局 | 45% | P1-401 / P1-402 已完成;仍是只讀 layout 與治理頁顯示,不是 runtime deploy | `ai_agent_deployment_layout_v1` schema、`ai_agent_deployment_layout_2026-06-11.json`、`GET /api/v1/agents/agent-deployment-layout`、治理頁自動化盤點 UI、`AI_AGENT_DEPLOYMENT_LAYOUT_2026-06-11.md` | -| OpenClaw / Hermes / NemoTron 主動溝通、學習與成長證據 | 100% | P2-401A 到 P2-132 已完成只讀證據面、runtime / report / result-capture gates、no-write readback、promotion review、writer implementation review、writer dry-run fixture、writer dry-run readback、owner promotion execution gate、owner-approved execution rehearsal、owner acceptance / maintenance window gate、owner acceptance readback / preflight hold、owner-approved preflight release package、owner-approved release readiness readback、owner release approval gate 與 post-release verifier / rollback gate;P2-132 已正式驗證,固定 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 worker、DB migration、production Redis consumer group、canonical runtime readback、live query、runtime score、result capture write、Telegram 實發、delivery receipt E2E、live report delivery、reviewer queue write、Gateway queue write、AI analysis runtime、中低風險 auto worker、KM / LOGBOOK / audit DB / timeline / PlayBook trust 寫入、SDK / 付費服務仍未開 gate | `ai_agent_result_capture_post_release_verifier_rollback_gate_v1`、`GET /api/v1/agents/agent-result-capture-post-release-verifier-rollback-gate`、正式站 deploy marker `934af770`、正式站 `/zh-TW/governance?tab=automation-inventory` desktop / mobile smoke、`ai_agent_result_capture_owner_release_approval_gate_v1`、`GET /api/v1/agents/agent-result-capture-owner-release-approval-gate`、正式站 deploy marker `03617db7`、`ai_agent_result_capture_owner_approved_release_readiness_readback_v1`、`GET /api/v1/agents/agent-result-capture-owner-approved-release-readiness-readback`、正式站 deploy marker `6fcf7241`、`ai_agent_result_capture_owner_approved_preflight_release_package_v1`、`GET /api/v1/agents/agent-result-capture-owner-approved-preflight-release-package`、`ai_agent_result_capture_owner_acceptance_readback_preflight_hold_v1`、`GET /api/v1/agents/agent-result-capture-owner-acceptance-readback-preflight-hold`、`ai_agent_result_capture_owner_acceptance_maintenance_gate_v1`、`GET /api/v1/agents/agent-result-capture-owner-acceptance-maintenance-gate`、`ai_agent_result_capture_owner_approved_execution_rehearsal_v1`、`GET /api/v1/agents/agent-result-capture-owner-approved-execution-rehearsal`、`ai_agent_result_capture_owner_promotion_review_v1`、`ai_agent_result_capture_writer_dry_run_readback_v1`、`ai_agent_result_capture_writer_dry_run_fixture_v1`、`ai_agent_result_capture_writer_implementation_review_v1`、`ai_agent_result_capture_write_gate_review_v1`、`ai_agent_interaction_learning_proof_v1`、MASTER §3.2.1b / §3.2.1d / §3.4.3 | +| OpenClaw / Hermes / NemoTron 主動溝通、學習與成長證據 | 100% | P2-401A 到 P2-133 已完成只讀證據面、runtime / report / result-capture gates、no-write readback、promotion review、writer implementation review、writer dry-run fixture、writer dry-run readback、owner promotion execution gate、owner-approved execution rehearsal、owner acceptance / maintenance window gate、owner acceptance readback / preflight hold、owner-approved preflight release package、owner-approved release readiness readback、owner release approval gate、post-release verifier / rollback gate 與 final release candidate readback;P2-132 已正式驗證,P2-133 已完成本地驗證並待推正式驗證,固定 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 worker、DB migration、production Redis consumer group、canonical runtime readback、live query、runtime score、result capture write、Telegram 實發、delivery receipt E2E、live report delivery、reviewer queue write、Gateway queue write、AI analysis runtime、中低風險 auto worker、KM / LOGBOOK / audit DB / timeline / PlayBook trust 寫入、SDK / 付費服務仍未開 gate | `ai_agent_result_capture_final_release_candidate_readback_v1`、`GET /api/v1/agents/agent-result-capture-final-release-candidate-readback`、P2-133 本地 pytest `14 passed`、Web typecheck / build / guard 通過、`ai_agent_result_capture_post_release_verifier_rollback_gate_v1`、`GET /api/v1/agents/agent-result-capture-post-release-verifier-rollback-gate`、正式站 deploy marker `934af770`、正式站 `/zh-TW/governance?tab=automation-inventory` desktop / mobile smoke、`ai_agent_result_capture_owner_release_approval_gate_v1`、`GET /api/v1/agents/agent-result-capture-owner-release-approval-gate`、正式站 deploy marker `03617db7`、`ai_agent_result_capture_owner_approved_release_readiness_readback_v1`、`GET /api/v1/agents/agent-result-capture-owner-approved-release-readiness-readback`、正式站 deploy marker `6fcf7241`、`ai_agent_result_capture_owner_approved_preflight_release_package_v1`、`GET /api/v1/agents/agent-result-capture-owner-approved-preflight-release-package`、`ai_agent_result_capture_owner_acceptance_readback_preflight_hold_v1`、`GET /api/v1/agents/agent-result-capture-owner-acceptance-readback-preflight-hold`、`ai_agent_result_capture_owner_acceptance_maintenance_gate_v1`、`GET /api/v1/agents/agent-result-capture-owner-acceptance-maintenance-gate`、`ai_agent_result_capture_owner_approved_execution_rehearsal_v1`、`GET /api/v1/agents/agent-result-capture-owner-approved-execution-rehearsal`、`ai_agent_result_capture_owner_promotion_review_v1`、`ai_agent_result_capture_writer_dry_run_readback_v1`、`ai_agent_result_capture_writer_dry_run_fixture_v1`、`ai_agent_result_capture_writer_implementation_review_v1`、`ai_agent_result_capture_write_gate_review_v1`、`ai_agent_interaction_learning_proof_v1`、MASTER §3.2.1b / §3.2.1d / §3.4.3 | | AI Agent 主動營運委派與版本生命週期 | 100% | P2-402A / P2-402B / P2-402C / P2-402D / P2-402E / P2-402F / P2-402G 已完成;已建立 repo-only 版本新鮮度快照、工具採用批准包、Telegram action-required digest policy、Gitea PR 草案 lane、host / K3s / stateful 版本只讀盤點、API 與 governance UI。定期排程、外部版本查詢、工具安裝、CI 變更、套件升級、主機更新、container pull、實際 PR creation、auto merge、Telegram 實發、SSH、kubectl、重啟仍未開 gate | `ai_agent_proactive_operations_contract_v1`、`ai_agent_version_freshness_snapshot_v1`、`ai_agent_tool_adoption_approval_package_v1`、`ai_agent_telegram_action_required_digest_policy_v1`、`ai_agent_gitea_pr_draft_lane_v1`、`ai_agent_host_stateful_version_inventory_v1`、`GET /api/v1/agents/agent-proactive-operations-contract`、`GET /api/v1/agents/agent-version-freshness-snapshot`、`GET /api/v1/agents/agent-tool-adoption-approval-package`、`GET /api/v1/agents/agent-telegram-action-required-digest-policy`、`GET /api/v1/agents/agent-gitea-pr-draft-lane`、`GET /api/v1/agents/agent-host-stateful-version-inventory`、`/zh-TW/governance?tab=automation-inventory`、MASTER §3.2.1c | | 本工作清單與分析報告 | 100% | 已完成 | 本 MD 文件 | -AI Agent 自動化工作包目前完成度:**99.5%**。本工作清單文件本身完成度:**100%**。 +AI Agent 自動化工作包目前完成度:**99.6%**。本工作清單文件本身完成度:**100%**。 三 Agent 佈建布局目前完成度:**45%**。第一波已完成只讀 schema / snapshot / API / 測試 / 報告,第二波已接入治理頁自動化盤點 UI;正式 runtime 佈署、Telegram E2E 發送與 AgentSession 工作流仍需逐項 gate。 -三 Agent 主動溝通、學習與成長證據目前完成度:**100%**。P2-403A 到 P2-132 已把互動證據、報表治理、runtime readback、reviewer / result capture / writer gates、no-write readback、promotion review、writer implementation review、writer dry-run fixture、writer dry-run readback、owner promotion execution gate、owner-approved execution rehearsal、owner acceptance / maintenance window gate、owner acceptance readback / preflight hold、owner-approved preflight release package、owner-approved release readiness readback、owner release approval gate 與 post-release verifier / rollback gate 全部固定成可驗證證據。P2-132 已正式驗證,deploy marker `934af770`,固定 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;正式 API、desktop / mobile smoke、禁用內部協作片語 `0`、水平溢位 `0`、P2-132 危險入口 `0` 均通過。目前 live AgentSession、Agent message、handoff、canonical runtime readback、live query、runtime score、result capture write、learning write、Telegram receipt、Gateway queue write、reviewer queue write、runtime verifier execution、live report delivery、AI analysis runtime、中低風險 auto worker、Telegram 實發、shadow worker live、delivery receipt E2E、KM / LOGBOOK / audit DB / timeline / PlayBook trust runtime 寫入仍全部為 `0`。真正下一步是 `P2-133`。 +三 Agent 主動溝通、學習與成長證據目前完成度:**100%**。P2-403A 到 P2-133 已把互動證據、報表治理、runtime readback、reviewer / result capture / writer gates、no-write readback、promotion review、writer implementation review、writer dry-run fixture、writer dry-run readback、owner promotion execution gate、owner-approved execution rehearsal、owner acceptance / maintenance window gate、owner acceptance readback / preflight hold、owner-approved preflight release package、owner-approved release readiness readback、owner release approval gate、post-release verifier / rollback gate 與 final release candidate readback 全部固定成可驗證證據。P2-132 已正式驗證,deploy marker `934af770`;P2-133 已本地完成,固定 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;JSON、pytest `14 passed`、typecheck、build 與 guard 均通過,正式 API / desktop / mobile smoke 待推版後驗證。目前 live AgentSession、Agent message、handoff、canonical runtime readback、live query、runtime score、result capture write、learning write、Telegram receipt、Gateway queue write、reviewer queue write、runtime verifier execution、live report delivery、AI analysis runtime、中低風險 auto worker、Telegram 實發、shadow worker live、delivery receipt E2E、KM / LOGBOOK / audit DB / timeline / PlayBook trust runtime 寫入仍全部為 `0`。真正下一步是 `P2-134`。 -AI Agent 主動營運委派與版本生命週期目前完成度:**100%**。已完成 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory、只讀 API、`P2-402B` repo-only daily version freshness snapshot、`P2-402C` Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包、`P2-402D` Telegram action-required digest policy、`P2-402E` Gitea PR 草案 lane、`P2-402F` host OS / K3s / stateful services 版本只讀盤點,以及 `P2-402G` governance UI 顯示可委派能力;`P2-403A` 到 `P2-132` 已補互動、學習證據面、live read model gate、Redis dry-run gate、learning writeback approval package、Telegram receipt approval package、owner-approved learning dry-run preview、runtime write gate review、post-write verifier package、runtime verifier evidence review、報表真相、TG 戰情室收斂、日週月報、Agent 工作量、圖表化報告、風險自動化政策、報表 runtime 啟動前閘門、no-write dry-run 證據包、fixture/readback/verifier dry-run 證據包、shadow/no-write execution gate、操作類別權限模型、13 類候選操作 dry-run 證據、任務結果稽核軌跡、matched PlayBook 學習缺口、critic / reviewer result capture、owner-approved result capture dry-run / readback、result capture writer dry-run fixture、writer dry-run readback、owner promotion execution gate、owner-approved execution rehearsal、owner acceptance / maintenance window gate、owner acceptance readback / preflight hold、owner-approved preflight release package、owner-approved release readiness readback、owner release approval gate 與 post-release verifier / rollback gate。下一步是 `P2-133`;外部 registry / package source / host probe / SSH / kubectl / 工具安裝 / CI 變更 / 實際 PR creation / Telegram 實發與 learning write 仍需 gate。 +AI Agent 主動營運委派與版本生命週期目前完成度:**100%**。已完成 12 類版本 domain、24 類可委派能力、5 種 cadence、8 類 MCP、4 類 RAG memory、只讀 API、`P2-402B` repo-only daily version freshness snapshot、`P2-402C` Renovate / OSV-Scanner / Trivy / Syft / Grype 工具採用批准包、`P2-402D` Telegram action-required digest policy、`P2-402E` Gitea PR 草案 lane、`P2-402F` host OS / K3s / stateful services 版本只讀盤點,以及 `P2-402G` governance UI 顯示可委派能力;`P2-403A` 到 `P2-133` 已補互動、學習證據面、live read model gate、Redis dry-run gate、learning writeback approval package、Telegram receipt approval package、owner-approved learning dry-run preview、runtime write gate review、post-write verifier package、runtime verifier evidence review、報表真相、TG 戰情室收斂、日週月報、Agent 工作量、圖表化報告、風險自動化政策、報表 runtime 啟動前閘門、no-write dry-run 證據包、fixture/readback/verifier dry-run 證據包、shadow/no-write execution gate、操作類別權限模型、13 類候選操作 dry-run 證據、任務結果稽核軌跡、matched PlayBook 學習缺口、critic / reviewer result capture、owner-approved result capture dry-run / readback、result capture writer dry-run fixture、writer dry-run readback、owner promotion execution gate、owner-approved execution rehearsal、owner acceptance / maintenance window gate、owner acceptance readback / preflight hold、owner-approved preflight release package、owner-approved release readiness readback、owner release approval gate、post-release verifier / rollback gate 與 final release candidate readback。下一步是 `P2-134`;外部 registry / package source / host probe / SSH / kubectl / 工具安裝 / CI 變更 / 實際 PR creation / Telegram 實發與 learning write 仍需 gate。 完成度計算模型: diff --git a/docs/evaluations/ai_agent_result_capture_final_release_candidate_readback_2026-06-14.json b/docs/evaluations/ai_agent_result_capture_final_release_candidate_readback_2026-06-14.json new file mode 100644 index 000000000..d741e8ff8 --- /dev/null +++ b/docs/evaluations/ai_agent_result_capture_final_release_candidate_readback_2026-06-14.json @@ -0,0 +1,474 @@ +{ + "schema_version": "ai_agent_result_capture_final_release_candidate_readback_v1", + "generated_at": "2026-06-14T05:55:00+08:00", + "program_status": { + "overall_completion_percent": 100, + "current_priority": "P2", + "current_task_id": "P2-133", + "next_task_id": "P2-134", + "read_only_mode": true, + "runtime_authority": "result_capture_final_release_candidate_readback_only_no_live_write", + "status_note": "P2-133 只把 P2-132 post-release verifier / rollback gate 讀回成 final release candidate readback;不得視為 owner release approved,不得批准 maintenance window,不得確認 rollback owner,不得通過 final candidate,不得釋放 live apply、套用 writer、寫 receipt、寫 result capture、寫 learning、寫 queue、送 Telegram 或寫入 production。" + }, + "source_refs": [ + "docs/evaluations/ai_agent_result_capture_post_release_verifier_rollback_gate_2026-06-14.json", + "docs/evaluations/ai_agent_result_capture_owner_release_approval_gate_2026-06-14.json", + "docs/superpowers/specs/2026-04-15-MASTER-ai-autonomous-flywheel-v2.md#53-建立-post-release-verifier--rollback-gate" + ], + "prior_post_release_verifier_rollback_gate": { + "schema_version": "ai_agent_result_capture_post_release_verifier_rollback_gate_v1", + "post_release_verifier_gate_count": 5, + "rollback_release_gate_count": 5, + "release_verification_hold_count": 5, + "live_apply_post_release_gate_count": 5, + "blocked_post_release_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, + "release_verification_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-132 已把 post-release verifier gate、rollback release gate、release verification hold、live-apply post-release gate 與 blocked post-release transition 固定為只讀 gate;P2-133 只讀回 final release candidate,不視為正式批准。" + }, + "final_release_candidate_truth": { + "p2_132_post_release_gate_loaded": true, + "final_release_candidate_readback_ready": true, + "rollback_candidate_readback_ready": true, + "candidate_acceptance_hold_active": true, + "live_apply_candidate_hold_active": true, + "owner_release_review_still_required": true, + "maintenance_window_review_still_required": true, + "rollback_owner_review_required": true, + "redaction_review_required": true, + "final_release_candidate_readback_only": true, + "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, + "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_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, + "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-133 只建立 final release candidate readback;candidate 可見不代表 final candidate approved、post-release verifier ready、rollback owner confirmed 或 live apply 可執行。" + }, + "final_release_candidate_readbacks": [ + { + "readback_id": "final_candidate_result_capture", + "display_name": "Result capture final release candidate readback", + "owner_agent": "openclaw", + "source_post_release_gate": "post_release_verifier_result_capture", + "status": "ready_for_owner_review", + "readback_mode": "final_release_candidate_readback", + "final_release_candidate_approved": false, + "final_release_candidate_passed": false, + "candidate_summary": "讀回 result capture final candidate;未取得 owner release、maintenance window、rollback owner 與 verifier pass 前不得寫 receipt 或 result capture。", + "evidence_hash": "sha256:f111111111111111111111111111111111111111111111111111111111111111" + }, + { + "readback_id": "final_candidate_learning", + "display_name": "Learning final release candidate readback", + "owner_agent": "hermes", + "source_post_release_gate": "post_release_verifier_learning", + "status": "approval_required", + "readback_mode": "final_release_candidate_readback", + "final_release_candidate_approved": false, + "final_release_candidate_passed": false, + "candidate_summary": "讀回 learning final candidate;不寫 KM、不寫 learning metadata、不更新 PlayBook trust。", + "evidence_hash": "sha256:f222222222222222222222222222222222222222222222222222222222222222" + }, + { + "readback_id": "final_candidate_playbook_trust", + "display_name": "PlayBook trust final release candidate readback", + "owner_agent": "nemotron", + "source_post_release_gate": "post_release_verifier_playbook_trust", + "status": "blocked_by_policy", + "readback_mode": "final_release_candidate_readback", + "final_release_candidate_approved": false, + "final_release_candidate_passed": false, + "candidate_summary": "PlayBook trust 仍需 OpenClaw 仲裁與 reviewer;未完成前不得通過 final candidate。", + "evidence_hash": "sha256:f333333333333333333333333333333333333333333333333333333333333333" + }, + { + "readback_id": "final_candidate_reviewer_queue", + "display_name": "Reviewer queue final release candidate readback", + "owner_agent": "openclaw", + "source_post_release_gate": "post_release_verifier_reviewer_queue", + "status": "approval_required", + "readback_mode": "final_release_candidate_readback", + "final_release_candidate_approved": false, + "final_release_candidate_passed": false, + "candidate_summary": "讀回 reviewer queue final candidate;不寫 reviewer queue、不建立 live queue item。", + "evidence_hash": "sha256:f444444444444444444444444444444444444444444444444444444444444444" + }, + { + "readback_id": "final_candidate_gateway_queue", + "display_name": "Gateway queue final release candidate readback", + "owner_agent": "nemotron", + "source_post_release_gate": "post_release_verifier_gateway_queue", + "status": "ready_for_owner_review", + "readback_mode": "final_release_candidate_readback", + "final_release_candidate_approved": false, + "final_release_candidate_passed": false, + "candidate_summary": "讀回 Gateway queue final candidate;不寫 Gateway queue、不送 Telegram、不呼叫 Bot API。", + "evidence_hash": "sha256:f555555555555555555555555555555555555555555555555555555555555555" + } + ], + "rollback_candidate_readbacks": [ + { + "readback_id": "rollback_candidate_result_capture", + "display_name": "Result capture rollback candidate readback", + "owner_agent": "openclaw", + "status": "ready_for_owner_review", + "readback_mode": "rollback_candidate_readback", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_enabled": false, + "rollback_summary": "讀回 result capture rollback candidate;不得釋放 rollback release。" + }, + { + "readback_id": "rollback_candidate_learning", + "display_name": "Learning rollback candidate readback", + "owner_agent": "hermes", + "status": "approval_required", + "readback_mode": "rollback_candidate_readback", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_enabled": false, + "rollback_summary": "讀回 learning rollback candidate;仍需 owner review。" + }, + { + "readback_id": "rollback_candidate_playbook_trust", + "display_name": "PlayBook trust rollback candidate readback", + "owner_agent": "nemotron", + "status": "blocked_by_policy", + "readback_mode": "rollback_candidate_readback", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_enabled": false, + "rollback_summary": "讀回 PlayBook trust rollback candidate;仍受政策阻擋。" + }, + { + "readback_id": "rollback_candidate_reviewer_queue", + "display_name": "Reviewer queue rollback candidate readback", + "owner_agent": "openclaw", + "status": "approval_required", + "readback_mode": "rollback_candidate_readback", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_enabled": false, + "rollback_summary": "讀回 reviewer queue rollback candidate;不寫 queue。" + }, + { + "readback_id": "rollback_candidate_gateway_queue", + "display_name": "Gateway queue rollback candidate readback", + "owner_agent": "nemotron", + "status": "ready_for_owner_review", + "readback_mode": "rollback_candidate_readback", + "rollback_owner_required": true, + "rollback_owner_confirmed": false, + "rollback_release_enabled": false, + "rollback_summary": "讀回 Gateway queue rollback candidate;不送 Telegram。" + } + ], + "candidate_acceptance_holds": [ + { + "hold_id": "candidate_acceptance_result_capture", + "display_name": "Result capture candidate acceptance hold", + "owner_agent": "openclaw", + "status": "ready_for_owner_review", + "hold_mode": "candidate_acceptance_hold", + "owner_release_approved": false, + "maintenance_window_approved": false, + "final_release_candidate_passed": false, + "hold_reason": "owner release approved、maintenance window approved、rollback owner confirmed 與 verifier ready 尚未成立。" + }, + { + "hold_id": "candidate_acceptance_learning", + "display_name": "Learning candidate acceptance hold", + "owner_agent": "hermes", + "status": "approval_required", + "hold_mode": "candidate_acceptance_hold", + "owner_release_approved": false, + "maintenance_window_approved": false, + "final_release_candidate_passed": false, + "hold_reason": "learning owner、rollback owner 與 maintenance window 尚未 approved。" + }, + { + "hold_id": "candidate_acceptance_playbook_trust", + "display_name": "PlayBook trust candidate acceptance hold", + "owner_agent": "nemotron", + "status": "blocked_by_policy", + "hold_mode": "candidate_acceptance_hold", + "owner_release_approved": false, + "maintenance_window_approved": false, + "final_release_candidate_passed": false, + "hold_reason": "OpenClaw 仲裁、trust reviewer 與負向強化審查尚未完成。" + }, + { + "hold_id": "candidate_acceptance_reviewer_queue", + "display_name": "Reviewer queue candidate acceptance hold", + "owner_agent": "openclaw", + "status": "approval_required", + "hold_mode": "candidate_acceptance_hold", + "owner_release_approved": false, + "maintenance_window_approved": false, + "final_release_candidate_passed": false, + "hold_reason": "reviewer queue owner、rollback owner 與 maintenance window 尚未 approved。" + }, + { + "hold_id": "candidate_acceptance_gateway_queue", + "display_name": "Gateway queue candidate acceptance hold", + "owner_agent": "nemotron", + "status": "ready_for_owner_review", + "hold_mode": "candidate_acceptance_hold", + "owner_release_approved": false, + "maintenance_window_approved": false, + "final_release_candidate_passed": false, + "hold_reason": "Telegram receipt owner、SRE route lock 與 maintenance window 尚未 approved。" + } + ], + "live_apply_candidate_holds": [ + { + "hold_id": "live_apply_candidate_result_capture", + "display_name": "Result capture live apply candidate hold", + "owner_agent": "openclaw", + "status": "ready_for_owner_review", + "hold_mode": "live_apply_candidate_hold", + "final_release_candidate_approved": false, + "live_apply_release_enabled": false, + "release_condition": "P2-134 只能先建立 release authorization hold,不得直接 writer apply。" + }, + { + "hold_id": "live_apply_candidate_learning", + "display_name": "Learning live apply candidate hold", + "owner_agent": "hermes", + "status": "approval_required", + "hold_mode": "live_apply_candidate_hold", + "final_release_candidate_approved": false, + "live_apply_release_enabled": false, + "release_condition": "需要 owner release approval、maintenance window、rollback owner 與 verifier gate 同時通過。" + }, + { + "hold_id": "live_apply_candidate_playbook_trust", + "display_name": "PlayBook trust live apply candidate hold", + "owner_agent": "nemotron", + "status": "blocked_by_policy", + "hold_mode": "live_apply_candidate_hold", + "final_release_candidate_approved": false, + "live_apply_release_enabled": false, + "release_condition": "不得因 final candidate 可見而更新 PlayBook trust。" + }, + { + "hold_id": "live_apply_candidate_reviewer_queue", + "display_name": "Reviewer queue live apply candidate hold", + "owner_agent": "openclaw", + "status": "approval_required", + "hold_mode": "live_apply_candidate_hold", + "final_release_candidate_approved": false, + "live_apply_release_enabled": false, + "release_condition": "不得寫 reviewer queue。" + }, + { + "hold_id": "live_apply_candidate_gateway_queue", + "display_name": "Gateway queue live apply candidate hold", + "owner_agent": "nemotron", + "status": "ready_for_owner_review", + "hold_mode": "live_apply_candidate_hold", + "final_release_candidate_approved": false, + "live_apply_release_enabled": false, + "release_condition": "不得寫 Gateway queue、不得送 Telegram、不得呼叫 Bot API。" + } + ], + "blocked_final_candidate_transitions": [ + { + "blocker_id": "blocked_writer_apply_final_candidate", + "display_name": "Writer apply final candidate blocked", + "severity": "critical", + "status": "blocked_by_policy", + "blocked_action": "writer_apply_after_final_release_candidate_readback", + "blocked_until": "owner release approved + maintenance window approved + final candidate passed + rollback owner confirmed", + "evidence_hash": "sha256:a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1" + }, + { + "blocker_id": "blocked_execution_apply_final_candidate", + "display_name": "Execution apply final candidate blocked", + "severity": "critical", + "status": "blocked_by_policy", + "blocked_action": "execution_apply_after_final_release_candidate_readback", + "blocked_until": "maintenance window approved + rollback release gate passed", + "evidence_hash": "sha256:b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2" + }, + { + "blocker_id": "blocked_receipt_write_final_candidate", + "display_name": "Receipt write final candidate blocked", + "severity": "critical", + "status": "approval_required", + "blocked_action": "receipt_write_after_final_release_candidate_readback", + "blocked_until": "final candidate approved + release verification passed", + "evidence_hash": "sha256:c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3" + }, + { + "blocker_id": "blocked_result_capture_write_final_candidate", + "display_name": "Result capture write final candidate blocked", + "severity": "critical", + "status": "blocked_by_policy", + "blocked_action": "result_capture_write_after_final_release_candidate_readback", + "blocked_until": "owner release approved + final candidate passed + rollback owner confirmed", + "evidence_hash": "sha256:d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4d4" + }, + { + "blocker_id": "blocked_gateway_queue_write_final_candidate", + "display_name": "Gateway queue write final candidate blocked", + "severity": "critical", + "status": "approval_required", + "blocked_action": "gateway_queue_write_after_final_release_candidate_readback", + "blocked_until": "SRE route lock + Telegram receipt owner approved + final candidate passed", + "evidence_hash": "sha256:e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5e5" + }, + { + "blocker_id": "blocked_telegram_send_final_candidate", + "display_name": "Telegram send final candidate blocked", + "severity": "high", + "status": "approval_required", + "blocked_action": "telegram_send_after_final_release_candidate_readback", + "blocked_until": "Gateway queue release approved + Telegram receipt E2E approved + rollback release gate passed", + "evidence_hash": "sha256:f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6" + } + ], + "operator_actions": [ + { + "action_id": "review_final_release_candidate_readbacks", + "owner_agent": "openclaw", + "status": "ready_for_operator_review", + "operator_instruction": "審核 5 個 final release candidate readback 是否仍符合 P2-132 gate 邊界;不要把 candidate 視為正式批准。", + "runtime_write_allowed": false + }, + { + "action_id": "verify_rollback_candidate_readback", + "owner_agent": "hermes", + "status": "ready_for_operator_review", + "operator_instruction": "確認 rollback candidate readback 仍全部未 confirmed,且 rollback_release_enabled 仍為 false。", + "runtime_write_allowed": false + }, + { + "action_id": "verify_candidate_acceptance_hold", + "owner_agent": "nemotron", + "status": "ready_for_operator_review", + "operator_instruction": "確認 candidate acceptance hold 已讀回但仍 hold;不可套用 writer 或執行正式寫入。", + "runtime_write_allowed": false + }, + { + "action_id": "verify_live_apply_candidate_hold", + "owner_agent": "openclaw", + "status": "approval_required", + "operator_instruction": "確認 live apply candidate hold 仍未 enabled,且 final_release_candidate_approved 仍為 false。", + "runtime_write_allowed": false + }, + { + "action_id": "prepare_p2_134_release_authorization_hold", + "owner_agent": "hermes", + "status": "ready_for_operator_review", + "operator_instruction": "下一關只可建立 P2-134 release authorization hold,不可放行 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": "前端只能顯示 final release candidate readback 摘要、狀態、數量、redacted hash 與 0 / false 邊界;不得顯示內部協作環境對話、原始提示詞、私有推理、secret、授權標頭或原始 runtime payload。" + }, + "rollups": { + "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_candidate_count": 2, + "blocked_candidate_count": 1, + "approval_required_rollback_count": 2, + "blocked_rollback_count": 1, + "approval_required_acceptance_hold_count": 2, + "blocked_acceptance_hold_count": 1, + "approval_required_live_apply_count": 2, + "blocked_live_apply_count": 1, + "critical_blocker_count": 5, + "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, + "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_final_release_candidate_readback_v1.schema.json b/docs/schemas/ai_agent_result_capture_final_release_candidate_readback_v1.schema.json new file mode 100644 index 000000000..2a12e265c --- /dev/null +++ b/docs/schemas/ai_agent_result_capture_final_release_candidate_readback_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_final_release_candidate_readback_v1.schema.json", + "title": "AI Agent result capture final release candidate readback v1", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "program_status", + "source_refs", + "prior_post_release_verifier_rollback_gate", + "final_release_candidate_truth", + "final_release_candidate_readbacks", + "rollback_candidate_readbacks", + "candidate_acceptance_holds", + "live_apply_candidate_holds", + "blocked_final_candidate_transitions", + "operator_actions", + "display_redaction_contract", + "rollups" + ], + "properties": { + "schema_version": { + "const": "ai_agent_result_capture_final_release_candidate_readback_v1" + }, + "generated_at": { + "type": "string" + }, + "program_status": { + "type": "object" + }, + "source_refs": { + "type": "array", + "items": { + "type": "string" + } + }, + "prior_post_release_verifier_rollback_gate": { + "type": "object" + }, + "final_release_candidate_truth": { + "type": "object" + }, + "final_release_candidate_readbacks": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "rollback_candidate_readbacks": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "candidate_acceptance_holds": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "live_apply_candidate_holds": { + "type": "array", + "minItems": 5, + "maxItems": 5, + "items": { + "type": "object" + } + }, + "blocked_final_candidate_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 080b00f82..b33afeb00 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 @@ -2125,6 +2125,16 @@ Phase 6 完成後 - 政策裁決:P2-132 只允許 post-release verifier / rollback gate 可視化;不得把 verifier gate 解讀成 post-release verifier ready、release verification passed、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、result capture write、learning write、PlayBook trust write、reviewer queue write、Gateway queue write、Telegram 實發、Bot API 呼叫、report receipt write 或 live writer 已啟用。 - 本波仍不讀 canonical runtime target、不做 live query、不寫 reviewer queue、不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不寫 report receipt、不寫 result capture、不寫 learning、不更新 PlayBook trust、不寫 production target、不讀 secret、不執行 destructive action、不回傳內部協作內容;下一步 P2-133。 +### 2026-06-14 06:14 (台北) — §3.2 / §5 — 本地完成 P2-133 final release candidate readback — 把 post-release gate 讀回成 final candidate 候選狀態 + +- 新增 `ai_agent_result_capture_final_release_candidate_readback_v1` schema / committed snapshot / loader / API / 測試,承接 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。 +- Governance automation inventory 新增 P2-133 區塊,將 final candidate readback、rollback candidate readback、candidate acceptance hold、live-apply candidate hold、blocked final candidate transition 與 operator action 全部以只讀方式呈現,並維持前端 redaction,不回傳內部協作內容。 +- 本地 API/service regression:P2-133 + P2-132 `14 passed`;JSON parse、Python 3.11 py_compile、`pnpm --filter @awoooi/web typecheck` 與 production build 通過;`git diff --check`、`doc-secrets-sanity-check.py docs .gitea`、`source-control-owner-response-guard.py --root .`、`security-mirror-progress-guard.py --root .` 通過。 +- 本地 snapshot 固定 `schema_version=ai_agent_result_capture_final_release_candidate_readback_v1`、current `P2-133`、next `P2-134`、completion `100`;final 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`。 +- 本地 0 / false 邊界: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、secret read 與 destructive operation 均為 `0 / false`。 +- 政策裁決:P2-133 只允許 final release candidate readback 可視化;不得把 candidate readback 解讀成 owner release approved、maintenance window approved、rollback owner confirmed、post-release verifier ready、final candidate passed、rollback release passed、live apply release passed、writer apply、execution apply、receipt write、result capture write、learning write、PlayBook trust write、reviewer queue write、Gateway queue write、Telegram 實發、Bot API 呼叫、report receipt write 或 live writer 已啟用。 +- 本波仍不讀 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-134 承接。 + ### 2026-06-14 07:08 (台北) — §3.2 / §5 — 完成並正式驗證 P2-131 owner release approval gate — 把 readiness 轉成可審核批准門檻 - 新增 `ai_agent_result_capture_owner_release_approval_gate_v1` schema / committed snapshot / loader / API / 測試,承接 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。