from __future__ import annotations import json import pytest from src.services.ai_agent_communication_learning_contract import ( load_latest_ai_agent_communication_learning_contract, ) def test_load_latest_ai_agent_communication_learning_contract_reads_committed_snapshot(): data = load_latest_ai_agent_communication_learning_contract() assert data["schema_version"] == "ai_agent_communication_learning_contract_v1" assert data["program_status"]["overall_completion_percent"] == 35 assert data["program_status"]["read_only_mode"] is True assert data["program_status"]["runtime_authority"] == "contract_only_no_runtime_worker" assert data["communication_plane"]["message_bus"] == "Redis Streams" assert data["communication_plane"]["stream_key_pattern"] == "aiops:agent:{session_id}" assert data["communication_plane"]["frontend_redaction"]["operator_conversation_display_allowed"] is False assert data["approval_boundaries"]["runtime_worker_allowed"] is False assert data["approval_boundaries"]["db_migration_allowed"] is False assert data["approval_boundaries"]["telegram_direct_send_allowed"] is False assert data["approval_boundaries"]["production_route_change_allowed"] is False assert data["rollups"]["agent_lane_count"] == 3 assert data["rollups"]["mcp_stack_count"] == len(data["mcp_stack"]) == 9 assert data["rollups"]["rag_layer_count"] == len(data["rag_memory_stack"]) == 3 assert data["rollups"]["learning_loop_count"] == len(data["learning_loops"]) == 5 assert data["rollups"]["intelligence_service_count"] == len(data["intelligence_services"]) == 7 assert data["rollups"]["optional_service_ids"] == ["langfuse", "phoenix", "qdrant", "milvus"] assert {lane["agent_id"] for lane in data["agent_lanes"]} == { "openclaw", "hermes", "nemotron", } def test_load_latest_ai_agent_communication_learning_contract_rejects_runtime_worker(tmp_path): snapshot = _snapshot() snapshot["approval_boundaries"]["runtime_worker_allowed"] = True (tmp_path / "ai_agent_communication_learning_contract_2026-06-11.json").write_text( json.dumps(snapshot), encoding="utf-8", ) with pytest.raises(ValueError, match="approval boundaries"): load_latest_ai_agent_communication_learning_contract(tmp_path) def test_load_latest_ai_agent_communication_learning_contract_rejects_rollup_mismatch(tmp_path): snapshot = _snapshot() snapshot["rollups"]["mcp_stack_count"] = 99 (tmp_path / "ai_agent_communication_learning_contract_2026-06-11.json").write_text( json.dumps(snapshot), encoding="utf-8", ) with pytest.raises(ValueError, match="rollup counts"): load_latest_ai_agent_communication_learning_contract(tmp_path) def test_load_latest_ai_agent_communication_learning_contract_rejects_nemotron_route_change(tmp_path): snapshot = _snapshot() snapshot["agent_lanes"][2]["blocked_actions"] = ["secret_plaintext_read"] (tmp_path / "ai_agent_communication_learning_contract_2026-06-11.json").write_text( json.dumps(snapshot), encoding="utf-8", ) with pytest.raises(ValueError, match="Nemotron"): load_latest_ai_agent_communication_learning_contract(tmp_path) def _snapshot() -> dict: return { "schema_version": "ai_agent_communication_learning_contract_v1", "generated_at": "2026-06-11T20:40:00+08:00", "program_status": { "overall_completion_percent": 35, "current_priority": "P2", "current_task_id": "P2-401A", "next_task_id": "P2-401B", "read_only_mode": True, "runtime_authority": "contract_only_no_runtime_worker", }, "communication_plane": { "message_bus": "Redis Streams", "stream_key_pattern": "aiops:agent:{session_id}", "session_table": "agent_sessions", "event_tables": ["timeline_events"], "turn_types": ["observe", "propose", "challenge", "review", "decide"], "frontend_redaction": { "operator_conversation_display_allowed": False, "agent_private_reasoning_display_allowed": False, }, }, "agent_lanes": [ { "agent_id": "openclaw", "display_name": "OpenClaw", "primary_role": "生產仲裁", "initiates": ["incident arbitration"], "responds_to": ["Hermes evidence dossier"], "writes_to": ["agent_sessions"], "blocked_actions": ["secret_plaintext_read", "self_approval"], }, { "agent_id": "hermes", "display_name": "Hermes", "primary_role": "治理", "initiates": ["knowledge review"], "responds_to": ["OpenClaw evidence request"], "writes_to": ["knowledge_entries"], "blocked_actions": ["secret_plaintext_read", "production_write"], }, { "agent_id": "nemotron", "display_name": "NemoTron", "primary_role": "離線評估", "initiates": ["replay scoring"], "responds_to": ["OpenClaw offline evaluation request"], "writes_to": ["agent_replay_results"], "blocked_actions": ["secret_plaintext_read", "production_route_change"], }, ], "mcp_stack": [_capability("mcp_gateway", "MCP Gateway", "openclaw", "existing")], "rag_memory_stack": [ _capability("hot_session_memory", "Hot", "openclaw", "contract_defined"), _capability("warm_rag_memory", "Warm", "hermes", "existing"), _capability("cold_replay_archive", "Cold", "nemotron", "planned"), ], "learning_loops": [_capability("incident_outcome_learning", "Incident", "openclaw", "planned")], "intelligence_services": [ _capability("postgres_pgvector", "PostgreSQL + pgvector", "hermes", "preferred_default"), _capability("langfuse", "Langfuse", "hermes", "optional_candidate"), ], "rollout_tasks": [ { "task_id": "P2-401A", "priority": "P2", "status": "ready_for_review", "completion_percent": 100, "owner_agent": "Hermes + OpenClaw", "summary": "契約。", "next_gate": "本地測試", }, { "task_id": "P2-401B", "priority": "P2", "status": "planned", "completion_percent": 0, "owner_agent": "OpenClaw", "summary": "Migration。", "next_gate": "DB migration approval gate", }, ], "approval_boundaries": { "runtime_worker_allowed": False, "db_migration_allowed": False, "telegram_direct_send_allowed": False, "paid_external_service_allowed": False, "secret_plaintext_allowed": False, "autonomous_host_mutation_allowed": False, "production_route_change_allowed": False, "sdk_installation_allowed": False, }, "rollups": { "agent_lane_count": 3, "mcp_stack_count": 1, "rag_layer_count": 3, "learning_loop_count": 1, "intelligence_service_count": 2, "rollout_task_count": 2, "blocked_task_ids": ["P2-401B"], "optional_service_ids": ["langfuse"], }, } def _capability(id_: str, display_name: str, owner: str, status: str) -> dict: return { "id": id_, "display_name": display_name, "primary_owner": owner, "purpose": "測試用途。", "storage_or_service": "test", "status": status, "approval_gate": "read_only_allowed", }