"""框架协议与运行工件的离线合同测试。""" from __future__ import annotations import json import pathlib import sys import tempfile import unittest from jsonschema import Draft202012Validator ROOT = next( parent for parent in (pathlib.Path(__file__).resolve().parent, *pathlib.Path(__file__).resolve().parents) if (parent / "AGENTS.md").is_file() and (parent / ".git").exists() ) if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from framework.adapters.pi.normalization import normalize_pi_transcript from framework.primitives.artifacts import ( ArtifactError, detect_seq_gaps, read_jsonl, write_jsonl_atomic, ) from framework.primitives.execution import ( FrameworkEvent, FrameworkExecutionRequest, FrameworkExecutionResult, ) SCHEMA_DIR = ROOT / "framework" / "primitives" / "schemas" class FrameworkProtocolTest(unittest.TestCase): def test_schemas_are_valid_draft_2020_documents(self) -> None: for path in sorted(SCHEMA_DIR.glob("*.schema.json")): with self.subTest(path=path.name): document = json.loads(path.read_text(encoding="utf-8")) Draft202012Validator.check_schema(document) def test_request_has_no_business_state_fields(self) -> None: request = FrameworkExecutionRequest( system_prompt="system", user_content="user", tool_allowlist=("read",), ) self.assertEqual(request.session_mode, "fresh") self.assertFalse(hasattr(request, "work_id")) self.assertFalse(hasattr(request, "candidate_status")) request_schema = json.loads((SCHEMA_DIR / "framework_execution_request.schema.json").read_text(encoding="utf-8")) Draft202012Validator(request_schema).validate(request.as_dict()) event = FrameworkEvent( framework="pi", framework_version="test", session_id="s1", source_seq=1, kind="agent", phase="completed", observed_at="2026-08-24T00:00:00Z", ) event_schema = json.loads((SCHEMA_DIR / "framework_event.schema.json").read_text(encoding="utf-8")) Draft202012Validator(event_schema).validate(event.as_dict()) result = FrameworkExecutionResult( status="completed", final_text="{}", requested_model="p/m" ) result_schema = json.loads((SCHEMA_DIR / "framework_execution_result.schema.json").read_text(encoding="utf-8")) Draft202012Validator(result_schema).validate(result.as_dict()) def test_pi_transcript_produces_common_events_and_preserves_unknown_type(self) -> None: with tempfile.TemporaryDirectory() as directory: source = pathlib.Path(directory) / "pi.jsonl" target = pathlib.Path(directory) / "framework-events.jsonl" source.write_text( json.dumps({"type": "session", "id": "s1"}) + "\n" + json.dumps({"type": "future_event", "payload": "not copied"}) + "\n", encoding="utf-8", ) result = normalize_pi_transcript(source, target, run_id="run-1", framework_version="test") self.assertEqual(result["unknownEventTypes"], ["future_event"]) normalized = read_jsonl(target) self.assertEqual([event["sourceSeq"] for event in normalized], [1, 2]) self.assertEqual(normalized[1]["kind"], "unknown") self.assertNotIn("payload", normalized[1]["safeDetails"]) def test_artifact_flush_replay_and_gap_detection(self) -> None: events = [ { "framework": "pi", "frameworkVersion": "test", "sessionId": "s1", "sourceSeq": 1, "kind": "session", "phase": "started", "safeDetails": {}, }, { "framework": "pi", "frameworkVersion": "test", "sessionId": "s1", "sourceSeq": 2, "kind": "agent", "phase": "completed", "safeDetails": {}, }, ] with tempfile.TemporaryDirectory() as directory: path = pathlib.Path(directory) / "transcript.jsonl" digest = write_jsonl_atomic(path, events) self.assertTrue(digest.startswith("sha256:")) self.assertEqual(read_jsonl(path), events) self.assertEqual(detect_seq_gaps([events[0], {**events[1], "sourceSeq": 4}]), [(2, 3)]) with self.assertRaises(ArtifactError): write_jsonl_atomic(path, [{**events[0], "sourceSeq": 2}]) if __name__ == "__main__": unittest.main()