feat(governance): 新增 release verifier owner review packet
All checks were successful
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / tests (push) Successful in 1m29s
CD Pipeline / build-and-deploy (push) Successful in 5m28s
CD Pipeline / post-deploy-checks (push) Successful in 28s

This commit is contained in:
Your Name
2026-06-14 10:17:17 +08:00
parent 5714cd34f1
commit f737f278dc
13 changed files with 1807 additions and 5 deletions

View File

@@ -154,6 +154,9 @@ from src.services.ai_agent_result_capture_release_authorization_readback_gate im
from src.services.ai_agent_result_capture_release_verifier_preflight_gate import (
load_latest_ai_agent_result_capture_release_verifier_preflight_gate,
)
from src.services.ai_agent_result_capture_release_verifier_owner_review_packet import (
load_latest_ai_agent_result_capture_release_verifier_owner_review_packet,
)
from src.services.ai_agent_result_capture_post_release_verifier_rollback_gate import (
load_latest_ai_agent_result_capture_post_release_verifier_rollback_gate,
)
@@ -2221,6 +2224,38 @@ async def get_agent_result_capture_release_verifier_preflight_gate() -> dict[str
) from exc
@router.get(
"/agent-result-capture-release-verifier-owner-review-packet",
response_model=dict[str, Any],
summary="取得 AI Agent result capture release verifier owner review packet",
description=(
"讀取最新已提交的 P2-137 release verifier owner review packet"
"此端點只回傳 owner review packet、verifier review packet、rollback owner review、maintenance window review hold、"
"live apply owner review hold、blocked owner review transition 與 operator action"
"不授權 owner release、不批准維護窗口、不確認 rollback owner、不通過 post-release verifier、"
"不核發或通過 release authorization、不釋放 rollback release 或 live apply、不套用 writer、"
"不寫 receipt、result capture、learning、PlayBook trust、reviewer queue 或 Gateway queue"
"不送 Telegram、不呼叫 Bot API、不讀 secret、不執行 production 寫入。"
),
)
async def get_agent_result_capture_release_verifier_owner_review_packet() -> dict[str, Any]:
"""Return the latest read-only release verifier owner review packet."""
try:
payload = await asyncio.to_thread(load_latest_ai_agent_result_capture_release_verifier_owner_review_packet)
return redact_public_lan_topology(payload)
except FileNotFoundError as exc:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=str(exc),
) from exc
except (json.JSONDecodeError, ValueError) as exc:
logger.error("ai_agent_result_capture_release_verifier_owner_review_packet_invalid", error=str(exc))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="AI Agent result capture release verifier owner review packet 無效",
) from exc
@router.get(
"/agent-owner-approved-fixture-dry-run",
response_model=dict[str, Any],

View File

@@ -0,0 +1,392 @@
"""
AI Agent result capture release verifier owner review packet snapshot.
Loads the latest committed P2-137 owner review packet. This module validates
committed evidence only; it never authorizes owner release, approves
maintenance windows, confirms rollback owners, passes release authorization,
releases live apply, applies writers, writes receipts, writes result captures,
writes learning records, updates PlayBook trust, writes reviewer / Gateway
queues, sends Telegram messages, reads secrets, or performs destructive
operations.
"""
from __future__ import annotations
import json
import re
from pathlib import Path
from typing import Any
from src.services.snapshot_paths import default_evaluations_dir
_DEFAULT_EVALUATIONS_DIR = default_evaluations_dir(Path(__file__))
_SNAPSHOT_PATTERN = "ai_agent_result_capture_release_verifier_owner_review_packet_*.json"
_SCHEMA_VERSION = "ai_agent_result_capture_release_verifier_owner_review_packet_v1"
_RUNTIME_AUTHORITY = "result_capture_release_verifier_owner_review_packet_only_no_live_write"
def load_latest_ai_agent_result_capture_release_verifier_owner_review_packet(
evaluations_dir: Path | None = None,
) -> dict[str, Any]:
"""Load the newest committed release verifier owner review packet."""
directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR
candidates = sorted(directory.glob(_SNAPSHOT_PATTERN))
if not candidates:
raise FileNotFoundError(f"no AI Agent result capture release verifier owner review packet 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_owner_review_packets(payload, label)
_require_verifier_packets(payload, label)
_require_rollback_owner_packets(payload, label)
_require_maintenance_holds(payload, label)
_require_live_apply_holds(payload, label)
_require_blocked_transitions(payload, label)
_require_actions(payload, label)
_require_display_redaction(payload, label)
_require_no_forbidden_display_terms(payload, label)
_require_rollup_consistency(payload, label)
return payload
def _require_schema(payload: dict[str, Any], label: str) -> None:
if payload.get("schema_version") != _SCHEMA_VERSION:
raise ValueError(f"{label}: expected schema_version={_SCHEMA_VERSION}")
status = payload.get("program_status") or {}
expected = {
"current_priority": "P2",
"current_task_id": "P2-137",
"next_task_id": "P2-138",
"read_only_mode": True,
"runtime_authority": _RUNTIME_AUTHORITY,
"overall_completion_percent": 100,
}
mismatches = _mismatches(status, expected)
if mismatches:
raise ValueError(f"{label}: program_status mismatch: {mismatches}")
if not status.get("status_note"):
raise ValueError(f"{label}: program_status.status_note is required")
def _require_prior(payload: dict[str, Any], label: str) -> None:
prior = payload.get("prior_release_verifier_preflight_gate") or {}
expected = {
"schema_version": "ai_agent_result_capture_release_verifier_preflight_gate_v1",
"release_verifier_preflight_count": 5,
"rollback_verifier_preflight_count": 5,
"maintenance_window_verifier_hold_count": 5,
"live_apply_verifier_hold_count": 5,
"blocked_verifier_preflight_transition_count": 6,
"operator_action_count": 5,
"approval_required_total": 8,
"blocked_total": 9,
}
expected.update(_zero_count_expectations(include_owner_review=True))
mismatches = _mismatches(prior, expected)
if mismatches:
raise ValueError(f"{label}: prior_release_verifier_preflight_gate mismatch: {mismatches}")
if not prior.get("readiness_note"):
raise ValueError(f"{label}: prior_release_verifier_preflight_gate.readiness_note is required")
def _require_truth(payload: dict[str, Any], label: str) -> None:
truth = payload.get("release_verifier_owner_review_truth") or {}
required_true = {
"p2_136_release_verifier_preflight_gate_loaded",
"owner_review_packet_ready",
"release_verifier_review_required",
"rollback_owner_review_required",
"maintenance_window_review_required",
"live_apply_review_required",
"redaction_review_required",
"owner_review_packet_only",
}
missing = sorted(field for field in required_true if truth.get(field) is not True)
if missing:
raise ValueError(f"{label}: owner review packet flags must remain true: {missing}")
required_false = {
"owner_release_authorized",
"owner_release_approved",
"owner_review_approved",
"maintenance_window_approved",
"rollback_owner_confirmed",
"post_release_verifier_ready",
"final_release_candidate_approved",
"final_release_candidate_passed",
"release_authorization_granted",
"release_authorization_passed",
"rollback_release_passed",
"live_apply_release_passed",
"writer_apply_enabled",
"execution_apply_enabled",
"receipt_write_enabled",
"reviewer_queue_write_enabled",
"gateway_queue_write_enabled",
"telegram_send_enabled",
"bot_api_call_enabled",
"report_receipt_write_enabled",
"result_capture_write_enabled",
"learning_write_enabled",
"playbook_trust_write_enabled",
"production_write_enabled",
"secret_read_enabled",
"destructive_operation_enabled",
}
unsafe = sorted(field for field in required_false if truth.get(field) is not False)
if unsafe:
raise ValueError(f"{label}: owner review packet live/send/write flags must remain false: {unsafe}")
non_zero = sorted(field for field in _zero_count_expectations(include_owner_review=True) if truth.get(field) != 0)
if non_zero:
raise ValueError(f"{label}: owner review packet live counters must remain zero: {non_zero}")
if not truth.get("truth_note"):
raise ValueError(f"{label}: release_verifier_owner_review_truth.truth_note is required")
def _require_owner_review_packets(payload: dict[str, Any], label: str) -> None:
items = payload.get("owner_review_packets") or []
_require_ids(
items,
"packet_id",
{
"owner_review_packet_result_capture",
"owner_review_packet_learning",
"owner_review_packet_playbook_trust",
"owner_review_packet_reviewer_queue",
"owner_review_packet_gateway_queue",
},
label,
"owner review packets",
)
for item in items:
item_id = item.get("packet_id")
if item.get("packet_mode") != "release_verifier_owner_review_packet":
raise ValueError(f"{label}: owner review packet {item_id} mode is invalid")
if item.get("owner_release_authorized") is not False or item.get("owner_review_approved") is not False:
raise ValueError(f"{label}: owner review packet {item_id} must stay unauthorized and unapproved")
if item.get("release_authorization_granted") is not False or item.get("release_authorization_passed") is not False:
raise ValueError(f"{label}: owner review packet {item_id} must stay ungranted and unpassed")
_require_valid_status(item, label, f"owner review packet {item_id}")
if not item.get("review_summary") or not _is_redacted_sha256(item.get("evidence_hash")):
raise ValueError(f"{label}: owner review packet {item_id} must include review_summary and redacted evidence_hash")
def _require_verifier_packets(payload: dict[str, Any], label: str) -> None:
items = payload.get("verifier_review_packets") or []
_require_count(items, 5, label, "verifier review packets")
for item in items:
item_id = item.get("packet_id")
if item.get("packet_mode") != "release_verifier_review_packet":
raise ValueError(f"{label}: verifier review packet {item_id} mode is invalid")
if item.get("post_release_verifier_required") is not True:
raise ValueError(f"{label}: verifier review packet {item_id} must require post-release verifier")
if item.get("post_release_verifier_ready") is not False or item.get("release_authorization_passed") is not False:
raise ValueError(f"{label}: verifier review packet {item_id} must stay unready and unpassed")
_require_valid_status(item, label, f"verifier review packet {item_id}")
def _require_rollback_owner_packets(payload: dict[str, Any], label: str) -> None:
items = payload.get("rollback_owner_review_packets") or []
_require_count(items, 5, label, "rollback owner review packets")
for item in items:
item_id = item.get("packet_id")
if item.get("packet_mode") != "rollback_owner_review_packet":
raise ValueError(f"{label}: rollback owner review packet {item_id} mode is invalid")
if item.get("rollback_owner_required") is not True:
raise ValueError(f"{label}: rollback owner review packet {item_id} must require rollback owner")
if item.get("rollback_owner_confirmed") is not False or item.get("rollback_release_passed") is not False:
raise ValueError(f"{label}: rollback owner review packet {item_id} must stay unconfirmed and unpassed")
_require_valid_status(item, label, f"rollback owner review packet {item_id}")
def _require_maintenance_holds(payload: dict[str, Any], label: str) -> None:
items = payload.get("maintenance_window_review_holds") or []
_require_count(items, 5, label, "maintenance window review holds")
for item in items:
item_id = item.get("hold_id")
if item.get("hold_mode") != "maintenance_window_owner_review_hold":
raise ValueError(f"{label}: maintenance window review hold {item_id} mode is invalid")
if item.get("maintenance_window_required") is not True:
raise ValueError(f"{label}: maintenance window review hold {item_id} must require maintenance window")
if item.get("owner_release_authorized") is not False or item.get("maintenance_window_approved") is not False:
raise ValueError(f"{label}: maintenance window review hold {item_id} must keep owner release and maintenance unapproved")
_require_valid_status(item, label, f"maintenance window review hold {item_id}")
def _require_live_apply_holds(payload: dict[str, Any], label: str) -> None:
items = payload.get("live_apply_owner_review_holds") or []
_require_count(items, 5, label, "live apply owner review holds")
for item in items:
item_id = item.get("hold_id")
if item.get("hold_mode") != "live_apply_owner_review_hold":
raise ValueError(f"{label}: live apply owner review hold {item_id} mode is invalid")
if (
item.get("release_authorization_granted") is not False
or item.get("release_authorization_passed") is not False
or item.get("live_apply_release_passed") is not False
or item.get("live_apply_release_enabled") is not False
):
raise ValueError(f"{label}: live apply owner review hold {item_id} must stay ungranted, unpassed, and disabled")
_require_valid_status(item, label, f"live apply owner review hold {item_id}")
def _require_blocked_transitions(payload: dict[str, Any], label: str) -> None:
items = payload.get("blocked_owner_review_transitions") or []
_require_count(items, 6, label, "blocked owner review transitions")
for item in items:
item_id = item.get("transition_id")
if item.get("runtime_write_allowed") is not False:
raise ValueError(f"{label}: blocked owner review transition {item_id} must keep runtime_write_allowed=false")
if item.get("status") not in {"blocked_by_policy", "approval_required"}:
raise ValueError(f"{label}: blocked owner review transition {item_id} status is invalid")
if not item.get("reason"):
raise ValueError(f"{label}: blocked owner review transition {item_id}.reason is required")
def _require_actions(payload: dict[str, Any], label: str) -> None:
items = payload.get("operator_actions") or []
_require_count(items, 5, label, "operator actions")
for item in items:
item_id = item.get("action_id")
if item.get("runtime_write_allowed") is not False:
raise ValueError(f"{label}: operator action {item_id} must keep runtime_write_allowed=false")
if item.get("status") not in {"ready_for_operator_review", "approval_required"}:
raise ValueError(f"{label}: operator action {item_id} status is invalid")
if not item.get("operator_instruction"):
raise ValueError(f"{label}: operator action {item_id}.operator_instruction is required")
def _require_display_redaction(payload: dict[str, Any], label: str) -> None:
contract = payload.get("display_redaction_contract") or {}
expected = {
"redaction_required": True,
"raw_prompt_display_allowed": False,
"private_reasoning_display_allowed": False,
"secret_value_display_allowed": False,
"raw_runtime_payload_display_allowed": False,
"internal_collaboration_content_display_allowed": False,
}
mismatches = _mismatches(contract, expected)
if mismatches:
raise ValueError(f"{label}: display_redaction_contract mismatch: {mismatches}")
if not contract.get("frontend_display_policy"):
raise ValueError(f"{label}: display_redaction_contract.frontend_display_policy is required")
def _require_rollup_consistency(payload: dict[str, Any], label: str) -> None:
rollups = payload.get("rollups") or {}
expected = {
"owner_review_packet_count": len(payload.get("owner_review_packets") or []),
"verifier_review_packet_count": len(payload.get("verifier_review_packets") or []),
"rollback_owner_review_packet_count": len(payload.get("rollback_owner_review_packets") or []),
"maintenance_window_review_hold_count": len(payload.get("maintenance_window_review_holds") or []),
"live_apply_owner_review_hold_count": len(payload.get("live_apply_owner_review_holds") or []),
"blocked_owner_review_transition_count": len(payload.get("blocked_owner_review_transitions") or []),
"operator_action_count": len(payload.get("operator_actions") or []),
}
expected.update(_zero_count_expectations(include_owner_review=True))
mismatches = _mismatches(rollups, expected)
if mismatches:
raise ValueError(f"{label}: rollups mismatch: {mismatches}")
def _require_no_forbidden_display_terms(payload: Any, label: str) -> None:
forbidden = {
"work_window_transcript",
"internal_collaboration_transcript",
"raw_prompt",
"private_reasoning",
"chain_of_thought",
"authorization_header",
"secret_value",
}
found = sorted(term for term in forbidden if _contains_term(payload, term))
if found:
raise ValueError(f"{label}: forbidden display terms leaked: {found}")
def _contains_term(value: Any, term: str) -> bool:
if isinstance(value, str):
return term.lower() in value.lower()
if isinstance(value, dict):
return any(_contains_term(child, term) for child in value.values())
if isinstance(value, list):
return any(_contains_term(child, term) for child in value)
return False
def _zero_count_expectations(*, include_owner_review: bool) -> dict[str, int]:
counts = {
"owner_release_authorized_count": 0,
"owner_release_approved_count": 0,
"maintenance_window_approved_count": 0,
"rollback_owner_confirmed_count": 0,
"post_release_verifier_ready_count": 0,
"final_release_candidate_approved_count": 0,
"final_release_candidate_pass_count": 0,
"release_authorization_granted_count": 0,
"release_authorization_pass_count": 0,
"rollback_release_pass_count": 0,
"live_apply_release_pass_count": 0,
"writer_apply_count": 0,
"execution_apply_count": 0,
"receipt_write_count": 0,
"reviewer_queue_write_count": 0,
"gateway_queue_write_count": 0,
"telegram_send_count": 0,
"bot_api_call_count": 0,
"report_receipt_write_count": 0,
"result_capture_write_count": 0,
"learning_write_count": 0,
"playbook_trust_write_count": 0,
"production_write_count": 0,
"secret_read_count": 0,
"destructive_operation_count": 0,
}
if include_owner_review:
counts["owner_review_approved_count"] = 0
return counts
def _require_ids(
items: list[dict[str, Any]],
field: str,
expected_ids: set[str],
label: str,
group_name: str,
) -> None:
actual_ids = {str(item.get(field)) for item in items}
if actual_ids != expected_ids:
raise ValueError(f"{label}: {group_name} ids mismatch: expected={sorted(expected_ids)} actual={sorted(actual_ids)}")
def _require_count(items: list[Any], expected: int, label: str, group_name: str) -> None:
if len(items) != expected:
raise ValueError(f"{label}: expected {expected} {group_name}, got {len(items)}")
def _require_valid_status(item: dict[str, Any], label: str, item_label: str) -> None:
if item.get("status") not in {"ready_for_owner_review", "approval_required", "blocked_by_policy"}:
raise ValueError(f"{label}: {item_label} status is invalid")
def _is_redacted_sha256(value: Any) -> bool:
return isinstance(value, str) and re.fullmatch(r"sha256:[0-9a-f]{64}", value) is not None
def _mismatches(payload: dict[str, Any], expected: dict[str, Any]) -> dict[str, dict[str, Any]]:
return {
key: {"expected": expected_value, "actual": payload.get(key)}
for key, expected_value in expected.items()
if payload.get(key) != expected_value
}