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
@@ -0,0 +1,20 @@
{
"schema_version": "missioncore.laboratory-evidence-definition/v2",
"work_id": "m48-object-centric-quality",
"evidence_lifecycle": [
{
"phase": "review",
"runtime_relative_root": "m48/object-quality-packs",
"result_id_prefix": "m48-object-quality-pack",
"document_name": "manifest.json",
"schema_version": "missioncore.m48-object-centric-quality-pack/v1"
},
{
"phase": "result",
"runtime_relative_root": "m48/object-quality-results",
"result_id_prefix": "m48-object-quality-result",
"document_name": "manifest.json",
"schema_version": "missioncore.m48-object-centric-quality-result/v1"
}
]
}
@@ -0,0 +1,10 @@
{
"schema_version": "missioncore.laboratory-evidence-definition/v1",
"work_id": "m48-small-static-passage-regression",
"evidence": {
"runtime_relative_root": "m48/small-static-passage-regression-results",
"result_id_prefix": "m48-small-static-passage-regression",
"document_name": "manifest.json",
"schema_version": "missioncore.m48-small-static-passage-regression-result/v1"
}
}
+28
View File
@@ -1,6 +1,34 @@
{
"schema_version": "missioncore.laboratory-execution-registry/v1",
"definitions": [
{
"work_id": "m48-small-static-passage-regression",
"lifecycle": "canonical",
"isolation": "core-adapter",
"adapter_id": "canonical.m48-small-static-passage-regression/v1",
"input_roles": ["pack_root", "correction_session_path", "profile_path"],
"contracts": {
"source": "missioncore.m48-object-centric-quality-pack/v1",
"provider": "missioncore.m48-assisted-object-correction-session/v1",
"graph": "missioncore.m48-assisted-anchor-comparison/v1",
"run": "missioncore.laboratory-run/v1",
"evidence": "missioncore.m48-small-static-passage-regression-result/v1"
}
},
{
"work_id": "m48-object-centric-quality",
"lifecycle": "canonical",
"isolation": "core-adapter",
"adapter_id": "canonical.m48-object-centric-quality/v1",
"input_roles": ["pack_root", "truth_seal_root"],
"contracts": {
"source": "missioncore.m48-object-centric-quality-pack/v1",
"provider": "missioncore.m48-object-truth-seal/v1",
"graph": "missioncore.m48-object-centric-quality-graph/v1",
"run": "missioncore.laboratory-run/v1",
"evidence": "missioncore.m48-object-centric-quality-result/v1"
}
},
{
"work_id": "m4-replay-threat",
"lifecycle": "canonical",
@@ -0,0 +1,235 @@
{
"schema_version": "missioncore.m48-object-quality-selection/v1",
"selection_id": "m48-ravnoves00-balanced-connected-clips/v1",
"source_id": "RAVNOVES00",
"source_session_id": "20260720T065719Z_viewer_live",
"selection_basis": "prediction-frozen-source-curation-before-independent-truth",
"camera_frame_size": {
"width": 800,
"height": 600
},
"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
},
"clips": [
{
"clip_id": "m48-clip-01",
"component_id": "m48-component-development-01",
"route_block": "route-block-01",
"time_block": "time-block-01",
"split": "development",
"start_sequence": 1,
"end_sequence": 61
},
{
"clip_id": "m48-clip-02",
"component_id": "m48-component-development-01",
"route_block": "route-block-01",
"time_block": "time-block-01",
"split": "development",
"start_sequence": 121,
"end_sequence": 181
},
{
"clip_id": "m48-clip-03",
"component_id": "m48-component-development-01",
"route_block": "route-block-01",
"time_block": "time-block-01",
"split": "development",
"start_sequence": 241,
"end_sequence": 301
},
{
"clip_id": "m48-clip-04",
"component_id": "m48-component-development-02",
"route_block": "route-block-01",
"time_block": "time-block-02",
"split": "development",
"start_sequence": 421,
"end_sequence": 481
},
{
"clip_id": "m48-clip-05",
"component_id": "m48-component-development-02",
"route_block": "route-block-01",
"time_block": "time-block-02",
"split": "development",
"start_sequence": 581,
"end_sequence": 641
},
{
"clip_id": "m48-clip-06",
"component_id": "m48-component-development-02",
"route_block": "route-block-02",
"time_block": "time-block-02",
"split": "development",
"start_sequence": 821,
"end_sequence": 881
},
{
"clip_id": "m48-clip-07",
"component_id": "m48-component-development-03",
"route_block": "route-block-02",
"time_block": "time-block-03",
"split": "development",
"start_sequence": 1041,
"end_sequence": 1101
},
{
"clip_id": "m48-clip-08",
"component_id": "m48-component-development-03",
"route_block": "route-block-02",
"time_block": "time-block-03",
"split": "development",
"start_sequence": 1221,
"end_sequence": 1281
},
{
"clip_id": "m48-clip-09",
"component_id": "m48-component-development-03",
"route_block": "route-block-02",
"time_block": "time-block-03",
"split": "development",
"start_sequence": 1421,
"end_sequence": 1481
},
{
"clip_id": "m48-clip-10",
"component_id": "m48-component-development-04",
"route_block": "route-block-03-development",
"time_block": "time-block-04",
"split": "development",
"start_sequence": 1681,
"end_sequence": 1741
},
{
"clip_id": "m48-clip-11",
"component_id": "m48-component-development-04",
"route_block": "route-block-03-development",
"time_block": "time-block-04",
"split": "development",
"start_sequence": 1830,
"end_sequence": 1890
},
{
"clip_id": "m48-clip-12",
"component_id": "m48-component-development-04",
"route_block": "route-block-03-development",
"time_block": "time-block-04",
"split": "development",
"start_sequence": 2041,
"end_sequence": 2101
},
{
"clip_id": "m48-clip-13",
"component_id": "m48-component-validation-01",
"route_block": "route-block-03-validation",
"time_block": "time-block-05",
"split": "validation",
"start_sequence": 2191,
"end_sequence": 2251
},
{
"clip_id": "m48-clip-14",
"component_id": "m48-component-validation-01",
"route_block": "route-block-03-validation",
"time_block": "time-block-05",
"split": "validation",
"start_sequence": 2371,
"end_sequence": 2431
},
{
"clip_id": "m48-clip-15",
"component_id": "m48-component-validation-01",
"route_block": "route-block-03-validation",
"time_block": "time-block-05",
"split": "validation",
"start_sequence": 2551,
"end_sequence": 2611
},
{
"clip_id": "m48-clip-16",
"component_id": "m48-component-validation-02",
"route_block": "route-block-04",
"time_block": "time-block-06",
"split": "validation",
"start_sequence": 2731,
"end_sequence": 2791
},
{
"clip_id": "m48-clip-17",
"component_id": "m48-component-validation-02",
"route_block": "route-block-04",
"time_block": "time-block-06",
"split": "validation",
"start_sequence": 2911,
"end_sequence": 2971
},
{
"clip_id": "m48-clip-18",
"component_id": "m48-component-validation-02",
"route_block": "route-block-04",
"time_block": "time-block-06",
"split": "validation",
"start_sequence": 3111,
"end_sequence": 3171
},
{
"clip_id": "m48-clip-19",
"component_id": "m48-component-validation-03",
"route_block": "route-block-04",
"time_block": "time-block-07",
"split": "validation",
"start_sequence": 3291,
"end_sequence": 3351
},
{
"clip_id": "m48-clip-20",
"component_id": "m48-component-validation-03",
"route_block": "route-block-04",
"time_block": "time-block-07",
"split": "validation",
"start_sequence": 3471,
"end_sequence": 3531
},
{
"clip_id": "m48-clip-21",
"component_id": "m48-component-validation-03",
"route_block": "route-block-05",
"time_block": "time-block-07",
"split": "validation",
"start_sequence": 3651,
"end_sequence": 3711
},
{
"clip_id": "m48-clip-22",
"component_id": "m48-component-validation-04",
"route_block": "route-block-05",
"time_block": "time-block-08",
"split": "validation",
"start_sequence": 3831,
"end_sequence": 3891
},
{
"clip_id": "m48-clip-23",
"component_id": "m48-component-validation-04",
"route_block": "route-block-05",
"time_block": "time-block-08",
"split": "validation",
"start_sequence": 4051,
"end_sequence": 4111
},
{
"clip_id": "m48-clip-24",
"component_id": "m48-component-validation-04",
"route_block": "route-block-05",
"time_block": "time-block-08",
"split": "validation",
"start_sequence": 4429,
"end_sequence": 4489
}
]
}
@@ -0,0 +1,72 @@
{
"schema_version": "missioncore.m48-object-quality-profile/v1",
"profile_id": "m48-ravnoves00-object-quality/v1",
"source_graph_id": "reference-perception-graph/v2",
"source_profile_id": "m4-ravnoves00-recorded-realtime/v1",
"clip_contract": {
"minimum_clip_count": 20,
"maximum_clip_count": 30,
"minimum_duration_seconds": 5.0,
"maximum_duration_seconds": 10.0,
"required_validation_hypotheses": [
"prediction-associated",
"prediction-fisheye-edge",
"prediction-moving",
"prediction-small-obstacle",
"prediction-sparse-scene",
"prediction-static",
"prediction-threat",
"prediction-unassociated"
],
"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
},
"splits": [
"development",
"validation"
],
"connected_component_split_overlap_allowed": false,
"route_or_time_block_split_overlap_allowed": false,
"release_gate_split": "validation"
},
"review_contract": {
"review_unit": "clip-local-object-tracklet",
"extent_labels": "sparse-normalized-xyxy-keyframes",
"state_labels": "contiguous-tracklet-state-segments",
"per_frame_expansion": {
"extent": "linear-between-bounding-keyframes",
"visibility": "left-keyframe-hold",
"state": "contiguous-state-segment"
},
"semantic_class_labels_allowed": false,
"independent_reviewers_required": 2,
"adjudication_required": true,
"predictions_frozen_before_label_reveal": true,
"prediction_content_visible_to_reviewers": false,
"selection_hypotheses_visible_to_reviewers": false
},
"matching": {
"extent_iou_threshold": 0.5
},
"release_thresholds": {
"terminal_outcome_accounting": 1.0,
"false_free_space_claims": 0,
"obstacle_presence_precision": 0.9,
"obstacle_presence_recall": 0.9,
"critical_corridor_obstacle_recall": 0.95,
"geometry_association_correctness": 0.9,
"freshness_correctness": 0.9,
"motion_decision_correctness": 0.9,
"critical_threat_not_threat": 0
},
"authority": {
"mode": "replay-simulated",
"physical_live": false,
"commands_enabled": false,
"actuation_allowed": false,
"navigation_or_safety_accepted": false
}
}
@@ -0,0 +1,13 @@
{
"schema_version": "missioncore.m48-small-static-passage-regression-profile/v1",
"profile_id": "m48-small-static-passage-regression/v1",
"pipeline_id": "m48-class-free-object-quality/v1",
"experiment_id": "m48-small-static-passage-regression/v1",
"human_lab_id": "M4.8",
"run_label": "M4.8R1",
"anchor_selection": "operator-added-tracklets-in-reviewed-clips/v1",
"extent_iou_threshold": 0.5,
"minimum_assisted_anchor_recall": 0.9,
"minimum_anchor_count": 1,
"independent_truth": false
}
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
"""Freeze the source-scoped RAVNOVES00 M4.8 independent-review pack."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
from k1link.laboratory.m48_ravnoves00_pack import prepare_m48_ravnoves00_pack
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--m47-lab-root", type=Path, required=True)
parser.add_argument("--graph-result-root", type=Path, required=True)
parser.add_argument("--threat-result-root", type=Path, required=True)
parser.add_argument("--geometry-result-root", type=Path, required=True)
parser.add_argument("--camera-index", type=Path, required=True)
parser.add_argument("--selection", type=Path, required=True)
parser.add_argument("--frozen-at-utc", required=True)
parser.add_argument("--output-root", type=Path, required=True)
args = parser.parse_args()
result = prepare_m48_ravnoves00_pack(
m47_lab_root=args.m47_lab_root,
graph_result_root=args.graph_result_root,
threat_result_root=args.threat_result_root,
geometry_result_root=args.geometry_result_root,
camera_index_path=args.camera_index,
selection_path=args.selection,
frozen_at_utc=args.frozen_at_utc,
output_root=args.output_root,
)
print(
json.dumps(
{
"result_id": result.result_id,
"result_root": str(result.result_root),
"status": result.report["status"],
"clip_count": result.report["metrics"]["clip_count"],
"frame_count": result.report["metrics"]["frame_count"],
"truth_labels_available": False,
},
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
)
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,79 @@
#!/usr/bin/env python3
"""Publish one canonical append-only M4.8 small-static regression run."""
from __future__ import annotations
import argparse
import json
import socket
from pathlib import Path
from k1link.compute.pipeline_telemetry import JsonlPipelineTelemetrySink
from k1link.laboratory import (
LaboratoryEvidenceRegistry,
LaboratoryExecutionRegistry,
LaboratoryRunner,
LaboratoryRunRequest,
)
def _parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser()
parser.add_argument("--pack-root", type=Path, required=True)
parser.add_argument("--correction-session", type=Path, required=True)
parser.add_argument("--profile", type=Path, required=True)
parser.add_argument("--output-root", type=Path, required=True)
parser.add_argument("--receipt-root", type=Path, required=True)
parser.add_argument("--telemetry-path", type=Path, required=True)
parser.add_argument("--run-id", required=True)
parser.add_argument("--request-id", required=True)
return parser
def main() -> int:
args = _parser().parse_args()
repository_root = Path(__file__).resolve().parents[1]
evidence = LaboratoryEvidenceRegistry.from_directory(
repository_root / "config" / "laboratories"
)
execution = LaboratoryExecutionRegistry.from_file(
repository_root / "config" / "laboratory-execution.json",
evidence,
)
runner = LaboratoryRunner(
registry=execution,
evidence_registry=evidence,
sink=JsonlPipelineTelemetrySink(args.telemetry_path),
)
pack_id = args.pack_root.name
result = runner.run(
LaboratoryRunRequest(
work_id="m48-small-static-passage-regression",
run_id=args.run_id,
request_id=args.request_id,
contour_id="mission-core-laboratory",
agent_id="local-control-plane",
node_id=socket.gethostname(),
source_id="RAVNOVES00",
source_package_id=pack_id,
method_id="m48-small-static-passage-regression/v1",
inputs={
"pack_root": args.pack_root,
"correction_session_path": args.correction_session,
"profile_path": args.profile,
},
output_root=args.output_root,
receipt_root=args.receipt_root,
)
)
print(json.dumps({
"result_id": result.result_id,
"result_root": str(result.result_root),
"receipt_id": result.receipt_id,
"receipt_root": str(result.receipt_root),
}, ensure_ascii=False, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
+4
View File
@@ -2,8 +2,10 @@
from k1link.laboratory.evidence_registry import (
LABORATORY_EVIDENCE_DEFINITION_SCHEMA,
LABORATORY_EVIDENCE_LIFECYCLE_DEFINITION_SCHEMA,
LaboratoryEvidenceDefinition,
LaboratoryEvidenceRegistry,
LaboratoryEvidenceVariant,
LaboratoryRegistryError,
)
from k1link.laboratory.evidence_report import (
@@ -34,9 +36,11 @@ from k1link.laboratory.value_review_registry import (
__all__ = [
"LABORATORY_EVIDENCE_DEFINITION_SCHEMA",
"LABORATORY_EVIDENCE_LIFECYCLE_DEFINITION_SCHEMA",
"LABORATORY_EVIDENCE_REPORT_SCHEMA",
"LaboratoryEvidenceDefinition",
"LaboratoryEvidenceRegistry",
"LaboratoryEvidenceVariant",
"LaboratoryEvidenceReportError",
"LaboratoryEvidenceReportNotFound",
"LaboratoryEvidenceReportService",
+128 -9
View File
@@ -7,13 +7,22 @@ from pathlib import Path, PurePosixPath
from typing import Final
LABORATORY_EVIDENCE_DEFINITION_SCHEMA: Final = "missioncore.laboratory-evidence-definition/v1"
LABORATORY_EVIDENCE_LIFECYCLE_DEFINITION_SCHEMA: Final = (
"missioncore.laboratory-evidence-definition/v2"
)
_DEFINITION_MAX_BYTES: Final = 16 * 1024
_IDENTIFIER = re.compile(r"^[a-z][a-z0-9-]{2,95}$")
_SCHEMA_VERSION = re.compile(r"^missioncore\.[a-z0-9.-]+/v[1-9][0-9]*$")
_TOP_LEVEL_KEYS: Final = frozenset({"schema_version", "work_id", "evidence"})
_LIFECYCLE_TOP_LEVEL_KEYS: Final = frozenset(
{"schema_version", "work_id", "evidence_lifecycle"}
)
_EVIDENCE_KEYS: Final = frozenset(
{"runtime_relative_root", "result_id_prefix", "document_name", "schema_version"}
)
_LIFECYCLE_EVIDENCE_KEYS: Final = frozenset(
{"phase", "runtime_relative_root", "result_id_prefix", "document_name", "schema_version"}
)
class LaboratoryRegistryError(ValueError):
@@ -21,15 +30,15 @@ class LaboratoryRegistryError(ValueError):
@dataclass(frozen=True, slots=True)
class LaboratoryEvidenceDefinition:
work_id: str
class LaboratoryEvidenceVariant:
phase: str
runtime_relative_root: PurePosixPath
result_id_prefix: str
document_name: str
result_schema_version: str
def __post_init__(self) -> None:
_identifier(self.work_id, "work_id")
_identifier(self.phase, "evidence phase")
_identifier(self.result_id_prefix, "result_id_prefix")
_document_name(self.document_name)
_schema_version(self.result_schema_version)
@@ -45,6 +54,69 @@ class LaboratoryEvidenceDefinition:
return runtime_root.joinpath(*self.runtime_relative_root.parts)
@dataclass(frozen=True, slots=True)
class LaboratoryEvidenceDefinition:
work_id: str
runtime_relative_root: PurePosixPath
result_id_prefix: str
document_name: str
result_schema_version: str
lifecycle_variants: tuple[LaboratoryEvidenceVariant, ...] = ()
def __post_init__(self) -> None:
_identifier(self.work_id, "work_id")
primary = LaboratoryEvidenceVariant(
phase="result",
runtime_relative_root=self.runtime_relative_root,
result_id_prefix=self.result_id_prefix,
document_name=self.document_name,
result_schema_version=self.result_schema_version,
)
if not self.lifecycle_variants:
return
if not all(
isinstance(variant, LaboratoryEvidenceVariant)
for variant in self.lifecycle_variants
):
raise LaboratoryRegistryError("LAB lifecycle variants must be immutable evidence")
if self.lifecycle_variants[-1] != primary:
raise LaboratoryRegistryError("LAB lifecycle terminal evidence must be primary")
phases = [variant.phase for variant in self.lifecycle_variants]
if len(phases) != len(set(phases)):
raise LaboratoryRegistryError("duplicate LAB evidence phase")
@property
def evidence_variants(self) -> tuple[LaboratoryEvidenceVariant, ...]:
if self.lifecycle_variants:
return self.lifecycle_variants
return (
LaboratoryEvidenceVariant(
phase="result",
runtime_relative_root=self.runtime_relative_root,
result_id_prefix=self.result_id_prefix,
document_name=self.document_name,
result_schema_version=self.result_schema_version,
),
)
@property
def result_id_pattern(self) -> re.Pattern[str]:
return re.compile(rf"^{re.escape(self.result_id_prefix)}-[a-f0-9]{{64}}$")
def result_root(self, runtime_root: Path) -> Path:
return runtime_root.joinpath(*self.runtime_relative_root.parts)
def variant_for_result_id(self, result_id: str) -> LaboratoryEvidenceVariant | None:
return next(
(
variant
for variant in self.evidence_variants
if variant.result_id_pattern.fullmatch(result_id) is not None
),
None,
)
@dataclass(frozen=True, slots=True)
class LaboratoryEvidenceRegistry:
definitions: tuple[LaboratoryEvidenceDefinition, ...]
@@ -89,14 +161,42 @@ def _read_definition(path: Path) -> LaboratoryEvidenceDefinition:
except (json.JSONDecodeError, OSError) as exc:
raise LaboratoryRegistryError(f"LAB definition is unreadable: {path.name}") from exc
document = _object(payload, f"LAB definition {path.name}")
_exact_keys(document, _TOP_LEVEL_KEYS, f"LAB definition {path.name}")
if document["schema_version"] != LABORATORY_EVIDENCE_DEFINITION_SCHEMA:
schema_version = document.get("schema_version")
if schema_version not in {
LABORATORY_EVIDENCE_DEFINITION_SCHEMA,
LABORATORY_EVIDENCE_LIFECYCLE_DEFINITION_SCHEMA,
}:
raise LaboratoryRegistryError(f"LAB definition schema is invalid: {path.name}")
expected_keys = (
_TOP_LEVEL_KEYS
if schema_version == LABORATORY_EVIDENCE_DEFINITION_SCHEMA
else _LIFECYCLE_TOP_LEVEL_KEYS
)
_exact_keys(document, expected_keys, f"LAB definition {path.name}")
work_id = _identifier(document["work_id"], "work_id")
if path.name != f"{work_id}.json":
raise LaboratoryRegistryError(f"LAB definition filename must match work_id: {path.name}")
evidence = _object(document["evidence"], f"LAB evidence {work_id}")
_exact_keys(evidence, _EVIDENCE_KEYS, f"LAB evidence {work_id}")
if schema_version == LABORATORY_EVIDENCE_DEFINITION_SCHEMA:
lifecycle_variants: tuple[LaboratoryEvidenceVariant, ...] = ()
evidence = _object(document["evidence"], f"LAB evidence {work_id}")
_exact_keys(evidence, _EVIDENCE_KEYS, f"LAB evidence {work_id}")
else:
lifecycle = document["evidence_lifecycle"]
if not isinstance(lifecycle, list) or len(lifecycle) < 2:
raise LaboratoryRegistryError(
f"LAB evidence lifecycle must contain at least two phases: {work_id}"
)
lifecycle_variants = tuple(
_read_variant(row, f"LAB evidence {work_id}[{index}]")
for index, row in enumerate(lifecycle)
)
terminal = lifecycle_variants[-1]
evidence = {
"runtime_relative_root": str(terminal.runtime_relative_root),
"result_id_prefix": terminal.result_id_prefix,
"document_name": terminal.document_name,
"schema_version": terminal.result_schema_version,
}
result_id_prefix = _identifier(evidence["result_id_prefix"], "result_id_prefix")
document_name = _document_name(evidence["document_name"])
result_schema_version = _schema_version(evidence["schema_version"])
@@ -106,6 +206,19 @@ def _read_definition(path: Path) -> LaboratoryEvidenceDefinition:
result_id_prefix=result_id_prefix,
document_name=document_name,
result_schema_version=result_schema_version,
lifecycle_variants=lifecycle_variants,
)
def _read_variant(value: object, label: str) -> LaboratoryEvidenceVariant:
evidence = _object(value, label)
_exact_keys(evidence, _LIFECYCLE_EVIDENCE_KEYS, label)
return LaboratoryEvidenceVariant(
phase=_identifier(evidence["phase"], f"{label}.phase"),
runtime_relative_root=_relative_root(evidence["runtime_relative_root"]),
result_id_prefix=_identifier(evidence["result_id_prefix"], "result_id_prefix"),
document_name=_document_name(evidence["document_name"]),
result_schema_version=_schema_version(evidence["schema_version"]),
)
@@ -177,9 +290,15 @@ def _relative_root(value: object) -> PurePosixPath:
def _reject_duplicates(definitions: tuple[LaboratoryEvidenceDefinition, ...]) -> None:
dimensions = {
"work_id": [definition.work_id for definition in definitions],
"result_id_prefix": [definition.result_id_prefix for definition in definitions],
"result_id_prefix": [
variant.result_id_prefix
for definition in definitions
for variant in definition.evidence_variants
],
"runtime_relative_root": [
str(definition.runtime_relative_root) for definition in definitions
str(variant.runtime_relative_root)
for definition in definitions
for variant in definition.evidence_variants
],
}
for label, values in dimensions.items():
+13 -10
View File
@@ -9,6 +9,7 @@ from typing import Any, Final
from k1link.laboratory.evidence_registry import (
LaboratoryEvidenceDefinition,
LaboratoryEvidenceRegistry,
LaboratoryEvidenceVariant,
)
LABORATORY_EVIDENCE_REPORT_SCHEMA: Final = "missioncore.laboratory-evidence-report/v1"
@@ -40,12 +41,13 @@ def verify_laboratory_evidence_result(
resolved = candidate.resolve(strict=True)
except OSError as exc:
raise LaboratoryEvidenceReportError("LAB evidence result is unavailable") from exc
if not resolved.is_dir() or definition.result_id_pattern.fullmatch(resolved.name) is None:
variant = definition.variant_for_result_id(resolved.name)
if not resolved.is_dir() or variant is None:
raise LaboratoryEvidenceReportError("LAB evidence result path is invalid")
document_path = _safe_file(resolved, definition.document_name)
document_path = _safe_file(resolved, variant.document_name)
document_bytes = _read_bounded(document_path, _DOCUMENT_MAX_BYTES, "LAB document")
document = _json_object(document_bytes, "LAB document")
_validate_document(document, definition, resolved.name)
_validate_document(document, variant, resolved.name)
identity = _object_or_none(document.get("identity"))
identity_sha256 = document.get("identity_sha256")
if identity is None or not isinstance(identity_sha256, str):
@@ -77,13 +79,14 @@ class LaboratoryEvidenceReportService:
def read(self, work_id: str, result_id: str) -> dict[str, object]:
definition = self._definitions.get(work_id)
if definition is None or definition.result_id_pattern.fullmatch(result_id) is None:
variant = definition.variant_for_result_id(result_id) if definition is not None else None
if definition is None or variant is None:
raise LaboratoryEvidenceReportNotFound("LAB evidence identity is unknown")
result_root = self._result_root(definition, result_id)
document_path = _safe_file(result_root, definition.document_name)
result_root = self._result_root(variant, result_id)
document_path = _safe_file(result_root, variant.document_name)
document_bytes = _read_bounded(document_path, _DOCUMENT_MAX_BYTES, "LAB document")
document = _json_object(document_bytes, "LAB document")
_validate_document(document, definition, result_id)
_validate_document(document, variant, result_id)
identity = _object_or_none(document.get("identity"))
identity_sha256 = document.get("identity_sha256")
@@ -210,7 +213,7 @@ class LaboratoryEvidenceReportService:
def _result_root(
self,
definition: LaboratoryEvidenceDefinition,
variant: LaboratoryEvidenceVariant,
result_id: str,
) -> Path:
configured = self._runtime_root_provider()
@@ -223,7 +226,7 @@ class LaboratoryEvidenceReportService:
runtime_root = runtime_root.resolve(strict=True)
except OSError as exc:
raise LaboratoryEvidenceReportNotFound("LAB runtime root is unavailable") from exc
candidate = definition.result_root(runtime_root) / result_id
candidate = variant.result_root(runtime_root) / result_id
if candidate.is_symlink():
raise LaboratoryEvidenceReportError("LAB result must not be a symlink")
try:
@@ -237,7 +240,7 @@ class LaboratoryEvidenceReportService:
def _validate_document(
document: dict[str, Any],
definition: LaboratoryEvidenceDefinition,
definition: LaboratoryEvidenceVariant,
result_id: str,
) -> None:
if document.get("schema_version") != definition.result_schema_version:
+44 -4
View File
@@ -169,10 +169,11 @@ class LaboratoryExecutionRegistry:
f"laboratory classification is incomplete; missing={missing}, unknown={unknown}"
)
for definition in self.definitions:
if (
evidence_by_work_id[definition.work_id].result_schema_version
!= definition.evidence_contract
):
evidence_contracts = {
variant.result_schema_version
for variant in evidence_by_work_id[definition.work_id].evidence_variants
}
if definition.evidence_contract not in evidence_contracts:
raise LaboratoryExecutionError(
f"laboratory evidence contract mismatch: {definition.work_id}"
)
@@ -311,6 +312,10 @@ class LaboratoryRunner:
def canonical_laboratory_adapters() -> dict[str, LaboratoryAdapter]:
return {
"canonical.m48-small-static-passage-regression/v1": (
_run_m48_small_static_passage_regression
),
"canonical.m48-object-centric-quality/v1": _run_m48_object_centric_quality,
"canonical.m4-replay-threat/v1": _run_m4_replay_threat,
"canonical.e33-worker-shadow/v1": _run_e33,
"canonical.e35-degradation-recovery/v1": _run_e35,
@@ -319,6 +324,41 @@ def canonical_laboratory_adapters() -> dict[str, LaboratoryAdapter]:
}
def _run_m48_small_static_passage_regression(
request: LaboratoryRunRequest,
) -> LaboratoryAdapterResult:
from k1link.laboratory.m48_small_static_regression import (
build_m48_small_static_passage_regression,
)
result = build_m48_small_static_passage_regression(
pack_root=request.inputs["pack_root"],
correction_session_path=request.inputs["correction_session_path"],
profile_path=request.inputs["profile_path"],
output_root=request.output_root,
)
return LaboratoryAdapterResult(
result_root=result.result_root,
result_id=result.result_id,
)
def _run_m48_object_centric_quality(
request: LaboratoryRunRequest,
) -> LaboratoryAdapterResult:
from k1link.laboratory.m48_object_quality import score_m48_object_quality
result = score_m48_object_quality(
pack_root=request.inputs["pack_root"],
truth_seal_root=request.inputs["truth_seal_root"],
output_root=request.output_root,
)
return LaboratoryAdapterResult(
result_root=result.result_root,
result_id=result.result_id,
)
def _run_m4_replay_threat(request: LaboratoryRunRequest) -> LaboratoryAdapterResult:
from k1link.perception.threat_replay import build_threat_replay
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,567 @@
"""Deterministic RAVNOVES00 adapter for the M4.8 object-quality pack."""
from __future__ import annotations
import hashlib
import json
import math
import re
from collections.abc import Iterator, Mapping
from itertools import zip_longest
from pathlib import Path
from typing import Any, Final
from k1link.laboratory.m47_reference_graph import read_m47_reference_graph_lab
from k1link.laboratory.m48_object_quality import (
M48_PREPARATION_PROVENANCE_SCHEMA,
M48_SELECTION_HYPOTHESIS_PROFILE,
M48ObjectQualityPack,
build_m48_object_quality_pack,
)
M48_SELECTION_SCHEMA: Final = "missioncore.m48-object-quality-selection/v1"
M48_SELECTION_ID: Final = "m48-ravnoves00-balanced-connected-clips/v1"
M48_SOURCE_ID: Final = "RAVNOVES00"
M48_SOURCE_SESSION_ID: Final = "20260720T065719Z_viewer_live"
M48_FRAME_COUNT: Final = 4_489
M48_IMAGE_WIDTH: Final = 800
M48_IMAGE_HEIGHT: Final = 600
_CAMERA_INDEX_SCHEMA: Final = "missioncore.camera-recording-index/v1"
_GRAPH_FRAME_SCHEMA: Final = "missioncore.local-obstacle-map/v1"
_THREAT_FRAME_SCHEMAS: Final = frozenset(
{
"missioncore.perception-threat-replay-frame/v1",
"missioncore.perception-threat-replay-frame/v2",
}
)
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
class M48Ravnoves00PackError(RuntimeError):
"""The source adapter escaped the accepted immutable RAVNOVES00 evidence."""
def prepare_m48_ravnoves00_pack(
*,
m47_lab_root: Path,
graph_result_root: Path,
threat_result_root: Path,
geometry_result_root: Path,
camera_index_path: Path,
selection_path: Path,
frozen_at_utc: str,
output_root: Path,
) -> M48ObjectQualityPack:
"""Freeze the selected M4.8 clips from the exact accepted M4.7 source."""
lab = read_m47_reference_graph_lab(m47_lab_root)
source = _mapping(lab.report.get("source"), "M4.7 source")
graph_root = _directory(graph_result_root, "M4.7 graph result")
threat_root = _directory(threat_result_root, "M4.6 visual result")
geometry_root = _directory(geometry_result_root, "M4.4 geometry result")
camera_index = _file(camera_index_path, "recorded camera index")
selection = _read_json(_file(selection_path, "M4.8 selection"), "M4.8 selection")
clips = _selection_clips(selection)
if (
graph_root.name != source.get("graph_result_id")
or threat_root.name != source.get("visual_result_id")
or source.get("source_id") != M48_SOURCE_ID
or source.get("source_session_id") != M48_SOURCE_SESSION_ID
):
raise M48Ravnoves00PackError("M4.8 source roots do not match the accepted M4.7 LAB")
graph_frames_path = _validate_graph_result(graph_root)
threat_frames_path, threat_identity = _validate_threat_result(
threat_root,
expected_frames_sha256=source.get("threat_frames_sha256"),
)
geometry_frames_path = _validate_geometry_result(
geometry_root,
expected_result_id=threat_identity.get("geometry_result_id"),
expected_frames_sha256=threat_identity.get("geometry_frames_sha256"),
)
camera_rows = tuple(_iter_jsonl(camera_index, "recorded camera index"))
_validate_camera_rows(camera_rows)
selected_sequences = {
sequence
for clip in clips
for sequence in range(
_integer(clip.get("start_sequence"), "clip start_sequence"),
_integer(clip.get("end_sequence"), "clip end_sequence") + 1,
)
}
frame_catalog: list[dict[str, object]] = []
predictions: list[dict[str, object]] = []
previous_source_time_ns = -1
graph_rows = _iter_jsonl(graph_frames_path, "M4.7 graph frames")
threat_rows = _iter_jsonl(threat_frames_path, "M4.6 threat frames")
geometry_rows = _iter_jsonl(geometry_frames_path, "M4.4 geometry frames")
for frame_index, values in enumerate(
zip_longest(graph_rows, threat_rows, geometry_rows, camera_rows),
):
graph_row, threat_row, geometry_row, camera_row = values
if graph_row is None or threat_row is None or geometry_row is None or camera_row is None:
raise M48Ravnoves00PackError("M4.8 source ledgers have different lengths")
sequence = frame_index + 1
source_time_ns = _validate_bound_frame(
graph_row=graph_row,
threat_row=threat_row,
geometry_row=geometry_row,
camera_row=camera_row,
frame_index=frame_index,
previous_source_time_ns=previous_source_time_ns,
)
previous_source_time_ns = source_time_ns
frame_catalog.append(
{
"sequence": sequence,
"source_time_ns": source_time_ns,
"camera_fragment_sha256": camera_row["sha256"],
}
)
if sequence in selected_sequences:
obstacle_map = _mapping(graph_row.get("obstacle_map"), "M4.7 obstacle map")
predictions.append(
{
"sequence": sequence,
"source_time_ns": source_time_ns,
"terminal_outcome": "delivered",
"terminal_reason": None,
"free_space_claimed": obstacle_map["free_space_claimed"],
"objects": _prediction_objects(
threat_row.get("camera_proposals"),
geometry_observations=geometry_row.get("observations"),
metric_obstacles=threat_row.get("metric_obstacles"),
),
}
)
if len(frame_catalog) != M48_FRAME_COUNT:
raise M48Ravnoves00PackError("M4.8 source frame count changed")
preparation_provenance = {
"schema_version": M48_PREPARATION_PROVENANCE_SCHEMA,
"adapter": {
"module": "k1link.laboratory.m48_ravnoves00_pack",
"sha256": _file_sha256(Path(__file__).resolve(strict=True)),
},
"selection": {
"selection_id": M48_SELECTION_ID,
"sha256": _file_sha256(selection_path),
},
"camera_index": {
"source_session_id": M48_SOURCE_SESSION_ID,
"sha256": _file_sha256(camera_index),
"byte_length": camera_index.stat().st_size,
"frame_count": len(camera_rows),
},
"graph": _source_provenance(graph_root, graph_frames_path),
"threat": _source_provenance(threat_root, threat_frames_path),
"geometry": _source_provenance(geometry_root, geometry_frames_path),
}
return build_m48_object_quality_pack(
m47_lab_root=lab.result_root,
frame_catalog=frame_catalog,
clips=clips,
predictions=predictions,
preparation_provenance=preparation_provenance,
frozen_at_utc=frozen_at_utc,
output_root=output_root,
)
def _validate_graph_result(root: Path) -> Path:
manifest = _read_json(_file(root / "manifest.json", "M4.7 graph manifest"), "graph manifest")
files = _mapping(manifest.get("files"), "M4.7 graph files")
descriptor = _mapping(files.get("frames.jsonl"), "M4.7 graph frame descriptor")
frames = _file(root / "frames.jsonl", "M4.7 graph frames")
expected_bytes = descriptor.get("bytes")
expected_sha256 = descriptor.get("sha256")
if (
manifest.get("schema_version") != "missioncore.reference-perception-graph-manifest/v1"
or manifest.get("result_id") != root.name
or manifest.get("accepted") is not True
or manifest.get("graph_id") != "reference-perception-graph/v2"
or manifest.get("run_mode") != "lossless-replay"
or not isinstance(expected_bytes, int)
or expected_bytes != frames.stat().st_size
or not _is_sha256(expected_sha256)
or _file_sha256(frames) != expected_sha256
):
raise M48Ravnoves00PackError("M4.7 graph result changed")
return frames
def _validate_threat_result(
root: Path,
*,
expected_frames_sha256: object,
) -> tuple[Path, dict[str, Any]]:
manifest = _read_json(
_file(root / "manifest.json", "M4.6 threat manifest"),
"threat manifest",
)
identity = _mapping(manifest.get("identity"), "M4.6 threat identity")
frames = _file(root / "frames.jsonl", "M4.6 threat frames")
if (
manifest.get("schema_version") != "missioncore.perception-threat-replay-result/v2"
or manifest.get("result_id") != root.name
or manifest.get("accepted") is not True
or identity.get("source_session_id") != M48_SOURCE_SESSION_ID
or not _is_sha256(expected_frames_sha256)
or identity.get("frames_sha256") != expected_frames_sha256
or _file_sha256(frames) != expected_frames_sha256
):
raise M48Ravnoves00PackError("M4.6 threat result changed")
return frames, identity
def _validate_geometry_result(
root: Path,
*,
expected_result_id: object,
expected_frames_sha256: object,
) -> Path:
manifest = _read_json(
_file(root / "manifest.json", "M4.4 geometry manifest"),
"geometry manifest",
)
identity = _mapping(manifest.get("identity"), "M4.4 geometry identity")
frames = _file(root / "frames.jsonl", "M4.4 geometry frames")
if (
manifest.get("schema_version") != "missioncore.perception-geometry-replay-result/v1"
or root.name != expected_result_id
or identity.get("accepted") is not True
or identity.get("source_pack_id")
!= "e10-lidar-pack-576c994a6c814e2592dd6240ace3902a5db94843312c759a73ba0c9166157d2b"
or not _is_sha256(expected_frames_sha256)
or identity.get("frames_sha256") != expected_frames_sha256
or _file_sha256(frames) != expected_frames_sha256
):
raise M48Ravnoves00PackError("M4.4 geometry result changed")
return frames
def _selection_clips(document: Mapping[str, object]) -> tuple[dict[str, object], ...]:
expected_keys = {
"schema_version",
"selection_id",
"source_id",
"source_session_id",
"selection_basis",
"camera_frame_size",
"selection_hypothesis_profile",
"clips",
}
frame_size = _mapping(document.get("camera_frame_size"), "selection frame size")
raw_clips = document.get("clips")
if (
set(document) != expected_keys
or document.get("schema_version") != M48_SELECTION_SCHEMA
or document.get("selection_id") != M48_SELECTION_ID
or document.get("source_id") != M48_SOURCE_ID
or document.get("source_session_id") != M48_SOURCE_SESSION_ID
or document.get("selection_basis")
!= "prediction-frozen-source-curation-before-independent-truth"
or frame_size != {"width": M48_IMAGE_WIDTH, "height": M48_IMAGE_HEIGHT}
or document.get("selection_hypothesis_profile") != M48_SELECTION_HYPOTHESIS_PROFILE
or not isinstance(raw_clips, list)
or any(not isinstance(item, dict) for item in raw_clips)
):
raise M48Ravnoves00PackError("M4.8 selection contract changed")
return tuple(dict(item) for item in raw_clips)
def _source_provenance(root: Path, frames_path: Path) -> dict[str, str]:
return {
"result_id": root.name,
"manifest_sha256": _file_sha256(root / "manifest.json"),
"frames_sha256": _file_sha256(frames_path),
}
def _validate_camera_rows(rows: tuple[dict[str, Any], ...]) -> None:
if len(rows) != M48_FRAME_COUNT:
raise M48Ravnoves00PackError("recorded camera index frame count changed")
previous_session_time = -1
for expected_sequence, row in enumerate(rows, start=1):
session_time = row.get("session_monotonic_ns")
if (
row.get("schema_version") != _CAMERA_INDEX_SCHEMA
or row.get("kind") != "media"
or row.get("sequence") != expected_sequence
or not isinstance(session_time, int)
or session_time <= previous_session_time
or not _is_sha256(row.get("sha256"))
):
raise M48Ravnoves00PackError("recorded camera index changed")
previous_session_time = session_time
def _validate_bound_frame(
*,
graph_row: Mapping[str, Any],
threat_row: Mapping[str, Any],
geometry_row: Mapping[str, Any],
camera_row: Mapping[str, Any],
frame_index: int,
previous_source_time_ns: int,
) -> int:
obstacle_map = _mapping(graph_row.get("obstacle_map"), "M4.7 obstacle map")
source_time_ns = threat_row.get("source_time_ns")
if (
graph_row.get("sequence") != frame_index
or obstacle_map.get("schema_version") != _GRAPH_FRAME_SCHEMA
or obstacle_map.get("frame_id") != f"frame-{frame_index:06d}"
or not isinstance(obstacle_map.get("free_space_claimed"), bool)
or threat_row.get("schema_version") not in _THREAT_FRAME_SCHEMAS
or threat_row.get("sequence") != frame_index
or threat_row.get("frame_id") != f"frame-{frame_index:06d}"
or geometry_row.get("schema_version") != "missioncore.perception-geometry-replay-frame/v1"
or geometry_row.get("sequence") != frame_index
or geometry_row.get("frame_id") != f"frame-{frame_index:06d}"
or geometry_row.get("source_available") != threat_row.get("source_available")
or not isinstance(source_time_ns, int)
or source_time_ns <= previous_source_time_ns
or camera_row.get("sequence") != frame_index + 1
):
raise M48Ravnoves00PackError("M4.8 frame binding changed")
return source_time_ns
def _prediction_objects(
value: object,
*,
geometry_observations: object,
metric_obstacles: object,
) -> list[dict[str, object]]:
if not isinstance(value, list) or any(not isinstance(item, dict) for item in value):
raise M48Ravnoves00PackError("M4.6 camera proposal collection changed")
observations = _proposal_observations(geometry_observations)
obstacles = _metric_obstacles(metric_obstacles)
objects: list[dict[str, object]] = []
seen: set[str] = set()
for proposal in value:
prediction_id = proposal.get("proposal_id")
occupied_support = proposal.get("occupied_support")
threat_value = proposal.get("threat_decision")
if (
not isinstance(prediction_id, str)
or prediction_id in seen
or not isinstance(occupied_support, bool)
or threat_value not in {None, "threat", "not-threat", "unknown"}
):
raise M48Ravnoves00PackError("M4.6 camera proposal identity changed")
seen.add(prediction_id)
observation = observations.get(prediction_id)
geometry = "associated" if occupied_support else "unknown"
freshness = "current"
motion = "unsupported"
threat = threat_value if isinstance(threat_value, str) else "unknown"
if occupied_support:
if observation is None:
raise M48Ravnoves00PackError("associated proposal lost its geometry observation")
currentness = observation.get("currentness")
if currentness not in {"current", "held", "stale", "unavailable"}:
raise M48Ravnoves00PackError("associated proposal currentness changed")
freshness = str(currentness)
centroid = _metric_centroid(observation)
obstacle = _match_metric_obstacle(centroid, obstacles)
raw_motion = obstacle.get("motion")
motion = {
"moving": "moving",
"stationary": "static",
"unknown": "unknown",
}.get(str(raw_motion), "")
assessment = _mapping(obstacle.get("assessment"), "metric obstacle assessment")
obstacle_threat = assessment.get("decision")
if not motion or obstacle_threat not in {"threat", "not-threat", "unknown"}:
raise M48Ravnoves00PackError("associated proposal state changed")
threat = str(obstacle_threat)
causes: set[str] = set()
if geometry == "unknown":
causes.add("insufficient-geometry-support")
if threat == "unknown":
causes.add("threat-evidence-insufficient")
if motion == "unknown":
causes.add("motion-not-supported")
objects.append(
{
"prediction_id": prediction_id,
"extent_xyxy": _normalized_extent(proposal.get("bbox_xyxy")),
"geometry_association": geometry,
"freshness": freshness,
"motion": motion,
"threat": threat,
"unknown_causes": sorted(causes),
}
)
return objects
def _proposal_observations(value: object) -> dict[str, dict[str, Any]]:
if not isinstance(value, list) or any(not isinstance(item, dict) for item in value):
raise M48Ravnoves00PackError("M4.4 observation collection changed")
mapped: dict[str, dict[str, Any]] = {}
for observation in value:
proposal_ids = observation.get("proposal_ids")
if not isinstance(proposal_ids, list) or any(
not isinstance(item, str) for item in proposal_ids
):
raise M48Ravnoves00PackError("M4.4 proposal binding changed")
for proposal_id in proposal_ids:
if proposal_id in mapped:
raise M48Ravnoves00PackError("M4.4 proposal has multiple observations")
mapped[proposal_id] = observation
return mapped
def _metric_obstacles(value: object) -> tuple[dict[str, Any], ...]:
if not isinstance(value, list) or any(not isinstance(item, dict) for item in value):
raise M48Ravnoves00PackError("M4.6 metric obstacle collection changed")
return tuple(value)
def _metric_centroid(observation: Mapping[str, Any]) -> tuple[float, float, float]:
geometry = _mapping(observation.get("metric_geometry"), "proposal metric geometry")
value = geometry.get("centroid_xyz_m")
if (
not isinstance(value, list)
or len(value) != 3
or any(
not isinstance(item, (int, float))
or isinstance(item, bool)
or not math.isfinite(float(item))
for item in value
)
):
raise M48Ravnoves00PackError("proposal metric centroid changed")
return float(value[0]), float(value[1]), float(value[2])
def _match_metric_obstacle(
centroid: tuple[float, float, float],
obstacles: tuple[dict[str, Any], ...],
) -> dict[str, Any]:
matches: list[dict[str, Any]] = []
for obstacle in obstacles:
value = obstacle.get("centroid_map_xyz_m")
if (
isinstance(value, list)
and len(value) == 3
and all(isinstance(item, (int, float)) and not isinstance(item, bool) for item in value)
and max(
abs(float(left) - float(right)) for left, right in zip(value, centroid, strict=True)
)
<= 1e-9
):
matches.append(obstacle)
if len(matches) != 1:
raise M48Ravnoves00PackError("proposal metric obstacle association is ambiguous")
return matches[0]
def _normalized_extent(value: object) -> list[float]:
if (
not isinstance(value, list)
or len(value) != 4
or any(
not isinstance(item, (int, float))
or isinstance(item, bool)
or not math.isfinite(float(item))
for item in value
)
):
raise M48Ravnoves00PackError("M4.6 proposal extent changed")
x_min, y_min, x_max, y_max = (float(item) for item in value)
extent = [
x_min / M48_IMAGE_WIDTH,
y_min / M48_IMAGE_HEIGHT,
x_max / M48_IMAGE_WIDTH,
y_max / M48_IMAGE_HEIGHT,
]
if not 0.0 <= extent[0] < extent[2] <= 1.0 or not 0.0 <= extent[1] < extent[3] <= 1.0:
raise M48Ravnoves00PackError("M4.6 proposal extent escaped the camera raster")
return extent
def _iter_jsonl(path: Path, label: str) -> Iterator[dict[str, Any]]:
with path.open("r", encoding="utf-8") as stream:
for line_number, line in enumerate(stream, start=1):
try:
value = json.loads(line)
except json.JSONDecodeError as exc:
raise M48Ravnoves00PackError(f"{label} row {line_number} is invalid") from exc
if not isinstance(value, dict):
raise M48Ravnoves00PackError(f"{label} row {line_number} is not an object")
yield value
def _read_json(path: Path, label: str) -> dict[str, Any]:
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise M48Ravnoves00PackError(f"{label} is invalid") from exc
if not isinstance(value, dict):
raise M48Ravnoves00PackError(f"{label} must be an object")
return value
def _mapping(value: object, label: str) -> dict[str, Any]:
if not isinstance(value, dict):
raise M48Ravnoves00PackError(f"{label} is invalid")
return value
def _integer(value: object, label: str) -> int:
if not isinstance(value, int) or isinstance(value, bool):
raise M48Ravnoves00PackError(f"{label} is invalid")
return value
def _directory(path: Path, label: str) -> Path:
candidate = path.expanduser().absolute()
if candidate.is_symlink():
raise M48Ravnoves00PackError(f"{label} must not be a symlink")
try:
resolved = candidate.resolve(strict=True)
except OSError as exc:
raise M48Ravnoves00PackError(f"{label} is unavailable") from exc
if not resolved.is_dir():
raise M48Ravnoves00PackError(f"{label} is unavailable")
return resolved
def _file(path: Path, label: str) -> Path:
candidate = path.expanduser().absolute()
if candidate.is_symlink():
raise M48Ravnoves00PackError(f"{label} must not be a symlink")
try:
resolved = candidate.resolve(strict=True)
except OSError as exc:
raise M48Ravnoves00PackError(f"{label} is unavailable") from exc
if not resolved.is_file():
raise M48Ravnoves00PackError(f"{label} is unavailable")
return resolved
def _file_sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
while chunk := stream.read(1024 * 1024):
digest.update(chunk)
return digest.hexdigest()
def _is_sha256(value: object) -> bool:
return isinstance(value, str) and _SHA256.fullmatch(value) is not None
__all__ = [
"M48Ravnoves00PackError",
"M48_SELECTION_ID",
"M48_SELECTION_SCHEMA",
"prepare_m48_ravnoves00_pack",
]
+507
View File
@@ -0,0 +1,507 @@
"""Prediction-free raw spatial evidence for the neutral M4.8 review surface.
This reader deliberately does not open the M4.7 graph payload or the frozen M4.8
prediction ledger. It reuses the already verified recorded-geometry and replay
body-frame primitives to expose only a bounded current LiDAR increment in the
virtual body frame, together with immutable rig/corridor parameters.
"""
from __future__ import annotations
import hashlib
import json
from collections.abc import Mapping
from dataclasses import dataclass
from pathlib import Path
from threading import RLock
from typing import Any, Final
from k1link.laboratory.m47_reference_graph import (
M47ReferenceGraphLabError,
read_m47_reference_graph_lab,
)
from k1link.laboratory.m48_object_quality import M48ObjectQualityPack
from k1link.perception.spatial_evidence import (
SpatialEvidenceProjectionError,
sample_points_in_body_frame,
)
from k1link.perception.threat_replay import (
ThreatReplayError,
ThreatReplayResult,
read_threat_replay_result,
)
from k1link.perception.threat_timeline import (
RECORDED_SPATIAL_POINT_LIMIT,
RecordedThreatTimeline,
RecordedThreatTimelineError,
)
M48_RAW_SPATIAL_FRAME_SCHEMA: Final = "missioncore.m48-neutral-object-review-spatial-frame/v1"
M48_EXPECTED_SOURCE_ID: Final = "RAVNOVES00"
M48_EXPECTED_E10_SOURCE_ID: Final = "sensor.camera.right"
M48_EXPECTED_SESSION_ID: Final = "20260720T065719Z_viewer_live"
M48_EXPECTED_FRAME_COUNT: Final = 4_489
M48_EXPECTED_SOURCE_PACK_ID: Final = (
"e10-lidar-pack-576c994a6c814e2592dd6240ace3902a5db94843312c759a73ba0c9166157d2b"
)
M48_EXPECTED_SOURCE_PACK_SHA256: Final = (
"0685d24219d8236caf8b7f1685e93f6d6b59e7fd015a768d88a92bbe8b154944"
)
M48_EXPECTED_THREAT_RESULT_ID: Final = (
"m4-threat-replay-2a953c5f27f2a5b1dddc5c658c1de2c323d7796084a099c024987a1da03aa324"
)
_E10_SCHEMA: Final = "missioncore.e10-lidar-replay-pack/v1"
_E10_ARTIFACT_NAME: Final = "lidar-pack.npz"
_FALSE_AUTHORITY: Final = {
"mode": "replay-simulated",
"physical_live": False,
"commands_enabled": False,
"actuation_allowed": False,
"navigation_or_safety_accepted": False,
}
_MANIFEST_KEYS: Final = {
"artifact",
"classification",
"created_at_utc",
"ground_truth",
"identity",
"identity_sha256",
"pack_id",
"schema_version",
}
_IDENTITY_KEYS: Final = {
"available_lidar_frames",
"calibration_sha256",
"camera_slot",
"e6_profile_sha256",
"e6_result_id",
"frame_count",
"input_sha256",
"job_id",
"point_count",
"producer_sha256",
"projection",
"schema_version",
"semantic_timeline_result_id",
"session_id",
"source_end_frame_index",
"source_id",
"source_start_frame_index",
"temporal_binding",
"temporal_policy",
"timeline_end_seconds",
"timeline_start_seconds",
}
_ARTIFACT_KEYS: Final = {"byte_length", "media_type", "path", "sha256"}
class M48RawEvidenceError(RuntimeError):
"""Neutral M4.8 spatial evidence escaped an immutable source binding."""
@dataclass(frozen=True, slots=True)
class _PackFrameBinding:
clip_id: str
source_time_ns: int
class M48RawEvidenceReader:
"""Provide one prediction-blind, bounded body-frame projection per call.
Construct production instances with :meth:`from_repository`. The object is
directly compatible with the M4.8 API provider callable:
``reader(pack, one_based_sequence)``.
"""
def __init__(
self,
*,
repository_root: Path,
threat_result: ThreatReplayResult,
timeline: RecordedThreatTimeline,
point_limit: int = RECORDED_SPATIAL_POINT_LIMIT,
) -> None:
if (
not isinstance(point_limit, int)
or isinstance(point_limit, bool)
or not 1 <= point_limit <= RECORDED_SPATIAL_POINT_LIMIT
):
raise M48RawEvidenceError("M4.8 raw evidence point limit is invalid")
self.repository_root = repository_root.resolve(strict=True)
self.threat_result = threat_result
self.timeline = timeline
self.point_limit = point_limit
self._pack_indices: dict[str, dict[int, _PackFrameBinding]] = {}
self._lock = RLock()
@classmethod
def from_repository(
cls,
*,
repository_root: Path,
threat_result_root: Path,
expected_source_pack_id: str = M48_EXPECTED_SOURCE_PACK_ID,
point_limit: int = RECORDED_SPATIAL_POINT_LIMIT,
) -> M48RawEvidenceReader:
"""Open the exact sealed M4 result and its exact E10 source generation.
``threat_result_root`` is the immutable result generation directory, not
the parent collection. No latest-by-mtime discovery is permitted.
"""
repository = _strict_directory(repository_root, "repository root")
if expected_source_pack_id != M48_EXPECTED_SOURCE_PACK_ID:
raise M48RawEvidenceError("M4.8 E10 pack id escaped the canonical binding")
threat_root = _strict_directory(threat_result_root, "threat result root")
try:
result = read_threat_replay_result(threat_root)
except (OSError, ValueError, ThreatReplayError) as exc:
raise M48RawEvidenceError("M4.8 threat result is invalid") from exc
_validate_threat_result(result, expected_source_pack_id=expected_source_pack_id)
pack_root = (
repository / ".runtime/compute-experiments/e10/lidar-packs" / expected_source_pack_id
)
_validate_e10_pack(
pack_root,
expected_pack_id=expected_source_pack_id,
expected_artifact_sha256=M48_EXPECTED_SOURCE_PACK_SHA256,
)
try:
timeline = RecordedThreatTimeline(repository_root=repository, result=result)
except (OSError, ValueError, RecordedThreatTimelineError) as exc:
raise M48RawEvidenceError("M4.8 recorded geometry timeline is invalid") from exc
if (
len(timeline.index.source_times_ns) != M48_EXPECTED_FRAME_COUNT
or timeline.profile.source_id != M48_EXPECTED_SOURCE_ID
or timeline.profile.session_id != M48_EXPECTED_SESSION_ID
or timeline.profile.source_pack_id != expected_source_pack_id
or timeline.profile.source_pack_sha256 != M48_EXPECTED_SOURCE_PACK_SHA256
):
raise M48RawEvidenceError("M4.8 recorded geometry binding changed")
return cls(
repository_root=repository,
threat_result=result,
timeline=timeline,
point_limit=point_limit,
)
def __call__(
self,
pack: M48ObjectQualityPack,
sequence: int,
) -> dict[str, object]:
return self.frame(pack=pack, sequence=sequence)
def frame(
self,
*,
pack: M48ObjectQualityPack,
sequence: int,
) -> dict[str, object]:
"""Return one one-based, clip-bound neutral spatial frame."""
if (
not isinstance(sequence, int)
or isinstance(sequence, bool)
or not 1 <= sequence <= M48_EXPECTED_FRAME_COUNT
):
raise M48RawEvidenceError("M4.8 raw evidence sequence is invalid")
binding = self._binding_for(pack, sequence)
frame_index = sequence - 1
try:
temporal = self.timeline.store.temporal_binding_for_index(frame_index)
body_frame = self.timeline.body_frames.body_frame_for_frame(f"frame-{frame_index:06d}")
except (RuntimeError, TypeError, ValueError) as exc:
raise M48RawEvidenceError("M4.8 source frame binding is invalid") from exc
if temporal.frame_index != frame_index or temporal.source_time_ns != binding.source_time_ns:
raise M48RawEvidenceError("M4.8 source time escaped the neutral frame reference")
points_body: list[list[float]] = []
if body_frame is not None:
if not temporal.source_available:
raise M48RawEvidenceError("unavailable source produced an M4.8 body frame")
points = self.timeline.store.current_points_for_frame(frame_index)
if points is None:
raise M48RawEvidenceError("qualified M4.8 body frame lacks current LiDAR")
try:
points_body, _ = sample_points_in_body_frame(
points,
body_frame,
point_limit=self.point_limit,
)
except SpatialEvidenceProjectionError as exc:
raise M48RawEvidenceError("M4.8 body-frame point projection failed") from exc
profile = self.timeline.profile
return {
"schema_version": M48_RAW_SPATIAL_FRAME_SCHEMA,
"pack_id": pack.result_id,
"clip_id": binding.clip_id,
"sequence": sequence,
"source_time_ns": temporal.source_time_ns,
"source_available": temporal.source_available,
"body_frame_available": body_frame is not None,
"point_cloud_body_xyz_m": points_body,
"rig": {
"profile_id": profile.rig.profile_id,
"length_m": profile.rig.body_length_m,
"width_m": profile.rig.body_width_m,
"lidar_reference": profile.rig.lidar_reference,
"nominal_sensor_height_m": profile.rig.nominal_sensor_height_m,
"physical_mount_claimed": False,
},
"corridor": {
"profile_id": profile.corridor.profile_id,
"forward_length_m": profile.corridor.forward_length_m,
"rear_margin_m": profile.corridor.rear_margin_m,
"lateral_clearance_m": profile.corridor.lateral_clearance_m,
"half_width_m": (
profile.rig.body_width_m / 2 + profile.corridor.lateral_clearance_m
),
"prediction_horizon_seconds": (profile.corridor.prediction_horizon_seconds),
},
"occupied_voxel_size_m": profile.corridor.occupied_voxel_size_m,
"candidate_identity_included": False,
"graph_boxes_ids_scores_included": False,
"frozen_predictions_included": False,
"strata_included": False,
"authority": dict(_FALSE_AUTHORITY),
}
def _binding_for(
self,
pack: M48ObjectQualityPack,
sequence: int,
) -> _PackFrameBinding:
with self._lock:
index = self._pack_indices.get(pack.result_id)
if index is None:
_validate_m47_pack_binding(
repository_root=self.repository_root,
pack=pack,
threat_result=self.threat_result,
)
index = _index_neutral_frame_references(pack)
self._pack_indices[pack.result_id] = index
binding = index.get(sequence)
if binding is None:
raise M48RawEvidenceError("M4.8 sequence is outside the selected neutral clips")
return binding
def _validate_threat_result(
result: ThreatReplayResult,
*,
expected_source_pack_id: str,
) -> None:
identity = result.manifest.get("identity")
metrics = identity.get("metrics") if isinstance(identity, dict) else None
frames = metrics.get("frames") if isinstance(metrics, dict) else None
if (
result.result_id != M48_EXPECTED_THREAT_RESULT_ID
or result.result_root.name != result.result_id
or result.accepted is not True
or not isinstance(identity, dict)
or identity.get("source_id") != M48_EXPECTED_SOURCE_ID
or identity.get("source_session_id") != M48_EXPECTED_SESSION_ID
or identity.get("source_pack_id") != expected_source_pack_id
or identity.get("source_pack_sha256") != M48_EXPECTED_SOURCE_PACK_SHA256
or not isinstance(frames, dict)
or frames.get("total") != M48_EXPECTED_FRAME_COUNT
or identity.get("authority")
!= {
**_FALSE_AUTHORITY,
"physical_collision_accepted": False,
"ground_truth": False,
}
):
raise M48RawEvidenceError("M4.8 threat result escaped the canonical source")
def _validate_e10_pack(
pack_root: Path,
*,
expected_pack_id: str,
expected_artifact_sha256: str,
) -> Path:
root = _strict_directory(pack_root, "E10 pack root")
if root.name != expected_pack_id:
raise M48RawEvidenceError("E10 pack path escaped its expected identity")
manifest_path = root / "manifest.json"
if (
manifest_path.is_symlink()
or not manifest_path.is_file()
or manifest_path.resolve(strict=True).parent != root
):
raise M48RawEvidenceError("E10 manifest path is invalid")
manifest = _read_json(manifest_path, "E10 manifest")
if set(manifest) != _MANIFEST_KEYS:
raise M48RawEvidenceError("E10 manifest fields changed")
identity = _mapping(manifest.get("identity"), "E10 identity")
artifact = _mapping(manifest.get("artifact"), "E10 artifact")
if set(identity) != _IDENTITY_KEYS or set(artifact) != _ARTIFACT_KEYS:
raise M48RawEvidenceError("E10 identity or artifact fields changed")
identity_sha256 = _canonical_sha256(identity)
if (
manifest.get("schema_version") != _E10_SCHEMA
or manifest.get("pack_id") != expected_pack_id
or manifest.get("identity_sha256") != identity_sha256
or expected_pack_id != f"e10-lidar-pack-{identity_sha256}"
or manifest.get("classification") != "private-recorded-sensor-replay-input"
or manifest.get("ground_truth") is not False
or identity.get("schema_version") != _E10_SCHEMA
or identity.get("source_id") != M48_EXPECTED_E10_SOURCE_ID
or identity.get("session_id") != M48_EXPECTED_SESSION_ID
or identity.get("frame_count") != M48_EXPECTED_FRAME_COUNT
or identity.get("source_start_frame_index") != 0
or identity.get("source_end_frame_index") != M48_EXPECTED_FRAME_COUNT - 1
or artifact.get("path") != _E10_ARTIFACT_NAME
or artifact.get("media_type") != "application/x-npz"
or artifact.get("sha256") != expected_artifact_sha256
):
raise M48RawEvidenceError("E10 pack identity changed")
byte_length = artifact.get("byte_length")
if not isinstance(byte_length, int) or isinstance(byte_length, bool) or byte_length < 1:
raise M48RawEvidenceError("E10 artifact byte length is invalid")
artifact_path = root / _E10_ARTIFACT_NAME
if (
artifact_path.is_symlink()
or not artifact_path.is_file()
or artifact_path.resolve(strict=True).parent != root
or artifact_path.stat().st_size != byte_length
or _file_sha256(artifact_path) != expected_artifact_sha256
):
raise M48RawEvidenceError("E10 artifact content changed")
return artifact_path.resolve(strict=True)
def _validate_m47_pack_binding(
*,
repository_root: Path,
pack: M48ObjectQualityPack,
threat_result: ThreatReplayResult,
) -> None:
identity = _mapping(pack.manifest.get("identity"), "M4.8 pack identity")
source = _mapping(identity.get("source"), "M4.8 pack source")
m47_id = source.get("m47_lab_result_id")
m47_manifest_sha256 = source.get("m47_lab_manifest_sha256")
if (
source.get("source_id") != M48_EXPECTED_SOURCE_ID
or source.get("source_session_id") != M48_EXPECTED_SESSION_ID
or not isinstance(m47_id, str)
or not isinstance(m47_manifest_sha256, str)
):
raise M48RawEvidenceError("M4.8 pack source binding changed")
m47_root = repository_root / ".runtime/compute-experiments/m47/reference-graph-labs" / m47_id
manifest_path = m47_root / "manifest.json"
if (
manifest_path.is_symlink()
or not manifest_path.is_file()
or _file_sha256(manifest_path) != m47_manifest_sha256
):
raise M48RawEvidenceError("M4.8 pack M4.7 manifest binding changed")
try:
m47 = read_m47_reference_graph_lab(m47_root)
except (OSError, ValueError, M47ReferenceGraphLabError) as exc:
raise M48RawEvidenceError("M4.8 pack M4.7 LAB is invalid") from exc
m47_source = _mapping(m47.report.get("source"), "M4.7 source")
threat_identity = _mapping(threat_result.manifest.get("identity"), "M4 threat identity")
if (
m47.manifest.get("accepted") is not True
or m47_source.get("source_id") != M48_EXPECTED_SOURCE_ID
or m47_source.get("source_session_id") != M48_EXPECTED_SESSION_ID
or m47_source.get("visual_result_id") != threat_result.result_id
or m47_source.get("threat_frames_sha256") != threat_identity.get("frames_sha256")
):
raise M48RawEvidenceError("M4.8 pack escaped its accepted M4.7 visual source")
def _index_neutral_frame_references(
pack: M48ObjectQualityPack,
) -> dict[int, _PackFrameBinding]:
index: dict[int, _PackFrameBinding] = {}
for raw in pack.frame_references:
row = _mapping(raw, "M4.8 neutral frame reference")
sequence = row.get("sequence")
source_time_ns = row.get("source_time_ns")
clip_id = row.get("clip_id")
if (
not isinstance(sequence, int)
or isinstance(sequence, bool)
or not 1 <= sequence <= M48_EXPECTED_FRAME_COUNT
or not isinstance(source_time_ns, int)
or isinstance(source_time_ns, bool)
or source_time_ns < 0
or not isinstance(clip_id, str)
or not clip_id
or sequence in index
):
raise M48RawEvidenceError("M4.8 neutral frame references are invalid")
index[sequence] = _PackFrameBinding(
clip_id=clip_id,
source_time_ns=source_time_ns,
)
if not index:
raise M48RawEvidenceError("M4.8 neutral frame reference set is empty")
return index
def _strict_directory(path: Path, label: str) -> Path:
candidate = path.expanduser().absolute()
if candidate.is_symlink():
raise M48RawEvidenceError(f"{label} must not be a symlink")
try:
resolved = candidate.resolve(strict=True)
except OSError as exc:
raise M48RawEvidenceError(f"{label} is unavailable") from exc
if not resolved.is_dir():
raise M48RawEvidenceError(f"{label} is not a directory")
return resolved
def _read_json(path: Path, label: str) -> dict[str, Any]:
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise M48RawEvidenceError(f"{label} is invalid") from exc
if not isinstance(value, dict):
raise M48RawEvidenceError(f"{label} is invalid")
return value
def _mapping(value: object, label: str) -> Mapping[str, Any]:
if not isinstance(value, dict):
raise M48RawEvidenceError(f"{label} is invalid")
return value
def _canonical_sha256(value: object) -> str:
return hashlib.sha256(
json.dumps(
value,
sort_keys=True,
separators=(",", ":"),
ensure_ascii=False,
).encode("utf-8")
).hexdigest()
def _file_sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
while chunk := handle.read(1024 * 1024):
digest.update(chunk)
return digest.hexdigest()
__all__ = [
"M48_EXPECTED_FRAME_COUNT",
"M48_EXPECTED_SESSION_ID",
"M48_EXPECTED_SOURCE_ID",
"M48_EXPECTED_SOURCE_PACK_ID",
"M48_EXPECTED_THREAT_RESULT_ID",
"M48_RAW_SPATIAL_FRAME_SCHEMA",
"M48RawEvidenceError",
"M48RawEvidenceReader",
]
@@ -0,0 +1,711 @@
"""Immutable M4.8 development regression over operator-added missed-object anchors.
The experiment deliberately stays inside M4.8 and reuses the frozen Worker 006
prediction pack. It snapshots only operator-added tracklets from reviewed clips,
compares the exact source frame against the already-frozen prediction row, and
publishes a separate append-only result. The assisted correction is never called
independent truth and the result grants no navigation or safety authority.
"""
from __future__ import annotations
import hashlib
import json
import os
import re
import shutil
import uuid
from dataclasses import dataclass
from datetime import UTC, datetime
from pathlib import Path
from typing import Any, Final
from k1link.laboratory.m48_object_quality import (
M48ObjectQualityError,
read_m48_object_quality_pack,
)
M48_SMALL_STATIC_PROFILE_SCHEMA: Final = (
"missioncore.m48-small-static-passage-regression-profile/v1"
)
M48_SMALL_STATIC_RESULT_SCHEMA: Final = (
"missioncore.m48-small-static-passage-regression-result/v1"
)
M48_SMALL_STATIC_REPORT_SCHEMA: Final = (
"missioncore.m48-small-static-passage-regression-report/v1"
)
M48_SMALL_STATIC_ANCHOR_SCHEMA: Final = (
"missioncore.m48-assisted-missed-object-anchor/v1"
)
M48_SMALL_STATIC_COMPARISON_SCHEMA: Final = (
"missioncore.m48-assisted-anchor-comparison/v1"
)
M48_SMALL_STATIC_PREFIX: Final = "m48-small-static-passage-regression-"
_CORRECTION_SCHEMA: Final = "missioncore.m48-assisted-object-correction-session/v1"
_METHOD_SCHEMA: Final = "missioncore.laboratory-method/v1"
_OBJECT_ID = re.compile(r"^object-[0-9]{2,}$")
_AUTHORITY: Final = {
"mode": "replay-simulated",
"physical_live": False,
"commands_enabled": False,
"actuation_allowed": False,
"navigation_or_safety_accepted": False,
}
class M48SmallStaticRegressionError(RuntimeError):
"""The assisted development-regression source or result is invalid."""
@dataclass(frozen=True, slots=True)
class M48SmallStaticRegressionResult:
result_id: str
result_root: Path
manifest: dict[str, Any]
report: dict[str, Any]
anchors: tuple[dict[str, Any], ...]
comparisons: tuple[dict[str, Any], ...]
def build_m48_small_static_passage_regression(
*,
pack_root: Path,
correction_session_path: Path,
profile_path: Path,
output_root: Path,
run_created_at_utc: str | None = None,
) -> M48SmallStaticRegressionResult:
"""Publish one append-only M4.8R development baseline without mutating inputs."""
try:
pack = read_m48_object_quality_pack(pack_root)
except M48ObjectQualityError as exc:
raise M48SmallStaticRegressionError("M4.8 frozen prediction pack is invalid") from exc
profile_bytes, profile = _read_profile(profile_path)
correction_bytes, correction = _read_correction(correction_session_path, pack.result_id)
anchors = _assisted_anchors(correction)
if len(anchors) < int(profile["minimum_anchor_count"]):
raise M48SmallStaticRegressionError("M4.8 assisted anchor set is too small")
prediction_rows: dict[tuple[str, int], dict[str, Any]] = {}
for row in pack.predictions:
clip_id = row.get("clip_id")
sequence = row.get("sequence")
if not isinstance(clip_id, str) or not _integer(sequence):
raise M48SmallStaticRegressionError("M4.8 frozen prediction binding is invalid")
key = (clip_id, int(sequence))
if key in prediction_rows:
raise M48SmallStaticRegressionError("M4.8 frozen prediction binding collided")
prediction_rows[key] = row
threshold = float(profile["extent_iou_threshold"])
comparisons = tuple(
_compare_anchor(anchor, prediction_rows, threshold)
for anchor in anchors
)
recalled = sum(bool(row["matched_at_threshold"]) for row in comparisons)
recall = recalled / len(comparisons)
passage_count = sum(bool(row["requires_avoidance_or_clearance"]) for row in anchors)
clip_count = len({str(row["clip_id"]) for row in anchors})
target = float(profile["minimum_assisted_anchor_recall"])
accepted = recall >= target
created_at = _utc_timestamp(run_created_at_utc or datetime.now(UTC).isoformat())
correction_sha256 = hashlib.sha256(correction_bytes).hexdigest()
profile_sha256 = hashlib.sha256(profile_bytes).hexdigest()
producer_sha256 = _file_sha256(Path(__file__).resolve())
pack_identity = _object(pack.manifest.get("identity"), "M4.8 pack identity")
freeze = _object(pack_identity.get("freeze"), "M4.8 pack freeze")
identity: dict[str, Any] = {
"schema_version": M48_SMALL_STATIC_RESULT_SCHEMA,
"human_lab_id": profile["human_lab_id"],
"run_label": profile["run_label"],
"run_created_at_utc": created_at,
"pipeline_id": profile["pipeline_id"],
"experiment_id": profile["experiment_id"],
"profile_id": profile["profile_id"],
"profile_sha256": profile_sha256,
"producer_sha256": producer_sha256,
"source": {
"source_id": _object(
pack_identity.get("source"), "M4.8 source"
).get("source_id"),
"source_session_id": _object(
pack_identity.get("source"), "M4.8 source"
).get("source_session_id"),
"pack_id": pack.result_id,
"pack_identity_sha256": pack.manifest["identity_sha256"],
"prediction_rows_sha256": freeze.get("prediction_rows_sha256"),
"correction_session_id": correction["session_id"],
"correction_revision": correction["revision"],
"correction_updated_at_utc": correction["updated_at_utc"],
"correction_document_sha256": correction_sha256,
"correction_independent_truth": False,
},
"selection": {
"anchor_selection": profile["anchor_selection"],
"assisted_tracklet_count": len({(row["clip_id"], row["object_id"]) for row in anchors}),
"anchor_count": len(anchors),
"clip_count": clip_count,
"requires_avoidance_or_clearance_count": passage_count,
},
"authority": dict(_AUTHORITY),
}
identity_sha256 = _canonical_sha256(identity)
result_id = f"{M48_SMALL_STATIC_PREFIX}{identity_sha256}"
method = {
"schema_version": _METHOD_SCHEMA,
"completeness": "complete",
"execution_class": "deterministic",
"pipeline_id": profile["pipeline_id"],
"components": [
{
"kind": "source",
"name": "M4.8 frozen Worker 006 predictions",
"version": pack.result_id,
"role": "immutable candidate rows from the current M4.8 pipeline",
"identity_sha256": freeze.get("prediction_rows_sha256"),
},
{
"kind": "source",
"name": "operator-added missed-object anchors",
"version": f"{correction['session_id']}:revision-{correction['revision']}",
"role": "assisted development regression seed; not independent truth",
"identity_sha256": correction_sha256,
},
{
"kind": "algorithm",
"name": "exact-frame class-free IoU comparator",
"version": profile["profile_id"],
"role": "diagnostic detection recall over operator-added anchors",
"identity_sha256": producer_sha256,
},
],
}
metrics = {
"assisted_anchor_count": len(comparisons),
"assisted_tracklet_count": len({(row["clip_id"], row["object_id"]) for row in anchors}),
"anchor_clip_count": clip_count,
"requires_avoidance_or_clearance_count": passage_count,
"worker_recalled_anchor_count": recalled,
"worker_missed_anchor_count": len(comparisons) - recalled,
"assisted_anchor_recall": recall,
"extent_iou_threshold": threshold,
"minimum_assisted_anchor_recall": target,
}
gates = {
"anchor_set_non_empty": len(comparisons) >= int(profile["minimum_anchor_count"]),
"development_anchor_recall_target": accepted,
"independent_truth_available": False,
}
decision = {
"state": (
"accepted-development-regression-baseline"
if accepted
else "failed-development-regression-baseline"
),
"summary": (
f"Worker 006 matched {recalled}/{len(comparisons)} exact-frame assisted anchors "
f"at IoU >= {threshold:.2f}."
),
"next_action": (
"Keep the pipeline contract fixed, change only the perception experiment, "
"and publish another immutable M4.8R run against this frozen seed."
),
}
limitations = [
"The anchors come from candidate-visible operator correction and are not "
"independent truth.",
"The seed is intentionally biased toward objects the current Worker 006 output missed.",
"A camera rectangle is evidence of a missed visible object, not a measured 3D collider.",
"No physical-live, navigation, command, actuation or collision-safety "
"authority is granted.",
]
report = {
"schema_version": M48_SMALL_STATIC_REPORT_SCHEMA,
"result_id": result_id,
"source": identity["source"],
"configuration": {
**profile,
"profile_sha256": profile_sha256,
},
"method": method,
"execution": {
"comparison_node": "mission-core-local-control-plane",
"source_worker_id": "006",
"frozen_prediction_rows_sha256": freeze.get("prediction_rows_sha256"),
"determinism": "exact canonical JSON + exact-frame IoU; no inference rerun",
},
"metrics": metrics,
"gates": gates,
"decision": decision,
"limitations": limitations,
"authority": dict(_AUTHORITY),
"visual_review": {
"viewer": "missioncore.laboratory-recorded-clip-viewer/v1",
"case_count": len(comparisons),
"camera_anchor_and_worker_boxes": True,
"camera_3d_plan_shared_clock": True,
},
}
destination = output_root.expanduser().absolute() / result_id
_publish_result(
destination=destination,
identity=identity,
created_at_utc=created_at,
accepted=accepted,
report=report,
anchors=anchors,
comparisons=comparisons,
)
return read_m48_small_static_passage_regression(destination)
def read_m48_small_static_passage_regression(
root: Path,
) -> M48SmallStaticRegressionResult:
candidate = root.expanduser().absolute()
if candidate.is_symlink():
raise M48SmallStaticRegressionError("M4.8 regression result must not be a symlink")
try:
resolved = candidate.resolve(strict=True)
except OSError as exc:
raise M48SmallStaticRegressionError("M4.8 regression result is unavailable") from exc
if not resolved.is_dir() or not resolved.name.startswith(M48_SMALL_STATIC_PREFIX):
raise M48SmallStaticRegressionError("M4.8 regression result path is invalid")
manifest = _read_json(resolved / "manifest.json", maximum=1024 * 1024)
identity = _object(manifest.get("identity"), "M4.8 regression identity")
identity_sha256 = _canonical_sha256(identity)
if (
manifest.get("schema_version") != M48_SMALL_STATIC_RESULT_SCHEMA
or manifest.get("result_id") != resolved.name
or manifest.get("identity_sha256") != identity_sha256
or resolved.name != f"{M48_SMALL_STATIC_PREFIX}{identity_sha256}"
or manifest.get("ground_truth") is not False
or manifest.get("authority") != _AUTHORITY
):
raise M48SmallStaticRegressionError("M4.8 regression identity changed")
artifacts = manifest.get("artifacts")
if not isinstance(artifacts, list) or len(artifacts) != 3:
raise M48SmallStaticRegressionError("M4.8 regression artifact inventory changed")
by_path: dict[str, dict[str, Any]] = {}
for raw in artifacts:
descriptor = _object(raw, "M4.8 regression artifact")
path_name = descriptor.get("path")
if not isinstance(path_name, str) or path_name not in {
"anchors.jsonl", "comparisons.jsonl", "report.json"
} or path_name in by_path:
raise M48SmallStaticRegressionError("M4.8 regression artifact path changed")
path = resolved / path_name
if (
path.is_symlink()
or not path.is_file()
or descriptor.get("byte_length") != path.stat().st_size
or descriptor.get("sha256") != _file_sha256(path)
):
raise M48SmallStaticRegressionError("M4.8 regression artifact proof changed")
by_path[path_name] = descriptor
report = _read_json(resolved / "report.json", maximum=1024 * 1024)
anchors = tuple(_read_jsonl(resolved / "anchors.jsonl"))
comparisons = tuple(_read_jsonl(resolved / "comparisons.jsonl"))
if (
report.get("schema_version") != M48_SMALL_STATIC_REPORT_SCHEMA
or report.get("result_id") != resolved.name
or len(anchors) != len(comparisons)
or any(row.get("schema_version") != M48_SMALL_STATIC_ANCHOR_SCHEMA for row in anchors)
or any(
row.get("schema_version") != M48_SMALL_STATIC_COMPARISON_SCHEMA
for row in comparisons
)
or [row.get("anchor_id") for row in anchors]
!= [row.get("anchor_id") for row in comparisons]
):
raise M48SmallStaticRegressionError("M4.8 regression content changed")
return M48SmallStaticRegressionResult(
result_id=resolved.name,
result_root=resolved,
manifest=manifest,
report=report,
anchors=anchors,
comparisons=comparisons,
)
def _read_profile(path: Path) -> tuple[bytes, dict[str, Any]]:
encoded, profile = _read_json_bytes(path, maximum=64 * 1024, label="M4.8 regression profile")
expected = {
"schema_version",
"profile_id",
"pipeline_id",
"experiment_id",
"human_lab_id",
"run_label",
"anchor_selection",
"extent_iou_threshold",
"minimum_assisted_anchor_recall",
"minimum_anchor_count",
"independent_truth",
}
if set(profile) != expected or profile.get("schema_version") != M48_SMALL_STATIC_PROFILE_SCHEMA:
raise M48SmallStaticRegressionError("M4.8 regression profile contract changed")
if (
profile.get("human_lab_id") != "M4.8"
or profile.get("anchor_selection") != "operator-added-tracklets-in-reviewed-clips/v1"
or profile.get("independent_truth") is not False
or not _rate(profile.get("extent_iou_threshold"))
or not _rate(profile.get("minimum_assisted_anchor_recall"))
or not _integer(profile.get("minimum_anchor_count"))
or int(profile["minimum_anchor_count"]) < 1
):
raise M48SmallStaticRegressionError("M4.8 regression profile is invalid")
for key in ("profile_id", "pipeline_id", "experiment_id", "run_label"):
if not isinstance(profile.get(key), str) or not str(profile[key]).strip():
raise M48SmallStaticRegressionError("M4.8 regression profile identity is invalid")
return encoded, profile
def _read_correction(path: Path, pack_id: str) -> tuple[bytes, dict[str, Any]]:
encoded, correction = _read_json_bytes(
path,
maximum=16 * 1024 * 1024,
label="M4.8 correction snapshot",
)
assistance = _object(correction.get("assistance"), "M4.8 correction assistance")
if (
correction.get("schema_version") != _CORRECTION_SCHEMA
or correction.get("pack_id") != pack_id
or correction.get("state") not in {"saved", "frozen"}
or not _integer(correction.get("revision"))
or int(correction["revision"]) < 1
or not isinstance(correction.get("session_id"), str)
or not isinstance(correction.get("updated_at_utc"), str)
or assistance.get("candidate_predictions_seen") is not True
or assistance.get("independent_truth_eligible") is not False
or correction.get("authority") != _AUTHORITY
or not isinstance(correction.get("clips"), list)
):
raise M48SmallStaticRegressionError("M4.8 correction snapshot is invalid")
return encoded, correction
def _assisted_anchors(correction: dict[str, Any]) -> tuple[dict[str, Any], ...]:
anchors: list[dict[str, Any]] = []
for clip_raw in correction["clips"]:
clip = _object(clip_raw, "M4.8 correction clip")
if clip.get("review_state") != "reviewed":
continue
clip_id = clip.get("clip_id")
tracklets = clip.get("tracklets")
if not isinstance(clip_id, str) or not isinstance(tracklets, list):
raise M48SmallStaticRegressionError("M4.8 correction clip is invalid")
for tracklet_raw in tracklets:
tracklet = _object(tracklet_raw, "M4.8 correction tracklet")
object_id = tracklet.get("object_id")
if not isinstance(object_id, str) or _OBJECT_ID.fullmatch(object_id) is None:
continue
keyframes = tracklet.get("keyframes")
if not isinstance(keyframes, list) or not keyframes:
raise M48SmallStaticRegressionError("M4.8 assisted tracklet has no keyframes")
for keyframe_raw in keyframes:
keyframe = _object(keyframe_raw, "M4.8 correction keyframe")
sequence = keyframe.get("sequence")
extent = _extent(keyframe.get("extent_xyxy"))
if not _integer(sequence):
raise M48SmallStaticRegressionError("M4.8 assisted anchor sequence is invalid")
state = _state_for_sequence(tracklet, int(sequence))
anchor_identity = {
"clip_id": clip_id,
"object_id": object_id,
"sequence": int(sequence),
"extent_xyxy": extent,
}
anchors.append({
"schema_version": M48_SMALL_STATIC_ANCHOR_SCHEMA,
"anchor_id": "anchor-" + _canonical_sha256(anchor_identity)[:24],
**anchor_identity,
"visibility": keyframe.get("visibility"),
"geometry_association": state.get("geometry_association"),
"freshness": state.get("freshness"),
"motion": state.get("motion"),
"threat": state.get("threat"),
"requires_avoidance_or_clearance": bool(
state.get("critical_corridor_obstacle")
),
"authority": "operator-assisted-development-anchor-not-truth",
})
anchors.sort(key=lambda row: (str(row["clip_id"]), int(row["sequence"]), str(row["object_id"])))
if len({str(row["anchor_id"]) for row in anchors}) != len(anchors):
raise M48SmallStaticRegressionError("M4.8 assisted anchor identity collided")
return tuple(anchors)
def _state_for_sequence(tracklet: dict[str, Any], sequence: int) -> dict[str, Any]:
segments = tracklet.get("state_segments")
if not isinstance(segments, list):
raise M48SmallStaticRegressionError("M4.8 assisted state segments are invalid")
matches = [
_object(row, "M4.8 assisted state segment")
for row in segments
if isinstance(row, dict)
and _integer(row.get("start_sequence"))
and _integer(row.get("end_sequence"))
and int(row["start_sequence"]) <= sequence <= int(row["end_sequence"])
]
if len(matches) != 1:
raise M48SmallStaticRegressionError("M4.8 assisted anchor state is ambiguous")
return matches[0]
def _compare_anchor(
anchor: dict[str, Any],
prediction_rows: dict[tuple[str, int], dict[str, Any]],
threshold: float,
) -> dict[str, Any]:
key = (str(anchor["clip_id"]), int(anchor["sequence"]))
row = prediction_rows.get(key)
if row is None or row.get("terminal_outcome") != "delivered":
raise M48SmallStaticRegressionError("M4.8 assisted anchor lacks delivered prediction row")
objects = row.get("objects")
if not isinstance(objects, list):
raise M48SmallStaticRegressionError("M4.8 prediction objects are invalid")
normalized: list[dict[str, Any]] = []
for raw in objects:
item = _object(raw, "M4.8 prediction object")
normalized.append({
"prediction_id": item.get("prediction_id"),
"extent_xyxy": _extent(item.get("extent_xyxy")),
"geometry_association": item.get("geometry_association"),
"freshness": item.get("freshness"),
"motion": item.get("motion"),
"threat": item.get("threat"),
})
ranked = sorted(
((_iou(anchor["extent_xyxy"], item["extent_xyxy"]), item) for item in normalized),
key=lambda pair: (pair[0], str(pair[1].get("prediction_id"))),
reverse=True,
)
best_iou, best = ranked[0] if ranked else (0.0, None)
return {
"schema_version": M48_SMALL_STATIC_COMPARISON_SCHEMA,
"anchor_id": anchor["anchor_id"],
"clip_id": anchor["clip_id"],
"sequence": anchor["sequence"],
"source_time_ns": row.get("source_time_ns"),
"anchor_extent_xyxy": anchor["extent_xyxy"],
"requires_avoidance_or_clearance": anchor["requires_avoidance_or_clearance"],
"worker_candidate_count": len(normalized),
"worker_objects": normalized,
"best_prediction_id": best.get("prediction_id") if best else None,
"best_iou": best_iou,
"extent_iou_threshold": threshold,
"matched_at_threshold": best_iou >= threshold,
"outcome": "recalled" if best_iou >= threshold else "missed-assisted-anchor",
}
def _publish_result(
*,
destination: Path,
identity: dict[str, Any],
created_at_utc: str,
accepted: bool,
report: dict[str, Any],
anchors: tuple[dict[str, Any], ...],
comparisons: tuple[dict[str, Any], ...],
) -> None:
parent = destination.parent
if parent.is_symlink():
raise M48SmallStaticRegressionError("M4.8 regression output root must not be a symlink")
parent.mkdir(mode=0o700, parents=True, exist_ok=True)
if not parent.is_dir():
raise M48SmallStaticRegressionError("M4.8 regression output root is invalid")
staging = parent / f".{destination.name}.{uuid.uuid4().hex}.tmp"
staging.mkdir(mode=0o700, exist_ok=False)
try:
_write_json(staging / "report.json", report)
_write_jsonl(staging / "anchors.jsonl", anchors)
_write_jsonl(staging / "comparisons.jsonl", comparisons)
artifacts = [
_artifact(
staging / "anchors.jsonl",
"assisted-regression-anchors",
M48_SMALL_STATIC_ANCHOR_SCHEMA,
),
_artifact(
staging / "comparisons.jsonl",
"exact-frame-worker-comparisons",
M48_SMALL_STATIC_COMPARISON_SCHEMA,
),
_artifact(
staging / "report.json",
"m48-small-static-regression-report",
M48_SMALL_STATIC_REPORT_SCHEMA,
),
]
manifest = {
"schema_version": M48_SMALL_STATIC_RESULT_SCHEMA,
"result_id": destination.name,
"identity_sha256": _canonical_sha256(identity),
"identity": identity,
"created_at_utc": created_at_utc,
"accepted": accepted,
"ground_truth": False,
"authority": dict(_AUTHORITY),
"artifacts": artifacts,
}
_write_json(staging / "manifest.json", manifest)
if destination.exists():
existing = {
path.name: _file_sha256(path)
for path in destination.iterdir()
if path.is_file()
}
proposed = {
path.name: _file_sha256(path)
for path in staging.iterdir()
if path.is_file()
}
if existing != proposed:
raise M48SmallStaticRegressionError("immutable M4.8 regression identity collided")
shutil.rmtree(staging)
return
os.replace(staging, destination)
except BaseException:
shutil.rmtree(staging, ignore_errors=True)
raise
def _artifact(path: Path, role: str, schema_version: str) -> dict[str, object]:
return {
"path": path.name,
"role": role,
"byte_length": path.stat().st_size,
"sha256": _file_sha256(path),
"schema_version": schema_version,
"media_type": "application/x-ndjson" if path.suffix == ".jsonl" else "application/json",
}
def _read_json_bytes(path: Path, *, maximum: int, label: str) -> tuple[bytes, dict[str, Any]]:
candidate = path.expanduser().absolute()
if candidate.is_symlink() or not candidate.is_file() or candidate.stat().st_size > maximum:
raise M48SmallStaticRegressionError(f"{label} is unavailable")
try:
encoded = candidate.read_bytes()
value = json.loads(encoded)
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise M48SmallStaticRegressionError(f"{label} is unreadable") from exc
return encoded, _object(value, label)
def _read_json(path: Path, *, maximum: int) -> dict[str, Any]:
return _read_json_bytes(path, maximum=maximum, label=path.name)[1]
def _read_jsonl(path: Path) -> list[dict[str, Any]]:
if path.is_symlink() or not path.is_file() or path.stat().st_size > 8 * 1024 * 1024:
raise M48SmallStaticRegressionError("M4.8 regression rows are unavailable")
rows: list[dict[str, Any]] = []
try:
with path.open("r", encoding="utf-8") as stream:
for line in stream:
if line.strip():
rows.append(_object(json.loads(line), "M4.8 regression row"))
except (OSError, json.JSONDecodeError) as exc:
raise M48SmallStaticRegressionError("M4.8 regression rows are unreadable") from exc
return rows
def _write_json(path: Path, value: object) -> None:
path.write_bytes(_canonical_json(value) + b"\n")
def _write_jsonl(path: Path, rows: tuple[dict[str, Any], ...]) -> None:
path.write_bytes(b"".join(_canonical_json(row) + b"\n" for row in rows))
def _object(value: object, label: str) -> dict[str, Any]:
if not isinstance(value, dict) or not all(isinstance(key, str) for key in value):
raise M48SmallStaticRegressionError(f"{label} must be an object")
return value
def _integer(value: object) -> bool:
return isinstance(value, int) and not isinstance(value, bool)
def _rate(value: object) -> bool:
return (
isinstance(value, (int, float))
and not isinstance(value, bool)
and 0.0 < float(value) <= 1.0
)
def _extent(value: object) -> list[float]:
if (
not isinstance(value, list)
or len(value) != 4
or any(not isinstance(item, (int, float)) or isinstance(item, bool) for item in value)
):
raise M48SmallStaticRegressionError("M4.8 extent is invalid")
extent = [float(item) for item in value]
if not (0.0 <= extent[0] < extent[2] <= 1.0 and 0.0 <= extent[1] < extent[3] <= 1.0):
raise M48SmallStaticRegressionError("M4.8 extent is outside the camera plane")
return extent
def _iou(left: list[float], right: list[float]) -> float:
x1 = max(left[0], right[0])
y1 = max(left[1], right[1])
x2 = min(left[2], right[2])
y2 = min(left[3], right[3])
intersection = max(0.0, x2 - x1) * max(0.0, y2 - y1)
left_area = (left[2] - left[0]) * (left[3] - left[1])
right_area = (right[2] - right[0]) * (right[3] - right[1])
union = left_area + right_area - intersection
return intersection / union if union > 0.0 else 0.0
def _canonical_json(value: object) -> bytes:
return json.dumps(
value,
sort_keys=True,
separators=(",", ":"),
ensure_ascii=False,
).encode("utf-8")
def _canonical_sha256(value: object) -> str:
return hashlib.sha256(_canonical_json(value)).hexdigest()
def _file_sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def _utc_timestamp(value: object) -> str:
if not isinstance(value, str) or not value.strip():
raise M48SmallStaticRegressionError("M4.8 run creation time is invalid")
text = value.strip()
try:
parsed = datetime.fromisoformat(text.replace("Z", "+00:00"))
except ValueError as exc:
raise M48SmallStaticRegressionError("M4.8 run creation time is invalid") from exc
if parsed.tzinfo is None or parsed.utcoffset() is None:
raise M48SmallStaticRegressionError("M4.8 run creation time must be UTC")
return parsed.astimezone(UTC).isoformat().replace("+00:00", "Z")
__all__ = [
"M48_SMALL_STATIC_RESULT_SCHEMA",
"M48SmallStaticRegressionError",
"M48SmallStaticRegressionResult",
"build_m48_small_static_passage_regression",
"read_m48_small_static_passage_regression",
]
+11 -5
View File
@@ -56,6 +56,7 @@ from k1link.compute.e40_perception_product_gate import (
read_e40_perception_product_gate,
)
from k1link.laboratory import LaboratoryEvidenceDefinition, LaboratoryEvidenceRegistry
from k1link.laboratory.evidence_registry import LaboratoryEvidenceVariant
from k1link.web.l3_pointpillars_visual_api import latest_l3_visual_identity
from k1link.web.l31_pointpillars_ravnoves_api import latest_l31_identity
from k1link.web.l32_pointpillars_camera_review_api import latest_l32_identity
@@ -259,7 +260,10 @@ def _advanced_index(
specs: tuple[_AdvancedIndexSpec, ...],
) -> dict[str, object]:
items: list[dict[str, object]] = []
selected_work_ids: set[str] = set()
for work_id, provider, pattern, document_name, schema_version in specs:
if work_id in selected_work_ids:
continue
root = _configured_root(provider)
if root is None:
continue
@@ -273,6 +277,7 @@ def _advanced_index(
schema_version=schema_version,
)
)
selected_work_ids.add(work_id)
break
except (json.JSONDecodeError, OSError, TypeError, ValueError):
continue
@@ -290,17 +295,18 @@ def _registry_index_specs(
return tuple(
(
definition.work_id,
_evidence_root_provider(definition, runtime_root_provider),
definition.result_id_pattern,
definition.document_name,
definition.result_schema_version,
_evidence_root_provider(variant, runtime_root_provider),
variant.result_id_pattern,
variant.document_name,
variant.result_schema_version,
)
for definition in registry.definitions
for variant in reversed(definition.evidence_variants)
)
def _evidence_root_provider(
definition: LaboratoryEvidenceDefinition,
definition: LaboratoryEvidenceDefinition | LaboratoryEvidenceVariant,
runtime_root_provider: RootProvider,
) -> RootProvider:
def result_root_provider() -> Path | None:
+85
View File
@@ -23,15 +23,23 @@ from k1link.compute import (
RecordedPerceptionOverlayMux,
RecordedPerceptionOverlayStore,
)
from k1link.compute.pipeline_telemetry import JsonlPipelineTelemetrySink
from k1link.laboratory import (
LaboratoryEvidenceRegistry,
LaboratoryEvidenceReportService,
LaboratoryExecutionRegistry,
LaboratoryRunner,
LaboratoryValueReviewRegistry,
)
from k1link.laboratory.m48_raw_evidence import (
M48_EXPECTED_THREAT_RESULT_ID,
M48RawEvidenceError,
M48RawEvidenceReader,
)
from k1link.sessions import (
MaterializedRecording,
RecordedCameraFrameService,
RecordedCameraPlaybackSource,
RecordedMediaInspector,
RecordedMediaManifest,
RecordingPreparationQueueFull,
@@ -116,6 +124,7 @@ from k1link.web.laboratory_report_api import build_laboratory_report_router
from k1link.web.lidar_api import build_lidar_router
from k1link.web.lidar_local_surface_service import K1LocalSurfaceReadService
from k1link.web.m4_threat_replay_api import build_m4_threat_replay_router
from k1link.web.m48_object_quality_api import build_m48_object_quality_router
from k1link.web.map_api import (
MapGatewayConfiguration,
MapGatewayProxy,
@@ -151,6 +160,13 @@ LABORATORY_EXECUTION_REGISTRY = LaboratoryExecutionRegistry.from_file(
REPOSITORY_ROOT / "config" / "laboratory-execution.json",
LABORATORY_EVIDENCE_REGISTRY,
)
LABORATORY_RUNNER = LaboratoryRunner(
registry=LABORATORY_EXECUTION_REGISTRY,
evidence_registry=LABORATORY_EVIDENCE_REGISTRY,
sink=JsonlPipelineTelemetrySink(
REPOSITORY_ROOT / ".runtime" / "telemetry" / "laboratory-runs.jsonl"
),
)
LABORATORY_VALUE_REVIEW_REGISTRY = LaboratoryValueReviewRegistry.from_file(
REPOSITORY_ROOT / "config" / "laboratory-value-review.json"
)
@@ -204,6 +220,20 @@ session_recorded_camera_frame_service = (
if _ffmpeg is not None
else None
)
try:
m48_raw_evidence_reader: M48RawEvidenceReader | None = M48RawEvidenceReader.from_repository(
repository_root=REPOSITORY_ROOT,
threat_result_root=(
REPOSITORY_ROOT
/ ".runtime"
/ "compute-experiments"
/ "m4"
/ "replay-threat"
/ M48_EXPECTED_THREAT_RESULT_ID
),
)
except (M48RawEvidenceError, OSError, ValueError):
m48_raw_evidence_reader = None
session_legacy_perception_overlay_store = (
RecordedPerceptionOverlayStore(
jobs_root=REPOSITORY_ROOT / ".runtime" / "compute-jobs",
@@ -295,6 +325,20 @@ session_recording_preparation_manager = SessionRecordingPreparationManager(
)
def _m48_recorded_camera_playback_source(
session_id: str,
) -> RecordedCameraPlaybackSource:
"""Publish the durable replay package before exposing its manifest URL."""
if session_recorded_camera_frame_service is None:
raise RuntimeError("recorded camera playback is unavailable")
command = session_store.prepare_replay(session_id, speed=1.0, loop=False)
snapshot = session_recording_preparation_manager.restore_published(command)
if snapshot is None or snapshot.state != "ready" or snapshot.recorded_media is None:
raise RuntimeError("recorded camera playback package is not published")
return session_recorded_camera_frame_service.playback_source(session_id)
def refresh_observation_catalog() -> tuple[str, ...]:
"""Discover completed or recoverable local evidence without copying payloads."""
@@ -859,6 +903,47 @@ app.include_router(
),
)
)
app.include_router(
build_m48_object_quality_router(
pack_root_provider=lambda: (
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "m48" / "object-quality-packs"
),
workflow_root_provider=lambda: (
REPOSITORY_ROOT / ".runtime" / "laboratory-annotations" / "m48-object-quality"
),
truth_root_provider=lambda: (
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "m48" / "object-truth-seals"
),
result_root_provider=lambda: (
REPOSITORY_ROOT / ".runtime" / "compute-experiments" / "m48" / "object-quality-results"
),
small_static_result_root_provider=lambda: (
REPOSITORY_ROOT
/ ".runtime"
/ "compute-experiments"
/ "m48"
/ "small-static-passage-regression-results"
),
camera_frame_provider=(
session_recorded_camera_frame_service.extract
if session_recorded_camera_frame_service is not None
else None
),
camera_playback_provider=(
_m48_recorded_camera_playback_source
if session_recorded_camera_frame_service is not None
else None
),
spatial_evidence_provider=m48_raw_evidence_reader,
evaluation_runner=LABORATORY_RUNNER,
evaluation_receipt_root_provider=lambda: (
REPOSITORY_ROOT
/ ".runtime"
/ "compute-experiments"
/ "laboratory-run-receipts"
),
)
)
app.include_router(
build_e47_semantic_slam_router(
root_provider=lambda: (
File diff suppressed because it is too large Load Diff
+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="2030"):
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)