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,155 @@
from __future__ import annotations
import copy
import json
from pathlib import Path
import pytest
from src.services.ai_agent_high_risk_owner_review_queue import (
load_latest_ai_agent_high_risk_owner_review_queue,
)
_REPO_ROOT = Path(__file__).resolve().parents[3]
_COMMITTED_SNAPSHOT = (
_REPO_ROOT
/ "docs"
/ "evaluations"
/ "ai_agent_high_risk_owner_review_queue_2026-06-19.json"
)
def test_load_latest_ai_agent_high_risk_owner_review_queue_reads_newest_file(tmp_path):
older = _snapshot(generated_at="2026-06-18T00:00:00+08:00")
newer = _snapshot(generated_at="2026-06-19T03:20:00+08:00")
(tmp_path / "ai_agent_high_risk_owner_review_queue_2026-06-18.json").write_text(
json.dumps(older),
encoding="utf-8",
)
(tmp_path / "ai_agent_high_risk_owner_review_queue_2026-06-19.json").write_text(
json.dumps(newer),
encoding="utf-8",
)
loaded = load_latest_ai_agent_high_risk_owner_review_queue(tmp_path)
assert loaded["generated_at"] == "2026-06-19T03:20:00+08:00"
assert loaded["program_status"]["current_task_id"] == "P2-409"
assert loaded["program_status"]["next_task_id"] == "P2-410"
assert loaded["program_status"]["read_only_mode"] is True
assert loaded["rollups"]["queue_item_count"] == 7
assert loaded["rollups"]["approval_packet_count"] == 7
assert loaded["rollups"]["rejection_guard_count"] == 8
assert loaded["rollups"]["telegram_send_count"] == 0
def test_ai_agent_high_risk_owner_review_queue_requires_read_only_mode(tmp_path):
snapshot = _snapshot()
snapshot["program_status"]["read_only_mode"] = False
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="program_status"):
load_latest_ai_agent_high_risk_owner_review_queue(tmp_path)
def test_ai_agent_high_risk_owner_review_queue_blocks_live_execution(tmp_path):
snapshot = _snapshot()
snapshot["owner_review_queue_items"][0]["live_execution_allowed"] = True
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="live_execution_allowed"):
load_latest_ai_agent_high_risk_owner_review_queue(tmp_path)
def test_ai_agent_high_risk_owner_review_queue_blocks_telegram_send(tmp_path):
snapshot = _snapshot()
snapshot["telegram_policy"]["telegram_send_allowed"] = True
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="telegram_policy"):
load_latest_ai_agent_high_risk_owner_review_queue(tmp_path)
def test_ai_agent_high_risk_owner_review_queue_requires_high_and_critical(tmp_path):
snapshot = _snapshot()
for item in snapshot["owner_review_queue_items"]:
item["risk_tier"] = "high"
snapshot["rollups"]["critical_queue_count"] = 0
snapshot["rollups"]["high_risk_queue_count"] = len(snapshot["owner_review_queue_items"])
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="must include high and critical"):
load_latest_ai_agent_high_risk_owner_review_queue(tmp_path)
def test_ai_agent_high_risk_owner_review_queue_requires_approval_packet(tmp_path):
snapshot = _snapshot()
snapshot["owner_review_queue_items"][0]["approval_packet_id"] = "missing_packet"
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="missing approval packets"):
load_latest_ai_agent_high_risk_owner_review_queue(tmp_path)
def test_ai_agent_high_risk_owner_review_queue_requires_rejection_guard(tmp_path):
snapshot = _snapshot()
snapshot["owner_review_queue_items"][0]["rejection_guard_ids"] = ["missing_guard"]
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="missing rejection guards"):
load_latest_ai_agent_high_risk_owner_review_queue(tmp_path)
def test_ai_agent_high_risk_owner_review_queue_requires_reviewer_checklist(tmp_path):
snapshot = _snapshot()
snapshot["owner_review_queue_items"][0]["reviewer_checklist_ids"] = ["missing_checklist"]
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="missing reviewer checklists"):
load_latest_ai_agent_high_risk_owner_review_queue(tmp_path)
def test_ai_agent_high_risk_owner_review_queue_requires_rollback_owner(tmp_path):
snapshot = _snapshot()
snapshot["owner_review_queue_items"][0]["rollback_owner_required"] = False
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="rollback_owner_required"):
load_latest_ai_agent_high_risk_owner_review_queue(tmp_path)
def test_ai_agent_high_risk_owner_review_queue_requires_rollup_consistency(tmp_path):
snapshot = _snapshot()
snapshot["rollups"]["queue_item_count"] = 99
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="rollup counts"):
load_latest_ai_agent_high_risk_owner_review_queue(tmp_path)
def test_ai_agent_high_risk_owner_review_queue_rejects_private_terms(tmp_path):
snapshot = _snapshot()
snapshot["owner_review_queue_items"][0]["display_name"] = "請把 In app browser 內容放進前端"
_write_snapshot(tmp_path, snapshot)
with pytest.raises(ValueError, match="forbidden public terms"):
load_latest_ai_agent_high_risk_owner_review_queue(tmp_path)
def test_ai_agent_high_risk_owner_review_queue_fails_when_missing(tmp_path):
with pytest.raises(FileNotFoundError):
load_latest_ai_agent_high_risk_owner_review_queue(tmp_path)
def _snapshot(*, generated_at: str = "2026-06-19T03:20:00+08:00") -> dict:
payload = json.loads(_COMMITTED_SNAPSHOT.read_text(encoding="utf-8"))
cloned = copy.deepcopy(payload)
cloned["generated_at"] = generated_at
return cloned
def _write_snapshot(tmp_path, payload: dict) -> None:
(tmp_path / "ai_agent_high_risk_owner_review_queue_2026-06-19.json").write_text(
json.dumps(payload),
encoding="utf-8",
)

