feat(lab): publish M4.8 assisted regression evidence

This commit is contained in:
DCCONSTRUCTIONS
2026-08-24 22:38:26 +03:00
parent 4fa6597b18
commit 4fa1669ab7
27 changed files with 10852 additions and 30 deletions
+78 -1
View File
@@ -9,7 +9,11 @@ from fastapi.routing import APIRoute
from pytest import MonkeyPatch
import k1link.web.advanced_laboratory_api as advanced_api
from k1link.laboratory import LaboratoryEvidenceDefinition, LaboratoryEvidenceRegistry
from k1link.laboratory import (
LaboratoryEvidenceDefinition,
LaboratoryEvidenceRegistry,
LaboratoryEvidenceVariant,
)
from k1link.web.advanced_laboratory_api import build_advanced_laboratory_router
@@ -74,6 +78,79 @@ def test_advanced_index_is_empty_when_not_configured() -> None:
}
def test_advanced_index_projects_one_most_mature_lifecycle_phase(tmp_path: Path) -> None:
variants = (
LaboratoryEvidenceVariant(
phase="review",
runtime_relative_root=PurePosixPath("packs"),
result_id_prefix="quality-pack",
document_name="manifest.json",
result_schema_version="missioncore.quality-pack/v1",
),
LaboratoryEvidenceVariant(
phase="result",
runtime_relative_root=PurePosixPath("results"),
result_id_prefix="quality-result",
document_name="manifest.json",
result_schema_version="missioncore.quality-result/v1",
),
)
registry = LaboratoryEvidenceRegistry(
definitions=(
LaboratoryEvidenceDefinition(
work_id="quality-lab",
runtime_relative_root=variants[-1].runtime_relative_root,
result_id_prefix=variants[-1].result_id_prefix,
document_name=variants[-1].document_name,
result_schema_version=variants[-1].result_schema_version,
lifecycle_variants=variants,
),
)
)
def publish(variant: LaboratoryEvidenceVariant, digest: str, created_at: str) -> str:
result_id = f"{variant.result_id_prefix}-{digest}"
result_root = variant.result_root(tmp_path) / result_id
result_root.mkdir(parents=True)
(result_root / variant.document_name).write_text(
json.dumps(
{
"schema_version": variant.result_schema_version,
"result_id": result_id,
"identity_sha256": digest,
"identity": {
"authority": {
"commands_enabled": False,
"navigation_or_safety_accepted": False,
}
},
"created_at_utc": created_at,
}
),
encoding="utf-8",
)
return result_id
pack_id = publish(variants[0], "a" * 64, "2026-08-24T10:00:00Z")
router = build_advanced_laboratory_router(
evidence_registry=registry,
evidence_runtime_root_provider=lambda: tmp_path,
)
route = _endpoint(router, "/api/v1/laboratory/advanced-index")
assert route()["items"][0]["result_id"] == pack_id # type: ignore[index,operator]
result_id = publish(variants[1], "b" * 64, "2026-08-24T11:00:00Z")
index = route() # type: ignore[operator]
assert index["items"] == [ # type: ignore[index]
{
"work_id": "quality-lab",
"result_id": result_id,
"created_at_utc": "2026-08-24T11:00:00Z",
"access": "read-only",
}
]
def test_advanced_index_includes_valid_l31_identity(
tmp_path: Path,
monkeypatch: MonkeyPatch,
+12 -1
View File
@@ -127,7 +127,7 @@ def test_product_registry_declares_every_advanced_evidence_source() -> None:
repository_root / "config" / "laboratories"
)
assert len(registry.definitions) == 34
assert len(registry.definitions) == 36
assert {item.work_id for item in registry.definitions} >= {
"e31-source-binding",
"e46j-raw-fisheye-realtime",
@@ -139,4 +139,15 @@ def test_product_registry_declares_every_advanced_evidence_source() -> None:
"l34f-adjudicated-reference",
"m4-replay-threat",
"m47-reference-graph-shadow",
"m48-object-centric-quality",
"m48-small-static-passage-regression",
}
m48 = next(
item for item in registry.definitions
if item.work_id == "m48-object-centric-quality"
)
assert [variant.phase for variant in m48.evidence_variants] == ["review", "result"]
assert [variant.result_id_prefix for variant in m48.evidence_variants] == [
"m48-object-quality-pack",
"m48-object-quality-result",
]
+68
View File
@@ -4,6 +4,7 @@ import hashlib
import json
from dataclasses import replace
from pathlib import Path
from types import SimpleNamespace
import pytest
@@ -90,6 +91,8 @@ def test_repository_registry_classifies_every_evidence_definition() -> None:
evidence, execution = _registries()
assert {row.work_id for row in execution.definitions} == {
"m48-small-static-passage-regression",
"m48-object-centric-quality",
"m4-replay-threat",
"e33-worker-shadow",
"e35-degradation-recovery",
@@ -97,6 +100,12 @@ def test_repository_registry_classifies_every_evidence_definition() -> None:
"e47-semantic-slam-shadow",
}
by_work_id = {row.work_id: row for row in execution.definitions}
assert by_work_id["m48-small-static-passage-regression"].evidence_contract == (
"missioncore.m48-small-static-passage-regression-result/v1"
)
assert by_work_id["m48-object-centric-quality"].evidence_contract == (
"missioncore.m48-object-centric-quality-result/v1"
)
assert by_work_id["e47-semantic-slam-shadow"].lifecycle == "experimental"
assert by_work_id["e47-semantic-slam-shadow"].isolation == "bounded-adapter"
assert all(
@@ -193,6 +202,65 @@ def test_runner_rejects_undeclared_input_before_adapter(tmp_path: Path) -> None:
assert called is False
def test_m48_evaluation_uses_registered_adapter_and_common_receipt(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
evidence, execution = _registries()
pack_root = tmp_path / "pack"
truth_root = tmp_path / "truth"
pack_root.mkdir()
truth_root.mkdir()
adapter_result = _evidence_result(
tmp_path / "results",
work_id="m48-object-centric-quality",
)
def score(**kwargs: Path) -> SimpleNamespace:
assert kwargs == {
"pack_root": pack_root,
"truth_seal_root": truth_root,
"output_root": tmp_path / "results",
}
return SimpleNamespace(
result_root=adapter_result.result_root,
result_id=adapter_result.result_id,
)
monkeypatch.setattr(
"k1link.laboratory.m48_object_quality.score_m48_object_quality",
score,
)
runner = LaboratoryRunner(
registry=execution,
evidence_registry=evidence,
sink=JsonlPipelineTelemetrySink(tmp_path / "pipeline.jsonl"),
)
request = LaboratoryRunRequest(
work_id="m48-object-centric-quality",
run_id="m48-evaluation-fixture",
request_id="evaluate-once",
contour_id="mission-core-lab",
agent_id="local-control-plane",
node_id="fixture-node",
source_id="m48-pack-fixture",
source_package_id="m48-truth-fixture",
method_id="m48-object-centric-quality/v1",
inputs={"pack_root": pack_root, "truth_seal_root": truth_root},
output_root=tmp_path / "results",
receipt_root=tmp_path / "receipts",
)
result = runner.run(request)
assert result.result_id == adapter_result.result_id
assert result.receipt["adapter_id"] == "canonical.m48-object-centric-quality/v1"
assert result.receipt["contracts"]["evidence"] == (
"missioncore.m48-object-centric-quality-result/v1"
)
assert (result.receipt_root / "receipt.json").is_file()
def _canonical_json(value: object) -> bytes:
return json.dumps(
value,
+613
View File
@@ -0,0 +1,613 @@
from __future__ import annotations
import copy
import hashlib
import json
from pathlib import Path
from types import SimpleNamespace
from typing import Any
import pytest
import k1link.laboratory.m48_object_quality as m48
from k1link.laboratory.evidence_registry import LaboratoryEvidenceRegistry
from k1link.laboratory.evidence_report import verify_laboratory_evidence_result
def _write_json(path: Path, value: object) -> None:
path.write_text(json.dumps(value, sort_keys=True, separators=(",", ":")) + "\n")
def _frame_catalog() -> list[dict[str, object]]:
return [
{
"sequence": sequence,
"source_time_ns": (sequence - 1) * 100_000_000,
"camera_fragment_sha256": hashlib.sha256(f"frame-{sequence}".encode()).hexdigest(),
}
for sequence in range(1, 4490)
]
def _clips() -> list[dict[str, object]]:
rows = []
for index in range(20):
start = 1 + index * 100
split = "development" if index < 10 else "validation"
split_index = index if index < 10 else index - 10
rows.append(
{
"clip_id": f"clip-{index:02d}",
"component_id": f"component-{split}-{split_index // 2:02d}",
"route_block": f"route-{split}-{split_index // 3:02d}",
"time_block": f"time-{split}-{split_index // 2:02d}",
"split": split,
"start_sequence": start,
"end_sequence": start + 50,
}
)
return rows
def _clip_fixture_state(clip_id: str) -> dict[str, object]:
local_index = int(clip_id.rsplit("-", 1)[1]) % 10
if local_index == 0:
return {
"extent_xyxy": [0.2, 0.2, 0.22, 0.22],
"geometry_association": "unknown",
"motion": "unsupported",
"threat": "unknown",
"unknown_causes": [
"insufficient-geometry-support",
"threat-evidence-insufficient",
],
}
if local_index == 1:
return {
"extent_xyxy": [0.01, 0.2, 0.2, 0.4],
"geometry_association": "associated",
"motion": "static",
"threat": "not-threat",
"unknown_causes": [],
}
if local_index == 2:
return {
"extent_xyxy": [0.1, 0.1, 0.3, 0.4],
"geometry_association": "associated",
"motion": "moving",
"threat": "threat",
"unknown_causes": [],
}
return {
"extent_xyxy": [0.1, 0.1, 0.3, 0.4],
"geometry_association": "associated",
"motion": "static",
"threat": "not-threat",
"unknown_causes": [],
}
def _preparation_provenance() -> dict[str, object]:
return {
"schema_version": m48.M48_PREPARATION_PROVENANCE_SCHEMA,
"adapter": {
"module": "k1link.laboratory.m48_ravnoves00_pack",
"sha256": "1" * 64,
},
"selection": {
"selection_id": "m48-ravnoves00-balanced-connected-clips/v1",
"sha256": "2" * 64,
},
"camera_index": {
"source_session_id": "20260720T065719Z_viewer_live",
"sha256": "3" * 64,
"byte_length": 1234,
"frame_count": 4489,
},
"graph": {
"result_id": "m47-reference-graph-" + "4" * 64,
"manifest_sha256": "5" * 64,
"frames_sha256": "6" * 64,
},
"threat": {
"result_id": "m4-threat-replay-" + "7" * 64,
"manifest_sha256": "8" * 64,
"frames_sha256": "9" * 64,
},
"geometry": {
"result_id": "m4-geometry-replay-" + "a" * 64,
"manifest_sha256": "b" * 64,
"frames_sha256": "c" * 64,
},
}
def _prediction_rows(
clips: list[dict[str, object]],
*,
unsafe_free_space: bool,
unsafe_free_space_split: str | None = None,
) -> list[dict[str, object]]:
rows: list[dict[str, object]] = []
for clip in clips:
fixture = _clip_fixture_state(str(clip["clip_id"]))
unsafe = unsafe_free_space and (
unsafe_free_space_split is None or clip["split"] == unsafe_free_space_split
)
for sequence in range(int(clip["start_sequence"]), int(clip["end_sequence"]) + 1):
objects = [
{
"prediction_id": f"prediction-{sequence}",
"extent_xyxy": fixture["extent_xyxy"],
"geometry_association": fixture["geometry_association"],
"freshness": "current",
"motion": fixture["motion"],
"threat": fixture["threat"],
"unknown_causes": fixture["unknown_causes"],
}
]
rows.append(
{
"sequence": sequence,
"source_time_ns": (sequence - 1) * 100_000_000,
"terminal_outcome": "delivered",
"terminal_reason": None,
"free_space_claimed": unsafe,
"objects": objects,
}
)
return rows
def _fake_m47(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
root = tmp_path / f"m47-reference-graph-lab-{'a' * 64}"
root.mkdir()
manifest = {
"schema_version": "missioncore.reference-perception-graph-lab/v2",
"accepted": True,
"ground_truth": False,
}
_write_json(root / "manifest.json", manifest)
report = {
"source": {
"source_id": "RAVNOVES00",
"source_session_id": "20260720T065719Z_viewer_live",
"graph_result_id": "m47-reference-graph-" + "b" * 64,
},
"method": {
"graph_id": "reference-perception-graph/v2",
"run_mode": "lossless-replay",
"canonical_payload_sha256": "c" * 64,
},
"decision": {
"state": "accepted-reference-graph-replay",
"next_gate": "independent-object-centric-detection-quality",
},
"acceptance": {"accepted": True},
"authority": {
"mode": "replay-simulated",
"physical_live": False,
"commands_enabled": False,
"actuation_allowed": False,
"navigation_or_safety_accepted": False,
"ground_truth": False,
},
}
monkeypatch.setattr(
m48,
"read_m47_reference_graph_lab",
lambda _: SimpleNamespace(
result_id=root.name,
result_root=root,
manifest=manifest,
report=report,
),
)
def _review_clips(clips: tuple[dict[str, Any], ...], *, state: str) -> list[dict[str, Any]]:
rows = []
for clip in clips:
fixture = _clip_fixture_state(str(clip["clip_id"]))
start = int(clip["start_sequence"])
end = int(clip["end_sequence"])
rows.append(
{
"clip_id": clip["clip_id"],
"start_sequence": start,
"end_sequence": end,
"review_state": state,
"no_object": False,
"tracklets": [
{
"object_id": "object-1",
"first_sequence": start,
"last_sequence": end,
"keyframes": [
{
"sequence": start,
"extent_xyxy": fixture["extent_xyxy"],
"visibility": "visible",
},
{
"sequence": end,
"extent_xyxy": fixture["extent_xyxy"],
"visibility": "partial",
},
],
"state_segments": [
{
"start_sequence": start,
"end_sequence": end,
"geometry_association": fixture["geometry_association"],
"freshness": "current",
"motion": fixture["motion"],
"threat": fixture["threat"],
"critical_corridor_obstacle": True,
}
],
"notes": None,
}
],
"notes": None,
}
)
return rows
def _review(pack: m48.M48ObjectQualityPack, reviewer_id: str) -> dict[str, Any]:
return {
"schema_version": m48.M48_REVIEW_SCHEMA,
"pack_id": pack.result_id,
"state": "completed-independent-no-predictions",
"reviewer_id": reviewer_id,
"review_round": 1,
"blindness": {
"candidate_identity_seen": False,
"model_predictions_seen": False,
"model_scores_seen": False,
"semantic_class_task_seen": False,
},
"clips": _review_clips(pack.clips, state="reviewed"),
"acceptance": {
"all_clips_reviewed": True,
"independent": True,
"submitted_at_utc": "2026-08-24T11:00:00Z",
},
}
def _build_generations(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
*,
unsafe_free_space: bool = False,
unsafe_free_space_split: str | None = None,
) -> tuple[m48.M48ObjectQualityPack, m48.M48ObjectTruthSeal]:
_fake_m47(tmp_path, monkeypatch)
clips = _clips()
pack = m48.build_m48_object_quality_pack(
m47_lab_root=tmp_path / "ignored-m47",
frame_catalog=_frame_catalog(),
clips=clips,
predictions=_prediction_rows(
clips,
unsafe_free_space=unsafe_free_space,
unsafe_free_space_split=unsafe_free_space_split,
),
preparation_provenance=_preparation_provenance(),
frozen_at_utc="2026-08-24T10:00:00Z",
output_root=tmp_path / "packs",
)
review_a = _review(pack, "reviewer-a")
review_b = _review(pack, "reviewer-b")
review_a_path = tmp_path / "review-a.json"
review_b_path = tmp_path / "review-b.json"
_write_json(review_a_path, review_a)
_write_json(review_b_path, review_b)
adjudication = {
"schema_version": m48.M48_ADJUDICATION_SCHEMA,
"pack_id": pack.result_id,
"state": "completed-adjudicated",
"adjudicator_id": "adjudicator-1",
"review_submission_sha256": sorted(
(m48._canonical_sha256(review_a), m48._canonical_sha256(review_b))
),
"clips": _review_clips(pack.clips, state="adjudicated"),
"acceptance": {
"all_clips_adjudicated": True,
"all_disagreements_resolved": True,
"sealed_at_utc": "2026-08-24T12:00:00Z",
},
}
adjudication_path = tmp_path / "adjudication.json"
_write_json(adjudication_path, adjudication)
truth = m48.build_m48_object_truth_seal(
pack_root=pack.result_root,
reviewer_a_path=review_a_path,
reviewer_b_path=review_b_path,
adjudication_path=adjudication_path,
output_root=tmp_path / "truth",
)
return pack, truth
def test_m48_pack_is_neutral_tracklet_review_evidence(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
pack, truth = _build_generations(tmp_path, monkeypatch)
reviewer_package = json.loads(
(pack.result_root / "reviewer-package.json").read_text(encoding="utf-8")
)
assert reviewer_package["strata_included"] is False
assert reviewer_package["frozen_predictions_included"] is False
assert all(
"strata" not in clip and "selection_hypotheses" not in clip
for clip in reviewer_package["clips"]
)
assert {
hypothesis
for clip in pack.clips
if clip["split"] == "validation"
for hypothesis in clip["selection_hypotheses"]
} == set(pack.manifest["identity"]["profile"]["required_validation_hypotheses"])
review_template = json.loads(
(pack.result_root / "review-template.json").read_text(encoding="utf-8")
)
assert "clips" in review_template and "frames" not in review_template
assert "tracklets" in review_template["clips"][0]
assert len(truth.truth_rows) == len(pack.frame_references)
assert truth.truth_rows[0]["objects"][0]["visibility"] == "visible"
assert truth.truth_rows[50]["objects"][0]["visibility"] == "partial"
def test_m48_perfect_class_free_result_passes_all_gates_and_registry(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
pack, truth = _build_generations(tmp_path, monkeypatch)
result = m48.score_m48_object_quality(
pack_root=pack.result_root,
truth_seal_root=truth.result_root,
output_root=tmp_path / "results",
)
assert result.report["acceptance"]["accepted"] is True
assert all(result.report["acceptance"]["gates"].values())
assert result.report["acceptance"]["scope"] == "validation-only"
assert result.report["metrics"] == result.report["metrics_by_split"]["validation"]
assert result.report["method"]["semantic_class_scored"] is False
assert result.report["decision"]["next_gate"] == ("m4.9-recorded-realtime-release-candidate")
repository_root = Path(__file__).resolve().parents[1]
registry = LaboratoryEvidenceRegistry.from_directory(repository_root / "config/laboratories")
definitions = {definition.work_id: definition for definition in registry.definitions}
pack_proof = verify_laboratory_evidence_result(
definitions["m48-object-centric-quality"], pack.result_root
)
result_proof = verify_laboratory_evidence_result(
definitions["m48-object-centric-quality"], result.result_root
)
assert pack_proof["artifact_count"] == 7
assert result_proof["artifact_count"] == 3
def test_m48_review_rejects_semantic_class_and_same_reviewer(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
_fake_m47(tmp_path, monkeypatch)
clips = _clips()
pack = m48.build_m48_object_quality_pack(
m47_lab_root=tmp_path / "ignored-m47",
frame_catalog=_frame_catalog(),
clips=clips,
predictions=_prediction_rows(clips, unsafe_free_space=False),
preparation_provenance=_preparation_provenance(),
frozen_at_utc="2026-08-24T10:00:00Z",
output_root=tmp_path / "packs",
)
review_a = _review(pack, "reviewer-a")
review_a["clips"][0]["tracklets"][0]["category"] = "car"
review_a_path = tmp_path / "review-a.json"
_write_json(review_a_path, review_a)
with pytest.raises(m48.M48ObjectQualityError, match="fields"):
m48.validate_m48_review_submission(pack_root=pack.result_root, review_path=review_a_path)
review_a = _review(pack, "reviewer-a")
review_b = copy.deepcopy(review_a)
review_a_path = tmp_path / "review-a-clean.json"
review_b_path = tmp_path / "review-b-same.json"
_write_json(review_a_path, review_a)
_write_json(review_b_path, review_b)
adjudication = {
"schema_version": m48.M48_ADJUDICATION_SCHEMA,
"pack_id": pack.result_id,
"state": "completed-adjudicated",
"adjudicator_id": "adjudicator-1",
"review_submission_sha256": [m48._canonical_sha256(review_a)] * 2,
"clips": _review_clips(pack.clips, state="adjudicated"),
"acceptance": {
"all_clips_adjudicated": True,
"all_disagreements_resolved": True,
"sealed_at_utc": "2026-08-24T12:00:00Z",
},
}
adjudication_path = tmp_path / "adjudication.json"
_write_json(adjudication_path, adjudication)
with pytest.raises(m48.M48ObjectQualityError, match="must differ"):
m48.build_m48_object_truth_seal(
pack_root=pack.result_root,
reviewer_a_path=review_a_path,
reviewer_b_path=review_b_path,
adjudication_path=adjudication_path,
output_root=tmp_path / "truth",
)
def test_m48_unsafe_free_space_fails_with_bounded_atlas(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
pack, truth = _build_generations(tmp_path, monkeypatch, unsafe_free_space=True)
result = m48.score_m48_object_quality(
pack_root=pack.result_root,
truth_seal_root=truth.result_root,
output_root=tmp_path / "results",
)
assert result.report["acceptance"]["accepted"] is False
assert result.report["acceptance"]["gates"]["false_free_space_claims"] is False
assert result.report["acceptance"]["gates"]["critical_corridor_obstacle_recall"] is True
assert result.failure_atlas
assert any("false-free-space-claim" in row["causes"] for row in result.failure_atlas)
assert result.report["decision"]["next_gate"] == (
"bounded-cause-remediation-on-failed-m48-clusters"
)
def test_m48_development_failures_are_reported_but_cannot_fail_release(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
pack, truth = _build_generations(
tmp_path,
monkeypatch,
unsafe_free_space=True,
unsafe_free_space_split="development",
)
result = m48.score_m48_object_quality(
pack_root=pack.result_root,
truth_seal_root=truth.result_root,
output_root=tmp_path / "results",
)
assert result.report["acceptance"]["accepted"] is True
assert result.report["metrics_by_split"]["development"]["false_free_space_claims"] > 0
assert result.report["metrics"]["false_free_space_claims"] == 0
assert all(result.report["acceptance"]["gates"].values())
assert any(row["split"] == "development" for row in result.failure_atlas)
def test_m48_rejects_incomplete_clip_contract(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
_fake_m47(tmp_path, monkeypatch)
clips = _clips()[:19]
with pytest.raises(m48.M48ObjectQualityError, match="20–30"):
m48.build_m48_object_quality_pack(
m47_lab_root=tmp_path / "ignored-m47",
frame_catalog=_frame_catalog(),
clips=clips,
predictions=_prediction_rows(clips, unsafe_free_space=False),
preparation_provenance=_preparation_provenance(),
frozen_at_utc="2026-08-24T10:00:00Z",
output_root=tmp_path / "packs",
)
def test_m48_rejects_cross_split_and_vacuous_grouping(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
_fake_m47(tmp_path, monkeypatch)
clips = _clips()
clips[10]["route_block"] = clips[0]["route_block"]
with pytest.raises(m48.M48ObjectQualityError, match="route_block crosses"):
m48.build_m48_object_quality_pack(
m47_lab_root=tmp_path / "ignored-m47",
frame_catalog=_frame_catalog(),
clips=clips,
predictions=_prediction_rows(clips, unsafe_free_space=False),
preparation_provenance=_preparation_provenance(),
frozen_at_utc="2026-08-24T10:00:00Z",
output_root=tmp_path / "packs-cross-split",
)
clips = _clips()
for index, clip in enumerate(clips):
clip["component_id"] = f"unique-component-{index:02d}"
with pytest.raises(m48.M48ObjectQualityError, match="non-vacuous"):
m48.build_m48_object_quality_pack(
m47_lab_root=tmp_path / "ignored-m47",
frame_catalog=_frame_catalog(),
clips=clips,
predictions=_prediction_rows(clips, unsafe_free_space=False),
preparation_provenance=_preparation_provenance(),
frozen_at_utc="2026-08-24T10:00:00Z",
output_root=tmp_path / "packs-vacuous",
)
def test_m48_profile_config_matches_executable_contract() -> None:
repository_root = Path(__file__).resolve().parents[1]
document = json.loads(
(repository_root / "config/perception/m48-object-quality-v1.json").read_text(
encoding="utf-8"
)
)
profile = m48.DEFAULT_M48_OBJECT_QUALITY_PROFILE
assert document["schema_version"] == m48.M48_PROFILE_SCHEMA
assert document["profile_id"] == profile.profile_id
assert document["clip_contract"]["minimum_clip_count"] == profile.minimum_clip_count
assert document["clip_contract"]["maximum_clip_count"] == profile.maximum_clip_count
assert document["review_contract"]["review_unit"] == "clip-local-object-tracklet"
assert document["review_contract"]["semantic_class_labels_allowed"] is False
assert document["review_contract"]["selection_hypotheses_visible_to_reviewers"] is False
assert document["clip_contract"]["release_gate_split"] == "validation"
assert document["clip_contract"]["required_validation_hypotheses"] == sorted(
m48.DEFAULT_M48_OBJECT_QUALITY_PROFILE.to_dict()["required_validation_hypotheses"]
)
assert document["release_thresholds"]["obstacle_presence_precision"] == (
profile.obstacle_presence_precision
)
assert document["release_thresholds"]["critical_corridor_obstacle_recall"] == (
profile.critical_corridor_obstacle_recall
)
def test_m48_pack_identity_is_stable_across_clip_and_prediction_input_order(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
_fake_m47(tmp_path, monkeypatch)
clips = _clips()
predictions = _prediction_rows(clips, unsafe_free_space=False)
first = m48.build_m48_object_quality_pack(
m47_lab_root=tmp_path / "ignored-m47",
frame_catalog=_frame_catalog(),
clips=clips,
predictions=predictions,
preparation_provenance=_preparation_provenance(),
frozen_at_utc="2026-08-24T10:00:00Z",
output_root=tmp_path / "packs-a",
)
second = m48.build_m48_object_quality_pack(
m47_lab_root=tmp_path / "ignored-m47",
frame_catalog=_frame_catalog(),
clips=reversed(clips),
predictions=reversed(predictions),
preparation_provenance=_preparation_provenance(),
frozen_at_utc="2026-08-24T10:00:00Z",
output_root=tmp_path / "packs-b",
)
assert first.result_id == second.result_id
assert first.manifest["identity_sha256"] == second.manifest["identity_sha256"]
changed_provenance = _preparation_provenance()
changed_provenance["camera_index"]["sha256"] = "d" * 64
third = m48.build_m48_object_quality_pack(
m47_lab_root=tmp_path / "ignored-m47",
frame_catalog=_frame_catalog(),
clips=clips,
predictions=predictions,
preparation_provenance=changed_provenance,
frozen_at_utc="2026-08-24T10:00:00Z",
output_root=tmp_path / "packs-c",
)
assert third.result_id != first.result_id
assert third.manifest["identity"]["preparation"]["camera_index"]["sha256"] == ("d" * 64)
File diff suppressed because it is too large Load Diff
+355
View File
@@ -0,0 +1,355 @@
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
+350
View File
@@ -0,0 +1,350 @@
from __future__ import annotations
import hashlib
import json
from dataclasses import dataclass
from pathlib import Path
from types import SimpleNamespace
from typing import cast
import numpy as np
import pytest
from k1link.laboratory import m48_raw_evidence as raw_module
from k1link.laboratory.m48_object_quality import M48ObjectQualityPack
from k1link.laboratory.m48_raw_evidence import (
M48_EXPECTED_FRAME_COUNT,
M48_EXPECTED_SESSION_ID,
M48_EXPECTED_SOURCE_ID,
M48_RAW_SPATIAL_FRAME_SCHEMA,
M48RawEvidenceError,
M48RawEvidenceReader,
)
from k1link.perception.geometry import RecordedFrameTemporalBinding
from k1link.perception.threat import ReplayBodyFrame, load_replay_threat_profile
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
PROFILE_PATH = REPOSITORY_ROOT / "config/perception/m4-replay-threat-v3.json"
def _canonical_sha256(value: object) -> str:
return hashlib.sha256(
json.dumps(
value,
sort_keys=True,
separators=(",", ":"),
ensure_ascii=False,
).encode()
).hexdigest()
def _sealed_e10_pack(tmp_path: Path) -> tuple[Path, str, str]:
payload = b"sealed-e10-lidar-pack"
artifact_sha256 = hashlib.sha256(payload).hexdigest()
identity = {
"available_lidar_frames": 3928,
"calibration_sha256": "1" * 64,
"camera_slot": "camera_1",
"e6_profile_sha256": "2" * 64,
"e6_result_id": "e6-fixture",
"frame_count": M48_EXPECTED_FRAME_COUNT,
"input_sha256": "3" * 64,
"job_id": "recorded-camera-fixture",
"point_count": 5,
"producer_sha256": "4" * 64,
"projection": {
"height": 600,
"model": "kb4",
"source_coordinates": "k1-map",
"target_camera": "sensor.camera.right",
"width": 800,
},
"schema_version": "missioncore.e10-lidar-replay-pack/v1",
"semantic_timeline_result_id": "result-fixture",
"session_id": M48_EXPECTED_SESSION_ID,
"source_end_frame_index": M48_EXPECTED_FRAME_COUNT - 1,
"source_id": "sensor.camera.right",
"source_start_frame_index": 0,
"temporal_binding": "accepted-e6-nearest-host-arrival-best-effort",
"temporal_policy": {
"binding": "nearest-host-arrival-best-effort",
"clock_source": "recorded-host-monotonic-arrival",
"maximum_lidar_camera_delta_ms": 100.0,
"maximum_pose_point_delta_ms": 100.0,
},
"timeline_end_seconds": 484.0,
"timeline_start_seconds": 35.0,
}
identity_sha256 = _canonical_sha256(identity)
pack_id = f"e10-lidar-pack-{identity_sha256}"
root = tmp_path / pack_id
root.mkdir()
(root / "lidar-pack.npz").write_bytes(payload)
manifest = {
"artifact": {
"byte_length": len(payload),
"media_type": "application/x-npz",
"path": "lidar-pack.npz",
"sha256": artifact_sha256,
},
"classification": "private-recorded-sensor-replay-input",
"created_at_utc": "2026-07-22T06:05:22.515Z",
"ground_truth": False,
"identity": identity,
"identity_sha256": identity_sha256,
"pack_id": pack_id,
"schema_version": "missioncore.e10-lidar-replay-pack/v1",
}
(root / "manifest.json").write_text(
json.dumps(manifest, sort_keys=True, separators=(",", ":")),
encoding="utf-8",
)
return root, pack_id, artifact_sha256
def test_e10_pack_validation_binds_identity_path_length_and_sha256(tmp_path: Path) -> None:
root, pack_id, artifact_sha256 = _sealed_e10_pack(tmp_path)
artifact = raw_module._validate_e10_pack(
root,
expected_pack_id=pack_id,
expected_artifact_sha256=artifact_sha256,
)
assert artifact == (root / "lidar-pack.npz").resolve()
(root / "lidar-pack.npz").write_bytes(b"tampered")
with pytest.raises(M48RawEvidenceError, match="artifact content changed"):
raw_module._validate_e10_pack(
root,
expected_pack_id=pack_id,
expected_artifact_sha256=artifact_sha256,
)
@pytest.mark.parametrize(
("mutation", "message"),
[
(lambda manifest: manifest["identity"].update(session_id="other"), "identity changed"),
(
lambda manifest: manifest["artifact"].update(path="../lidar-pack.npz"),
"identity changed",
),
(lambda manifest: manifest.update(pack_id="e10-lidar-pack-wrong"), "identity changed"),
],
)
def test_e10_pack_validation_rejects_manifest_escape(
tmp_path: Path,
mutation: object,
message: str,
) -> None:
root, pack_id, artifact_sha256 = _sealed_e10_pack(tmp_path)
manifest_path = root / "manifest.json"
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
assert callable(mutation)
mutation(manifest)
manifest_path.write_text(json.dumps(manifest), encoding="utf-8")
with pytest.raises(M48RawEvidenceError, match=message):
raw_module._validate_e10_pack(
root,
expected_pack_id=pack_id,
expected_artifact_sha256=artifact_sha256,
)
@dataclass
class _Store:
source_time_ns: int = 2_000_000_000
source_available: bool = True
def temporal_binding_for_index(self, frame_index: int) -> RecordedFrameTemporalBinding:
return RecordedFrameTemporalBinding(
frame_index=frame_index,
source_time_ns=self.source_time_ns,
source_available=self.source_available,
lidar_camera_delta_ms=1.0 if self.source_available else None,
pose_point_delta_ms=1.0 if self.source_available else None,
)
def current_points_for_frame(self, frame_index: int) -> np.ndarray:
del frame_index
return np.asarray(
[
[1.0, 0.0, 0.0],
[2.0, 0.0, 0.0],
[3.0, 0.0, 0.0],
[4.0, 0.0, 0.0],
[5.0, 0.0, 0.0],
],
dtype=np.float64,
)
@dataclass
class _BodyFrames:
available: bool = True
def body_frame_for_frame(self, frame_id: str) -> ReplayBodyFrame | None:
if not self.available:
return None
return ReplayBodyFrame(
frame_id=frame_id,
origin_map_xyz_m=(1.0, 0.0, 0.0),
basis_map_from_body=((1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)),
sensor_height_m=1.25,
surface_slope_deg=0.0,
forward_source="fixture",
camera_forward_alignment_deg=0.0,
)
class _PredictionTrapPack:
result_id = "m48-object-quality-pack-" + "a" * 64
result_root = REPOSITORY_ROOT
manifest = {
"identity": {
"source": {
"source_id": M48_EXPECTED_SOURCE_ID,
"source_session_id": M48_EXPECTED_SESSION_ID,
}
}
}
report: dict[str, object] = {}
clips: tuple[dict[str, object], ...] = ()
frame_references = (
{
"clip_id": "clip-01",
"sequence": 2,
"source_time_ns": 2_000_000_000,
},
)
@property
def predictions(self) -> object:
raise AssertionError("neutral raw reader opened frozen predictions")
def _reader(
tmp_path: Path,
*,
store: _Store | None = None,
body_frames: _BodyFrames | None = None,
) -> M48RawEvidenceReader:
profile = load_replay_threat_profile(PROFILE_PATH)
timeline = SimpleNamespace(
store=store or _Store(),
body_frames=body_frames or _BodyFrames(),
profile=profile,
)
threat = SimpleNamespace(
result_id="m4-threat-replay-fixture",
result_root=tmp_path,
manifest={"identity": {"frames_sha256": "f" * 64}},
)
return M48RawEvidenceReader(
repository_root=tmp_path,
threat_result=threat,
timeline=timeline,
point_limit=2,
)
def test_raw_reader_is_one_based_bounded_body_frame_and_prediction_free(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(raw_module, "_validate_m47_pack_binding", lambda **_: None)
reader = _reader(tmp_path)
pack = cast(M48ObjectQualityPack, _PredictionTrapPack())
frame = reader(pack, 2)
assert set(frame) == {
"schema_version",
"pack_id",
"clip_id",
"sequence",
"source_time_ns",
"source_available",
"body_frame_available",
"point_cloud_body_xyz_m",
"rig",
"corridor",
"occupied_voxel_size_m",
"candidate_identity_included",
"graph_boxes_ids_scores_included",
"frozen_predictions_included",
"strata_included",
"authority",
}
assert frame["schema_version"] == M48_RAW_SPATIAL_FRAME_SCHEMA
assert frame["clip_id"] == "clip-01"
assert frame["sequence"] == 2
assert frame["source_available"] is True
assert frame["body_frame_available"] is True
assert frame["point_cloud_body_xyz_m"] == [[0.0, 0.0, 0.0], [3.0, 0.0, 0.0]]
assert len(cast(list[object], frame["point_cloud_body_xyz_m"])) <= 2
assert frame["candidate_identity_included"] is False
assert frame["graph_boxes_ids_scores_included"] is False
assert frame["frozen_predictions_included"] is False
assert frame["strata_included"] is False
assert "metric_obstacles" not in frame
assert "camera_proposals" not in frame
assert "decision_counts" not in frame
assert "body_frame" not in frame
def test_raw_reader_fails_closed_on_clip_or_source_time_escape(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(raw_module, "_validate_m47_pack_binding", lambda **_: None)
pack = cast(M48ObjectQualityPack, _PredictionTrapPack())
reader = _reader(tmp_path)
with pytest.raises(M48RawEvidenceError, match="outside the selected neutral clips"):
reader(pack, 1)
mismatched = _reader(tmp_path, store=_Store(source_time_ns=2_000_000_001))
with pytest.raises(M48RawEvidenceError, match="source time escaped"):
mismatched(pack, 2)
def test_raw_reader_emits_empty_cloud_when_body_frame_is_unavailable(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(raw_module, "_validate_m47_pack_binding", lambda **_: None)
reader = _reader(
tmp_path,
store=_Store(source_available=False),
body_frames=_BodyFrames(available=False),
)
pack = cast(M48ObjectQualityPack, _PredictionTrapPack())
frame = reader.frame(pack=pack, sequence=2)
assert frame["source_available"] is False
assert frame["body_frame_available"] is False
assert frame["point_cloud_body_xyz_m"] == []
assert isinstance(frame["rig"], dict)
assert isinstance(frame["corridor"], dict)
@pytest.mark.parametrize("point_limit", [0, 4097, True])
def test_raw_reader_rejects_unbounded_point_limits(
tmp_path: Path,
point_limit: int,
) -> None:
profile = load_replay_threat_profile(PROFILE_PATH)
timeline = SimpleNamespace(store=_Store(), body_frames=_BodyFrames(), profile=profile)
threat = SimpleNamespace(result_id="fixture", result_root=tmp_path, manifest={})
with pytest.raises(M48RawEvidenceError, match="point limit"):
M48RawEvidenceReader(
repository_root=tmp_path,
threat_result=threat,
timeline=timeline,
point_limit=point_limit,
)
+236
View File
@@ -0,0 +1,236 @@
from __future__ import annotations
import json
from pathlib import Path
from types import SimpleNamespace
import pytest
import k1link.laboratory.m48_small_static_regression as regression
from k1link.laboratory.evidence_registry import LaboratoryEvidenceRegistry
from k1link.laboratory.evidence_report import verify_laboratory_evidence_result
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
AUTHORITY = {
"mode": "replay-simulated",
"physical_live": False,
"commands_enabled": False,
"actuation_allowed": False,
"navigation_or_safety_accepted": False,
}
def _canonical(value: object) -> bytes:
return json.dumps(
value,
sort_keys=True,
separators=(",", ":"),
ensure_ascii=False,
).encode()
def _write_json(path: Path, value: object) -> None:
path.write_bytes(_canonical(value) + b"\n")
def _correction(pack_id: str) -> dict[str, object]:
def tracklet(object_id: str, extent: list[float], *, passage: bool) -> dict[str, object]:
return {
"object_id": object_id,
"first_sequence": 10,
"last_sequence": 10,
"keyframes": [{
"sequence": 10,
"extent_xyxy": extent,
"visibility": "visible",
}],
"state_segments": [{
"start_sequence": 10,
"end_sequence": 10,
"geometry_association": "unknown",
"freshness": "current",
"motion": "static",
"threat": "not-threat",
"critical_corridor_obstacle": passage,
}],
"notes": None,
}
return {
"schema_version": "missioncore.m48-assisted-object-correction-session/v1",
"pack_id": pack_id,
"session_id": "m48-correction-session-" + "b" * 64,
"title": "fixture",
"revision": 7,
"state": "saved",
"created_at_utc": "2026-08-24T10:00:00Z",
"updated_at_utc": "2026-08-24T11:00:00Z",
"clips": [{
"clip_id": "m48-clip-01",
"start_sequence": 1,
"end_sequence": 20,
"review_state": "reviewed",
"no_object": False,
"tracklets": [
tracklet("object-01", [0.1, 0.1, 0.2, 0.2], passage=True),
tracklet("object-02", [0.7, 0.7, 0.8, 0.8], passage=False),
{
**tracklet("object-03", [0.3, 0.3, 0.4, 0.4], passage=True),
"object_id": "proposal-10-0",
},
],
"notes": None,
}],
"progress": {"reviewed_clip_count": 1, "clip_count": 1, "complete": True},
"seed_summary": {
"worker_id": "006",
"clip_count": 1,
"frame_count": 1,
"object_count": 1,
"prediction_rows_sha256": "c" * 64,
},
"evidence_summary": None,
"assistance": {
"mode": "frozen-candidate-seeded",
"candidate_predictions_seen": True,
"model_scores_seen": False,
"semantic_class_task_seen": False,
"independent_truth_eligible": False,
},
"authority": AUTHORITY,
"last_save_idempotency_key": "save-7",
"reviewer_id": None,
"submitted_at_utc": None,
"submission_sha256": None,
"frozen_document_name": None,
}
def test_builds_separate_assisted_baseline_without_truth_claim(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
pack_id = "m48-object-quality-pack-" + "a" * 64
pack_root = tmp_path / pack_id
pack_root.mkdir()
pack = SimpleNamespace(
result_id=pack_id,
result_root=pack_root,
manifest={
"identity_sha256": "a" * 64,
"identity": {
"source": {
"source_id": "RAVNOVES00",
"source_session_id": "source-session",
},
"freeze": {"prediction_rows_sha256": "c" * 64},
},
},
predictions=({
"schema_version": "missioncore.m48-frozen-prediction-row/v1",
"clip_id": "m48-clip-01",
"sequence": 10,
"source_time_ns": 100,
"terminal_outcome": "delivered",
"terminal_reason": None,
"free_space_claimed": False,
"objects": [{
"prediction_id": "proposal-10-0",
"extent_xyxy": [0.1, 0.1, 0.2, 0.2],
"geometry_association": "unknown",
"freshness": "current",
"motion": "static",
"threat": "not-threat",
}],
},),
)
monkeypatch.setattr(regression, "read_m48_object_quality_pack", lambda _: pack)
correction_path = tmp_path / "correction.json"
_write_json(correction_path, _correction(pack_id))
profile_path = REPOSITORY_ROOT / "config/perception/m48-small-static-passage-regression-v1.json"
result = regression.build_m48_small_static_passage_regression(
pack_root=pack_root,
correction_session_path=correction_path,
profile_path=profile_path,
output_root=tmp_path / "results",
run_created_at_utc="2026-08-24T12:00:00Z",
)
assert result.report["metrics"]["assisted_anchor_count"] == 2
assert result.report["metrics"]["worker_recalled_anchor_count"] == 1
assert result.report["metrics"]["worker_missed_anchor_count"] == 1
assert result.report["metrics"]["assisted_anchor_recall"] == 0.5
assert result.manifest["accepted"] is False
assert result.manifest["ground_truth"] is False
assert result.manifest["identity"]["human_lab_id"] == "M4.8"
assert result.manifest["identity"]["experiment_id"] == (
"m48-small-static-passage-regression/v1"
)
assert result.report["method"]["execution_class"] == "deterministic"
assert all(
row["authority"] == "operator-assisted-development-anchor-not-truth"
for row in result.anchors
)
registry = LaboratoryEvidenceRegistry.from_directory(
REPOSITORY_ROOT / "config/laboratories"
)
definition = next(
row
for row in registry.definitions
if row.work_id == "m48-small-static-passage-regression"
)
proof = verify_laboratory_evidence_result(definition, result.result_root)
assert proof["result_id"] == result.result_id
assert proof["artifact_count"] == 3
def test_reader_rejects_changed_comparison_artifact(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
pack_id = "m48-object-quality-pack-" + "a" * 64
pack_root = tmp_path / pack_id
pack_root.mkdir()
pack = SimpleNamespace(
result_id=pack_id,
result_root=pack_root,
manifest={
"identity_sha256": "a" * 64,
"identity": {
"source": {"source_id": "RAVNOVES00", "source_session_id": "source"},
"freeze": {"prediction_rows_sha256": "c" * 64},
},
},
predictions=({
"clip_id": "m48-clip-01",
"sequence": 10,
"source_time_ns": 100,
"terminal_outcome": "delivered",
"objects": [],
},),
)
monkeypatch.setattr(regression, "read_m48_object_quality_pack", lambda _: pack)
correction_path = tmp_path / "correction.json"
document = _correction(pack_id)
document["clips"][0]["tracklets"] = document["clips"][0]["tracklets"][:1]
_write_json(correction_path, document)
result = regression.build_m48_small_static_passage_regression(
pack_root=pack_root,
correction_session_path=correction_path,
profile_path=(
REPOSITORY_ROOT
/ "config/perception/m48-small-static-passage-regression-v1.json"
),
output_root=tmp_path / "results",
run_created_at_utc="2026-08-24T12:00:00Z",
)
comparison_path = result.result_root / "comparisons.jsonl"
comparison_path.write_bytes(comparison_path.read_bytes() + b"{}\n")
with pytest.raises(
regression.M48SmallStaticRegressionError,
match="artifact proof",
):
regression.read_m48_small_static_passage_regression(result.result_root)