feat(governance): 新增 TG canary owner review gate
Some checks failed
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / tests (push) Successful in 1m46s
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-18 09:38:22 +08:00
parent 52272a942d
commit efde109760
13 changed files with 2734 additions and 38 deletions

View File

@@ -768,7 +768,7 @@ async def get_agent_12_agent_war_room() -> dict[str, Any]:
response_model=dict[str, Any],
summary="取得 AI Agent 專業任務擴展與 Telegram Runtime Bridge 快照",
description=(
"讀取最新已提交的 P2-405A AI Agent 專業任務擴展與 Telegram Runtime Bridge 只讀快照;"
"讀取最新已提交的 P2-405F AI Agent 專業任務擴展與 Telegram Runtime Bridge 只讀快照;"
"此端點只呈現 OpenClaw、Hermes、NemoTron 與專責 Agent 可承接的專業任務、MCP/RAG、"
"風險分層、Telegram no-send preview 與後續 canary gate"
"不寫 Gateway queue、不送 Telegram、不呼叫 Bot API、不讀 secret、不執行 production write、"

View File

@@ -1,11 +1,11 @@
"""
AI Agent professional task expansion and Telegram runtime bridge snapshot.
Loads the latest committed P2-405E read-only contract. The contract expands
Loads the latest committed P2-405F read-only contract. The contract expands
professional AI Agent work and defines Telegram no-send previews plus canary
delivery rehearsal evidence, but it does not write Telegram Gateway queues,
send Telegram messages, call the Bot API, read secrets, or execute production
changes.
delivery rehearsal and owner review gate evidence, but it does not write
Telegram Gateway queues, send Telegram messages, call the Bot API, read secrets,
or execute production changes.
"""
from __future__ import annotations
@@ -32,6 +32,7 @@ _EXPECTED_CANARY_PACKAGE_COUNT = 1
_EXPECTED_CANARY_APPROVAL_PACKET_COUNT = 1
_EXPECTED_CANARY_DELIVERY_GATE_COUNT = 1
_EXPECTED_CANARY_DELIVERY_REHEARSAL_COUNT = 1
_EXPECTED_CANARY_LIVE_DELIVERY_OWNER_REVIEW_GATE_COUNT = 1
_ZERO_ROLLUP_FIELDS = {
"current_live_count",
"gateway_queue_write_count",
@@ -68,6 +69,14 @@ _ZERO_ROLLUP_FIELDS = {
"canary_delivery_rehearsal_gateway_queue_write_enabled_count",
"canary_delivery_rehearsal_bot_api_call_enabled_count",
"canary_delivery_rehearsal_receipt_write_enabled_count",
"canary_live_delivery_owner_review_received_count",
"canary_live_delivery_owner_review_accepted_count",
"canary_live_delivery_approved_count",
"canary_live_delivery_attempt_allowed_count",
"canary_live_delivery_gateway_queue_write_enabled_count",
"canary_live_delivery_bot_api_call_enabled_count",
"canary_live_delivery_telegram_send_enabled_count",
"canary_live_delivery_receipt_write_enabled_count",
}
_FORBIDDEN_PUBLIC_TERMS = {
"work_window_transcript",
@@ -115,11 +124,11 @@ def _require_schema(payload: dict[str, Any], label: str) -> None:
status = payload.get("program_status") or {}
expected = {
"current_priority": "P2",
"current_task_id": "P2-405E",
"next_task_id": "P2-405F",
"current_task_id": "P2-405F",
"next_task_id": "P2-406B",
"read_only_mode": True,
"runtime_authority": _RUNTIME_AUTHORITY,
"overall_completion_percent": 98,
"overall_completion_percent": 99,
}
mismatches = _mismatches(status, expected)
if mismatches:
@@ -162,6 +171,7 @@ def _require_telegram_bridge(payload: dict[str, Any], label: str) -> None:
_require_canary_send_approval_packet(bridge, label)
_require_canary_delivery_gate(bridge, label)
_require_canary_delivery_rehearsal(bridge, label)
_require_canary_live_delivery_owner_review_gate(bridge, label)
def _require_no_send_previews(bridge: dict[str, Any], label: str) -> None:
@@ -509,6 +519,96 @@ def _require_canary_delivery_rehearsal(bridge: dict[str, Any], label: str) -> No
raise ValueError(f"{label}: canary rehearsal execution flags mismatch: {mismatches}")
def _require_canary_live_delivery_owner_review_gate(
bridge: dict[str, Any], label: str
) -> None:
gate = bridge.get("canary_live_delivery_owner_review_gate") or {}
expected = {
"status": "ready_for_owner_review_no_send",
"gate_ready": True,
"approval_required": True,
"owner_review_received": False,
"owner_review_accepted": False,
"live_canary_delivery_approved": False,
"delivery_attempt_allowed": False,
"prior_rehearsal_status": "ready_no_send_rehearsal",
"prior_readback_completed_check_count": 8,
"prior_readback_failed_check_count": 0,
"selected_message_type": "daily_agent_workload_digest",
"selected_preview_id": "p2_405b_preview_daily_agent_workload_digest_v1",
"selected_receipt_expectation_id": "p2_405b_receipt_daily_agent_workload_digest_v1",
"target_room_env": "SRE_GROUP_CHAT_ID",
"target_room_value_visible": False,
"owner_agent": "telegram_ops_liaison",
"receipt_readback_owner": "hermes",
"arbiter": "openclaw",
}
mismatches = _mismatches(gate, expected)
if mismatches:
raise ValueError(
f"{label}: canary_live_delivery_owner_review_gate mismatch: {mismatches}"
)
if not gate:
raise ValueError(
f"{label}: expected "
f"{_EXPECTED_CANARY_LIVE_DELIVERY_OWNER_REVIEW_GATE_COUNT} "
"canary live delivery owner review gate"
)
required_fields = gate.get("required_owner_fields") or []
if len(required_fields) != 9:
raise ValueError(f"{label}: canary live owner review must define 9 fields")
for field in required_fields:
if field.get("required") is not True:
raise ValueError(f"{label}: canary live owner field must be required")
if field.get("current_value_status") != "waiting_owner_response":
raise ValueError(
f"{label}: canary live owner field must wait for owner response"
)
if field.get("value_display_allowed") is not False:
raise ValueError(f"{label}: canary live owner field values must stay hidden")
if len(gate.get("acceptance_checks") or []) != 9:
raise ValueError(f"{label}: canary live owner review must define 9 checks")
if len(gate.get("rejection_reasons") or []) != 8:
raise ValueError(f"{label}: canary live owner review must define 8 rejections")
if len(gate.get("reviewer_actions") or []) != 6:
raise ValueError(f"{label}: canary live owner review must define 6 actions")
receipt = gate.get("receipt_readback_plan") or {}
if receipt.get("owner_agent") != "hermes":
raise ValueError(f"{label}: canary live owner receipt owner must be hermes")
if len(receipt.get("required_checks") or []) != 8:
raise ValueError(f"{label}: canary live owner receipt plan must define 8 checks")
if receipt.get("completed_check_count") != 0:
raise ValueError(f"{label}: canary live owner receipt checks must not complete yet")
if receipt.get("failed_check_count") != 0:
raise ValueError(f"{label}: canary live owner receipt failed checks must be zero")
if receipt.get("production_receipt_write_enabled") is not False:
raise ValueError(f"{label}: canary live owner receipt write must remain false")
if receipt.get("live_receipt_readback_enabled") is not False:
raise ValueError(f"{label}: canary live owner live readback must remain false")
execution_flags = gate.get("execution_flags") or {}
expected_execution = {
"live_delivery_enabled": False,
"gateway_queue_write_enabled": False,
"bot_api_call_enabled": False,
"telegram_send_enabled": False,
"delivery_receipt_write_enabled": False,
"production_write_enabled": False,
"secret_read_enabled": False,
"paid_api_enabled": False,
}
mismatches = _mismatches(execution_flags, expected_execution)
if mismatches:
raise ValueError(
f"{label}: canary live owner review execution flags mismatch: {mismatches}"
)
if gate.get("owner_decision_log") != []:
raise ValueError(f"{label}: canary live owner decision log must remain empty")
def _require_professional_tasks(payload: dict[str, Any], label: str) -> None:
domains = payload.get("professional_task_domains") or []
if len(domains) != _EXPECTED_DOMAIN_COUNT:
@@ -676,6 +776,42 @@ def _require_rollups(payload: dict[str, Any], label: str) -> None:
or {}
).get("failed_check_count")
),
"canary_live_delivery_owner_review_gate_count": 1
if bridge.get("canary_live_delivery_owner_review_gate")
else 0,
"canary_live_delivery_owner_review_required_field_count": len(
(bridge.get("canary_live_delivery_owner_review_gate") or {}).get(
"required_owner_fields"
)
or []
),
"canary_live_delivery_owner_review_acceptance_check_count": len(
(bridge.get("canary_live_delivery_owner_review_gate") or {}).get(
"acceptance_checks"
)
or []
),
"canary_live_delivery_owner_review_rejection_reason_count": len(
(bridge.get("canary_live_delivery_owner_review_gate") or {}).get(
"rejection_reasons"
)
or []
),
"canary_live_delivery_owner_review_reviewer_action_count": len(
(bridge.get("canary_live_delivery_owner_review_gate") or {}).get(
"reviewer_actions"
)
or []
),
"canary_live_delivery_owner_review_receipt_check_count": len(
(
(bridge.get("canary_live_delivery_owner_review_gate") or {}).get(
"receipt_readback_plan"
)
or {}
).get("required_checks")
or []
),
}
mismatches = _mismatches(rollups, expected)
if mismatches:

View File

@@ -18,9 +18,9 @@ def test_load_latest_ai_agent_professional_task_expansion_snapshot() -> None:
snapshot = load_latest_ai_agent_professional_task_expansion()
assert snapshot["schema_version"] == "ai_agent_professional_task_expansion_v1"
assert snapshot["program_status"]["current_task_id"] == "P2-405E"
assert snapshot["program_status"]["next_task_id"] == "P2-405F"
assert snapshot["program_status"]["overall_completion_percent"] == 98
assert snapshot["program_status"]["current_task_id"] == "P2-405F"
assert snapshot["program_status"]["next_task_id"] == "P2-406B"
assert snapshot["program_status"]["overall_completion_percent"] == 99
assert snapshot["program_status"]["runtime_authority"] == (
"professional_task_expansion_and_telegram_bridge_read_only_no_send"
)
@@ -117,6 +117,20 @@ def test_load_latest_ai_agent_professional_task_expansion_snapshot() -> None:
assert rollups["canary_delivery_rehearsal_gateway_queue_write_enabled_count"] == 0
assert rollups["canary_delivery_rehearsal_bot_api_call_enabled_count"] == 0
assert rollups["canary_delivery_rehearsal_receipt_write_enabled_count"] == 0
assert rollups["canary_live_delivery_owner_review_gate_count"] == 1
assert rollups["canary_live_delivery_owner_review_required_field_count"] == 9
assert rollups["canary_live_delivery_owner_review_acceptance_check_count"] == 9
assert rollups["canary_live_delivery_owner_review_rejection_reason_count"] == 8
assert rollups["canary_live_delivery_owner_review_reviewer_action_count"] == 6
assert rollups["canary_live_delivery_owner_review_receipt_check_count"] == 8
assert rollups["canary_live_delivery_owner_review_received_count"] == 0
assert rollups["canary_live_delivery_owner_review_accepted_count"] == 0
assert rollups["canary_live_delivery_approved_count"] == 0
assert rollups["canary_live_delivery_attempt_allowed_count"] == 0
assert rollups["canary_live_delivery_gateway_queue_write_enabled_count"] == 0
assert rollups["canary_live_delivery_bot_api_call_enabled_count"] == 0
assert rollups["canary_live_delivery_telegram_send_enabled_count"] == 0
assert rollups["canary_live_delivery_receipt_write_enabled_count"] == 0
def test_professional_tasks_cover_required_agents_and_reporting() -> None:
@@ -288,6 +302,46 @@ def test_canary_delivery_rehearsal_is_ready_without_live_send() -> None:
assert all(value is False for value in rehearsal["execution_flags"].values())
def test_canary_live_delivery_owner_review_gate_waits_for_owner_response() -> None:
snapshot = load_latest_ai_agent_professional_task_expansion()
gate = snapshot["telegram_runtime_bridge"]["canary_live_delivery_owner_review_gate"]
assert gate["status"] == "ready_for_owner_review_no_send"
assert gate["gate_ready"] is True
assert gate["approval_required"] is True
assert gate["owner_review_received"] is False
assert gate["owner_review_accepted"] is False
assert gate["live_canary_delivery_approved"] is False
assert gate["delivery_attempt_allowed"] is False
assert gate["prior_readback_completed_check_count"] == 8
assert gate["prior_readback_failed_check_count"] == 0
assert gate["selected_message_type"] == "daily_agent_workload_digest"
assert gate["target_room_env"] == "SRE_GROUP_CHAT_ID"
assert gate["target_room_value_visible"] is False
assert gate["owner_agent"] == "telegram_ops_liaison"
assert gate["receipt_readback_owner"] == "hermes"
assert gate["arbiter"] == "openclaw"
assert len(gate["required_owner_fields"]) == 9
assert len(gate["acceptance_checks"]) == 9
assert len(gate["rejection_reasons"]) == 8
assert len(gate["reviewer_actions"]) == 6
assert gate["owner_decision_log"] == []
for field in gate["required_owner_fields"]:
assert field["required"] is True
assert field["current_value_status"] == "waiting_owner_response"
assert field["value_display_allowed"] is False
receipt = gate["receipt_readback_plan"]
assert receipt["owner_agent"] == "hermes"
assert len(receipt["required_checks"]) == 8
assert receipt["completed_check_count"] == 0
assert receipt["failed_check_count"] == 0
assert receipt["production_receipt_write_enabled"] is False
assert receipt["live_receipt_readback_enabled"] is False
assert all(value is False for value in gate["execution_flags"].values())
def test_rejects_telegram_send_enabled(tmp_path: Path) -> None:
snapshot = copy.deepcopy(load_latest_ai_agent_professional_task_expansion())
snapshot["telegram_runtime_bridge"]["telegram_send_enabled"] = True
@@ -424,6 +478,28 @@ def test_rejects_canary_delivery_rehearsal_receipt_write_enabled(tmp_path: Path)
load_latest_ai_agent_professional_task_expansion(tmp_path)
def test_rejects_canary_live_delivery_owner_review_received(tmp_path: Path) -> None:
snapshot = copy.deepcopy(load_latest_ai_agent_professional_task_expansion())
gate = snapshot["telegram_runtime_bridge"]["canary_live_delivery_owner_review_gate"]
gate["owner_review_received"] = True
snapshot["rollups"]["canary_live_delivery_owner_review_received_count"] = 1
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="canary_live_delivery_owner_review_gate mismatch"):
load_latest_ai_agent_professional_task_expansion(tmp_path)
def test_rejects_canary_live_delivery_telegram_send_enabled(tmp_path: Path) -> None:
snapshot = copy.deepcopy(load_latest_ai_agent_professional_task_expansion())
gate = snapshot["telegram_runtime_bridge"]["canary_live_delivery_owner_review_gate"]
gate["execution_flags"]["telegram_send_enabled"] = True
snapshot["rollups"]["canary_live_delivery_telegram_send_enabled_count"] = 1
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="canary live owner review execution flags mismatch"):
load_latest_ai_agent_professional_task_expansion(tmp_path)
def test_rejects_high_risk_without_approval(tmp_path: Path) -> None:
snapshot = copy.deepcopy(load_latest_ai_agent_professional_task_expansion())
high_task = next(task for task in snapshot["professional_tasks"] if task["risk_tier"] == "high")