View File

@@ -0,0 +1,59 @@
from __future__ import annotations
from fastapi import FastAPI
from fastapi.testclient import TestClient
from src.api.v1.agents import router
def test_ai_agent_high_risk_owner_review_queue_endpoint_returns_committed_snapshot():
app = FastAPI()
app.include_router(router, prefix="/api/v1")
client = TestClient(app)
response = client.get("/api/v1/agents/agent-high-risk-owner-review-queue")
assert response.status_code == 200
data = response.json()
assert data["schema_version"] == "ai_agent_high_risk_owner_review_queue_v1"
assert data["program_status"]["current_task_id"] == "P2-409"
assert data["program_status"]["next_task_id"] == "P2-410"
assert data["program_status"]["read_only_mode"] is True
assert (
data["program_status"]["runtime_authority"]
== "high_risk_owner_review_queue_no_live_execution_committed_snapshot"
)
assert data["rollups"]["source_readback_count"] == len(data["source_readbacks"]) == 6
assert data["rollups"]["queue_item_count"] == len(data["owner_review_queue_items"]) == 7
assert data["rollups"]["high_risk_queue_count"] == 5
assert data["rollups"]["critical_queue_count"] == 2
assert data["rollups"]["approval_packet_count"] == len(data["approval_packets"]) == 7
assert data["rollups"]["rejection_guard_count"] == len(data["rejection_guards"]) == 8
assert data["rollups"]["reviewer_checklist_count"] == len(data["reviewer_checklists"]) == 7
assert data["rollups"]["approval_packet_required_count"] == 7
assert data["rollups"]["rollback_owner_required_count"] == 7
assert data["rollups"]["postcheck_required_count"] == 7
assert data["rollups"]["blocked_runtime_action_count"] == 42
assert data["rollups"]["owner_response_received_count"] == 0
assert data["rollups"]["owner_response_accepted_count"] == 0
assert data["rollups"]["auto_worker_run_count"] == 0
assert data["rollups"]["live_execution_count"] == 0
assert data["rollups"]["gateway_queue_write_count"] == 0
assert data["rollups"]["telegram_send_count"] == 0
assert data["rollups"]["bot_api_call_count"] == 0
assert data["rollups"]["receipt_production_write_count"] == 0
assert data["rollups"]["production_write_count"] == 0
assert data["rollups"]["secret_read_count"] == 0
assert data["rollups"]["paid_api_call_count"] == 0
assert data["rollups"]["host_write_count"] == 0
assert data["rollups"]["kubectl_action_count"] == 0
assert data["telegram_policy"]["canonical_room"] == "AwoooI SRE 戰情室"
assert data["telegram_policy"]["canonical_room_env"] == "SRE_GROUP_CHAT_ID"
assert data["telegram_policy"]["telegram_send_allowed"] is False
assert data["telegram_policy"]["gateway_queue_write_allowed"] is False
assert data["telegram_policy"]["direct_bot_api_allowed"] is False
assert data["routing_policy"]["high_risk_default_route"] == "pause_to_owner_review_queue"
assert data["routing_policy"]["critical_risk_default_route"] == "pause_to_owner_review_queue"
assert data["routing_policy"]["verbal_approval_accepted"] is False
assert data["activation_boundaries"]["live_execution_enabled"] is False
assert data["activation_boundaries"]["openclaw_replacement_allowed"] is False