from __future__ import annotations import hashlib import json from itertools import pairwise from pathlib import Path from types import SimpleNamespace from k1link.laboratory.m47_reference_graph import M47_REFERENCE_GRAPH_LAB_SCHEMA from k1link.laboratory.m48_object_quality import read_m48_object_quality_pack from k1link.laboratory.m48_ravnoves00_pack import ( M48_FRAME_COUNT, M48_SELECTION_SCHEMA, _prediction_objects, prepare_m48_ravnoves00_pack, ) def _canonical_json(value: object) -> str: return json.dumps(value, sort_keys=True, separators=(",", ":")) def _write_json(path: Path, value: object) -> None: path.write_text(_canonical_json(value) + "\n", encoding="utf-8") def _write_jsonl(path: Path, rows: list[dict[str, object]]) -> str: raw = "".join(_canonical_json(row) + "\n" for row in rows).encode() path.write_bytes(raw) return hashlib.sha256(raw).hexdigest() def _recursive_keys(value: object) -> set[str]: if isinstance(value, dict): return set(value) | {key for item in value.values() for key in _recursive_keys(item)} if isinstance(value, list): return {key for item in value for key in _recursive_keys(item)} return set() def test_prediction_projection_is_class_free_and_conservative() -> None: rows = _prediction_objects( [ { "proposal_id": "proposal-0-1", "bbox_xyxy": [80.0, 60.0, 400.0, 300.0], "occupied_support": False, "threat_decision": "unknown", "semantic_hint": "person", "objectness": 0.99, }, { "proposal_id": "proposal-0-2", "bbox_xyxy": [400.0, 300.0, 720.0, 540.0], "occupied_support": True, "threat_decision": "threat", "semantic_hint": "car", "objectness": 0.98, }, ], geometry_observations=[ { "proposal_ids": ["proposal-0-1"], "currentness": "current", "metric_geometry": None, }, { "proposal_ids": ["proposal-0-2"], "currentness": "current", "metric_geometry": {"centroid_xyz_m": [1.0, 2.0, 3.0]}, }, ], metric_obstacles=[ { "centroid_map_xyz_m": [1.0, 2.0, 3.0], "motion": "moving", "assessment": {"decision": "threat"}, } ], ) assert rows == [ { "prediction_id": "proposal-0-1", "extent_xyxy": [0.1, 0.1, 0.5, 0.5], "geometry_association": "unknown", "freshness": "current", "motion": "unsupported", "threat": "unknown", "unknown_causes": [ "insufficient-geometry-support", "threat-evidence-insufficient", ], }, { "prediction_id": "proposal-0-2", "extent_xyxy": [0.5, 0.5, 0.9, 0.9], "geometry_association": "associated", "freshness": "current", "motion": "moving", "threat": "threat", "unknown_causes": [], }, ] assert "semantic_hint" not in _canonical_json(rows) assert "objectness" not in _canonical_json(rows) def test_real_selection_contract_is_balanced_and_prediction_blind() -> None: repository_root = Path(__file__).resolve().parents[1] document = json.loads( (repository_root / "config/perception/m48-object-quality-selection-v1.json").read_text() ) clips = document["clips"] assert document["schema_version"] == M48_SELECTION_SCHEMA assert len(clips) == 24 assert {clip["split"] for clip in clips} == {"development", "validation"} assert all("strata" not in clip and "hypotheses" not in clip for clip in clips) assert document["selection_hypothesis_profile"] == { "derivation": "exact-frozen-prediction-rows-before-independent-truth", "small_obstacle_max_normalized_area": 0.001, "fisheye_edge_margin_normalized": 0.08, "sparse_scene_max_median_prediction_count": 2.0, } for field in ("component_id", "route_block", "time_block"): group_splits: dict[str, set[str]] = {} for clip in clips: group_splits.setdefault(clip[field], set()).add(clip["split"]) assert all(len(splits) == 1 for splits in group_splits.values()) assert len(group_splits) < len(clips) assert all(left["end_sequence"] < right["start_sequence"] for left, right in pairwise(clips)) forbidden = {"label", "labels", "truth", "review", "adjudication"} assert forbidden.isdisjoint(document) def test_prepare_pack_binds_all_source_ledgers_and_freezes_selected_frames( tmp_path: Path, monkeypatch, ) -> None: repository_root = Path(__file__).resolve().parents[1] graph_root = tmp_path / ("m47-reference-graph-" + "a" * 64) threat_root = tmp_path / ("m4-threat-replay-" + "b" * 64) geometry_root = tmp_path / ("m4-geometry-replay-" + "e" * 64) lab_root = tmp_path / ("m47-reference-graph-lab-" + "c" * 64) graph_root.mkdir() threat_root.mkdir() geometry_root.mkdir() lab_root.mkdir() _write_json(lab_root / "manifest.json", {"fixture": True}) graph_rows: list[dict[str, object]] = [] threat_rows: list[dict[str, object]] = [] geometry_rows: list[dict[str, object]] = [] camera_rows: list[dict[str, object]] = [] for frame_index in range(M48_FRAME_COUNT): source_time_ns = 35_421_857_292 + frame_index * 100_000_000 graph_rows.append( { "sequence": frame_index, "obstacle_map": { "schema_version": "missioncore.local-obstacle-map/v1", "frame_id": f"frame-{frame_index:06d}", "free_space_claimed": False, }, "threats": [], } ) threat_rows.append( { "schema_version": "missioncore.perception-threat-replay-frame/v2", "sequence": frame_index, "frame_id": f"frame-{frame_index:06d}", "source_time_ns": source_time_ns, "source_available": True, "camera_proposals": [ { "proposal_id": f"proposal-{frame_index}-0", "bbox_xyxy": [0.0, 0.0, 20.0, 20.0], "occupied_support": True, "threat_decision": "threat" if frame_index % 2 == 0 else "not-threat", }, { "proposal_id": f"proposal-{frame_index}-1", "bbox_xyxy": [80.0, 60.0, 400.0, 300.0], "occupied_support": False, "threat_decision": "unknown", }, ], "metric_obstacles": [ { "centroid_map_xyz_m": [1.0, 2.0, 3.0], "motion": "moving" if frame_index % 2 == 0 else "stationary", "assessment": { "decision": "threat" if frame_index % 2 == 0 else "not-threat" }, } ], } ) geometry_rows.append( { "schema_version": "missioncore.perception-geometry-replay-frame/v1", "sequence": frame_index, "frame_id": f"frame-{frame_index:06d}", "source_available": True, "observations": [ { "proposal_ids": [f"proposal-{frame_index}-0"], "currentness": "current", "metric_geometry": {"centroid_xyz_m": [1.0, 2.0, 3.0]}, }, { "proposal_ids": [f"proposal-{frame_index}-1"], "currentness": "current", "metric_geometry": None, }, ], } ) camera_rows.append( { "schema_version": "missioncore.camera-recording-index/v1", "sequence": frame_index + 1, "kind": "media", "session_monotonic_ns": frame_index + 1, "sha256": hashlib.sha256(f"camera-{frame_index}".encode()).hexdigest(), } ) graph_sha256 = _write_jsonl(graph_root / "frames.jsonl", graph_rows) threat_sha256 = _write_jsonl(threat_root / "frames.jsonl", threat_rows) geometry_sha256 = _write_jsonl(geometry_root / "frames.jsonl", geometry_rows) camera_index = tmp_path / "index.jsonl" _write_jsonl(camera_index, camera_rows) _write_json( graph_root / "manifest.json", { "schema_version": "missioncore.reference-perception-graph-manifest/v1", "result_id": graph_root.name, "accepted": True, "graph_id": "reference-perception-graph/v2", "run_mode": "lossless-replay", "files": { "frames.jsonl": { "bytes": (graph_root / "frames.jsonl").stat().st_size, "sha256": graph_sha256, } }, }, ) _write_json( threat_root / "manifest.json", { "schema_version": "missioncore.perception-threat-replay-result/v2", "result_id": threat_root.name, "accepted": True, "identity": { "source_session_id": "20260720T065719Z_viewer_live", "frames_sha256": threat_sha256, "geometry_result_id": geometry_root.name, "geometry_frames_sha256": geometry_sha256, }, }, ) _write_json( geometry_root / "manifest.json", { "schema_version": "missioncore.perception-geometry-replay-result/v1", "identity": { "accepted": True, "source_pack_id": ( "e10-lidar-pack-" "576c994a6c814e2592dd6240ace3902a5db94843312c759a73ba0c9166157d2b" ), "frames_sha256": geometry_sha256, }, }, ) authority = { "mode": "replay-simulated", "physical_live": False, "commands_enabled": False, "actuation_allowed": False, "navigation_or_safety_accepted": False, } lab = SimpleNamespace( result_id=lab_root.name, result_root=lab_root, manifest={ "schema_version": M47_REFERENCE_GRAPH_LAB_SCHEMA, "accepted": True, "ground_truth": False, }, report={ "source": { "graph_result_id": graph_root.name, "visual_result_id": threat_root.name, "threat_frames_sha256": threat_sha256, "source_id": "RAVNOVES00", "source_session_id": "20260720T065719Z_viewer_live", }, "method": { "graph_id": "reference-perception-graph/v2", "run_mode": "lossless-replay", "canonical_payload_sha256": "d" * 64, }, "decision": { "state": "accepted-reference-graph-replay", "next_gate": "independent-object-centric-detection-quality", }, "acceptance": {"accepted": True}, "authority": authority, }, ) monkeypatch.setattr( "k1link.laboratory.m48_ravnoves00_pack.read_m47_reference_graph_lab", lambda _: lab, ) monkeypatch.setattr( "k1link.laboratory.m48_object_quality.read_m47_reference_graph_lab", lambda _: lab, ) result = prepare_m48_ravnoves00_pack( m47_lab_root=lab_root, graph_result_root=graph_root, threat_result_root=threat_root, geometry_result_root=geometry_root, camera_index_path=camera_index, selection_path=(repository_root / "config/perception/m48-object-quality-selection-v1.json"), frozen_at_utc="2026-08-24T00:00:00Z", output_root=tmp_path / "runtime/m48/object-quality-packs", ) assert read_m48_object_quality_pack(result.result_root) == result assert result.report["metrics"]["clip_count"] == 24 assert result.report["metrics"]["frame_count"] == 24 * 61 assert len(result.predictions) == 24 * 61 assert result.manifest["identity"]["preparation"]["adapter"]["sha256"] == ( hashlib.sha256( (repository_root / "src/k1link/laboratory/m48_ravnoves00_pack.py").read_bytes() ).hexdigest() ) assert result.manifest["identity"]["preparation"]["selection"]["sha256"] == ( hashlib.sha256( ( repository_root / "config/perception/m48-object-quality-selection-v1.json" ).read_bytes() ).hexdigest() ) reviewer_package = json.loads((result.result_root / "reviewer-package.json").read_text()) reviewer_keys = _recursive_keys(reviewer_package) assert "strata" not in reviewer_keys assert "prediction_id" not in reviewer_keys assert "semantic_hint" not in reviewer_keys