View File

@@ -17,9 +17,9 @@ def test_ai_agent_professional_task_expansion_endpoint() -> None:
assert response.status_code == 200
payload = response.json()
assert payload["schema_version"] == "ai_agent_professional_task_expansion_v1"
assert payload["program_status"]["current_task_id"] == "P2-405E"
assert payload["program_status"]["next_task_id"] == "P2-405F"
assert payload["program_status"]["overall_completion_percent"] == 98
assert payload["program_status"]["current_task_id"] == "P2-405F"
assert payload["program_status"]["next_task_id"] == "P2-406B"
assert payload["program_status"]["overall_completion_percent"] == 99
assert payload["program_status"]["runtime_authority"] == (
"professional_task_expansion_and_telegram_bridge_read_only_no_send"
)
@@ -61,6 +61,12 @@ def test_ai_agent_professional_task_expansion_endpoint() -> None:
assert payload["rollups"]["canary_delivery_rehearsal_gateway_queue_write_enabled_count"] == 0
assert payload["rollups"]["canary_delivery_rehearsal_bot_api_call_enabled_count"] == 0
assert payload["rollups"]["canary_delivery_rehearsal_receipt_write_enabled_count"] == 0
assert payload["rollups"]["canary_live_delivery_owner_review_gate_count"] == 1
assert payload["rollups"]["canary_live_delivery_owner_review_required_field_count"] == 9
assert payload["rollups"]["canary_live_delivery_owner_review_received_count"] == 0
assert payload["rollups"]["canary_live_delivery_owner_review_accepted_count"] == 0
assert payload["rollups"]["canary_live_delivery_approved_count"] == 0
assert payload["rollups"]["canary_live_delivery_telegram_send_enabled_count"] == 0
assert payload["telegram_runtime_bridge"]["canary_approval_package"]["live_send_enabled"] is False
assert payload["telegram_runtime_bridge"]["canary_send_approval_packet"]["approval_granted"] is False
assert (
@@ -81,5 +87,11 @@ def test_ai_agent_professional_task_expansion_endpoint() -> None:
]
is False
)
owner_gate = payload["telegram_runtime_bridge"]["canary_live_delivery_owner_review_gate"]
assert owner_gate["gate_ready"] is True
assert owner_gate["owner_review_received"] is False
assert owner_gate["owner_review_accepted"] is False
assert owner_gate["live_canary_delivery_approved"] is False
assert owner_gate["execution_flags"]["telegram_send_enabled"] is False
assert len(payload["telegram_runtime_bridge"]["no_send_message_previews"]) == 6
assert len(payload["telegram_runtime_bridge"]["receipt_expectations"]) == 6