feat(agents): 新增高風險 owner review queue
Some checks failed
Code Review / ai-code-review (push) Successful in 14s
CD Pipeline / tests (push) Successful in 1m54s
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled

This commit is contained in:
Your Name
2026-06-19 00:29:36 +08:00
parent 4a14860c60
commit f390cddb4d
13 changed files with 2193 additions and 7 deletions

View File

@@ -0,0 +1,448 @@
"""
P2-409 AI Agent high-risk owner review queue snapshot.
Loads the latest committed high-risk owner review queue. This module only
validates read-only approval packets, rejection guards, and reviewer checklists.
It does not run workers, send Telegram, write Gateway queues, read secrets, call
paid APIs, mutate hosts, run kubectl, or write production state.
"""
from __future__ import annotations
import json
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_high_risk_owner_review_queue_*.json"
_SCHEMA_VERSION = "ai_agent_high_risk_owner_review_queue_v1"
_RUNTIME_AUTHORITY = "high_risk_owner_review_queue_no_live_execution_committed_snapshot"
_EXPECTED_CURRENT_TASK = "P2-409"
_EXPECTED_NEXT_TASK = "P2-410"
_EXPECTED_CANONICAL_ROOM = "AwoooI SRE 戰情室"
_EXPECTED_CANONICAL_ROOM_ENV = "SRE_GROUP_CHAT_ID"
_EXPECTED_SOURCE_SCHEMAS = {
"ai_agent_low_medium_risk_whitelist_v1",
"ai_agent_receipt_readback_owner_review_v1",
"ai_agent_report_source_health_v1",
"awoooi_work_items_report_source_gap_owner_review_v1",
"telegram_notification_egress_inventory_v1",
"telegram_notification_egress_owner_request_draft_v1",
}
_TRUE_TRUTH_FLAGS = {
"p2_408_redirects_loaded",
"p2_406b_receipt_owner_review_loaded",
"p2_110d_report_source_gap_loaded",
"p2_110e_work_items_owner_review_loaded",
"telegram_egress_inventory_loaded",
"telegram_owner_request_draft_loaded",
"all_high_risk_actions_paused",
"approval_packets_ready",
"rejection_guards_ready",
"reviewer_checklists_ready",
"high_risk_owner_review_required",
}
_FALSE_TRUTH_FLAGS = {
"auto_worker_enabled",
"live_execution_enabled",
"gateway_queue_write_enabled",
"telegram_send_enabled",
"bot_api_call_enabled",
"receipt_production_write_enabled",
"production_write_enabled",
"secret_read_enabled",
"paid_api_call_enabled",
"host_write_enabled",
"kubectl_action_enabled",
"destructive_operation_enabled",
"openclaw_replacement_allowed",
}
_ZERO_TRUTH_COUNTS = {
"auto_worker_run_count_24h",
"live_execution_count_24h",
"gateway_queue_write_count_24h",
"telegram_send_count_24h",
"bot_api_call_count_24h",
"receipt_production_write_count_24h",
"production_write_count_24h",
"secret_read_count_24h",
"paid_api_call_count_24h",
"host_write_count_24h",
"kubectl_action_count_24h",
"destructive_operation_count_24h",
"owner_response_received_count_24h",
"owner_response_accepted_count_24h",
"redacted_payload_ingested_count_24h",
}
_TRUE_BOUNDARY_FLAGS = {
"read_only_owner_review_queue_allowed",
"approval_packet_preview_allowed",
"rejection_guard_preview_allowed",
"reviewer_checklist_allowed",
}
_FALSE_BOUNDARY_FLAGS = {
"auto_worker_enabled",
"live_execution_enabled",
"gateway_queue_write_enabled",
"telegram_send_enabled",
"bot_api_call_enabled",
"receipt_production_write_enabled",
"production_write_enabled",
"secret_read_enabled",
"paid_api_call_enabled",
"host_write_enabled",
"kubectl_action_enabled",
"destructive_operation_enabled",
"openclaw_replacement_allowed",
}
_ZERO_ROLLUP_FIELDS = {
"owner_response_received_count",
"owner_response_accepted_count",
"owner_response_rejected_count",
"redacted_payload_ingested_count",
"auto_worker_run_count",
"live_execution_count",
"gateway_queue_write_count",
"telegram_send_count",
"bot_api_call_count",
"receipt_production_write_count",
"production_write_count",
"secret_read_count",
"paid_api_call_count",
"host_write_count",
"kubectl_action_count",
"destructive_operation_count",
}
_FORBIDDEN_PUBLIC_TERMS = {
"批准!繼續",
"In app browser",
"My request for Codex",
"chain_of_thought",
"chain-of-thought",
"private reasoning text",
"authorization_header",
"authorization header value",
"telegram token value",
"raw prompt",
"raw_payload",
}
def load_latest_ai_agent_high_risk_owner_review_queue(
evaluations_dir: Path | None = None,
) -> dict[str, Any]:
"""Load the newest committed P2-409 high-risk owner review queue snapshot."""
directory = evaluations_dir or _DEFAULT_EVALUATIONS_DIR
candidates = sorted(directory.glob(_SNAPSHOT_PATTERN))
if not candidates:
raise FileNotFoundError(f"no AI Agent high-risk owner review queue 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_sources(payload, label)
_require_queue_truth(payload, label)
_require_queue_items(payload, label)
_require_approval_packets(payload, label)
_require_rejection_guards(payload, label)
_require_reviewer_checklists(payload, label)
_require_routing_policy(payload, label)
_require_boundaries(payload, label)
_require_rollups(payload, label)
_require_no_forbidden_public_terms(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 = {
"overall_completion_percent": 100,
"current_priority": "P0",
"current_task_id": _EXPECTED_CURRENT_TASK,
"next_task_id": _EXPECTED_NEXT_TASK,
"read_only_mode": True,
"runtime_authority": _RUNTIME_AUTHORITY,
}
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_sources(payload: dict[str, Any], label: str) -> None:
if not payload.get("source_refs"):
raise ValueError(f"{label}: source_refs must not be empty")
sources = payload.get("source_readbacks") or []
schemas = {item.get("source_schema_version") for item in sources}
missing = sorted(_EXPECTED_SOURCE_SCHEMAS - schemas)
if missing:
raise ValueError(f"{label}: missing source schemas: {missing}")
for item in sources:
readback_id = item.get("readback_id") or "<missing>"
for field in ("source_ref", "endpoint", "owner_agent", "status", "key_readback", "next_action"):
if not item.get(field):
raise ValueError(f"{label}: source readback {readback_id} missing {field}")
def _require_queue_truth(payload: dict[str, Any], label: str) -> None:
truth = payload.get("queue_truth") or {}
missing_true = sorted(flag for flag in _TRUE_TRUTH_FLAGS if truth.get(flag) is not True)
if missing_true:
raise ValueError(f"{label}: queue truth flags must remain true: {missing_true}")
unsafe_false = sorted(flag for flag in _FALSE_TRUTH_FLAGS if truth.get(flag) is not False)
if unsafe_false:
raise ValueError(f"{label}: queue truth flags must remain false: {unsafe_false}")
non_zero = sorted(field for field in _ZERO_TRUTH_COUNTS if truth.get(field) != 0)
if non_zero:
raise ValueError(f"{label}: queue live counts must remain zero: {non_zero}")
if not truth.get("truth_note"):
raise ValueError(f"{label}: queue_truth.truth_note is required")
def _require_queue_items(payload: dict[str, Any], label: str) -> None:
items = payload.get("owner_review_queue_items") or []
if len(items) < 1:
raise ValueError(f"{label}: owner_review_queue_items must not be empty")
risk_tiers = {item.get("risk_tier") for item in items}
if not {"high", "critical"}.issubset(risk_tiers):
raise ValueError(f"{label}: owner_review_queue_items must include high and critical items")
for item in items:
item_id = item.get("queue_item_id") or "<missing>"
if item.get("risk_tier") not in {"high", "critical"}:
raise ValueError(f"{label}: queue item {item_id} must be high or critical")
if item.get("queue_status") not in {
"paused_owner_review_required",
"blocked_missing_owner_response",
"approval_packet_preview_ready",
}:
raise ValueError(f"{label}: queue item {item_id} status is invalid")
for field in (
"source_readback_ids",
"approval_packet_id",
"rejection_guard_ids",
"reviewer_checklist_ids",
"required_owner_fields",
"blocked_runtime_actions",
"next_gate",
):
if not item.get(field):
raise ValueError(f"{label}: queue item {item_id} missing {field}")
for flag in ("owner_response_required", "rollback_owner_required", "postcheck_required"):
if item.get(flag) is not True:
raise ValueError(f"{label}: queue item {item_id}.{flag} must remain true")
for flag in (
"live_execution_allowed",
"gateway_queue_write_allowed",
"telegram_send_allowed",
"production_write_allowed",
):
if item.get(flag) is not False:
raise ValueError(f"{label}: queue item {item_id}.{flag} must remain false")
if item.get("side_effect_count") != 0:
raise ValueError(f"{label}: queue item {item_id} side_effect_count must remain zero")
def _require_approval_packets(payload: dict[str, Any], label: str) -> None:
packets = payload.get("approval_packets") or []
packet_ids = {item.get("approval_packet_id") for item in packets}
queue_packet_ids = {item.get("approval_packet_id") for item in payload.get("owner_review_queue_items") or []}
missing = sorted(queue_packet_ids - packet_ids)
if missing:
raise ValueError(f"{label}: missing approval packets referenced by queue items: {missing}")
queue_ids = {item.get("queue_item_id") for item in payload.get("owner_review_queue_items") or []}
for packet in packets:
packet_id = packet.get("approval_packet_id") or "<missing>"
if packet.get("queue_item_id") not in queue_ids:
raise ValueError(f"{label}: approval packet {packet_id} references unknown queue item")
if packet.get("packet_status") not in {"draft_ready_owner_response_required", "blocked_missing_owner_response"}:
raise ValueError(f"{label}: approval packet {packet_id} status is invalid")
for field in ("required_owner_fields", "required_evidence_refs", "reviewer_checklist_id", "rejection_guard_ids"):
if not packet.get(field):
raise ValueError(f"{label}: approval packet {packet_id} missing {field}")
for flag in ("rollback_owner_required", "postcheck_required"):
if packet.get(flag) is not True:
raise ValueError(f"{label}: approval packet {packet_id}.{flag} must remain true")
for flag in (
"sensitive_payload_allowed",
"live_execution_allowed",
"gateway_queue_write_allowed",
"telegram_send_allowed",
"production_write_allowed",
):
if packet.get(flag) is not False:
raise ValueError(f"{label}: approval packet {packet_id}.{flag} must remain false")
def _require_rejection_guards(payload: dict[str, Any], label: str) -> None:
guards = payload.get("rejection_guards") or []
guard_ids = {item.get("guard_id") for item in guards}
referenced_ids = {
guard_id
for item in payload.get("owner_review_queue_items") or []
for guard_id in (item.get("rejection_guard_ids") or [])
} | {
guard_id
for item in payload.get("approval_packets") or []
for guard_id in (item.get("rejection_guard_ids") or [])
}
missing = sorted(referenced_ids - guard_ids)
if missing:
raise ValueError(f"{label}: missing rejection guards referenced by packets or queue items: {missing}")
for guard in guards:
guard_id = guard.get("guard_id") or "<missing>"
tiers = set(guard.get("applies_to_risk_tiers") or [])
if not tiers or not tiers.issubset({"high", "critical"}):
raise ValueError(f"{label}: rejection guard {guard_id} tiers are invalid")
for field in ("rejection_condition", "blocked_runtime_actions", "reviewer_action"):
if not guard.get(field):
raise ValueError(f"{label}: rejection guard {guard_id} missing {field}")
def _require_reviewer_checklists(payload: dict[str, Any], label: str) -> None:
checklists = payload.get("reviewer_checklists") or []
checklist_ids = {item.get("checklist_id") for item in checklists}
referenced_ids = {
checklist_id
for item in payload.get("owner_review_queue_items") or []
for checklist_id in (item.get("reviewer_checklist_ids") or [])
} | {item.get("reviewer_checklist_id") for item in payload.get("approval_packets") or []}
missing = sorted(referenced_ids - checklist_ids)
if missing:
raise ValueError(f"{label}: missing reviewer checklists referenced by packets or queue items: {missing}")
for checklist in checklists:
checklist_id = checklist.get("checklist_id") or "<missing>"
if not checklist.get("required_checks") or not checklist.get("pass_condition"):
raise ValueError(f"{label}: reviewer checklist {checklist_id} missing checks or pass condition")
for flag in ("approval_decision_allowed", "checklist_write_allowed"):
if checklist.get(flag) is not False:
raise ValueError(f"{label}: reviewer checklist {checklist_id}.{flag} must remain false")
if checklist.get("side_effect_count") != 0:
raise ValueError(f"{label}: reviewer checklist {checklist_id} side_effect_count must remain zero")
def _require_routing_policy(payload: dict[str, Any], label: str) -> None:
policy = payload.get("routing_policy") or {}
expected = {
"high_risk_default_route": "pause_to_owner_review_queue",
"critical_risk_default_route": "pause_to_owner_review_queue",
"low_medium_runtime_route": "pause_until_owner_approved_runtime_gate",
"owner_response_required": True,
"verbal_approval_accepted": False,
"redacted_payload_only": True,
}
mismatches = _mismatches(policy, expected)
if mismatches:
raise ValueError(f"{label}: routing_policy mismatch: {mismatches}")
def _require_boundaries(payload: dict[str, Any], label: str) -> None:
boundaries = payload.get("activation_boundaries") or {}
missing_true = sorted(flag for flag in _TRUE_BOUNDARY_FLAGS if boundaries.get(flag) is not True)
if missing_true:
raise ValueError(f"{label}: activation boundaries must remain true: {missing_true}")
unsafe_false = sorted(flag for flag in _FALSE_BOUNDARY_FLAGS if boundaries.get(flag) is not False)
if unsafe_false:
raise ValueError(f"{label}: activation boundaries must remain false: {unsafe_false}")
telegram = payload.get("telegram_policy") or {}
expected_telegram = {
"canonical_room": _EXPECTED_CANONICAL_ROOM,
"canonical_room_env": _EXPECTED_CANONICAL_ROOM_ENV,
"gateway_queue_write_allowed": False,
"direct_bot_api_allowed": False,
"telegram_send_allowed": False,
"receipt_write_allowed": False,
}
mismatches = _mismatches(telegram, expected_telegram)
if mismatches:
raise ValueError(f"{label}: telegram_policy mismatch: {mismatches}")
redaction = payload.get("display_redaction_contract") or {}
if redaction.get("redaction_required") is not True:
raise ValueError(f"{label}: display redaction must remain required")
for flag in (
"unsafe_payload_display_allowed",
"private_reasoning_display_allowed",
"secret_value_display_allowed",
"work_window_transcript_display_allowed",
):
if redaction.get(flag) is not False:
raise ValueError(f"{label}: display redaction flag {flag} must remain false")
def _require_rollups(payload: dict[str, Any], label: str) -> None:
rollups = payload.get("rollups") or {}
items = payload.get("owner_review_queue_items") or []
packets = payload.get("approval_packets") or []
guards = payload.get("rejection_guards") or []
checklists = payload.get("reviewer_checklists") or []
sources = payload.get("source_readbacks") or []
blocked_actions = {
*(
action
for item in items
for action in (item.get("blocked_runtime_actions") or [])
),
*(
action
for guard in guards
for action in (guard.get("blocked_runtime_actions") or [])
),
}
blocked_actions.discard(None)
expected = {
"source_readback_count": len(sources),
"queue_item_count": len(items),
"high_risk_queue_count": sum(1 for item in items if item.get("risk_tier") == "high"),
"critical_queue_count": sum(1 for item in items if item.get("risk_tier") == "critical"),
"approval_packet_count": len(packets),
"rejection_guard_count": len(guards),
"reviewer_checklist_count": len(checklists),
"approval_packet_required_count": len(items),
"rejection_guard_required_queue_count": sum(1 for item in items if item.get("rejection_guard_ids")),
"rollback_owner_required_count": sum(1 for item in items if item.get("rollback_owner_required") is True),
"postcheck_required_count": sum(1 for item in items if item.get("postcheck_required") is True),
"blocked_runtime_action_count": len(blocked_actions),
}
mismatches = {
key: {"expected": value, "actual": rollups.get(key)}
for key, value in expected.items()
if rollups.get(key) != value
}
if mismatches:
raise ValueError(f"{label}: rollup counts must match payload sections: {mismatches}")
non_zero = sorted(field for field in _ZERO_ROLLUP_FIELDS if rollups.get(field) != 0)
if non_zero:
raise ValueError(f"{label}: live rollup counts must remain zero: {non_zero}")
def _require_no_forbidden_public_terms(payload: dict[str, Any], label: str) -> None:
public_text = json.dumps(payload, ensure_ascii=False)
lower_public_text = public_text.lower()
leaked_terms = sorted(
term
for term in _FORBIDDEN_PUBLIC_TERMS
if (term.lower() if term.isascii() else term) in lower_public_text
)
if leaked_terms:
raise ValueError(f"{label}: forbidden public terms present: {leaked_terms}")
def _mismatches(actual: dict[str, Any], expected: dict[str, Any]) -> dict[str, dict[str, Any]]:
return {
key: {"expected": expected_value, "actual": actual.get(key)}
for key, expected_value in expected.items()
if actual.get(key) != expected_value
}