feat(perception): integrate calibrated operator pipeline

Add calibrated K1 projection, recorded and near-live perception qualification, unified Rerun operator layers, bounded replay admission, audited viewer controls, worker experiments, and lab evidence.
This commit is contained in:
DCCONSTRUCTIONS
2026-07-23 00:23:28 +03:00
parent ada2a55ee6
commit b53d6d5a45
221 changed files with 55923 additions and 1357 deletions
+132
View File
@@ -1,11 +1,80 @@
"""Host-owned compute handoff contracts."""
from .annotation_workspace import (
ANNOTATION_WORKSPACE_SCHEMA,
AnnotationWorkspace,
AnnotationWorkspaceError,
prepare_annotation_workspace,
validate_annotation_workspace,
)
from .evaluation_pack import (
ANNOTATION_CONTRACT_SCHEMA,
EVALUATION_PACK_SCHEMA,
EvaluationFrameRequest,
EvaluationPackFrame,
RecordedEvaluationPack,
RecordedEvaluationPackError,
prepare_recorded_evaluation_pack,
validate_recorded_evaluation_pack,
)
from .fusion_epoch import (
RecordedCalibratedFusion,
RecordedCalibratedFusionStore,
RecordedPerceptionOverlayMux,
validate_recorded_calibrated_fusion,
)
from .integrated_perception import (
IntegratedPerceptionOverlayStore,
IntegratedPerceptionResult,
validate_integrated_perception_result,
)
from .jobs import (
COMPUTE_JOB_SCHEMA,
CameraComputeJob,
prepare_camera_compute_job,
validate_camera_compute_job,
)
from .live_perception import (
LIVE_INGRESS_SCHEMA,
LIVE_INGRESS_WIRE_SCHEMA,
TELEMETRY_SCHEMA,
WORLD_STATE_SCHEMA,
LatestWinsQueue,
LiveIngressEvent,
LivePerceptionIngress,
QueueSnapshot,
WorldStateProjector,
classify_health,
)
from .live_replay_qualification import (
LiveReplayQualificationResult,
validate_live_replay_qualification_result,
)
from .multirate_perception_qualification import (
MultiratePerceptionArtifact,
MultiratePerceptionQualificationResult,
validate_multirate_perception_qualification_result,
)
from .perception_epoch import (
RecordedPerceptionEpochResult,
RecordedPerceptionEpochStore,
RecordedPerceptionVideo,
validate_recorded_perception_epoch_result,
)
from .qualification import (
DEFAULT_QUALIFICATION_FRAME_COUNT,
QUALIFICATION_POLICY,
QualificationFrame,
RecordedQualificationSlice,
RecordedQualificationSliceError,
prepare_recorded_qualification_slice,
validate_recorded_qualification_slice,
)
from .realtime_tracking_qualification import (
RealtimeTrackingArtifact,
RealtimeTrackingQualificationResult,
validate_realtime_tracking_qualification_result,
)
from .results import (
DetectionFrame,
ObjectDetection,
@@ -14,16 +83,79 @@ from .results import (
RecordedPerceptionResult,
validate_recorded_perception_result,
)
from .tracked_fusion_qualification import (
TrackedFusionQualificationResult,
validate_tracked_fusion_qualification_result,
)
from .tracking_qualification import (
TrackingQualificationArtifact,
TrackingQualificationResult,
validate_tracking_qualification_result,
)
__all__ = [
"ANNOTATION_CONTRACT_SCHEMA",
"ANNOTATION_WORKSPACE_SCHEMA",
"AnnotationWorkspace",
"AnnotationWorkspaceError",
"COMPUTE_JOB_SCHEMA",
"DEFAULT_QUALIFICATION_FRAME_COUNT",
"EVALUATION_PACK_SCHEMA",
"EvaluationFrameRequest",
"EvaluationPackFrame",
"LatestWinsQueue",
"LIVE_INGRESS_SCHEMA",
"LIVE_INGRESS_WIRE_SCHEMA",
"LiveIngressEvent",
"LivePerceptionIngress",
"IntegratedPerceptionOverlayStore",
"IntegratedPerceptionResult",
"LiveReplayQualificationResult",
"MultiratePerceptionArtifact",
"MultiratePerceptionQualificationResult",
"QUALIFICATION_POLICY",
"QueueSnapshot",
"RecordedCalibratedFusion",
"RecordedCalibratedFusionStore",
"RecordedEvaluationPack",
"RecordedEvaluationPackError",
"RecordedPerceptionOverlayMux",
"validate_recorded_calibrated_fusion",
"CameraComputeJob",
"prepare_camera_compute_job",
"prepare_annotation_workspace",
"prepare_recorded_evaluation_pack",
"validate_camera_compute_job",
"RecordedPerceptionEpochResult",
"RecordedPerceptionEpochStore",
"RecordedPerceptionVideo",
"QualificationFrame",
"RealtimeTrackingArtifact",
"RealtimeTrackingQualificationResult",
"RecordedQualificationSlice",
"RecordedQualificationSliceError",
"validate_recorded_perception_epoch_result",
"validate_live_replay_qualification_result",
"validate_integrated_perception_result",
"validate_multirate_perception_qualification_result",
"prepare_recorded_qualification_slice",
"DetectionFrame",
"ObjectDetection",
"RecordedPerceptionOverlayError",
"RecordedPerceptionOverlayStore",
"RecordedPerceptionResult",
"TrackingQualificationArtifact",
"TrackingQualificationResult",
"TrackedFusionQualificationResult",
"TELEMETRY_SCHEMA",
"WORLD_STATE_SCHEMA",
"WorldStateProjector",
"classify_health",
"validate_recorded_perception_result",
"validate_recorded_evaluation_pack",
"validate_recorded_qualification_slice",
"validate_realtime_tracking_qualification_result",
"validate_tracking_qualification_result",
"validate_tracked_fusion_qualification_result",
"validate_annotation_workspace",
]
File diff suppressed because it is too large Load Diff
+777
View File
@@ -0,0 +1,777 @@
from __future__ import annotations
import hashlib
import json
import math
import os
import re
import secrets
import shutil
import stat
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from PIL import Image
from k1link.artifacts import utc_now_iso, write_json_atomic
from k1link.device_plugins.xgrids_k1.analyze.valid_fov import (
validate_k1_valid_fov_mask,
)
from k1link.sessions import SessionIntegrityError
from .jobs import validate_camera_compute_job
from .qualification import validate_recorded_qualification_slice
EVALUATION_PACK_SCHEMA = "missioncore.perception-evaluation-pack/v1"
EVALUATION_PACK_IDENTITY_SCHEMA = "missioncore.perception-evaluation-pack-identity/v1"
ANNOTATION_CONTRACT_SCHEMA = "missioncore.perception-annotation-contract/v1"
ANNOTATION_TEMPLATE_SCHEMA = "missioncore.perception-annotation-template/v1"
SELECTION_POLICY = "reviewed-anchors-plus-temporal-clips/v1"
MIN_EVALUATION_FRAMES = 16
MAX_EVALUATION_FRAMES = 128
MAX_MANIFEST_BYTES = 32 * 1024 * 1024
MAX_TIMELINE_LINE_BYTES = 16 * 1024
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
_SAFE_GENERATION = re.compile(r"^evaluation-pack-[a-f0-9]{64}$")
_SAFE_GROUP = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$")
class RecordedEvaluationPackError(RuntimeError):
"""A recorded-perception evaluation pack is invalid."""
@dataclass(frozen=True, slots=True)
class EvaluationFrameRequest:
frame_index: int
role: str
group_id: str
@dataclass(frozen=True, slots=True)
class EvaluationPackFrame:
image_id: int
frame_index: int
sequence: int
segment_sha256: str
session_seconds: float
role: str
group_id: str
raw_path: Path
valid_fov_fill_path: Path
@dataclass(frozen=True, slots=True)
class RecordedEvaluationPack:
generation_id: str
root: Path
manifest_path: Path
annotation_contract_path: Path
annotation_template_path: Path
job_id: str
input_sha256: str
qualification_generation_id: str
valid_fov_generation_id: str
calibration_sha256: str
frames: tuple[EvaluationPackFrame, ...]
def prepare_recorded_evaluation_pack(
*,
job_root: Path,
qualification_root: Path,
valid_fov_root: Path,
decoded_frames_root: Path,
timeline_path: Path,
output_root: Path,
selection: tuple[EvaluationFrameRequest, ...],
decoder_version: str,
selection_document_sha256: str,
producer_files: tuple[tuple[str, str], ...],
) -> RecordedEvaluationPack:
"""Seal exact raw/fixed-fill frames and an annotation contract for model A/B.
The pack is immutable. Human annotations are authored in a separate working
copy of ``annotation-template.json`` and later sealed as their own generation.
This keeps source imagery and the evolving review state from being conflated.
"""
job = validate_camera_compute_job(job_root)
qualification = validate_recorded_qualification_slice(
qualification_root,
job_root=job.job_root,
)
valid_fov = validate_k1_valid_fov_mask(valid_fov_root)
if not isinstance(decoder_version, str) or not 1 <= len(decoder_version) <= 256:
raise RecordedEvaluationPackError("decoder version is invalid")
if _SHA256.fullmatch(selection_document_sha256) is None:
raise RecordedEvaluationPackError("selection document SHA-256 is invalid")
producer_identity = _validate_producer_files(producer_files)
if (
valid_fov.source_id != job.source_id
or valid_fov.width != 800
or valid_fov.height != 600
):
raise RecordedEvaluationPackError("valid-FOV does not match the camera job")
selected = _validate_selection(selection, job.segment_count, qualification)
timestamps = _read_timeline(
timeline_path,
job.segment_count,
job.timeline_start_seconds,
job.timeline_end_seconds,
)
frames_root = decoded_frames_root.expanduser().resolve(strict=True)
if not frames_root.is_dir():
raise RecordedEvaluationPackError("decoded frame root is not a directory")
with Image.open(valid_fov.mask_path) as opened_mask:
mask = opened_mask.copy()
if mask.mode != "L" or mask.size != (valid_fov.width, valid_fov.height):
raise RecordedEvaluationPackError("valid-FOV mask cannot be applied to the frames")
source_by_index = {frame.frame_index: frame for frame in qualification.frames}
job_index = _read_job_index(job.job_root, job.source_id, job.codec_epoch)
decoded: list[dict[str, Any]] = []
for image_id, request in enumerate(selected, start=1):
source = source_by_index.get(request.frame_index)
row = job_index[request.frame_index]
sequence = _nonnegative_integer(row.get("sequence"), "segment sequence")
segment_sha256 = row.get("sha256")
if (
sequence != request.frame_index + 1
or not isinstance(segment_sha256, str)
or _SHA256.fullmatch(segment_sha256) is None
or (source is not None and source.segment_sha256 != segment_sha256)
):
raise RecordedEvaluationPackError("selected frame identity changed")
source_path = frames_root / f"frame-{request.frame_index:06d}.png"
_confined_regular_file(source_path, frames_root)
try:
with Image.open(source_path) as opened:
raw = opened.convert("RGB")
except OSError as exc:
raise RecordedEvaluationPackError("decoded evaluation frame is unavailable") from exc
if raw.size != (valid_fov.width, valid_fov.height):
raise RecordedEvaluationPackError("decoded evaluation frame dimensions changed")
fill = Image.composite(raw, Image.new("RGB", raw.size, (0, 0, 0)), mask)
decoded.append(
{
"image_id": image_id,
"request": request,
"sequence": sequence,
"segment_sha256": segment_sha256,
"session_seconds": timestamps[request.frame_index],
"raw": raw,
"fill": fill,
"raw_rgb_sha256": _pixel_sha256(raw),
"fill_rgb_sha256": _pixel_sha256(fill),
}
)
identity = {
"schema_version": EVALUATION_PACK_IDENTITY_SCHEMA,
"job_id": job.job_id,
"input_sha256": job.input_sha256,
"session_id": job.session_id,
"source_id": job.source_id,
"codec_epoch": job.codec_epoch,
"qualification_generation_id": qualification.generation_id,
"qualification_policy": qualification.policy,
"valid_fov_generation_id": valid_fov.generation_id,
"calibration_sha256": valid_fov.calibration_sha256,
"calibration_slot": valid_fov.calibration_slot,
"selection_policy": SELECTION_POLICY,
"selection_document_sha256": selection_document_sha256,
"preprocessing_profile": "fixed-valid-fov-fill/v1",
"decoder_version": decoder_version,
"producer_files": producer_identity,
"resolution": [valid_fov.width, valid_fov.height],
"frames": [
{
"image_id": item["image_id"],
"frame_index": item["request"].frame_index,
"sequence": item["sequence"],
"segment_sha256": item["segment_sha256"],
"session_seconds": item["session_seconds"],
"role": item["request"].role,
"group_id": item["request"].group_id,
"raw_rgb_sha256": item["raw_rgb_sha256"],
"valid_fov_fill_rgb_sha256": item["fill_rgb_sha256"],
}
for item in decoded
],
}
identity_sha256 = hashlib.sha256(_canonical_json(identity)).hexdigest()
generation_id = f"evaluation-pack-{identity_sha256}"
root = _prepare_private_directory(output_root)
final = root / generation_id
if final.exists():
existing = validate_recorded_evaluation_pack(
final,
job_root=job.job_root,
qualification_root=qualification.root,
valid_fov_root=valid_fov.root,
)
if existing.input_sha256 != job.input_sha256:
raise RecordedEvaluationPackError("evaluation generation collides with another input")
return existing
staging = root / f".{generation_id}.{secrets.token_hex(12)}.incomplete"
published = False
try:
(staging / "images" / "raw").mkdir(mode=0o700, parents=True)
(staging / "images" / "valid-fov-fill").mkdir(mode=0o700, parents=True)
artifacts: list[dict[str, Any]] = []
for item in decoded:
image_id = int(item["image_id"])
frame_index = item["request"].frame_index
name = f"image-{image_id:03d}-frame-{frame_index:06d}.png"
raw_path = staging / "images" / "raw" / name
fill_path = staging / "images" / "valid-fov-fill" / name
item["raw"].save(raw_path, format="PNG", optimize=False)
item["fill"].save(fill_path, format="PNG", optimize=False)
_fsync_file(raw_path)
_fsync_file(fill_path)
os.chmod(raw_path, 0o600)
os.chmod(fill_path, 0o600)
artifacts.extend((_artifact(raw_path, staging), _artifact(fill_path, staging)))
contract = _annotation_contract(identity_sha256)
template = _annotation_template(generation_id, identity["frames"])
contract_path = staging / "annotation-contract.json"
template_path = staging / "annotation-template.json"
write_json_atomic(contract_path, contract)
write_json_atomic(template_path, template)
os.chmod(contract_path, 0o600)
os.chmod(template_path, 0o600)
artifacts.extend((_artifact(contract_path, staging), _artifact(template_path, staging)))
manifest = {
"schema_version": EVALUATION_PACK_SCHEMA,
"generation_id": generation_id,
"identity_sha256": identity_sha256,
"identity": identity,
"created_at_utc": utc_now_iso(),
"artifacts": artifacts,
"annotation_state": {
"state": "unannotated",
"mutable_inside_pack": False,
"workflow": (
"copy annotation-template.json to a review workspace; seal reviewed "
"annotations as a separate content-addressed ground-truth generation"
),
},
}
write_json_atomic(staging / "manifest.json", manifest)
os.chmod(staging / "manifest.json", 0o600)
_fsync_tree(staging)
os.replace(staging, final)
_fsync_directory(root)
published = True
finally:
if not published and staging.exists():
shutil.rmtree(staging)
return validate_recorded_evaluation_pack(
final,
job_root=job.job_root,
qualification_root=qualification.root,
valid_fov_root=valid_fov.root,
)
def validate_recorded_evaluation_pack(
pack_root: Path,
*,
job_root: Path,
qualification_root: Path,
valid_fov_root: Path,
) -> RecordedEvaluationPack:
root = pack_root.expanduser().resolve(strict=True)
if not root.is_dir() or _SAFE_GENERATION.fullmatch(root.name) is None:
raise RecordedEvaluationPackError("evaluation pack root is invalid")
manifest = _read_json_object(root / "manifest.json", root, MAX_MANIFEST_BYTES)
identity = manifest.get("identity")
identity_sha256 = manifest.get("identity_sha256")
if (
manifest.get("schema_version") != EVALUATION_PACK_SCHEMA
or manifest.get("generation_id") != root.name
or not isinstance(identity, dict)
or identity.get("schema_version") != EVALUATION_PACK_IDENTITY_SCHEMA
or not isinstance(identity_sha256, str)
or _SHA256.fullmatch(identity_sha256) is None
or root.name != f"evaluation-pack-{identity_sha256}"
or hashlib.sha256(_canonical_json(identity)).hexdigest() != identity_sha256
):
raise RecordedEvaluationPackError("evaluation pack identity is inconsistent")
job = validate_camera_compute_job(job_root)
qualification = validate_recorded_qualification_slice(
qualification_root,
job_root=job.job_root,
)
valid_fov = validate_k1_valid_fov_mask(valid_fov_root)
if (
identity.get("job_id") != job.job_id
or identity.get("input_sha256") != job.input_sha256
or identity.get("session_id") != job.session_id
or identity.get("source_id") != job.source_id
or identity.get("codec_epoch") != job.codec_epoch
or identity.get("qualification_generation_id") != qualification.generation_id
or identity.get("qualification_policy") != qualification.policy
or identity.get("valid_fov_generation_id") != valid_fov.generation_id
or identity.get("calibration_sha256") != valid_fov.calibration_sha256
or identity.get("calibration_slot") != valid_fov.calibration_slot
or identity.get("selection_policy") != SELECTION_POLICY
or not isinstance(identity.get("selection_document_sha256"), str)
or _SHA256.fullmatch(str(identity["selection_document_sha256"])) is None
or identity.get("preprocessing_profile") != "fixed-valid-fov-fill/v1"
or not isinstance(identity.get("decoder_version"), str)
or not 1 <= len(str(identity["decoder_version"])) <= 256
or identity.get("resolution") != [valid_fov.width, valid_fov.height]
):
raise RecordedEvaluationPackError("evaluation pack bindings changed")
producer_files = identity.get("producer_files")
if not isinstance(producer_files, list):
raise RecordedEvaluationPackError("evaluation producer identity is unavailable")
normalized_producer = _validate_producer_files(
tuple(
(str(row.get("name")), str(row.get("sha256")))
for row in producer_files
if isinstance(row, dict)
)
)
if producer_files != normalized_producer:
raise RecordedEvaluationPackError("evaluation producer identity changed")
rows = identity.get("frames")
if (
not isinstance(rows, list)
or not MIN_EVALUATION_FRAMES <= len(rows) <= MAX_EVALUATION_FRAMES
):
raise RecordedEvaluationPackError("evaluation pack frame set is invalid")
artifact_rows = manifest.get("artifacts")
if not isinstance(artifact_rows, list) or len(artifact_rows) != len(rows) * 2 + 2:
raise RecordedEvaluationPackError("evaluation pack artifact set is invalid")
artifacts: dict[str, dict[str, Any]] = {}
for artifact in artifact_rows:
if not isinstance(artifact, dict) or not isinstance(artifact.get("path"), str):
raise RecordedEvaluationPackError("evaluation artifact descriptor is invalid")
path_text = str(artifact["path"])
if path_text in artifacts:
raise RecordedEvaluationPackError("evaluation artifact is duplicated")
path = root / path_text
metadata = _confined_regular_file(path, root)
digest = artifact.get("sha256")
if (
artifact.get("byte_length") != metadata.st_size
or not isinstance(digest, str)
or _SHA256.fullmatch(digest) is None
or _sha256_file(path) != digest
):
raise RecordedEvaluationPackError("evaluation artifact changed")
artifacts[path_text] = artifact
contract_path = root / "annotation-contract.json"
template_path = root / "annotation-template.json"
contract = _read_json_object(contract_path, root, MAX_MANIFEST_BYTES)
template = _read_json_object(template_path, root, MAX_MANIFEST_BYTES)
if (
contract.get("schema_version") != ANNOTATION_CONTRACT_SCHEMA
or contract.get("evaluation_identity_sha256") != identity_sha256
or template.get("schema_version") != ANNOTATION_TEMPLATE_SCHEMA
or template.get("evaluation_pack_id") != root.name
or template.get("images") != rows
or "annotation-contract.json" not in artifacts
or "annotation-template.json" not in artifacts
):
raise RecordedEvaluationPackError("annotation contract changed")
qualification_indices = {frame.frame_index for frame in qualification.frames}
job_index = _read_job_index(job.job_root, job.source_id, job.codec_epoch)
with Image.open(valid_fov.mask_path) as opened_mask:
validation_mask = opened_mask.copy()
frames: list[EvaluationPackFrame] = []
previous_index = -1
temporal_groups: dict[str, list[int]] = {}
for position, row in enumerate(rows, start=1):
if not isinstance(row, dict):
raise RecordedEvaluationPackError("evaluation frame descriptor is invalid")
frame_index = row.get("frame_index")
image_id = row.get("image_id")
role = row.get("role")
group_id = row.get("group_id")
session_seconds = row.get("session_seconds")
if (
image_id != position
or not isinstance(frame_index, int)
or isinstance(frame_index, bool)
or not previous_index < frame_index < job.segment_count
or role not in {"anchor", "temporal"}
or not isinstance(group_id, str)
or _SAFE_GROUP.fullmatch(group_id) is None
or not isinstance(session_seconds, (int, float))
or isinstance(session_seconds, bool)
or not math.isfinite(float(session_seconds))
or not job.timeline_start_seconds <= float(session_seconds) <= job.timeline_end_seconds
):
raise RecordedEvaluationPackError("evaluation frame ordering is invalid")
if role == "anchor" and frame_index not in qualification_indices:
raise RecordedEvaluationPackError("anchor escaped the qualification slice")
if role == "temporal":
temporal_groups.setdefault(group_id, []).append(frame_index)
name = f"image-{image_id:03d}-frame-{frame_index:06d}.png"
raw_relative = f"images/raw/{name}"
fill_relative = f"images/valid-fov-fill/{name}"
if raw_relative not in artifacts or fill_relative not in artifacts:
raise RecordedEvaluationPackError("evaluation image artifacts are incomplete")
raw_path = root / raw_relative
fill_path = root / fill_relative
with Image.open(raw_path) as raw_opened, Image.open(fill_path) as fill_opened:
raw = raw_opened.convert("RGB")
fill = fill_opened.convert("RGB")
expected_fill = Image.composite(
raw,
Image.new("RGB", raw.size, (0, 0, 0)),
validation_mask,
)
expected_segment_sha256 = job_index[frame_index].get("sha256")
if (
raw.size != (valid_fov.width, valid_fov.height)
or fill.size != raw.size
or _pixel_sha256(raw) != row.get("raw_rgb_sha256")
or _pixel_sha256(fill) != row.get("valid_fov_fill_rgb_sha256")
or _pixel_sha256(expected_fill) != row.get("valid_fov_fill_rgb_sha256")
or not isinstance(row.get("segment_sha256"), str)
or _SHA256.fullmatch(str(row["segment_sha256"])) is None
or row.get("segment_sha256") != expected_segment_sha256
or row.get("sequence") != frame_index + 1
):
raise RecordedEvaluationPackError("evaluation frame pixels or identity changed")
frames.append(
EvaluationPackFrame(
image_id=image_id,
frame_index=frame_index,
sequence=frame_index + 1,
segment_sha256=str(row["segment_sha256"]),
session_seconds=float(session_seconds),
role=role,
group_id=group_id,
raw_path=raw_path,
valid_fov_fill_path=fill_path,
)
)
previous_index = frame_index
for group in temporal_groups.values():
if len(group) < 3 or any(
right != left + 1 for left, right in zip(group, group[1:], strict=False)
):
raise RecordedEvaluationPackError("temporal group is not a consecutive clip")
if len(temporal_groups) < 2:
raise RecordedEvaluationPackError("evaluation pack lacks temporal coverage")
return RecordedEvaluationPack(
generation_id=root.name,
root=root,
manifest_path=root / "manifest.json",
annotation_contract_path=contract_path,
annotation_template_path=template_path,
job_id=job.job_id,
input_sha256=job.input_sha256,
qualification_generation_id=qualification.generation_id,
valid_fov_generation_id=valid_fov.generation_id,
calibration_sha256=valid_fov.calibration_sha256,
frames=tuple(frames),
)
def _validate_selection(
selection: tuple[EvaluationFrameRequest, ...],
source_count: int,
qualification: Any,
) -> tuple[EvaluationFrameRequest, ...]:
if not MIN_EVALUATION_FRAMES <= len(selection) <= MAX_EVALUATION_FRAMES:
raise RecordedEvaluationPackError("evaluation selection size is outside bounds")
qualification_indices = {frame.frame_index for frame in qualification.frames}
previous = -1
temporal_groups: dict[str, list[int]] = {}
anchors = 0
for request in selection:
if (
not isinstance(request, EvaluationFrameRequest)
or not isinstance(request.frame_index, int)
or isinstance(request.frame_index, bool)
or not previous < request.frame_index < source_count
or request.role not in {"anchor", "temporal"}
or _SAFE_GROUP.fullmatch(request.group_id) is None
):
raise RecordedEvaluationPackError("evaluation selection is invalid")
if request.role == "anchor":
if request.frame_index not in qualification_indices:
raise RecordedEvaluationPackError("anchor is outside the qualification slice")
anchors += 1
else:
temporal_groups.setdefault(request.group_id, []).append(request.frame_index)
previous = request.frame_index
if anchors < MIN_EVALUATION_FRAMES:
raise RecordedEvaluationPackError("evaluation selection has too few anchors")
if len(temporal_groups) < 2:
raise RecordedEvaluationPackError("evaluation selection needs at least two temporal clips")
for group in temporal_groups.values():
if len(group) < 3 or any(
right != left + 1 for left, right in zip(group, group[1:], strict=False)
):
raise RecordedEvaluationPackError("temporal clip frames must be consecutive")
return selection
def _annotation_contract(evaluation_identity_sha256: str) -> dict[str, Any]:
categories = [
(1, "person", "thing"),
(2, "bicycle", "thing"),
(3, "motorcycle", "thing"),
(4, "car", "thing"),
(5, "heavy_vehicle", "thing"),
(6, "building_structure", "stuff"),
(7, "paved_road", "stuff"),
(8, "sidewalk_curb", "stuff"),
(9, "ground_dirt", "stuff"),
(10, "grass_low_vegetation", "stuff"),
(11, "tree_woody_vegetation", "stuff"),
(12, "sky", "stuff"),
(13, "static_obstacle", "thing"),
(14, "animal", "thing"),
(15, "other_background", "stuff"),
]
return {
"schema_version": ANNOTATION_CONTRACT_SCHEMA,
"evaluation_identity_sha256": evaluation_identity_sha256,
"task": "2d-panoptic-and-instance-qualification",
"categories": [
{"id": category_id, "name": name, "kind": kind}
for category_id, name, kind in categories
],
"label_map": {
"outside_valid_fov": 0,
"ambiguous_or_unresolvable": 255,
"metric_category_ids": [category_id for category_id, _name, _kind in categories],
},
"policy": {
"coverage": "label every resolvable pixel inside the fixed valid-FOV mask",
"instances": (
"annotate every identifiable thing instance; preserve occluded and truncated "
"flags; use ambiguous=255 only when class or boundary cannot be resolved"
),
"lens_exterior": "must remain label 0 and is excluded from all accuracy metrics",
"prelabels": (
"model-assisted prelabels are permitted only as drafts; every accepted object "
"and semantic region requires human review"
),
"review": "two-pass review: annotator complete, then reviewer accepted",
},
"required_metrics": {
"semantic": ["per_class_iou", "macro_miou_present", "frequency_weighted_iou"],
"instance": ["ap_50_95", "ap50", "ap75", "ar100", "per_class_recall"],
"safety_proxies": [
"person_vehicle_miss_rate",
"false_large_instance_rate",
"valid_fov_boundary_leakage",
],
"temporal": ["class_flicker_rate", "instance_id_switches", "mask_iou_jitter"],
},
"acceptance": {
"unreviewed_frames": 0,
"pixels_outside_valid_fov_nonzero": 0,
"invalid_category_pixels": 0,
"duplicate_instance_ids_per_frame": 0,
"metric_reporting": "mean, p50, p95 and worst-frame identities where applicable",
},
}
def _validate_producer_files(files: tuple[tuple[str, str], ...]) -> list[dict[str, str]]:
if not 1 <= len(files) <= 8:
raise RecordedEvaluationPackError("evaluation producer file set is invalid")
normalized: list[dict[str, str]] = []
seen: set[str] = set()
for name, digest in files:
if (
not isinstance(name, str)
or _SAFE_GROUP.fullmatch(name) is None
or name in seen
or not isinstance(digest, str)
or _SHA256.fullmatch(digest) is None
):
raise RecordedEvaluationPackError("evaluation producer file identity is invalid")
normalized.append({"name": name, "sha256": digest})
seen.add(name)
return normalized
def _annotation_template(generation_id: str, frames: Any) -> dict[str, Any]:
return {
"schema_version": ANNOTATION_TEMPLATE_SCHEMA,
"evaluation_pack_id": generation_id,
"state": "unannotated",
"images": frames,
"semantic_masks": [],
"instances": [],
"reviews": [
{
"image_id": row["image_id"],
"annotation_status": "unannotated",
"review_status": "unreviewed",
"notes": "",
}
for row in frames
],
}
def _read_timeline(path: Path, count: int, start: float, end: float) -> list[float]:
root = path.expanduser().resolve(strict=True).parent
resolved = path.expanduser().resolve(strict=True)
_confined_regular_file(resolved, root)
values: list[float] = []
try:
with resolved.open("r", encoding="utf-8") as stream:
for expected, line in enumerate(stream):
if len(line.encode("utf-8")) > MAX_TIMELINE_LINE_BYTES:
raise RecordedEvaluationPackError("decoded timeline row is too large")
row = json.loads(line)
value = row.get("session_seconds") if isinstance(row, dict) else None
if row.get("frame_index") != expected or not isinstance(value, (int, float)):
raise RecordedEvaluationPackError("decoded timeline order changed")
timestamp = float(value)
if not math.isfinite(timestamp) or not start <= timestamp <= end:
raise RecordedEvaluationPackError("decoded timeline escapes the job")
if values and timestamp <= values[-1]:
raise RecordedEvaluationPackError("decoded timeline is not monotonic")
values.append(timestamp)
except (OSError, UnicodeDecodeError, json.JSONDecodeError, AttributeError) as exc:
raise RecordedEvaluationPackError("decoded timeline is unavailable") from exc
if len(values) != count:
raise RecordedEvaluationPackError("decoded timeline frame count changed")
return values
def _read_job_index(job_root: Path, source_id: str, codec_epoch: int) -> list[dict[str, Any]]:
path = job_root / "input" / "camera" / source_id / f"epoch-{codec_epoch}" / "index.jsonl"
rows: list[dict[str, Any]] = []
try:
with path.open("r", encoding="utf-8") as stream:
for line in stream:
row = json.loads(line)
if not isinstance(row, dict):
raise RecordedEvaluationPackError("camera index row is invalid")
rows.append(row)
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise RecordedEvaluationPackError("camera index is unavailable") from exc
if not rows:
raise RecordedEvaluationPackError("camera index is empty")
return rows
def _nonnegative_integer(value: Any, label: str) -> int:
if not isinstance(value, int) or isinstance(value, bool) or value < 0:
raise RecordedEvaluationPackError(f"{label} is invalid")
return value
def _pixel_sha256(image: Image.Image) -> str:
digest = hashlib.sha256()
digest.update(image.width.to_bytes(4, "big"))
digest.update(image.height.to_bytes(4, "big"))
digest.update(image.mode.encode("ascii"))
digest.update(b"\x00")
digest.update(image.tobytes())
return digest.hexdigest()
def _artifact(path: Path, root: Path) -> dict[str, Any]:
return {
"path": path.relative_to(root).as_posix(),
"byte_length": path.stat().st_size,
"sha256": _sha256_file(path),
}
def _prepare_private_directory(path: Path) -> Path:
candidate = path.expanduser()
candidate.mkdir(mode=0o700, parents=True, exist_ok=True)
metadata = candidate.lstat()
if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):
raise RecordedEvaluationPackError("evaluation output root must be a real directory")
root = candidate.resolve(strict=True)
os.chmod(root, 0o700)
return root
def _read_json_object(path: Path, root: Path, maximum_bytes: int) -> dict[str, Any]:
metadata = _confined_regular_file(path, root)
if not 0 < metadata.st_size <= maximum_bytes:
raise RecordedEvaluationPackError("evaluation JSON is outside bounds")
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise RecordedEvaluationPackError("evaluation JSON is unavailable") from exc
if not isinstance(value, dict):
raise RecordedEvaluationPackError("evaluation JSON is not an object")
return value
def _confined_regular_file(path: Path, root: Path) -> os.stat_result:
try:
resolved_root = root.resolve(strict=True)
resolved = path.resolve(strict=True)
metadata = path.lstat()
except OSError as exc:
raise RecordedEvaluationPackError("evaluation artifact is unavailable") from exc
if (
stat.S_ISLNK(metadata.st_mode)
or not stat.S_ISREG(metadata.st_mode)
or not resolved.is_relative_to(resolved_root)
):
raise RecordedEvaluationPackError("evaluation artifact is not confined")
return metadata
def _sha256_file(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 _canonical_json(value: object) -> bytes:
try:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
except (TypeError, ValueError) as exc:
raise SessionIntegrityError("evaluation identity is not canonical JSON") from exc
def _fsync_file(path: Path) -> None:
with path.open("rb") as stream:
os.fsync(stream.fileno())
def _fsync_tree(root: Path) -> None:
for path in sorted(root.rglob("*"), reverse=True):
if path.is_file():
_fsync_file(path)
elif path.is_dir():
_fsync_directory(path)
_fsync_directory(root)
def _fsync_directory(path: Path) -> None:
flags = os.O_RDONLY
if hasattr(os, "O_DIRECTORY"):
flags |= os.O_DIRECTORY
descriptor = os.open(path, flags)
try:
os.fsync(descriptor)
finally:
os.close(descriptor)
+542
View File
@@ -0,0 +1,542 @@
"""Validated calibrated 3D fusion results projected into an opened Rerun recording."""
from __future__ import annotations
import hashlib
import json
import os
import re
import stat
import threading
from contextlib import suppress
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Protocol
import numpy as np
import rerun as rr
from rerun.components import FillMode
from k1link.artifacts import write_json_atomic
from k1link.sessions import SessionIntegrityError
from .jobs import CameraComputeJob, validate_camera_compute_job
from .perception_epoch import validate_recorded_perception_epoch_result
from .results import RecordedPerceptionOverlayError
FUSION_SCHEMA = "missioncore.recorded-calibrated-fusion/v1"
FUSION_IDENTITY_SCHEMA = "missioncore.recorded-calibrated-fusion-identity/v1"
SESSION_TIMELINE = "session_time"
MAX_JSON_BYTES = 64 * 1024 * 1024
MAX_SCAN = 512
MAX_POINTS_PER_FRAME = 100_000
MAX_BOXES_PER_FRAME = 10_000
_SAFE_FUSION_ID = re.compile(r"^fusion-[a-f0-9]{64}$")
_SAFE_RECORDING_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$")
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
@dataclass(frozen=True, slots=True)
class RecordedCalibratedFusion:
fusion_id: str
root: Path
job: CameraComputeJob
perception_result_id: str
created_at_utc: str
arrays_path: Path
labels_path: Path
frame_count: int
class _OverlayProvider(Protocol):
def render(
self,
session_id: str,
*,
application_id: str,
recording_id: str,
) -> bytes | None: ...
class RecordedCalibratedFusionStore:
"""Discover full-epoch fusion and serialize it for the opened recording ID."""
def __init__(
self,
*,
jobs_root: Path,
perception_results_root: Path,
fusion_results_root: Path,
cache_root: Path,
) -> None:
self.jobs_root = jobs_root.expanduser().absolute()
self.perception_results_root = perception_results_root.expanduser().absolute()
self.fusion_results_root = fusion_results_root.expanduser().absolute()
self.cache_root = cache_root.expanduser().absolute()
self._lock = threading.Lock()
def render(
self,
session_id: str,
*,
application_id: str,
recording_id: str,
) -> bytes | None:
if _SAFE_RECORDING_ID.fullmatch(session_id) is None:
raise ValueError("observation session id is invalid")
if application_id != "nodedc_mission_core_recorded":
raise ValueError("recorded fusion application id is invalid")
if _SAFE_RECORDING_ID.fullmatch(recording_id) is None:
raise ValueError("recorded fusion recording id is invalid")
with self._lock:
fusion = self._latest(session_id)
if fusion is None:
return None
cache_root = _private_directory(self.cache_root)
session_cache = _private_child_directory(cache_root, session_id)
fusion_cache = _private_child_directory(session_cache, fusion.fusion_id)
output = fusion_cache / f"{recording_id}.rrd"
sidecar = output.with_suffix(".rrd.cache.json")
cached = _read_cache(output, sidecar, fusion, recording_id)
if cached is not None:
return cached
payload = _render_fusion(
fusion,
application_id=application_id,
recording_id=recording_id,
)
temporary = output.with_name(f".{output.name}.{os.getpid()}.tmp")
try:
with temporary.open("xb") as stream:
stream.write(payload)
stream.flush()
os.fsync(stream.fileno())
os.chmod(temporary, 0o600)
os.replace(temporary, output)
write_json_atomic(
sidecar,
{
"schema_version": "missioncore.calibrated-fusion-overlay-cache/v1",
"fusion_id": fusion.fusion_id,
"recording_id": recording_id,
"byte_length": len(payload),
"sha256": hashlib.sha256(payload).hexdigest(),
},
)
finally:
temporary.unlink(missing_ok=True)
return payload
def _latest(self, session_id: str) -> RecordedCalibratedFusion | None:
try:
jobs = sorted(self.jobs_root.iterdir())
except FileNotFoundError:
return None
if len(jobs) > MAX_SCAN:
raise RecordedPerceptionOverlayError("compute job catalog is outside bounds")
matches: list[RecordedCalibratedFusion] = []
for job_root in jobs:
if job_root.is_symlink():
continue
try:
job = validate_camera_compute_job(job_root)
except (OSError, SessionIntegrityError):
continue
if job.session_id != session_id:
continue
parent = self.fusion_results_root / job.job_id
try:
candidates = sorted(
path
for path in parent.iterdir()
if path.is_dir()
and not path.is_symlink()
and _SAFE_FUSION_ID.fullmatch(path.name) is not None
)
except FileNotFoundError:
continue
if len(candidates) > MAX_SCAN:
raise RecordedPerceptionOverlayError("fusion result catalog is outside bounds")
for candidate in candidates:
try:
matches.append(
validate_recorded_calibrated_fusion(
job_root,
self.perception_results_root,
candidate,
)
)
except (OSError, SessionIntegrityError):
continue
if not matches:
return None
return max(matches, key=lambda value: (value.created_at_utc, value.fusion_id))
class RecordedPerceptionOverlayMux:
"""Prefer calibrated full-epoch 3D fusion and retain the legacy fallback."""
def __init__(self, primary: _OverlayProvider, fallback: _OverlayProvider | None) -> None:
self.primary = primary
self.fallback = fallback
def render(
self,
session_id: str,
*,
application_id: str,
recording_id: str,
) -> bytes | None:
payload = self.primary.render(
session_id,
application_id=application_id,
recording_id=recording_id,
)
if payload is not None or self.fallback is None:
return payload
return self.fallback.render(
session_id,
application_id=application_id,
recording_id=recording_id,
)
def validate_recorded_calibrated_fusion(
job_root: Path,
perception_results_root: Path,
fusion_root: Path,
) -> RecordedCalibratedFusion:
job = validate_camera_compute_job(job_root)
root = fusion_root.expanduser().resolve(strict=True)
if not root.is_dir() or _SAFE_FUSION_ID.fullmatch(root.name) is None:
raise SessionIntegrityError("calibrated fusion root is invalid")
manifest = _read_json_object(root / "manifest.json", root)
identity = manifest.get("identity")
identity_sha256 = manifest.get("identity_sha256")
if (
manifest.get("schema_version") != FUSION_SCHEMA
or manifest.get("fusion_id") != root.name
or not isinstance(identity, dict)
or identity.get("schema_version") != FUSION_IDENTITY_SCHEMA
or not isinstance(identity_sha256, str)
or _SHA256.fullmatch(identity_sha256) is None
or root.name != f"fusion-{identity_sha256}"
or hashlib.sha256(_canonical_json(identity)).hexdigest() != identity_sha256
or identity.get("job_id") != job.job_id
or identity.get("input_sha256") != job.input_sha256
or manifest.get("session_id") != job.session_id
or manifest.get("source_id") != job.source_id
or manifest.get("frame_count") != job.segment_count
or manifest.get("timeline_start_seconds") != job.timeline_start_seconds
or manifest.get("timeline_end_seconds") != job.timeline_end_seconds
):
raise SessionIntegrityError("calibrated fusion identity is inconsistent")
perception_result_id = identity.get("perception_result_id")
if not isinstance(perception_result_id, str):
raise SessionIntegrityError("calibrated fusion perception binding is invalid")
perception = validate_recorded_perception_epoch_result(
job_root,
perception_results_root / job.job_id / perception_result_id,
)
if (
identity.get("calibration_sha256") != perception.calibration_sha256
or identity.get("camera_slot") != perception.calibration_slot
):
raise SessionIntegrityError("calibrated fusion calibration binding changed")
artifacts = manifest.get("artifacts")
expected = {"fusion.npz", "box-labels.json", "fusion-frames.jsonl"}
if not isinstance(artifacts, list) or len(artifacts) != len(expected):
raise SessionIntegrityError("calibrated fusion artifacts are incomplete")
paths: dict[str, Path] = {}
for artifact in artifacts:
if not isinstance(artifact, dict) or artifact.get("name") not in expected:
raise SessionIntegrityError("calibrated fusion artifact is invalid")
name = str(artifact["name"])
if name in paths:
raise SessionIntegrityError("calibrated fusion artifact is duplicated")
path = root / name
metadata = _confined_regular_file(path, root)
if (
artifact.get("byte_length") != metadata.st_size
or not isinstance(artifact.get("sha256"), str)
or _SHA256.fullmatch(str(artifact["sha256"])) is None
or _sha256(path) != artifact["sha256"]
):
raise SessionIntegrityError("calibrated fusion artifact identity changed")
paths[name] = path
labels = _read_labels(paths["box-labels.json"])
_validate_arrays(paths["fusion.npz"], job, len(labels))
created_at = manifest.get("created_at_utc")
if not isinstance(created_at, str) or not 1 <= len(created_at) <= 64:
raise SessionIntegrityError("calibrated fusion creation time is invalid")
return RecordedCalibratedFusion(
fusion_id=root.name,
root=root,
job=job,
perception_result_id=perception_result_id,
created_at_utc=created_at,
arrays_path=paths["fusion.npz"],
labels_path=paths["box-labels.json"],
frame_count=job.segment_count,
)
def _validate_arrays(path: Path, job: CameraComputeJob, label_count: int) -> None:
try:
with np.load(path, allow_pickle=False) as arrays:
required = {
"frame_times_ns",
"point_offsets",
"points",
"point_colors",
"box_offsets",
"box_centers",
"box_half_sizes",
"box_colors",
}
if set(arrays.files) != required:
raise SessionIntegrityError("calibrated fusion array set is invalid")
frame_times = arrays["frame_times_ns"]
point_offsets = arrays["point_offsets"]
points = arrays["points"]
point_colors = arrays["point_colors"]
box_offsets = arrays["box_offsets"]
box_centers = arrays["box_centers"]
box_half_sizes = arrays["box_half_sizes"]
box_colors = arrays["box_colors"]
if (
frame_times.dtype != np.int64
or frame_times.shape != (job.segment_count,)
or point_offsets.dtype != np.int64
or point_offsets.shape != (job.segment_count + 1,)
or box_offsets.dtype != np.int64
or box_offsets.shape != (job.segment_count + 1,)
or points.dtype != np.float32
or points.ndim != 2
or points.shape[1:] != (3,)
or point_colors.dtype != np.uint8
or point_colors.shape != points.shape
or box_centers.dtype != np.float32
or box_centers.ndim != 2
or box_centers.shape[1:] != (3,)
or box_half_sizes.dtype != np.float32
or box_half_sizes.shape != box_centers.shape
or box_colors.dtype != np.uint8
or box_colors.shape != (box_centers.shape[0], 4)
or label_count != box_centers.shape[0]
):
raise SessionIntegrityError("calibrated fusion array shapes changed")
_validate_offsets(point_offsets, points.shape[0], MAX_POINTS_PER_FRAME)
_validate_offsets(box_offsets, box_centers.shape[0], MAX_BOXES_PER_FRAME)
if (
np.any(np.diff(frame_times) <= 0)
or frame_times[0] < round(job.timeline_start_seconds * 1e9) - 1_000_000
or frame_times[-1] > round(job.timeline_end_seconds * 1e9) + 1_000_000
or not np.isfinite(points).all()
or not np.isfinite(box_centers).all()
or not np.isfinite(box_half_sizes).all()
or np.any(box_half_sizes <= 0)
):
raise SessionIntegrityError("calibrated fusion arrays are inconsistent")
except (OSError, ValueError, KeyError) as exc:
raise SessionIntegrityError("calibrated fusion arrays are unavailable") from exc
def _validate_offsets(
offsets: np.ndarray[Any, np.dtype[np.int64]],
total: int,
maximum: int,
) -> None:
if (
offsets[0] != 0
or offsets[-1] != total
or np.any(np.diff(offsets) < 0)
or np.any(np.diff(offsets) > maximum)
):
raise SessionIntegrityError("calibrated fusion offsets are inconsistent")
def _render_fusion(
fusion: RecordedCalibratedFusion,
*,
application_id: str,
recording_id: str,
) -> bytes:
labels = _read_labels(fusion.labels_path)
recording = rr.RecordingStream(application_id, recording_id=recording_id)
stream = rr.binary_stream(recording)
try:
recording.log("/world", rr.ViewCoordinates.RIGHT_HAND_Z_UP, static=True)
recording.log(
"/world/perception/contract",
rr.TextDocument(
"Factory-calibrated KB4 mask-to-LiDAR diagnostic. Distances and support-gated "
"boxes are not ground-truthed or safety accepted."
),
static=True,
)
with np.load(fusion.arrays_path, allow_pickle=False) as arrays:
frame_times = arrays["frame_times_ns"]
point_offsets = arrays["point_offsets"]
points = arrays["points"]
point_colors = arrays["point_colors"]
box_offsets = arrays["box_offsets"]
centers = arrays["box_centers"]
half_sizes = arrays["box_half_sizes"]
box_colors = arrays["box_colors"]
for index, timestamp in enumerate(frame_times):
recording.set_time(
SESSION_TIMELINE,
duration=np.timedelta64(int(timestamp), "ns"),
)
point_start, point_end = int(point_offsets[index]), int(point_offsets[index + 1])
if point_end > point_start:
recording.log(
"/world/perception/semantic_points",
rr.Points3D(
points[point_start:point_end],
colors=point_colors[point_start:point_end],
radii=rr.Radius.ui_points(2.5),
),
)
else:
recording.log(
"/world/perception/semantic_points",
rr.Clear(recursive=False),
)
box_start, box_end = int(box_offsets[index]), int(box_offsets[index + 1])
if box_end > box_start:
recording.log(
"/world/perception/boxes3d",
rr.Boxes3D(
centers=centers[box_start:box_end],
half_sizes=half_sizes[box_start:box_end],
colors=box_colors[box_start:box_end],
labels=labels[box_start:box_end],
fill_mode=FillMode.Solid,
show_labels=True,
),
)
else:
recording.log(
"/world/perception/boxes3d",
rr.Clear(recursive=False),
)
payload = stream.read(flush=True, flush_timeout_sec=300.0)
except Exception as exc:
raise RecordedPerceptionOverlayError("failed to serialize calibrated fusion") from exc
finally:
with suppress(Exception):
recording.disconnect()
if payload is None or not payload.startswith(b"RRF2"):
raise RecordedPerceptionOverlayError("serialized calibrated fusion is invalid")
return payload
def _read_cache(
output: Path,
sidecar: Path,
fusion: RecordedCalibratedFusion,
recording_id: str,
) -> bytes | None:
try:
value = _read_json_object(sidecar, sidecar.parent)
payload = output.read_bytes()
except (OSError, SessionIntegrityError):
return None
if (
value.get("schema_version") != "missioncore.calibrated-fusion-overlay-cache/v1"
or value.get("fusion_id") != fusion.fusion_id
or value.get("recording_id") != recording_id
or value.get("byte_length") != len(payload)
or value.get("sha256") != hashlib.sha256(payload).hexdigest()
or not payload.startswith(b"RRF2")
):
return None
return payload
def _read_labels(path: Path) -> list[str]:
metadata = _confined_regular_file(path, path.parent)
if not 1 <= metadata.st_size <= MAX_JSON_BYTES:
raise SessionIntegrityError("calibrated fusion labels are outside bounds")
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SessionIntegrityError("calibrated fusion labels are unavailable") from exc
if (
not isinstance(value, list)
or any(not isinstance(item, str) or not 1 <= len(item) <= 256 for item in value)
):
raise SessionIntegrityError("calibrated fusion labels are invalid")
return value
def _read_json_object(path: Path, root: Path) -> dict[str, Any]:
metadata = _confined_regular_file(path, root)
if not 0 < metadata.st_size <= MAX_JSON_BYTES:
raise SessionIntegrityError("calibrated fusion JSON is outside bounds")
try:
value = json.loads(path.read_text(encoding="utf-8-sig"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SessionIntegrityError("calibrated fusion JSON is unavailable") from exc
if not isinstance(value, dict):
raise SessionIntegrityError("calibrated fusion JSON is not an object")
return value
def _confined_regular_file(path: Path, root: Path) -> os.stat_result:
try:
resolved_root = root.resolve(strict=True)
resolved = path.resolve(strict=True)
metadata = path.lstat()
except OSError as exc:
raise SessionIntegrityError("calibrated fusion artifact is unavailable") from exc
if (
stat.S_ISLNK(metadata.st_mode)
or not stat.S_ISREG(metadata.st_mode)
or not resolved.is_relative_to(resolved_root)
):
raise SessionIntegrityError("calibrated fusion artifact is not confined")
return metadata
def _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 _canonical_json(value: object) -> bytes:
try:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode()
except (TypeError, ValueError) as exc:
raise SessionIntegrityError("calibrated fusion identity cannot be encoded") from exc
def _private_directory(path: Path) -> Path:
path.mkdir(mode=0o700, parents=True, exist_ok=True)
if path.is_symlink() or not path.is_dir():
raise RecordedPerceptionOverlayError("fusion cache root is invalid")
with suppress(OSError):
os.chmod(path, 0o700)
return path.resolve(strict=True)
def _private_child_directory(root: Path, name: str) -> Path:
child = root / name
child.mkdir(mode=0o700, exist_ok=True)
if child.is_symlink() or not child.is_dir() or child.resolve(strict=True).parent != root:
raise RecordedPerceptionOverlayError("fusion cache directory is invalid")
with suppress(OSError):
os.chmod(child, 0o700)
return child.resolve(strict=True)
+925
View File
@@ -0,0 +1,925 @@
"""Validate and project accepted LAB E10 integrated perception results."""
from __future__ import annotations
import hashlib
import json
import os
import re
import shutil
import stat
import subprocess
import tempfile
import threading
from contextlib import suppress
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Protocol, TypeGuard
import numpy as np
import rerun as rr
from rerun.components import FillMode
from k1link.artifacts import write_json_atomic
from k1link.sessions import SessionIntegrityError
from .jobs import CameraComputeJob, validate_camera_compute_job
from .results import RecordedPerceptionOverlayError
RESULT_SCHEMA = "missioncore.e10-integrated-perception-result/v1"
IDENTITY_SCHEMA = "missioncore.e10-integrated-perception-identity/v1"
REPORT_SCHEMA = "missioncore.e10-integrated-perception-report/v1"
PACK_SCHEMA = "missioncore.e10-lidar-replay-pack/v1"
SEMANTIC_SCHEMA = "missioncore.e10-semantic-frame/v1"
FUSION_SCHEMA = "missioncore.e10-fusion-frame/v1"
WORLD_SCHEMA = "missioncore.live-perception-world-state/v1"
SESSION_TIMELINE = "session_time"
MAX_SCAN = 512
MAX_JSON_BYTES = 64 * 1024 * 1024
MAX_LINE_BYTES = 4 * 1024 * 1024
MAX_SOURCE_BYTES = 512 * 1024 * 1024
OVERLAY_RENDERER_VERSION = "4"
CUBOID_PRESENTATION_HOLD_NS = 500_000_000
_SAFE_RESULT_ID = re.compile(r"^e10-integrated-perception-[a-f0-9]{64}$")
_SAFE_PACK_ID = re.compile(r"^e10-lidar-pack-[a-f0-9]{64}$")
_SAFE_RECORDING_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$")
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
@dataclass(frozen=True, slots=True)
class IntegratedPerceptionResult:
result_id: str
result_root: Path
job: CameraComputeJob
pack_root: Path
created_at_utc: str
accepted: bool
publication_scope: str
source_start_frame_index: int
frame_count: int
timeline_start_seconds: float
timeline_end_seconds: float
semantic_path: Path
fusion_path: Path
world_path: Path
arrays_path: Path
report_path: Path
@dataclass(frozen=True, slots=True)
class _PresentedCuboid:
observed_ns: int
track_id: int
label: str
association_group: str
distance_m: float
support_points: int
center: np.ndarray
half_size: np.ndarray
quaternion: np.ndarray
color: np.ndarray
class _CuboidPresentationState:
"""Hold accepted cuboids briefly for operator presentation only.
The persisted world-state remains fail-closed and frame-exact. This bounded
latest-at projection only prevents one rejected LiDAR association from
visually clearing an otherwise stable tracked object for a single frame.
"""
def __init__(self, hold_ns: int = CUBOID_PRESENTATION_HOLD_NS) -> None:
if hold_ns <= 0:
raise ValueError("cuboid presentation hold must be positive")
self._hold_ns = hold_ns
self._latest: dict[int, _PresentedCuboid] = {}
def update(
self,
timestamp_ns: int,
objects: list[dict[str, Any]],
centers: np.ndarray,
half_sizes: np.ndarray,
quaternions: np.ndarray,
colors: np.ndarray,
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, list[str]] | None:
accepted = [
item
for item in objects
if str(item.get("cuboid_status", "")).startswith("accepted-")
]
if not (
len(accepted)
== len(centers)
== len(half_sizes)
== len(quaternions)
== len(colors)
):
raise RecordedPerceptionOverlayError(
"integrated perception cuboid presentation arrays are inconsistent"
)
for item, center, half_size, quaternion, color in zip(
accepted,
centers,
half_sizes,
quaternions,
colors,
strict=True,
):
track_id = item.get("track_id")
if not isinstance(track_id, int) or isinstance(track_id, bool):
raise RecordedPerceptionOverlayError(
"integrated perception cuboid track identity is invalid"
)
self._latest[track_id] = _PresentedCuboid(
observed_ns=timestamp_ns,
track_id=track_id,
label=str(item.get("label", "object")),
association_group=str(item.get("association_group", "object")),
distance_m=float(item["distance_smoothed_m"]),
support_points=int(item["clustered_points"]),
center=np.asarray(center).copy(),
half_size=np.asarray(half_size).copy(),
quaternion=np.asarray(quaternion).copy(),
color=np.asarray(color).copy(),
)
expired = [
track_id
for track_id, cuboid in self._latest.items()
if timestamp_ns - cuboid.observed_ns > self._hold_ns
]
for track_id in expired:
del self._latest[track_id]
if not self._latest:
return None
presented = sorted(self._latest.values(), key=lambda cuboid: cuboid.track_id)
presented_colors: list[np.ndarray] = []
labels: list[str] = []
for cuboid in presented:
age_ns = max(0, timestamp_ns - cuboid.observed_ns)
color = cuboid.color.copy()
if age_ns > 0 and color.shape == (4,):
fade = min(1.0, age_ns / self._hold_ns)
color[3] = max(24, round(float(color[3]) * (1.0 - 0.55 * fade)))
presented_colors.append(color)
age_label = "" if age_ns == 0 else f" · hold {age_ns / 1_000_000:.0f} ms"
labels.append(
f"{cuboid.association_group} #{cuboid.track_id} {cuboid.label} · "
f"{cuboid.distance_m:.1f} m · {cuboid.support_points} pts{age_label}"
)
return (
np.stack([cuboid.center for cuboid in presented]),
np.stack([cuboid.half_size for cuboid in presented]),
np.stack([cuboid.quaternion for cuboid in presented]),
np.stack(presented_colors),
labels,
)
class _OverlayProvider(Protocol):
def render(
self,
session_id: str,
*,
application_id: str,
recording_id: str,
) -> bytes | None: ...
def validate_integrated_perception_result(
job_root: Path,
result_root: Path,
lidar_packs_root: Path,
) -> IntegratedPerceptionResult:
job = validate_camera_compute_job(job_root)
root = result_root.expanduser().resolve(strict=True)
if not root.is_dir() or _SAFE_RESULT_ID.fullmatch(root.name) is None:
raise SessionIntegrityError("integrated perception root is invalid")
result = _read_object(root / "result.json", root)
identity = result.get("identity")
identity_sha256 = result.get("identity_sha256")
if (
result.get("schema_version") != RESULT_SCHEMA
or result.get("result_id") != root.name
or not isinstance(identity, dict)
or identity.get("schema_version") != IDENTITY_SCHEMA
or not isinstance(identity_sha256, str)
or _SHA256.fullmatch(identity_sha256) is None
or root.name != f"e10-integrated-perception-{identity_sha256}"
or hashlib.sha256(_canonical_json(identity)).hexdigest() != identity_sha256
or identity.get("job_id") != job.job_id
or identity.get("input_sha256") != job.input_sha256
or identity.get("session_id") != job.session_id
or identity.get("source_id") != job.source_id
or result.get("ground_truth") is not False
or result.get("publication_scope")
not in {
"recorded-integrated-realtime-qualification-only",
"recorded-integrated-semantic-loss-negative-control-only",
}
or result.get("acceptance_state") not in {"accepted", "rejected"}
):
raise SessionIntegrityError("integrated perception identity is inconsistent")
selection = identity.get("selection")
if not isinstance(selection, dict):
raise SessionIntegrityError("integrated perception selection is missing")
count = selection.get("frame_count")
start = selection.get("source_start_frame_index")
end = selection.get("source_end_frame_index")
timeline_start = selection.get("timeline_start_seconds")
timeline_end = selection.get("timeline_end_seconds")
if (
not isinstance(count, int)
or isinstance(count, bool)
or count < 2
or not isinstance(start, int)
or isinstance(start, bool)
or not isinstance(end, int)
or isinstance(end, bool)
or end - start + 1 != count
or start < 0
or end >= job.segment_count
or not _finite(timeline_start)
or not _finite(timeline_end)
or float(timeline_end) <= float(timeline_start)
or result.get("frames_processed") != count
):
raise SessionIntegrityError("integrated perception selection is invalid")
pack_id = identity.get("lidar_pack_id")
if not isinstance(pack_id, str) or _SAFE_PACK_ID.fullmatch(pack_id) is None:
raise SessionIntegrityError("integrated perception LiDAR pack binding is invalid")
pack_root = lidar_packs_root.expanduser().resolve(strict=True) / pack_id
_validate_pack(pack_root, job, identity, count, start, end)
artifacts = _validate_artifacts(root, result.get("artifacts"))
semantic_path = artifacts["e10-semantic-frames"]
fusion_path = artifacts["e10-fusion-frames"]
world_path = artifacts["e10-world-state"]
arrays_path = artifacts["e10-transient-perception"]
report_path = artifacts["e10-run-report"]
semantic_rows = _read_rows(semantic_path, root, SEMANTIC_SCHEMA)
fusion_rows = _read_rows(fusion_path, root, FUSION_SCHEMA)
world_rows = _read_rows(world_path, root, WORLD_SCHEMA)
if len(fusion_rows) != count or len(world_rows) != count or not semantic_rows:
raise SessionIntegrityError("integrated perception frame counts are incomplete")
for index, (fusion, world) in enumerate(zip(fusion_rows, world_rows, strict=True)):
source_index = start + index
if (
fusion.get("frame_index") != index
or fusion.get("source_frame_index") != source_index
or world.get("frame_index") != index
or world.get("source_frame_index") != source_index
or not isinstance(fusion.get("objects"), list)
or not isinstance(world.get("objects"), list)
or world.get("object_count") != len(world["objects"])
or not isinstance(world.get("delivery"), dict)
):
raise SessionIntegrityError("integrated perception frame identity changed")
semantic_indices: list[int] = []
for row in semantic_rows:
frame_index = row.get("frame_index")
if not isinstance(frame_index, int) or isinstance(frame_index, bool):
raise SessionIntegrityError("integrated perception semantic timeline is invalid")
semantic_indices.append(frame_index)
if (
semantic_indices != sorted(set(semantic_indices))
or semantic_indices[0] != 0
or semantic_indices[-1] >= count
):
raise SessionIntegrityError("integrated perception semantic timeline is invalid")
_validate_arrays(arrays_path, count, semantic_rows, fusion_rows)
report = _read_object(report_path, root)
acceptance = report.get("acceptance")
if (
report.get("schema_version") != REPORT_SCHEMA
or report.get("result_id") != root.name
or report.get("identity") != identity
or report.get("ground_truth") is not False
or not isinstance(acceptance, dict)
or acceptance.get("accepted") is not (result.get("acceptance_state") == "accepted")
or acceptance.get("navigation_or_safety_accepted") is not False
):
raise SessionIntegrityError("integrated perception report is inconsistent")
created = result.get("created_at_utc")
if not isinstance(created, str) or not 1 <= len(created) <= 64:
raise SessionIntegrityError("integrated perception creation time is invalid")
return IntegratedPerceptionResult(
result_id=root.name,
result_root=root,
job=job,
pack_root=pack_root,
created_at_utc=created,
accepted=result.get("acceptance_state") == "accepted",
publication_scope=str(result["publication_scope"]),
source_start_frame_index=start,
frame_count=count,
timeline_start_seconds=float(timeline_start),
timeline_end_seconds=float(timeline_end),
semantic_path=semantic_path,
fusion_path=fusion_path,
world_path=world_path,
arrays_path=arrays_path,
report_path=report_path,
)
class IntegratedPerceptionOverlayStore:
"""Prefer the newest accepted E10 result for a recorded session."""
def __init__(
self,
*,
jobs_root: Path,
results_root: Path,
lidar_packs_root: Path,
cache_root: Path,
ffmpeg_path: Path,
) -> None:
self.jobs_root = jobs_root.expanduser().absolute()
self.results_root = results_root.expanduser().absolute()
self.lidar_packs_root = lidar_packs_root.expanduser().absolute()
self.cache_root = cache_root.expanduser().absolute()
self.ffmpeg_path = ffmpeg_path.expanduser().absolute()
self._lock = threading.Lock()
def render(
self,
session_id: str,
*,
application_id: str,
recording_id: str,
) -> bytes | None:
if application_id != "nodedc_mission_core_recorded":
raise ValueError("integrated perception application id is invalid")
if (
_SAFE_RECORDING_ID.fullmatch(session_id) is None
or _SAFE_RECORDING_ID.fullmatch(recording_id) is None
):
raise ValueError("integrated perception recording id is invalid")
with self._lock:
result = self._latest(session_id)
if result is None:
return None
cache = _private_child(
_private_child(_private_directory(self.cache_root), session_id),
result.result_id,
)
output = cache / f"{recording_id}.rrd"
sidecar = output.with_suffix(".rrd.cache.json")
cached = _read_cache(output, sidecar, result, recording_id)
if cached is not None:
return cached
payload = _render(
result,
application_id=application_id,
recording_id=recording_id,
ffmpeg_path=self.ffmpeg_path,
temporary_root=cache,
)
temporary = output.with_name(f".{output.name}.{os.getpid()}.tmp")
try:
with temporary.open("xb") as stream:
stream.write(payload)
stream.flush()
os.fsync(stream.fileno())
os.chmod(temporary, 0o600)
os.replace(temporary, output)
write_json_atomic(
sidecar,
{
"schema_version": "missioncore.e10-overlay-cache/v1",
"renderer_version": OVERLAY_RENDERER_VERSION,
"result_id": result.result_id,
"recording_id": recording_id,
"byte_length": len(payload),
"sha256": hashlib.sha256(payload).hexdigest(),
},
)
finally:
temporary.unlink(missing_ok=True)
return payload
def _latest(self, session_id: str) -> IntegratedPerceptionResult | None:
try:
jobs = sorted(self.jobs_root.iterdir())
candidates = sorted(self.results_root.iterdir())
except FileNotFoundError:
return None
if len(jobs) > MAX_SCAN or len(candidates) > MAX_SCAN:
raise RecordedPerceptionOverlayError("integrated perception catalog is outside bounds")
matches = []
for job_root in jobs:
if job_root.is_symlink():
continue
try:
job = validate_camera_compute_job(job_root)
except (OSError, SessionIntegrityError):
continue
if job.session_id != session_id:
continue
for candidate in candidates:
if candidate.is_symlink() or _SAFE_RESULT_ID.fullmatch(candidate.name) is None:
continue
try:
value = validate_integrated_perception_result(
job_root,
candidate,
self.lidar_packs_root,
)
except (OSError, SessionIntegrityError):
continue
if (
value.accepted
and value.publication_scope == "recorded-integrated-realtime-qualification-only"
):
matches.append(value)
if not matches:
return None
return max(matches, key=lambda value: (value.created_at_utc, value.result_id))
def _render(
result: IntegratedPerceptionResult,
*,
application_id: str,
recording_id: str,
ffmpeg_path: Path,
temporary_root: Path,
) -> bytes:
fusion_rows = _read_rows(result.fusion_path, result.result_root, FUSION_SCHEMA)
with np.load(result.arrays_path, allow_pickle=False) as arrays:
frame_times = arrays["frame_times_ns"]
semantic_indices = arrays["semantic_frame_indices"]
semantic_masks = arrays["semantic_masks"]
semantic_by_frame = {
int(index): mask for index, mask in zip(semantic_indices, semantic_masks, strict=True)
}
support_offsets = arrays["support_offsets"]
support = arrays["support_points"]
support_colors = arrays["support_colors"]
box_offsets = arrays["box_offsets"]
centers = arrays["box_centers"]
half_sizes = arrays["box_half_sizes"]
quaternions = arrays["box_quaternions"]
box_colors = arrays["box_colors"]
cuboid_presentation = _CuboidPresentationState()
recording = rr.RecordingStream(application_id, recording_id=recording_id)
stream = rr.binary_stream(recording)
source_path: Path | None = None
proxy_path: Path | None = None
try:
recording.log("/world", rr.ViewCoordinates.RIGHT_HAND_Z_UP, static=True)
recording.log(
"/world/perception/contract",
rr.TextDocument(
"LAB E10 integrated source-paced replay. Generic AI and host-arrival "
"synchronization are diagnostic, not navigation or safety accepted."
),
static=True,
)
source_path = _camera_source_file(
result.job, result.source_start_frame_index + result.frame_count, temporary_root
)
source_end_frame_index = result.source_start_frame_index + result.frame_count - 1
proxy_path = _camera_proxy_file(
source_path,
result.source_start_frame_index,
source_end_frame_index,
ffmpeg_path,
temporary_root,
)
video = rr.AssetVideo(path=proxy_path)
video_timestamps = video.read_frame_timestamps_nanos()
if len(video_timestamps) != len(frame_times):
raise RecordedPerceptionOverlayError(
"integrated perception video frame count changed"
)
recording.log(
"/perception/camera/image",
video,
static=True,
)
for index, timestamp in enumerate(frame_times):
recording.set_time(SESSION_TIMELINE, duration=np.timedelta64(int(timestamp), "ns"))
recording.log(
"/perception/camera/image",
rr.VideoFrameReference(nanoseconds=int(video_timestamps[index])),
)
mask = semantic_by_frame.get(index)
if mask is not None:
recording.log(
"/perception/camera/segmentation",
rr.SegmentationImage(mask),
)
objects = fusion_rows[index]["objects"]
if objects:
recording.log(
"/perception/camera/detections",
rr.Boxes2D(
array=[item["bbox_xyxy"] for item in objects],
array_format=rr.Box2DFormat.XYXY,
labels=[
f"#{item['track_id']} {item['label']} · {float(item['score']):.0%}"
for item in objects
],
show_labels=True,
),
)
else:
recording.log("/perception/camera/detections", rr.Clear(recursive=False))
point_start, point_end = (
int(support_offsets[index]),
int(support_offsets[index + 1]),
)
if point_end > point_start:
recording.log(
"/world/perception/support",
rr.Points3D(
support[point_start:point_end],
colors=support_colors[point_start:point_end],
radii=rr.Radius.ui_points(3.0),
),
)
else:
recording.log("/world/perception/support", rr.Clear(recursive=False))
box_start, box_end = int(box_offsets[index]), int(box_offsets[index + 1])
presented_cuboids = cuboid_presentation.update(
int(timestamp),
objects,
centers[box_start:box_end],
half_sizes[box_start:box_end],
quaternions[box_start:box_end],
box_colors[box_start:box_end],
)
if presented_cuboids is not None:
(
presented_centers,
presented_half_sizes,
presented_quaternions,
presented_colors,
presented_labels,
) = presented_cuboids
recording.log(
"/world/perception/boxes3d",
rr.Boxes3D(
centers=presented_centers,
half_sizes=presented_half_sizes,
quaternions=presented_quaternions,
colors=presented_colors,
labels=presented_labels,
fill_mode=FillMode.Solid,
show_labels=True,
),
)
else:
recording.log("/world/perception/boxes3d", rr.Clear(recursive=False))
payload = stream.read(flush=True, flush_timeout_sec=300.0)
except RecordedPerceptionOverlayError:
raise
except Exception as exc:
raise RecordedPerceptionOverlayError(
"failed to serialize integrated perception"
) from exc
finally:
with suppress(Exception):
recording.disconnect()
if source_path is not None:
source_path.unlink(missing_ok=True)
if proxy_path is not None:
proxy_path.unlink(missing_ok=True)
if payload is None or not payload.startswith(b"RRF2"):
raise RecordedPerceptionOverlayError("integrated perception Rerun stream is invalid")
return payload
def _camera_source_file(job: CameraComputeJob, segment_count: int, root: Path) -> Path:
epoch = job.job_root / "input" / "camera" / job.source_id / f"epoch-{job.codec_epoch}"
paths = [epoch / "init.mp4"] + [
epoch / "segments" / f"{sequence}.m4s" for sequence in range(1, segment_count + 1)
]
total = sum(path.stat().st_size for path in paths)
if total <= 0 or total > MAX_SOURCE_BYTES:
raise RecordedPerceptionOverlayError(
"integrated perception camera source is outside bounds"
)
descriptor, name = tempfile.mkstemp(prefix=".e10-camera-", suffix=".mp4", dir=root)
path = Path(name)
try:
with os.fdopen(descriptor, "wb") as output:
for source in paths:
with source.open("rb") as stream:
shutil.copyfileobj(stream, output, length=1024 * 1024)
output.flush()
os.fsync(output.fileno())
return path
except BaseException:
path.unlink(missing_ok=True)
raise
def _camera_proxy_file(
source: Path,
start_frame_index: int,
end_frame_index: int,
ffmpeg_path: Path,
root: Path,
) -> Path:
descriptor, name = tempfile.mkstemp(prefix=".e10-camera-proxy-", suffix=".mp4", dir=root)
os.close(descriptor)
path = Path(name)
path.unlink(missing_ok=True)
frame_filter = (
f"select=between(n\\,{start_frame_index}\\,{end_frame_index}),setpts=N/(10*TB)"
)
try:
completed = subprocess.run(
[
str(ffmpeg_path),
"-hide_banner",
"-loglevel",
"error",
"-i",
str(source),
"-vf",
frame_filter,
"-an",
"-c:v",
"libx264",
"-preset",
"veryfast",
"-crf",
"28",
"-g",
"20",
"-keyint_min",
"20",
"-pix_fmt",
"yuv420p",
"-movflags",
"+faststart",
str(path),
],
check=False,
capture_output=True,
timeout=300,
)
if completed.returncode != 0 or not path.is_file() or path.stat().st_size <= 0:
raise RecordedPerceptionOverlayError(
f"integrated perception video proxy failed: {completed.stderr[-1000:]!r}"
)
os.chmod(path, 0o600)
return path
except BaseException:
path.unlink(missing_ok=True)
raise
def _validate_pack(
root: Path,
job: CameraComputeJob,
result_identity: dict[str, Any],
count: int,
start: int,
end: int,
) -> None:
if not root.is_dir() or _SAFE_PACK_ID.fullmatch(root.name) is None:
raise SessionIntegrityError("integrated perception LiDAR pack root is invalid")
manifest = _read_object(root / "manifest.json", root)
identity = manifest.get("identity")
identity_sha256 = manifest.get("identity_sha256")
arrays_path = root / "lidar-pack.npz"
artifact = manifest.get("artifact")
if (
manifest.get("schema_version") != PACK_SCHEMA
or manifest.get("pack_id") != root.name
or not isinstance(identity, dict)
or identity.get("schema_version") != PACK_SCHEMA
or identity.get("job_id") != job.job_id
or identity.get("input_sha256") != job.input_sha256
or identity.get("calibration_sha256")
!= result_identity["configuration"]["profile"]["source"]["calibration_sha256"]
or identity.get("frame_count") != count
or identity.get("source_start_frame_index") != start
or identity.get("source_end_frame_index") != end
or not isinstance(identity_sha256, str)
or hashlib.sha256(_canonical_json(identity)).hexdigest() != identity_sha256
or root.name != f"e10-lidar-pack-{identity_sha256}"
or not isinstance(artifact, dict)
or artifact.get("path") != arrays_path.name
or artifact.get("byte_length") != arrays_path.stat().st_size
or artifact.get("sha256") != _sha256(arrays_path)
):
raise SessionIntegrityError("integrated perception LiDAR pack is inconsistent")
def _validate_artifacts(root: Path, raw: object) -> dict[str, Path]:
expected = {
"e10-semantic-frames": ("semantic-frames.jsonl", SEMANTIC_SCHEMA),
"e10-fusion-frames": ("fusion-frames.jsonl", FUSION_SCHEMA),
"e10-world-state": ("world-state.jsonl", WORLD_SCHEMA),
"e10-transient-perception": ("transient-perception.npz", None),
"worker-gpu-telemetry": ("gpu-telemetry.jsonl", None),
"e10-run-report": ("run-report.json", REPORT_SCHEMA),
}
if not isinstance(raw, list) or len(raw) != len(expected):
raise SessionIntegrityError("integrated perception artifact set is incomplete")
result = {}
for value in raw:
kind = value.get("kind") if isinstance(value, dict) else None
if not isinstance(kind, str) or kind not in expected or kind in result:
raise SessionIntegrityError("integrated perception artifact descriptor is invalid")
name, schema = expected[kind]
path = root / name
metadata = _confined_file(path, root)
if (
value.get("path") != name
or value.get("schema_version") != schema
or value.get("byte_length") != metadata.st_size
or value.get("sha256") != _sha256(path)
):
raise SessionIntegrityError("integrated perception artifact identity changed")
result[kind] = path
return result
def _validate_arrays(
path: Path,
count: int,
semantic_rows: list[dict[str, Any]],
fusion_rows: list[dict[str, Any]],
) -> None:
with np.load(path, allow_pickle=False) as arrays:
required = {
"frame_times_ns",
"semantic_frame_indices",
"semantic_masks",
"support_offsets",
"support_points",
"support_colors",
"box_offsets",
"box_centers",
"box_half_sizes",
"box_quaternions",
"box_colors",
}
if set(arrays.files) != required:
raise SessionIntegrityError("integrated perception array set changed")
times = arrays["frame_times_ns"]
semantic_indices = arrays["semantic_frame_indices"]
masks = arrays["semantic_masks"]
support_offsets = arrays["support_offsets"]
support = arrays["support_points"]
support_colors = arrays["support_colors"]
box_offsets = arrays["box_offsets"]
centers = arrays["box_centers"]
half_sizes = arrays["box_half_sizes"]
quaternions = arrays["box_quaternions"]
colors = arrays["box_colors"]
expected_boxes = sum(
sum(
str(item.get("cuboid_status", "")).startswith("accepted-")
for item in row["objects"]
)
for row in fusion_rows
)
if (
times.dtype != np.int64
or times.shape != (count,)
or np.any(np.diff(times) <= 0)
or semantic_indices.dtype != np.int64
or semantic_indices.shape != (len(semantic_rows),)
or not np.array_equal(semantic_indices, [row["frame_index"] for row in semantic_rows])
or masks.dtype != np.uint8
or masks.shape != (len(semantic_rows), 600, 800)
or support_offsets.shape != (count + 1,)
or support.shape[1:] != (3,)
or support_colors.shape != support.shape
or box_offsets.shape != (count + 1,)
or centers.shape != (expected_boxes, 3)
or half_sizes.shape != centers.shape
or quaternions.shape != (expected_boxes, 4)
or colors.shape != (expected_boxes, 4)
or int(support_offsets[-1]) != support.shape[0]
or int(box_offsets[-1]) != expected_boxes
or np.any(np.diff(support_offsets) < 0)
or np.any(np.diff(box_offsets) < 0)
or not np.isfinite(support).all()
or not np.isfinite(centers).all()
or not np.isfinite(half_sizes).all()
or np.any(half_sizes <= 0)
):
raise SessionIntegrityError("integrated perception arrays are inconsistent")
def _read_rows(path: Path, root: Path, schema: str) -> list[dict[str, Any]]:
_confined_file(path, root)
rows = []
with path.open(encoding="utf-8") as stream:
for line in stream:
if len(line.encode()) > MAX_LINE_BYTES:
raise SessionIntegrityError("integrated perception row is oversized")
value = json.loads(line)
if not isinstance(value, dict) or value.get("schema_version") != schema:
raise SessionIntegrityError("integrated perception row schema changed")
rows.append(value)
return rows
def _read_cache(
output: Path,
sidecar: Path,
result: IntegratedPerceptionResult,
recording_id: str,
) -> bytes | None:
try:
value = _read_object(sidecar, sidecar.parent)
payload = output.read_bytes()
except (OSError, SessionIntegrityError):
return None
if (
value.get("schema_version") != "missioncore.e10-overlay-cache/v1"
or value.get("renderer_version") != OVERLAY_RENDERER_VERSION
or value.get("result_id") != result.result_id
or value.get("recording_id") != recording_id
or value.get("byte_length") != len(payload)
or value.get("sha256") != hashlib.sha256(payload).hexdigest()
or not payload.startswith(b"RRF2")
):
return None
return payload
def _read_object(path: Path, root: Path) -> dict[str, Any]:
metadata = _confined_file(path, root)
if not 0 < metadata.st_size <= MAX_JSON_BYTES:
raise SessionIntegrityError("integrated perception JSON is outside bounds")
try:
value = json.loads(path.read_text(encoding="utf-8-sig"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SessionIntegrityError("integrated perception JSON is unavailable") from exc
if not isinstance(value, dict):
raise SessionIntegrityError("integrated perception JSON is not an object")
return value
def _confined_file(path: Path, root: Path) -> os.stat_result:
try:
resolved_root = root.resolve(strict=True)
resolved = path.resolve(strict=True)
metadata = path.lstat()
except OSError as exc:
raise SessionIntegrityError("integrated perception artifact is unavailable") from exc
if (
stat.S_ISLNK(metadata.st_mode)
or not stat.S_ISREG(metadata.st_mode)
or not resolved.is_relative_to(resolved_root)
):
raise SessionIntegrityError("integrated perception artifact is not confined")
return metadata
def _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 _canonical_json(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode()
def _finite(value: object) -> TypeGuard[int | float]:
return (
isinstance(value, int | float)
and not isinstance(value, bool)
and bool(np.isfinite(value))
)
def _private_directory(path: Path) -> Path:
path.mkdir(mode=0o700, parents=True, exist_ok=True)
if path.is_symlink() or not path.is_dir():
raise RecordedPerceptionOverlayError("integrated perception cache root is invalid")
with suppress(OSError):
os.chmod(path, 0o700)
return path.resolve(strict=True)
def _private_child(root: Path, name: str) -> Path:
child = root / name
child.mkdir(mode=0o700, exist_ok=True)
if child.is_symlink() or not child.is_dir() or child.resolve(strict=True).parent != root:
raise RecordedPerceptionOverlayError("integrated perception cache child is invalid")
with suppress(OSError):
os.chmod(child, 0o700)
return child.resolve(strict=True)
+991
View File
@@ -0,0 +1,991 @@
"""Bounded latest-wins primitives for derived live perception."""
from __future__ import annotations
import json
import math
import statistics
import struct
import threading
import time
import zlib
from collections import deque
from collections.abc import Mapping, Sequence
from dataclasses import dataclass
from hashlib import sha256
from typing import Any, Final, Literal
import numpy as np
from k1link.data_plane import DecodedPointCloudView, DecodedPoseView
WORLD_STATE_SCHEMA = "missioncore.live-perception-world-state/v1"
TELEMETRY_SCHEMA = "missioncore.live-perception-telemetry/v1"
HealthState = Literal["healthy", "degraded", "stale", "unavailable"]
LiveIngressModality = Literal[
"control",
"camera-init",
"camera-frame",
"lidar",
"pose",
]
LIVE_INGRESS_SCHEMA: Final = "missioncore.live-perception-ingress/v1"
LIVE_INGRESS_WIRE_SCHEMA: Final = "missioncore.live-perception-wire/v1"
LIVE_RESULT_WIRE_SCHEMA: Final = "missioncore.live-perception-result-wire/v1"
LIVE_RESULT_MAGIC: Final = b"MCPR"
LIVE_RESULT_MAX_HEADER_BYTES: Final = 256 * 1024
LIVE_RESULT_MAX_PAYLOAD_BYTES: Final = 2 * 1024 * 1024
@dataclass(frozen=True, slots=True)
class LivePerceptionResultFrame:
frame_index: int
source_frame_index: int
session_seconds: float
captured_at_epoch_ns: int
image_jpeg: bytes
segmentation_mask: np.ndarray[Any, np.dtype[np.uint8]] | None
objects: tuple[dict[str, Any], ...]
delivery: dict[str, Any]
def encode_live_perception_result(
*,
frame_index: int,
source_frame_index: int,
session_seconds: float,
captured_at_epoch_ns: int,
image_jpeg: bytes,
segmentation_mask: np.ndarray[Any, Any] | None,
objects: Sequence[Mapping[str, Any]],
delivery: Mapping[str, Any],
) -> bytes:
"""Encode one bounded, non-authoritative worker-to-viewer result frame."""
if (
frame_index < 0
or source_frame_index < 0
or captured_at_epoch_ns < 0
or not math.isfinite(session_seconds)
or session_seconds < 0
or not 4 <= len(image_jpeg) <= 1024 * 1024
or not image_jpeg.startswith(b"\xff\xd8")
or not image_jpeg.endswith(b"\xff\xd9")
):
raise ValueError("live perception result identity or image is invalid")
normalized_objects = tuple(_normalize_live_result_object(value) for value in objects)
if len(normalized_objects) > 128:
raise ValueError("live perception result object count exceeds the bound")
mask_payload = b""
mask_shape: list[int] | None = None
if segmentation_mask is not None:
mask = np.asarray(segmentation_mask, dtype=np.uint8)
if mask.shape != (600, 800):
raise ValueError("live perception segmentation shape is invalid")
mask_payload = zlib.compress(mask.tobytes(order="C"), level=1)
mask_shape = [600, 800]
payload = bytes(image_jpeg) + mask_payload
if len(payload) > LIVE_RESULT_MAX_PAYLOAD_BYTES:
raise ValueError("live perception result payload exceeds the bound")
header = {
"schema_version": LIVE_RESULT_WIRE_SCHEMA,
"frame_index": frame_index,
"source_frame_index": source_frame_index,
"session_seconds": session_seconds,
"captured_at_epoch_ns": captured_at_epoch_ns,
"image": {"codec": "jpeg", "byte_length": len(image_jpeg)},
"segmentation": (
None
if mask_shape is None
else {
"codec": "zlib-uint8-c1",
"shape": mask_shape,
"byte_length": len(mask_payload),
}
),
"objects": normalized_objects,
"delivery": dict(delivery),
"payload_bytes": len(payload),
"payload_sha256": sha256(payload).hexdigest(),
"authority": "shadow-diagnostic-only",
"commands_enabled": False,
"navigation_or_safety_accepted": False,
}
encoded_header = json.dumps(
header,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
if len(encoded_header) > LIVE_RESULT_MAX_HEADER_BYTES:
raise ValueError("live perception result header exceeds the bound")
return LIVE_RESULT_MAGIC + struct.pack("!I", len(encoded_header)) + encoded_header + payload
def decode_live_perception_result(encoded: bytes) -> LivePerceptionResultFrame:
"""Validate and decode one result frame before it reaches the Rerun bridge."""
if len(encoded) < 10 or not encoded.startswith(LIVE_RESULT_MAGIC):
raise ValueError("live perception result frame is truncated")
header_length = struct.unpack("!I", encoded[4:8])[0]
if not 2 <= header_length <= LIVE_RESULT_MAX_HEADER_BYTES:
raise ValueError("live perception result header length is invalid")
boundary = 8 + header_length
if boundary > len(encoded):
raise ValueError("live perception result header is truncated")
try:
header = json.loads(encoded[8:boundary])
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise ValueError("live perception result header is invalid") from exc
payload = encoded[boundary:]
if (
not isinstance(header, dict)
or header.get("schema_version") != LIVE_RESULT_WIRE_SCHEMA
or header.get("authority") != "shadow-diagnostic-only"
or header.get("commands_enabled") is not False
or header.get("navigation_or_safety_accepted") is not False
or header.get("payload_bytes") != len(payload)
or len(payload) > LIVE_RESULT_MAX_PAYLOAD_BYTES
or header.get("payload_sha256") != sha256(payload).hexdigest()
):
raise ValueError("live perception result contract is invalid")
image = header.get("image")
segmentation = header.get("segmentation")
objects = header.get("objects")
delivery = header.get("delivery")
if (
not isinstance(image, dict)
or image.get("codec") != "jpeg"
or not isinstance(image.get("byte_length"), int)
or not isinstance(objects, list)
or len(objects) > 128
or not isinstance(delivery, dict)
):
raise ValueError("live perception result content descriptor is invalid")
image_length = image["byte_length"]
if not 4 <= image_length <= min(len(payload), 1024 * 1024):
raise ValueError("live perception result image length is invalid")
image_jpeg = payload[:image_length]
if not image_jpeg.startswith(b"\xff\xd8") or not image_jpeg.endswith(b"\xff\xd9"):
raise ValueError("live perception result JPEG is invalid")
mask: np.ndarray[Any, np.dtype[np.uint8]] | None = None
if segmentation is None:
if len(payload) != image_length:
raise ValueError("live perception result has an undescribed payload tail")
else:
if (
not isinstance(segmentation, dict)
or segmentation.get("codec") != "zlib-uint8-c1"
or segmentation.get("shape") != [600, 800]
or not isinstance(segmentation.get("byte_length"), int)
or segmentation["byte_length"] != len(payload) - image_length
):
raise ValueError("live perception segmentation descriptor is invalid")
try:
raw_mask = zlib.decompress(payload[image_length:])
except zlib.error as exc:
raise ValueError("live perception segmentation payload is invalid") from exc
if len(raw_mask) != 600 * 800:
raise ValueError("live perception segmentation byte length is invalid")
mask = np.frombuffer(raw_mask, dtype=np.uint8).reshape((600, 800)).copy()
normalized_objects = tuple(_normalize_live_result_object(value) for value in objects)
frame_index = header.get("frame_index")
source_frame_index = header.get("source_frame_index")
session_seconds = header.get("session_seconds")
captured_at_epoch_ns = header.get("captured_at_epoch_ns")
if (
not isinstance(frame_index, int)
or isinstance(frame_index, bool)
or frame_index < 0
or not isinstance(source_frame_index, int)
or isinstance(source_frame_index, bool)
or source_frame_index < 0
or not isinstance(captured_at_epoch_ns, int)
or isinstance(captured_at_epoch_ns, bool)
or captured_at_epoch_ns < 0
or not isinstance(session_seconds, (int, float))
or isinstance(session_seconds, bool)
or not math.isfinite(float(session_seconds))
or float(session_seconds) < 0
):
raise ValueError("live perception result time identity is invalid")
return LivePerceptionResultFrame(
frame_index=frame_index,
source_frame_index=source_frame_index,
session_seconds=float(session_seconds),
captured_at_epoch_ns=captured_at_epoch_ns,
image_jpeg=image_jpeg,
segmentation_mask=mask,
objects=normalized_objects,
delivery=dict(delivery),
)
def _normalize_live_result_object(value: Mapping[str, Any]) -> dict[str, Any]:
if not isinstance(value, Mapping):
raise ValueError("live perception result object is invalid")
track_id = value.get("track_id")
label = value.get("label")
score = value.get("score")
bbox = value.get("bbox_xyxy")
if (
not isinstance(track_id, int)
or isinstance(track_id, bool)
or track_id < 0
or not isinstance(label, str)
or not 1 <= len(label) <= 64
or not isinstance(score, (int, float))
or isinstance(score, bool)
or not math.isfinite(float(score))
or not isinstance(bbox, Sequence)
or isinstance(bbox, (str, bytes))
or len(bbox) != 4
):
raise ValueError("live perception result object identity is invalid")
bbox_values = [float(item) for item in bbox]
if not all(math.isfinite(item) for item in bbox_values):
raise ValueError("live perception result 2D box is invalid")
normalized: dict[str, Any] = {
"track_id": track_id,
"label": label,
"score": float(score),
"bbox_xyxy": bbox_values,
}
distance = value.get("distance_smoothed_m", value.get("distance_median_m"))
if distance is not None:
if (
not isinstance(distance, (int, float))
or isinstance(distance, bool)
or not math.isfinite(float(distance))
or float(distance) < 0
):
raise ValueError("live perception result distance is invalid")
normalized["distance_m"] = float(distance)
else:
normalized["distance_m"] = None
cuboid_fields = (
("cuboid_center_map", 3),
("cuboid_half_size", 3),
("cuboid_quaternion_xyzw", 4),
)
present = [value.get(name) is not None for name, _ in cuboid_fields]
if any(present) and not all(present):
raise ValueError("live perception result cuboid is incomplete")
for name, length in cuboid_fields:
candidate = value.get(name)
if candidate is None:
normalized[name] = None
continue
if (
not isinstance(candidate, Sequence)
or isinstance(candidate, (str, bytes))
or len(candidate) != length
):
raise ValueError("live perception result cuboid geometry is invalid")
values = [float(item) for item in candidate]
if not all(math.isfinite(item) for item in values):
raise ValueError("live perception result cuboid contains non-finite values")
normalized[name] = values
return normalized
@dataclass(frozen=True, slots=True)
class LiveIngressEvent:
"""One raw-first, derived-only event admitted to the shadow transport."""
ingress_sequence: int
session_id: str
modality: LiveIngressModality
source_id: str
source_sequence: int
captured_at_epoch_ns: int
received_monotonic_ns: int
payload: bytes
def wire_bytes(self) -> bytes:
header = json.dumps(
{
"schema_version": LIVE_INGRESS_WIRE_SCHEMA,
"ingress_sequence": self.ingress_sequence,
"session_id": self.session_id,
"modality": self.modality,
"source_id": self.source_id,
"source_sequence": self.source_sequence,
"captured_at_epoch_ns": self.captured_at_epoch_ns,
"received_monotonic_ns": self.received_monotonic_ns,
"payload_bytes": len(self.payload),
"payload_sha256": sha256(self.payload).hexdigest(),
"authority": "shadow-diagnostic-only",
"commands_enabled": False,
"navigation_or_safety_accepted": False,
},
ensure_ascii=True,
separators=(",", ":"),
sort_keys=True,
).encode("utf-8")
return struct.pack("!I", len(header)) + header + self.payload
@dataclass(frozen=True, slots=True)
class LiveIngressQueueSnapshot:
capacity: int
depth: int
maximum_depth: int
published: int
consumed: int
dropped_overflow: int
rejected_oversize: int
@dataclass(slots=True)
class _LiveIngressQueue:
capacity: int
items: deque[LiveIngressEvent]
maximum_depth: int = 0
published: int = 0
consumed: int = 0
dropped_overflow: int = 0
rejected_oversize: int = 0
class LivePerceptionIngress:
"""Exclusive, bounded fan-out from committed K1 evidence to one AI worker.
The ingress is deliberately not an acquisition source and has no command
surface. Camera and MQTT producers call it only after their raw evidence
commit has completed. Separate modality queues prevent camera bursts from
evicting pose or LiDAR observations.
"""
_CAPACITIES: Final[dict[LiveIngressModality, int]] = {
"control": 4,
"camera-init": 1,
"camera-frame": 2,
"lidar": 2,
"pose": 16,
}
_MAX_PAYLOAD_BYTES: Final[dict[LiveIngressModality, int]] = {
"control": 16 * 1024,
"camera-init": 1024 * 1024,
"camera-frame": 1024 * 1024,
"lidar": 2 * 1024 * 1024,
"pose": 2 * 1024 * 1024,
}
def __init__(self) -> None:
self._condition = threading.Condition()
self._queues = {
modality: _LiveIngressQueue(capacity, deque())
for modality, capacity in self._CAPACITIES.items()
}
self._ingress_sequence = 0
self._session_id: str | None = None
self._active = False
self._closed = False
self._consumer_id: str | None = None
def begin_session(self, session_id: str) -> None:
if not session_id or len(session_id) > 160:
raise ValueError("live perception session id is invalid")
with self._condition:
if self._closed:
raise RuntimeError("live perception ingress is closed")
if self._active:
if self._session_id == session_id:
return
raise RuntimeError("another live perception session is active")
self._session_id = session_id
self._active = True
self._publish_locked(
modality="control",
source_id="mission-core",
source_sequence=0,
captured_at_epoch_ns=time.time_ns(),
received_monotonic_ns=time.monotonic_ns(),
payload=b'{"event":"session-start"}',
)
def end_session(self, session_id: str) -> None:
with self._condition:
if not self._active or self._session_id != session_id:
return
self._publish_locked(
modality="control",
source_id="mission-core",
source_sequence=0,
captured_at_epoch_ns=time.time_ns(),
received_monotonic_ns=time.monotonic_ns(),
payload=b'{"event":"session-end"}',
)
self._active = False
def publish(
self,
*,
modality: LiveIngressModality,
source_id: str,
source_sequence: int,
captured_at_epoch_ns: int,
received_monotonic_ns: int,
payload: bytes,
) -> bool:
if modality == "control":
raise ValueError("control events are owned by the ingress lifecycle")
if not source_id or source_sequence < 0:
raise ValueError("live perception source identity is invalid")
if captured_at_epoch_ns < 0 or received_monotonic_ns < 0:
raise ValueError("live perception timestamps must be non-negative")
with self._condition:
if self._closed or not self._active:
return False
return self._publish_locked(
modality=modality,
source_id=source_id,
source_sequence=source_sequence,
captured_at_epoch_ns=captured_at_epoch_ns,
received_monotonic_ns=received_monotonic_ns,
payload=payload,
)
def open_consumer(self, consumer_id: str) -> None:
if not consumer_id or len(consumer_id) > 128:
raise ValueError("live perception consumer id is invalid")
with self._condition:
if self._closed:
raise RuntimeError("live perception ingress is closed")
if self._consumer_id is not None and self._consumer_id != consumer_id:
raise RuntimeError("live perception ingress already has a consumer")
self._consumer_id = consumer_id
def close_consumer(self, consumer_id: str) -> None:
with self._condition:
if self._consumer_id == consumer_id:
self._consumer_id = None
self._condition.notify_all()
def take_next(
self,
consumer_id: str,
*,
timeout: float | None = None,
) -> LiveIngressEvent | None:
with self._condition:
if self._consumer_id != consumer_id:
raise RuntimeError("live perception consumer lease is not active")
ready = self._condition.wait_for(
lambda: any(queue.items for queue in self._queues.values()) or self._closed,
timeout=timeout,
)
if not ready:
return None
candidates = [
(queue.items[0].ingress_sequence, modality, queue)
for modality, queue in self._queues.items()
if queue.items
]
if not candidates:
return None
_, _, selected = min(candidates, key=lambda item: item[0])
selected.consumed += 1
return selected.items.popleft()
def close(self) -> None:
with self._condition:
self._closed = True
self._active = False
self._condition.notify_all()
def snapshot(self) -> dict[str, Any]:
with self._condition:
return {
"schema_version": LIVE_INGRESS_SCHEMA,
"mode": "shadow-diagnostic-only",
"active": self._active,
"session_id": self._session_id,
"consumer_connected": self._consumer_id is not None,
"commands_enabled": False,
"navigation_or_safety_accepted": False,
"closed": self._closed,
"queues": {
modality: {
"capacity": queue.capacity,
"depth": len(queue.items),
"maximum_depth": queue.maximum_depth,
"published": queue.published,
"consumed": queue.consumed,
"dropped_overflow": queue.dropped_overflow,
"rejected_oversize": queue.rejected_oversize,
}
for modality, queue in self._queues.items()
},
}
def _publish_locked(
self,
*,
modality: LiveIngressModality,
source_id: str,
source_sequence: int,
captured_at_epoch_ns: int,
received_monotonic_ns: int,
payload: bytes,
) -> bool:
queue = self._queues[modality]
if len(payload) > self._MAX_PAYLOAD_BYTES[modality]:
queue.rejected_oversize += 1
return False
session_id = self._session_id
if session_id is None:
return False
self._ingress_sequence += 1
event = LiveIngressEvent(
ingress_sequence=self._ingress_sequence,
session_id=session_id,
modality=modality,
source_id=source_id,
source_sequence=source_sequence,
captured_at_epoch_ns=captured_at_epoch_ns,
received_monotonic_ns=received_monotonic_ns,
payload=bytes(payload),
)
if len(queue.items) == queue.capacity:
queue.items.popleft()
queue.dropped_overflow += 1
queue.items.append(event)
queue.published += 1
queue.maximum_depth = max(queue.maximum_depth, len(queue.items))
self._condition.notify_all()
return True
@dataclass(frozen=True, slots=True)
class QueueSnapshot:
capacity: int
depth: int
maximum_depth: int
published: int
consumed: int
dropped_overflow: int
dropped_superseded: int
closed: bool
@property
def dropped_total(self) -> int:
return self.dropped_overflow + self.dropped_superseded
class LatestWinsQueue[T]:
"""A bounded derived-data queue that never lets old preview work accumulate."""
def __init__(self, capacity: int) -> None:
if capacity < 1:
raise ValueError("latest-wins queue capacity must be positive")
self._capacity = capacity
self._items: deque[T] = deque()
self._condition = threading.Condition()
self._maximum_depth = 0
self._published = 0
self._consumed = 0
self._dropped_overflow = 0
self._dropped_superseded = 0
self._closed = False
def publish(self, item: T) -> None:
with self._condition:
if self._closed:
raise RuntimeError("cannot publish to a closed latest-wins queue")
if len(self._items) == self._capacity:
self._items.popleft()
self._dropped_overflow += 1
self._items.append(item)
self._published += 1
self._maximum_depth = max(self._maximum_depth, len(self._items))
self._condition.notify()
def take_next(self, timeout: float | None = None) -> T | None:
with self._condition:
ready = self._condition.wait_for(
lambda: bool(self._items) or self._closed,
timeout=timeout,
)
if not ready or not self._items:
return None
item = self._items.popleft()
self._consumed += 1
return item
def close(self) -> None:
with self._condition:
self._closed = True
self._condition.notify_all()
def snapshot(self) -> QueueSnapshot:
with self._condition:
return QueueSnapshot(
capacity=self._capacity,
depth=len(self._items),
maximum_depth=self._maximum_depth,
published=self._published,
consumed=self._consumed,
dropped_overflow=self._dropped_overflow,
dropped_superseded=self._dropped_superseded,
closed=self._closed,
)
@dataclass(frozen=True, slots=True)
class LiveSensorBinding:
"""One bounded camera→LiDAR→pose match for live diagnostic fusion."""
state: Literal[
"fused-ready",
"lidar-unavailable",
"lidar-camera-delta-exceeded",
"pose-unavailable",
"pose-point-delta-exceeded",
]
point_cloud: DecodedPointCloudView | None
pose: DecodedPoseView | None
lidar_camera_delta_ms: float | None
pose_point_delta_ms: float | None
class LiveSensorSynchronizer:
"""Keep a small arrival-time window and bind sensors without back-pressure.
The synchronizer deliberately uses the already-recorded host arrival clock
carried by the shadow wire contract. It does not claim hardware-clock
synchronization. A short wait budget lets a LiDAR or pose event that is
already in flight reach the receiver while keeping the detector/world-state
latency bounded.
"""
def __init__(
self,
*,
maximum_lidar_camera_delta_ms: float,
maximum_pose_point_delta_ms: float,
capacity_per_modality: int = 32,
retention_seconds: float = 3.0,
) -> None:
if (
maximum_lidar_camera_delta_ms <= 0
or maximum_pose_point_delta_ms <= 0
or capacity_per_modality < 2
or retention_seconds <= 0
):
raise ValueError("live sensor synchronizer bounds are invalid")
self._maximum_lidar_camera_delta_ns = round(
maximum_lidar_camera_delta_ms * 1_000_000
)
self._maximum_pose_point_delta_ns = round(maximum_pose_point_delta_ms * 1_000_000)
self._capacity = capacity_per_modality
self._retention_ns = round(retention_seconds * 1_000_000_000)
self._condition = threading.Condition()
self._points: deque[DecodedPointCloudView] = deque()
self._poses: deque[DecodedPoseView] = deque()
self._published_points = 0
self._published_poses = 0
self._evicted_points = 0
self._evicted_poses = 0
self._maximum_point_depth = 0
self._maximum_pose_depth = 0
def publish_point_cloud(self, value: DecodedPointCloudView) -> None:
with self._condition:
self._points.append(value)
self._published_points += 1
self._evicted_points += self._prune(self._points)
self._maximum_point_depth = max(self._maximum_point_depth, len(self._points))
self._condition.notify_all()
def publish_pose(self, value: DecodedPoseView) -> None:
with self._condition:
self._poses.append(value)
self._published_poses += 1
self._evicted_poses += self._prune(self._poses)
self._maximum_pose_depth = max(self._maximum_pose_depth, len(self._poses))
self._condition.notify_all()
def bind_camera(
self,
captured_at_epoch_ns: int,
*,
wait_seconds: float = 0.0,
) -> LiveSensorBinding:
if captured_at_epoch_ns < 0 or wait_seconds < 0 or not math.isfinite(wait_seconds):
raise ValueError("camera synchronization input is invalid")
deadline = time.monotonic() + wait_seconds
with self._condition:
while True:
binding = self._binding(captured_at_epoch_ns)
if binding.state == "fused-ready" or wait_seconds == 0:
return binding
remaining = deadline - time.monotonic()
if remaining <= 0:
return binding
self._condition.wait(timeout=remaining)
def snapshot(self) -> dict[str, int | float]:
with self._condition:
return {
"capacity_per_modality": self._capacity,
"retention_seconds": self._retention_ns / 1_000_000_000,
"point_depth": len(self._points),
"pose_depth": len(self._poses),
"maximum_point_depth": self._maximum_point_depth,
"maximum_pose_depth": self._maximum_pose_depth,
"published_points": self._published_points,
"published_poses": self._published_poses,
"evicted_points": self._evicted_points,
"evicted_poses": self._evicted_poses,
}
def _binding(self, captured_at_epoch_ns: int) -> LiveSensorBinding:
if not self._points:
return LiveSensorBinding("lidar-unavailable", None, None, None, None)
point = min(
self._points,
key=lambda value: abs(value.context.captured_at_epoch_ns - captured_at_epoch_ns),
)
lidar_delta_ns = point.context.captured_at_epoch_ns - captured_at_epoch_ns
lidar_delta_ms = lidar_delta_ns / 1_000_000
if abs(lidar_delta_ns) > self._maximum_lidar_camera_delta_ns:
return LiveSensorBinding(
"lidar-camera-delta-exceeded",
point,
None,
lidar_delta_ms,
None,
)
if not self._poses:
return LiveSensorBinding(
"pose-unavailable",
point,
None,
lidar_delta_ms,
None,
)
pose = min(
self._poses,
key=lambda value: abs(
value.context.captured_at_epoch_ns - point.context.captured_at_epoch_ns
),
)
pose_delta_ns = pose.context.captured_at_epoch_ns - point.context.captured_at_epoch_ns
pose_delta_ms = pose_delta_ns / 1_000_000
if abs(pose_delta_ns) > self._maximum_pose_point_delta_ns:
return LiveSensorBinding(
"pose-point-delta-exceeded",
point,
pose,
lidar_delta_ms,
pose_delta_ms,
)
return LiveSensorBinding(
"fused-ready",
point,
pose,
lidar_delta_ms,
pose_delta_ms,
)
def _prune(self, values: deque[DecodedPointCloudView] | deque[DecodedPoseView]) -> int:
removed = 0
newest = values[-1].context.captured_at_epoch_ns
oldest_allowed = newest - self._retention_ns
while values and (
len(values) > self._capacity
or values[0].context.captured_at_epoch_ns < oldest_allowed
):
values.popleft()
removed += 1
return removed
def classify_health(
*,
source_available: bool,
fusion_state: str,
result_age_ms: float,
stale_after_ms: float,
unavailable_after_ms: float,
) -> tuple[HealthState, tuple[str, ...]]:
"""Classify freshness separately from whether depth was available."""
if stale_after_ms <= 0 or unavailable_after_ms <= stale_after_ms:
raise ValueError("health thresholds are invalid")
if not source_available or result_age_ms >= unavailable_after_ms:
return "unavailable", ("source-unavailable",)
if result_age_ms >= stale_after_ms:
return "stale", ("result-age-exceeded",)
if fusion_state != "fused":
return "degraded", (fusion_state,)
return "healthy", ()
class WorldStateProjector:
"""Project accepted E6 observations into a control-facing, timestamped state."""
def __init__(self, *, velocity_history_limit_s: float = 1.0) -> None:
if velocity_history_limit_s <= 0:
raise ValueError("velocity history limit must be positive")
self._velocity_history_limit_s = velocity_history_limit_s
self._track_history: dict[
int, deque[tuple[float, tuple[float, float, float]]]
] = {}
def project(
self,
*,
frame: Mapping[str, Any],
lidar_positions: Mapping[int, Sequence[float]],
clearance: Mapping[str, Any],
) -> dict[str, Any]:
session_seconds = float(frame["session_seconds"])
objects: list[dict[str, Any]] = []
for raw in frame.get("objects", []):
if not str(raw.get("cuboid_status", "")).startswith("accepted-"):
continue
track_id = int(raw["track_id"])
center_map = _vector3(raw["cuboid_center_map"], "cuboid center")
half_size = _vector3(raw["cuboid_half_size"], "cuboid half-size")
quaternion = _vector4(raw["cuboid_quaternion_xyzw"], "cuboid quaternion")
velocity, velocity_status, velocity_residual = self._velocity(
track_id, session_seconds, center_map
)
speed = None
if velocity is not None:
speed = math.sqrt(sum(component * component for component in velocity))
lidar = lidar_positions.get(track_id)
objects.append(
{
"track_id": track_id,
"class": str(raw["association_group"]),
"detector_label": str(raw["label"]),
"confidence": float(raw["score"]),
"position_map_m": list(center_map),
"position_lidar_m": (
None if lidar is None else [float(value) for value in lidar]
),
"orientation_map_xyzw": list(quaternion),
"size_m": [2.0 * value for value in half_size],
"range_m": float(raw["distance_smoothed_m"]),
"velocity_map_mps": None if velocity is None else list(velocity),
"speed_mps": speed,
"velocity_status": velocity_status,
"velocity_residual_m": velocity_residual,
"support_points": int(raw["clustered_points"]),
"geometry": "point-supported-visible-surface-envelope",
}
)
self._prune(session_seconds)
return {
"schema_version": WORLD_STATE_SCHEMA,
"frame_index": int(frame["frame_index"]),
"source_frame_index": int(frame["source_frame_index"]),
"session_seconds": session_seconds,
"coordinate_frames": {
"world": "k1-map",
"sensor_relative": "k1-lidar",
"vehicle_body": "unavailable-no-rig-to-vehicle-transform",
},
"fusion_state": str(frame["state"]),
"objects": objects,
"object_count": len(objects),
"clearance": dict(clearance),
}
def _velocity(
self,
track_id: int,
session_seconds: float,
center_map: tuple[float, float, float],
) -> tuple[tuple[float, float, float] | None, str, float | None]:
history = self._track_history.setdefault(track_id, deque(maxlen=32))
if history and session_seconds <= history[-1][0]:
return None, "unavailable-nonmonotonic-time", None
history.append((session_seconds, center_map))
oldest = session_seconds - self._velocity_history_limit_s
while history and history[0][0] < oldest:
history.popleft()
if len(history) < 4 or history[-1][0] - history[0][0] < 0.4:
return None, "unavailable-insufficient-history", None
slopes: list[tuple[float, float, float]] = []
values = list(history)
for left, (left_time, left_center) in enumerate(values):
for right_time, right_center in values[left + 1 :]:
delta = right_time - left_time
if delta < 0.2:
continue
slopes.append(
(
(right_center[0] - left_center[0]) / delta,
(right_center[1] - left_center[1]) / delta,
(right_center[2] - left_center[2]) / delta,
)
)
if not slopes:
return None, "unavailable-insufficient-baseline", None
velocity = (
statistics.median(item[0] for item in slopes),
statistics.median(item[1] for item in slopes),
statistics.median(item[2] for item in slopes),
)
speed = math.sqrt(sum(component * component for component in velocity))
latest_time, latest_center = values[-1]
residuals = []
for observed_time, observed_center in values:
predicted = tuple(
latest - component * (latest_time - observed_time)
for latest, component in zip(latest_center, velocity, strict=True)
)
residuals.append(
math.sqrt(
sum(
(observed - expected) ** 2
for observed, expected in zip(
observed_center, predicted, strict=True
)
)
)
)
residual = statistics.median(residuals)
if speed > 20.0:
return None, "rejected-speed-bound", residual
if residual > 0.75:
return None, "rejected-position-residual", residual
return velocity, "diagnostic-robust-history", residual
def _prune(self, session_seconds: float) -> None:
oldest = session_seconds - self._velocity_history_limit_s
expired = [
track_id
for track_id, history in self._track_history.items()
if not history or history[-1][0] < oldest
]
for track_id in expired:
del self._track_history[track_id]
def wait_until(deadline: float) -> float:
"""Wait for a replay deadline and return non-negative scheduling lag seconds."""
remaining = deadline - time.perf_counter()
if remaining > 0:
time.sleep(remaining)
return max(0.0, time.perf_counter() - deadline)
def _vector3(value: Sequence[Any], label: str) -> tuple[float, float, float]:
if len(value) != 3:
raise ValueError(f"{label} must contain three values")
return float(value[0]), float(value[1]), float(value[2])
def _vector4(value: Sequence[Any], label: str) -> tuple[float, float, float, float]:
if len(value) != 4:
raise ValueError(f"{label} must contain four values")
return float(value[0]), float(value[1]), float(value[2]), float(value[3])
@@ -0,0 +1,368 @@
"""Validation for immutable LAB E7 replay-as-live qualification results."""
from __future__ import annotations
import hashlib
import json
import math
import os
import re
import stat
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from k1link.sessions import SessionIntegrityError
from .live_perception import TELEMETRY_SCHEMA, WORLD_STATE_SCHEMA
from .tracked_fusion_qualification import (
TrackedFusionQualificationResult,
validate_tracked_fusion_qualification_result,
)
RESULT_SCHEMA = "missioncore.e7-live-replay-result/v1"
IDENTITY_SCHEMA = "missioncore.e7-live-replay-identity/v1"
REPORT_SCHEMA = "missioncore.e7-live-replay-run-report/v1"
MAX_JSON_BYTES = 64 * 1024 * 1024
MAX_LINE_BYTES = 2 * 1024 * 1024
_SAFE_RESULT_ID = re.compile(r"^e7-live-replay-[a-f0-9]{64}$")
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
@dataclass(frozen=True, slots=True)
class LiveReplayQualificationResult:
result_id: str
result_root: Path
upstream: TrackedFusionQualificationResult
frame_count: int
accepted: bool
artifacts: tuple[dict[str, Any], ...]
def validate_live_replay_qualification_result(
job_root: Path,
tracking_root: Path,
semantic_root: Path,
e6_root: Path,
result_root: Path,
) -> LiveReplayQualificationResult:
upstream = validate_tracked_fusion_qualification_result(
job_root,
tracking_root,
semantic_root,
e6_root,
)
root = result_root.expanduser().resolve(strict=True)
if not root.is_dir() or _SAFE_RESULT_ID.fullmatch(root.name) is None:
raise SessionIntegrityError("live replay result root is invalid")
result = _read_object(root / "result.json", root)
identity = result.get("identity")
identity_sha256 = result.get("identity_sha256")
if (
result.get("schema_version") != RESULT_SCHEMA
or result.get("result_id") != root.name
or not isinstance(identity, dict)
or identity.get("schema_version") != IDENTITY_SCHEMA
or not isinstance(identity_sha256, str)
or _SHA256.fullmatch(identity_sha256) is None
or root.name != f"e7-live-replay-{identity_sha256}"
or hashlib.sha256(_canonical_json(identity)).hexdigest() != identity_sha256
or identity.get("e6_result_id") != upstream.result_id
or identity.get("job_id") != upstream.job.job_id
or identity.get("input_sha256") != upstream.job.input_sha256
or identity.get("session_id") != upstream.job.session_id
or identity.get("source_id") != upstream.job.source_id
or identity.get("calibration_sha256") != upstream.semantic.calibration_sha256
or identity.get("camera_slot") != upstream.semantic.calibration_slot
or not _valid_sha256(identity.get("producer_sha256"))
or not _valid_sha256(identity.get("runtime_contract_sha256"))
or not _valid_sha256(identity.get("result_validator_sha256"))
or not _valid_sha256(identity.get("e6_adapter_sha256"))
or result.get("ground_truth") is not False
or result.get("publication_scope") != "replay-qualification-only"
or result.get("acceptance_state") not in {"accepted", "rejected"}
):
raise SessionIntegrityError("live replay identity is inconsistent")
selection = identity.get("selection")
configuration = identity.get("configuration")
if not isinstance(selection, dict) or not isinstance(configuration, dict):
raise SessionIntegrityError("live replay configuration is missing")
start = selection.get("start_frame_index")
count = selection.get("frame_count")
if (
not isinstance(start, int)
or not isinstance(count, int)
or start < 0
or count < 1
or start + count > upstream.frame_count
or configuration.get("selection") != selection
):
raise SessionIntegrityError("live replay selection is invalid")
metrics = result.get("metrics")
if not isinstance(metrics, dict):
raise SessionIntegrityError("live replay metrics are invalid")
processed = metrics.get("frames_processed")
dropped = metrics.get("frames_dropped")
queue = metrics.get("queue")
health = metrics.get("health_counts")
if (
metrics.get("frames_attempted") != count
or not isinstance(processed, int)
or not isinstance(dropped, int)
or processed < 1
or dropped < 0
or processed + dropped != count
or not isinstance(queue, dict)
or queue.get("published") != count
or queue.get("consumed") != processed
or queue.get("final_depth") != 0
or queue.get("dropped_overflow", 0) + queue.get("dropped_superseded", 0)
!= dropped
or not isinstance(health, dict)
or sum(health.values()) != processed
):
raise SessionIntegrityError("live replay accounting is inconsistent")
artifacts = _validate_artifacts(root, result.get("artifacts"))
world_indices = _validate_world_rows(
_artifact_path(artifacts, "live-world-state", root),
root,
expected_count=processed,
selection_start=start,
selection_count=count,
)
_validate_telemetry_rows(
_artifact_path(artifacts, "live-telemetry", root),
root,
expected_indices=world_indices,
)
report = _read_object(_artifact_path(artifacts, "live-run-report", root), root)
acceptance = report.get("acceptance")
if (
report.get("schema_version") != REPORT_SCHEMA
or report.get("result_id") != root.name
or report.get("identity") != identity
or report.get("metrics") != metrics
or not isinstance(acceptance, dict)
or not isinstance(acceptance.get("accepted"), bool)
or acceptance.get("navigation_or_safety_accepted") is not False
or (result.get("acceptance_state") == "accepted")
!= acceptance.get("accepted")
):
raise SessionIntegrityError("live replay report is inconsistent")
rrd = _artifact_path(artifacts, "live-rerun", root)
with rrd.open("rb") as stream:
if stream.read(4) != b"RRF2":
raise SessionIntegrityError("live replay Rerun stream is invalid")
return LiveReplayQualificationResult(
result_id=root.name,
result_root=root,
upstream=upstream,
frame_count=processed,
accepted=bool(acceptance["accepted"]),
artifacts=artifacts,
)
def _validate_artifacts(root: Path, raw: object) -> tuple[dict[str, Any], ...]:
expected = {
"live-world-state": (
"world-state.jsonl",
"application/x-ndjson",
WORLD_STATE_SCHEMA,
),
"live-telemetry": (
"telemetry.jsonl",
"application/x-ndjson",
TELEMETRY_SCHEMA,
),
"live-rerun": ("world-state.rrd", "application/vnd.rerun.rrd", None),
"live-run-report": ("run-report.json", "application/json", REPORT_SCHEMA),
}
if not isinstance(raw, list) or len(raw) != len(expected):
raise SessionIntegrityError("live replay artifact set is invalid")
artifacts: list[dict[str, Any]] = []
seen: set[str] = set()
for value in raw:
if not isinstance(value, dict):
raise SessionIntegrityError("live replay artifact descriptor is invalid")
kind = value.get("kind")
if not isinstance(kind, str) or kind in seen or kind not in expected:
raise SessionIntegrityError("live replay artifact kind is invalid")
seen.add(kind)
name, media_type, schema = expected[kind]
path = root / name
metadata = _confined_regular_file(path, root)
if (
value.get("path") != name
or value.get("media_type") != media_type
or value.get("schema_version") != schema
or value.get("byte_length") != metadata.st_size
or not isinstance(value.get("sha256"), str)
or _SHA256.fullmatch(str(value["sha256"])) is None
or _sha256(path) != value["sha256"]
):
raise SessionIntegrityError("live replay artifact identity changed")
artifacts.append(value)
return tuple(artifacts)
def _validate_world_rows(
path: Path,
root: Path,
*,
expected_count: int,
selection_start: int,
selection_count: int,
) -> tuple[int, ...]:
indices: list[int] = []
previous_time: float | None = None
_confined_regular_file(path, root)
with path.open(encoding="utf-8") as stream:
for line in stream:
if len(line.encode()) > MAX_LINE_BYTES:
raise SessionIntegrityError("live replay world-state line is oversized")
value = json.loads(line)
frame_index = value.get("frame_index") if isinstance(value, dict) else None
session_seconds = value.get("session_seconds") if isinstance(value, dict) else None
objects = value.get("objects") if isinstance(value, dict) else None
delivery = value.get("delivery") if isinstance(value, dict) else None
coordinate_frames = (
value.get("coordinate_frames") if isinstance(value, dict) else None
)
if (
not isinstance(value, dict)
or value.get("schema_version") != WORLD_STATE_SCHEMA
or not isinstance(frame_index, int)
or not selection_start <= frame_index < selection_start + selection_count
or (indices and frame_index <= indices[-1])
or not isinstance(session_seconds, int | float)
or (previous_time is not None and float(session_seconds) <= previous_time)
or not isinstance(objects, list)
or value.get("object_count") != len(objects)
or not isinstance(delivery, dict)
or delivery.get("health")
not in {"healthy", "degraded", "stale", "unavailable"}
or not isinstance(delivery.get("result_age_ms"), int | float)
or not isinstance(coordinate_frames, dict)
or coordinate_frames.get("sensor_relative") != "k1-lidar"
):
raise SessionIntegrityError("live replay world-state row is invalid")
for item in objects:
if not _valid_object(item):
raise SessionIntegrityError("live replay world object is invalid")
indices.append(frame_index)
previous_time = float(session_seconds)
if len(indices) != expected_count:
raise SessionIntegrityError("live replay world-state count changed")
return tuple(indices)
def _valid_object(value: object) -> bool:
if not isinstance(value, dict):
return False
position_map = value.get("position_map_m")
position_lidar = value.get("position_lidar_m")
size = value.get("size_m")
velocity = value.get("velocity_map_mps")
return (
isinstance(value.get("track_id"), int)
and isinstance(value.get("range_m"), int | float)
and float(value["range_m"]) >= 0
and _finite_vector(position_map, 3)
and (position_lidar is None or _finite_vector(position_lidar, 3))
and _finite_vector(size, 3)
and (velocity is None or _finite_vector(velocity, 3))
and isinstance(value.get("velocity_status"), str)
and value.get("geometry") == "point-supported-visible-surface-envelope"
)
def _finite_vector(value: object, length: int) -> bool:
return (
isinstance(value, list)
and len(value) == length
and all(isinstance(item, int | float) and math.isfinite(float(item)) for item in value)
)
def _valid_sha256(value: object) -> bool:
return isinstance(value, str) and _SHA256.fullmatch(value) is not None
def _validate_telemetry_rows(
path: Path,
root: Path,
*,
expected_indices: tuple[int, ...],
) -> None:
indices: list[int] = []
_confined_regular_file(path, root)
with path.open(encoding="utf-8") as stream:
for line in stream:
if len(line.encode()) > MAX_LINE_BYTES:
raise SessionIntegrityError("live replay telemetry line is oversized")
value = json.loads(line)
if (
not isinstance(value, dict)
or value.get("schema_version") != TELEMETRY_SCHEMA
or not isinstance(value.get("frame_index"), int)
or not isinstance(value.get("end_to_end_latency_ms"), int | float)
or float(value["end_to_end_latency_ms"]) < 0
or value.get("health")
not in {"healthy", "degraded", "stale", "unavailable"}
):
raise SessionIntegrityError("live replay telemetry row is invalid")
indices.append(int(value["frame_index"]))
if tuple(indices) != expected_indices:
raise SessionIntegrityError("live replay telemetry binding changed")
def _artifact_path(
artifacts: tuple[dict[str, Any], ...], kind: str, root: Path
) -> Path:
matches = [value for value in artifacts if value.get("kind") == kind]
if len(matches) != 1 or not isinstance(matches[0].get("path"), str):
raise SessionIntegrityError("live replay artifact lookup is invalid")
return root / str(matches[0]["path"])
def _read_object(path: Path, root: Path) -> dict[str, Any]:
metadata = _confined_regular_file(path, root)
if metadata.st_size > MAX_JSON_BYTES:
raise SessionIntegrityError("live replay JSON is oversized")
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SessionIntegrityError("live replay JSON is unreadable") from exc
if not isinstance(value, dict):
raise SessionIntegrityError("live replay JSON root must be an object")
return value
def _confined_regular_file(path: Path, root: Path) -> os.stat_result:
resolved = path.resolve(strict=True)
if not resolved.is_relative_to(root) or not resolved.is_file():
raise SessionIntegrityError("live replay artifact escapes its result root")
metadata = resolved.stat()
if stat.S_ISLNK(path.lstat().st_mode) or not stat.S_ISREG(metadata.st_mode):
raise SessionIntegrityError("live replay artifact must be a regular file")
return metadata
def _canonical_json(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode()
def _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()
@@ -0,0 +1,534 @@
"""Validation for immutable LAB E9 multirate perception results."""
from __future__ import annotations
import hashlib
import json
import math
import os
import re
import stat
from dataclasses import dataclass
from pathlib import Path
from typing import Any, TypeGuard
from k1link.sessions import SessionIntegrityError
from .jobs import CameraComputeJob, validate_camera_compute_job
RESULT_SCHEMA = "missioncore.e9-multirate-perception-result/v1"
IDENTITY_SCHEMA = "missioncore.e9-multirate-perception-identity/v1"
REPORT_SCHEMA = "missioncore.e9-multirate-perception-report/v1"
PROFILE_SCHEMA = "missioncore.e9-multirate-perception-profile/v1"
DETECTOR_FRAME_SCHEMA = "missioncore.e9-multirate-detector-frame/v1"
SEMANTIC_FRAME_SCHEMA = "missioncore.e9-multirate-semantic-frame/v1"
MERGED_FRAME_SCHEMA = "missioncore.e9-multirate-merged-frame/v1"
MAX_JSON_BYTES = 64 * 1024 * 1024
MAX_LINE_BYTES = 2 * 1024 * 1024
MAX_SAFE_INTEGER = (1 << 53) - 1
_SAFE_RESULT_ID = re.compile(r"^e9-multirate-perception-[a-f0-9]{64}$")
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
@dataclass(frozen=True, slots=True)
class MultiratePerceptionArtifact:
kind: str
path: Path
media_type: str
byte_length: int
sha256: str
schema_version: str | None
@dataclass(frozen=True, slots=True)
class MultiratePerceptionQualificationResult:
result_id: str
result_root: Path
job: CameraComputeJob
expected_detector_frames: int
processed_detector_frames: int
processed_semantic_frames: int
accepted: bool
artifacts: tuple[MultiratePerceptionArtifact, ...]
def artifact(self, kind: str) -> MultiratePerceptionArtifact:
matches = tuple(item for item in self.artifacts if item.kind == kind)
if len(matches) != 1:
raise SessionIntegrityError(f"multirate artifact is unavailable: {kind}")
return matches[0]
def validate_multirate_perception_qualification_result(
job_root: Path,
result_root: Path,
) -> MultiratePerceptionQualificationResult:
job = validate_camera_compute_job(job_root)
root = result_root.expanduser().resolve(strict=True)
if not root.is_dir() or _SAFE_RESULT_ID.fullmatch(root.name) is None:
raise SessionIntegrityError("multirate result root is invalid")
result = _read_object(root / "result.json", root)
identity = result.get("identity")
identity_sha256 = result.get("identity_sha256")
if (
result.get("schema_version") != RESULT_SCHEMA
or result.get("result_id") != root.name
or not isinstance(identity, dict)
or identity.get("schema_version") != IDENTITY_SCHEMA
or not isinstance(identity_sha256, str)
or _SHA256.fullmatch(identity_sha256) is None
or root.name != f"e9-multirate-perception-{identity_sha256}"
or hashlib.sha256(_canonical_json(identity)).hexdigest() != identity_sha256
or identity.get("job_id") != job.job_id
or identity.get("input_sha256") != job.input_sha256
or identity.get("session_id") != job.session_id
or identity.get("source_id") != job.source_id
or result.get("ground_truth") is not False
or result.get("publication_scope") != "recorded-multirate-qualification-only"
or result.get("acceptance_state") not in {"accepted", "rejected"}
):
raise SessionIntegrityError("multirate identity is inconsistent")
selection = identity.get("selection")
configuration = identity.get("configuration")
if not isinstance(selection, dict) or not isinstance(configuration, dict):
raise SessionIntegrityError("multirate selection is unavailable")
expected = selection.get("frame_count")
source_start = selection.get("source_start_frame_index")
source_end = selection.get("source_end_frame_index")
timeline_start = selection.get("timeline_start_seconds")
timeline_end = selection.get("timeline_end_seconds")
profile = configuration.get("profile")
if (
not isinstance(expected, int)
or isinstance(expected, bool)
or expected < 2
or not isinstance(source_start, int)
or isinstance(source_start, bool)
or not isinstance(source_end, int)
or isinstance(source_end, bool)
or source_start < 0
or source_end - source_start + 1 != expected
or source_end >= job.segment_count
or not _finite_number(timeline_start)
or not _finite_number(timeline_end)
or float(timeline_start) < job.timeline_start_seconds
or float(timeline_end) > job.timeline_end_seconds
or float(timeline_end) <= float(timeline_start)
or not isinstance(profile, dict)
or profile.get("schema_version") != PROFILE_SCHEMA
or not isinstance(profile.get("replay"), dict)
):
raise SessionIntegrityError("multirate selection is invalid")
replay = profile["replay"]
stride = replay.get("semantic_sample_every_frames")
if (
not isinstance(stride, int)
or isinstance(stride, bool)
or stride < 2
or replay.get("detector_queue_capacity") not in range(1, 9)
or replay.get("semantic_queue_capacity") not in range(1, 5)
):
raise SessionIntegrityError("multirate scheduling profile is invalid")
artifacts = _validate_artifacts(root, result.get("artifacts"))
report = _read_object(_artifact(artifacts, "multirate-run-report").path, root)
metrics = report.get("metrics")
acceptance = report.get("acceptance")
if (
report.get("schema_version") != REPORT_SCHEMA
or report.get("result_id") != root.name
or report.get("identity") != identity
or report.get("ground_truth") is not False
or report.get("state") != result.get("acceptance_state")
or not isinstance(metrics, dict)
or not isinstance(acceptance, dict)
or not isinstance(acceptance.get("accepted"), bool)
or acceptance.get("navigation_or_safety_accepted") is not False
or (result.get("acceptance_state") == "accepted") != acceptance["accepted"]
):
raise SessionIntegrityError("multirate report is inconsistent")
detector = metrics.get("detector")
semantic = metrics.get("semantic")
if not isinstance(detector, dict) or not isinstance(semantic, dict):
raise SessionIntegrityError("multirate metrics are unavailable")
detector_processed, detector_dropped = _validate_queue_metrics(
detector,
expected=expected,
capacity=int(replay["detector_queue_capacity"]),
expected_key="frames_expected",
)
semantic_scheduled = ((expected - 1) // stride) + 1
semantic_processed, _semantic_dropped = _validate_queue_metrics(
semantic,
expected=semantic_scheduled,
capacity=int(replay["semantic_queue_capacity"]),
expected_key="frames_scheduled",
)
if (
result.get("frames_processed") != detector_processed
or detector_processed + detector_dropped != expected
or not isinstance(semantic.get("binding_status_counts"), dict)
or sum(semantic["binding_status_counts"].values()) != detector_processed
):
raise SessionIntegrityError("multirate result accounting is inconsistent")
detector_indices = _validate_detector_rows(
_artifact(artifacts, "multirate-detector-frames").path,
root,
expected_count=detector_processed,
selection_count=expected,
source_start=source_start,
timeline_start=float(timeline_start),
timeline_end=float(timeline_end),
)
semantic_indices = _validate_semantic_rows(
_artifact(artifacts, "multirate-semantic-frames").path,
root,
expected_count=semantic_processed,
selection_count=expected,
source_start=source_start,
stride=stride,
timeline_start=float(timeline_start),
timeline_end=float(timeline_end),
)
_validate_merged_rows(
_artifact(artifacts, "multirate-merged-frames").path,
root,
expected_indices=detector_indices,
source_start=source_start,
semantic_indices=set(semantic_indices),
)
return MultiratePerceptionQualificationResult(
result_id=root.name,
result_root=root,
job=job,
expected_detector_frames=expected,
processed_detector_frames=detector_processed,
processed_semantic_frames=semantic_processed,
accepted=bool(acceptance["accepted"]),
artifacts=artifacts,
)
def _validate_queue_metrics(
metrics: dict[str, Any],
*,
expected: int,
capacity: int,
expected_key: str,
) -> tuple[int, int]:
processed = metrics.get("frames_processed")
dropped = metrics.get("frames_dropped")
queue = metrics.get("queue")
if (
metrics.get(expected_key) != expected
or not isinstance(processed, int)
or isinstance(processed, bool)
or not 1 <= processed <= expected
or not isinstance(dropped, int)
or isinstance(dropped, bool)
or dropped < 0
or processed + dropped != expected
or not isinstance(queue, dict)
or queue.get("capacity") != capacity
or queue.get("published") != expected
or queue.get("consumed") != processed
or queue.get("dropped_overflow") != dropped
or queue.get("final_depth") != 0
or queue.get("closed") is not True
or not isinstance(queue.get("maximum_depth"), int)
or not 0 <= queue["maximum_depth"] <= capacity
):
raise SessionIntegrityError("multirate queue accounting is inconsistent")
return processed, dropped
def _validate_detector_rows(
path: Path,
root: Path,
*,
expected_count: int,
selection_count: int,
source_start: int,
timeline_start: float,
timeline_end: float,
) -> tuple[int, ...]:
rows = _read_jsonl(path, root)
indices: list[int] = []
previous_time = -math.inf
for value in rows:
index = value.get("frame_index")
session_seconds = value.get("session_seconds")
if (
value.get("schema_version") != DETECTOR_FRAME_SCHEMA
or not isinstance(index, int)
or isinstance(index, bool)
or not 0 <= index < selection_count
or (indices and index <= indices[-1])
or value.get("source_frame_index") != source_start + index
or not _finite_number(session_seconds)
or not timeline_start - 0.001 <= float(session_seconds) <= timeline_end + 0.001
or float(session_seconds) <= previous_time
or not _finite_number(value.get("result_age_ms"))
or float(value["result_age_ms"]) < 0
or not isinstance(value.get("detections"), list)
or not isinstance(value.get("tracks"), list)
):
raise SessionIntegrityError("multirate detector row is invalid")
indices.append(index)
previous_time = float(session_seconds)
if len(indices) != expected_count:
raise SessionIntegrityError("multirate detector row count changed")
return tuple(indices)
def _validate_semantic_rows(
path: Path,
root: Path,
*,
expected_count: int,
selection_count: int,
source_start: int,
stride: int,
timeline_start: float,
timeline_end: float,
) -> tuple[int, ...]:
rows = _read_jsonl(path, root)
indices: list[int] = []
previous_time = -math.inf
for value in rows:
index = value.get("frame_index")
session_seconds = value.get("session_seconds")
class_pixels = value.get("class_pixels")
fractions = value.get("class_fractions")
if (
value.get("schema_version") != SEMANTIC_FRAME_SCHEMA
or not isinstance(index, int)
or isinstance(index, bool)
or not 0 <= index < selection_count
or index % stride != 0
or (indices and index <= indices[-1])
or value.get("source_frame_index") != source_start + index
or not _finite_number(session_seconds)
or not timeline_start - 0.001 <= float(session_seconds) <= timeline_end + 0.001
or float(session_seconds) <= previous_time
or not _finite_number(value.get("completion_age_ms"))
or float(value["completion_age_ms"]) < 0
or not isinstance(value.get("mask_sha256"), str)
or _SHA256.fullmatch(value["mask_sha256"]) is None
or not isinstance(class_pixels, dict)
or not isinstance(fractions, dict)
or set(class_pixels) != set(fractions)
or any(not isinstance(count, int) or count < 0 for count in class_pixels.values())
or any(
not _finite_number(fraction) or not 0 <= float(fraction) <= 1
for fraction in fractions.values()
)
):
raise SessionIntegrityError("multirate semantic row is invalid")
indices.append(index)
previous_time = float(session_seconds)
if len(indices) != expected_count:
raise SessionIntegrityError("multirate semantic row count changed")
return tuple(indices)
def _validate_merged_rows(
path: Path,
root: Path,
*,
expected_indices: tuple[int, ...],
source_start: int,
semantic_indices: set[int],
) -> None:
rows = _read_jsonl(path, root)
indices: list[int] = []
for value in rows:
index = value.get("frame_index")
semantic = value.get("semantic")
if (
value.get("schema_version") != MERGED_FRAME_SCHEMA
or not isinstance(index, int)
or value.get("source_frame_index") != source_start + index
or not isinstance(value.get("tracks"), list)
or not _finite_number(value.get("detector_result_age_ms"))
or not isinstance(semantic, dict)
or semantic.get("status") not in {"fresh", "stale", "unavailable"}
):
raise SessionIntegrityError("multirate merged row is invalid")
semantic_source = semantic.get("source_frame_index")
if semantic["status"] == "unavailable":
if any(
semantic.get(key) is not None
for key in (
"source_frame_index",
"source_age_ms",
"completion_age_ms",
"mask_sha256",
)
):
raise SessionIntegrityError("unavailable semantic binding has invented data")
elif (
not isinstance(semantic_source, int)
or semantic_source > source_start + index
or semantic_source - source_start not in semantic_indices
or not _finite_number(semantic.get("source_age_ms"))
or float(semantic["source_age_ms"]) < 0
or not _finite_number(semantic.get("completion_age_ms"))
or not isinstance(semantic.get("mask_sha256"), str)
or _SHA256.fullmatch(semantic["mask_sha256"]) is None
):
raise SessionIntegrityError("semantic binding is inconsistent")
indices.append(index)
if tuple(indices) != expected_indices:
raise SessionIntegrityError("multirate merged binding changed")
def _validate_artifacts(
root: Path,
raw: object,
) -> tuple[MultiratePerceptionArtifact, ...]:
expected = {
"multirate-detector-frames": (
"detector-frames.jsonl",
"application/x-ndjson",
DETECTOR_FRAME_SCHEMA,
),
"multirate-semantic-frames": (
"semantic-frames.jsonl",
"application/x-ndjson",
SEMANTIC_FRAME_SCHEMA,
),
"multirate-merged-frames": (
"merged-frames.jsonl",
"application/x-ndjson",
MERGED_FRAME_SCHEMA,
),
"worker-gpu-telemetry": ("gpu-telemetry.jsonl", "application/x-ndjson", None),
"multirate-run-report": ("run-report.json", "application/json", REPORT_SCHEMA),
}
if not isinstance(raw, list) or len(raw) != len(expected):
raise SessionIntegrityError("multirate artifact set is invalid")
artifacts: list[MultiratePerceptionArtifact] = []
seen: set[str] = set()
for value in raw:
if not isinstance(value, dict):
raise SessionIntegrityError("multirate artifact descriptor is invalid")
kind = value.get("kind")
if not isinstance(kind, str) or kind in seen or kind not in expected:
raise SessionIntegrityError("multirate artifact kind is invalid")
seen.add(kind)
expected_path, expected_media_type, expected_schema = expected[kind]
path = root / expected_path
metadata = _confined_regular_file(path, root)
byte_length = value.get("byte_length")
digest = value.get("sha256")
if (
value.get("path") != expected_path
or value.get("media_type") != expected_media_type
or value.get("schema_version") != expected_schema
or not isinstance(byte_length, int)
or isinstance(byte_length, bool)
or not 0 < byte_length <= MAX_SAFE_INTEGER
or metadata.st_size != byte_length
or not isinstance(digest, str)
or _SHA256.fullmatch(digest) is None
or _sha256_file(path) != digest
):
raise SessionIntegrityError("multirate artifact identity changed")
artifacts.append(
MultiratePerceptionArtifact(
kind=kind,
path=path,
media_type=expected_media_type,
byte_length=byte_length,
sha256=digest,
schema_version=expected_schema,
)
)
return tuple(artifacts)
def _artifact(
artifacts: tuple[MultiratePerceptionArtifact, ...],
kind: str,
) -> MultiratePerceptionArtifact:
matches = tuple(item for item in artifacts if item.kind == kind)
if len(matches) != 1:
raise SessionIntegrityError(f"multirate artifact is unavailable: {kind}")
return matches[0]
def _read_jsonl(path: Path, root: Path) -> list[dict[str, Any]]:
_confined_regular_file(path, root)
rows: list[dict[str, Any]] = []
try:
with path.open("rb") as stream:
for line in stream:
if not line or len(line) > MAX_LINE_BYTES:
raise SessionIntegrityError("multirate JSONL line is outside bounds")
value = json.loads(line)
if not isinstance(value, dict):
raise SessionIntegrityError("multirate JSONL row is invalid")
rows.append(value)
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SessionIntegrityError("multirate JSONL is unavailable") from exc
return rows
def _read_object(path: Path, root: Path) -> dict[str, Any]:
metadata = _confined_regular_file(path, root)
if not 0 < metadata.st_size <= MAX_JSON_BYTES:
raise SessionIntegrityError("multirate JSON is outside bounds")
try:
value = json.loads(path.read_text(encoding="utf-8-sig"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SessionIntegrityError("multirate JSON is unavailable") from exc
if not isinstance(value, dict):
raise SessionIntegrityError("multirate JSON root is invalid")
return value
def _confined_regular_file(path: Path, root: Path) -> os.stat_result:
try:
resolved_root = root.resolve(strict=True)
resolved = path.resolve(strict=True)
metadata = path.lstat()
except OSError as exc:
raise SessionIntegrityError("multirate artifact is unavailable") from exc
if (
stat.S_ISLNK(metadata.st_mode)
or not stat.S_ISREG(metadata.st_mode)
or not resolved.is_relative_to(resolved_root)
):
raise SessionIntegrityError("multirate artifact is not confined")
return metadata
def _sha256_file(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 _canonical_json(value: object) -> bytes:
try:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
except (TypeError, ValueError) as exc:
raise SessionIntegrityError("multirate identity is not canonical JSON") from exc
def _finite_number(value: object) -> TypeGuard[int | float]:
return (
isinstance(value, (int, float))
and not isinstance(value, bool)
and math.isfinite(float(value))
)
+500
View File
@@ -0,0 +1,500 @@
"""Validated full-epoch perception results and native recorded-video delivery."""
from __future__ import annotations
import hashlib
import json
import math
import os
import re
import stat
import subprocess
import threading
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from k1link.sessions import SessionIntegrityError
from .jobs import CameraComputeJob, validate_camera_compute_job
RESULT_SCHEMA = "missioncore.recorded-perception-result/v2"
IDENTITY_SCHEMA = "missioncore.recorded-perception-identity/v2"
REPORT_SCHEMA = "missioncore.perception-run-report/v1"
FRAME_SCHEMA = "missioncore.panoptic-frame/v1"
MAX_JSON_BYTES = 64 * 1024 * 1024
MAX_FRAME_LINE_BYTES = 1024 * 1024
MAX_JOB_SCAN = 512
MAX_SAFE_INTEGER = (1 << 53) - 1
_SAFE_RESULT_ID = re.compile(r"^result-[a-f0-9]{64}$")
_SAFE_RECORDING_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$")
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
@dataclass(frozen=True, slots=True)
class PerceptionArtifact:
kind: str
path: Path
media_type: str
byte_length: int
sha256: str
schema_version: str | None
@dataclass(frozen=True, slots=True)
class RecordedPerceptionEpochResult:
result_id: str
result_root: Path
job: CameraComputeJob
created_at_utc: str
calibration_sha256: str
calibration_slot: str
artifacts: tuple[PerceptionArtifact, ...]
def artifact(self, kind: str) -> PerceptionArtifact:
matches = tuple(item for item in self.artifacts if item.kind == kind)
if len(matches) != 1:
raise SessionIntegrityError(f"perception result artifact is unavailable: {kind}")
return matches[0]
@dataclass(frozen=True, slots=True)
class RecordedPerceptionVideo:
result_id: str
session_id: str
source_id: str
public_source_id: str
label: str
path: Path
media_type: str
byte_length: int
sha256: str
timeline_start_seconds: float
timeline_end_seconds: float
@dataclass(frozen=True, slots=True)
class _LatestResultCacheEntry:
jobs_root_mtime_ns: int
result_parent_mtime_ns: int
file_fingerprint: tuple[tuple[str, int, int, int, int], ...]
result: RecordedPerceptionEpochResult
class RecordedPerceptionEpochStore:
"""Discover immutable full-epoch results and expose their panoptic video."""
def __init__(self, *, jobs_root: Path, results_root: Path, ffprobe_path: Path) -> None:
self.jobs_root = jobs_root.expanduser().absolute()
self.results_root = results_root.expanduser().absolute()
self.ffprobe_path = ffprobe_path.expanduser().resolve(strict=True)
self._lock = threading.Lock()
self._video_probe_cache: dict[tuple[str, int, int], tuple[float, str, int, int]] = {}
self._latest_result_cache: dict[str, _LatestResultCacheEntry] = {}
def latest(self, session_id: str) -> RecordedPerceptionEpochResult | None:
if _SAFE_RECORDING_ID.fullmatch(session_id) is None:
raise ValueError("observation session id is invalid")
with self._lock:
return self._latest_unlocked(session_id)
def video(
self,
session_id: str,
result_id: str | None = None,
) -> RecordedPerceptionVideo | None:
if _SAFE_RECORDING_ID.fullmatch(session_id) is None:
raise ValueError("observation session id is invalid")
if result_id is not None and _SAFE_RESULT_ID.fullmatch(result_id) is None:
raise ValueError("perception result id is invalid")
with self._lock:
result = (
self._latest_unlocked(session_id)
if result_id is None
else self._resolve_unlocked(session_id, result_id)
)
if result is None:
return None
artifact = result.artifact("panoptic-overlay-video")
duration, codec, width, height = self._probe_video(artifact)
expected = result.job.timeline_end_seconds - result.job.timeline_start_seconds
if codec != "h264" or width != 800 or height != 600 or abs(duration - expected) > 1:
raise SessionIntegrityError("panoptic video stream contract is invalid")
suffix = result.job.source_id.removeprefix("sensor.camera.")
return RecordedPerceptionVideo(
result_id=result.result_id,
session_id=result.job.session_id,
source_id=result.job.source_id,
public_source_id=f"recorded.perception.{suffix}",
label=f"Сегментация · камера {suffix}",
path=artifact.path,
media_type='video/mp4; codecs="avc1.640028"',
byte_length=artifact.byte_length,
sha256=artifact.sha256,
timeline_start_seconds=result.job.timeline_start_seconds,
timeline_end_seconds=result.job.timeline_end_seconds,
)
def _latest_unlocked(self, session_id: str) -> RecordedPerceptionEpochResult | None:
cached = self._latest_result_cache.get(session_id)
if cached is not None:
if self._latest_cache_is_current(cached):
return cached.result
self._latest_result_cache.pop(session_id, None)
try:
job_roots = sorted(self.jobs_root.iterdir())
except FileNotFoundError:
return None
if len(job_roots) > MAX_JOB_SCAN:
raise SessionIntegrityError("compute job catalog is outside bounds")
matches: list[RecordedPerceptionEpochResult] = []
for job_root in job_roots:
if job_root.is_symlink():
continue
try:
job = validate_camera_compute_job(job_root)
except (OSError, SessionIntegrityError):
continue
if job.session_id != session_id:
continue
parent = self.results_root / job.job_id
try:
result_roots = sorted(
path
for path in parent.iterdir()
if path.is_dir()
and not path.is_symlink()
and _SAFE_RESULT_ID.fullmatch(path.name) is not None
)
except FileNotFoundError:
continue
for result_root in result_roots:
try:
matches.append(validate_recorded_perception_epoch_result(job_root, result_root))
except (OSError, SessionIntegrityError):
continue
if not matches:
return None
latest = max(matches, key=lambda value: (value.created_at_utc, value.result_id))
self._latest_result_cache[session_id] = self._latest_cache_entry(latest)
return latest
def _latest_cache_entry(
self,
result: RecordedPerceptionEpochResult,
) -> _LatestResultCacheEntry:
result_parent = self.results_root / result.job.job_id
return _LatestResultCacheEntry(
jobs_root_mtime_ns=self.jobs_root.stat().st_mtime_ns,
result_parent_mtime_ns=result_parent.stat().st_mtime_ns,
file_fingerprint=_result_file_fingerprint(result),
result=result,
)
def _latest_cache_is_current(self, cached: _LatestResultCacheEntry) -> bool:
"""Reuse a fully validated immutable publication while it is unchanged.
New jobs and new result generations invalidate through their parent
directory mtimes. In-place changes to the manifest or any served
result artifact invalidate through the bounded file fingerprint and
force the full hash validation again.
"""
try:
result_parent = self.results_root / cached.result.job.job_id
return (
self.jobs_root.stat().st_mtime_ns == cached.jobs_root_mtime_ns
and result_parent.stat().st_mtime_ns == cached.result_parent_mtime_ns
and _result_file_fingerprint(cached.result) == cached.file_fingerprint
)
except (OSError, SessionIntegrityError):
return False
def _resolve_unlocked(
self,
session_id: str,
result_id: str,
) -> RecordedPerceptionEpochResult | None:
latest = self._latest_unlocked(session_id)
if latest is None or latest.result_id != result_id:
return None
return latest
def _probe_video(self, artifact: PerceptionArtifact) -> tuple[float, str, int, int]:
metadata = _confined_regular_file(artifact.path, artifact.path.parent)
key = (artifact.sha256, metadata.st_mtime_ns, metadata.st_size)
cached = self._video_probe_cache.get(key)
if cached is not None:
return cached
try:
completed = subprocess.run(
[
str(self.ffprobe_path),
"-v",
"error",
"-select_streams",
"v:0",
"-show_entries",
"stream=codec_name,width,height:format=duration",
"-of",
"json",
str(artifact.path),
],
capture_output=True,
check=False,
text=True,
timeout=30,
)
document = json.loads(completed.stdout)
stream = document["streams"][0]
value = (
float(document["format"]["duration"]),
str(stream["codec_name"]),
int(stream["width"]),
int(stream["height"]),
)
except (OSError, subprocess.SubprocessError, ValueError, KeyError, IndexError) as exc:
raise SessionIntegrityError("panoptic video metadata is unavailable") from exc
if completed.returncode != 0 or not math.isfinite(value[0]) or value[0] <= 0:
raise SessionIntegrityError("panoptic video could not be decoded")
self._video_probe_cache = {key: value}
return value
def validate_recorded_perception_epoch_result(
job_root: Path,
result_root: Path,
) -> RecordedPerceptionEpochResult:
job = validate_camera_compute_job(job_root)
root = result_root.expanduser().resolve(strict=True)
if not root.is_dir() or _SAFE_RESULT_ID.fullmatch(root.name) is None:
raise SessionIntegrityError("full-epoch perception result root is invalid")
result = _read_json_object(root / "result.json", root)
identity = result.get("identity")
identity_sha256 = result.get("identity_sha256")
if (
result.get("schema_version") != RESULT_SCHEMA
or result.get("result_id") != root.name
or not isinstance(identity, dict)
or identity.get("schema_version") != IDENTITY_SCHEMA
or not isinstance(identity_sha256, str)
or _SHA256.fullmatch(identity_sha256) is None
or root.name != f"result-{identity_sha256}"
or hashlib.sha256(_canonical_json(identity)).hexdigest() != identity_sha256
or result.get("job_id") != job.job_id
or result.get("input_sha256") != job.input_sha256
or result.get("session_id") != job.session_id
or result.get("source_id") != job.source_id
or result.get("codec_epoch") != job.codec_epoch
or result.get("timestamp_basis") != "session-time-seconds"
or result.get("timeline_start_seconds") != job.timeline_start_seconds
or result.get("timeline_end_seconds") != job.timeline_end_seconds
or result.get("frames_processed") != job.segment_count
):
raise SessionIntegrityError("full-epoch perception result identity is inconsistent")
if identity.get("job_id") != job.job_id or identity.get("input_sha256") != job.input_sha256:
raise SessionIntegrityError("full-epoch perception identity is not bound to its job")
calibration = identity.get("calibration")
if (
not isinstance(calibration, dict)
or not isinstance(calibration.get("content_identity_sha256"), str)
or _SHA256.fullmatch(calibration["content_identity_sha256"]) is None
or not isinstance(calibration.get("camera_slot"), str)
or not calibration["camera_slot"]
):
raise SessionIntegrityError("full-epoch perception calibration binding is invalid")
artifacts = _validate_artifacts(root, result.get("artifacts"))
report_artifact = _artifact(artifacts, "perception-run-report")
report = _read_json_object(report_artifact.path, root)
metrics = report.get("metrics")
report_input = report.get("input")
if (
report.get("schema_version") != REPORT_SCHEMA
or report.get("state") != "published"
or report.get("result_id") != root.name
or not isinstance(metrics, dict)
or metrics.get("frames_expected") != job.segment_count
or metrics.get("frames_processed") != job.segment_count
or metrics.get("frames_failed") != 0
or metrics.get("frames_skipped") != 0
or not isinstance(report_input, dict)
or report_input.get("job_id") != job.job_id
or report_input.get("input_sha256") != job.input_sha256
):
raise SessionIntegrityError("full-epoch perception run report is inconsistent")
_validate_frame_metadata(_artifact(artifacts, "panoptic-frame-metadata"), job)
created_at_utc = result.get("created_at_utc")
if not isinstance(created_at_utc, str) or not 1 <= len(created_at_utc) <= 64:
raise SessionIntegrityError("full-epoch perception creation time is invalid")
return RecordedPerceptionEpochResult(
result_id=root.name,
result_root=root,
job=job,
created_at_utc=created_at_utc,
calibration_sha256=calibration["content_identity_sha256"],
calibration_slot=calibration["camera_slot"],
artifacts=artifacts,
)
def _validate_artifacts(root: Path, raw: object) -> tuple[PerceptionArtifact, ...]:
expected = {
"panoptic-overlay-video": ("perception.mp4", "video/mp4", None),
"panoptic-mask-archive": ("masks.tar.gz", "application/gzip", None),
"panoptic-frame-metadata": ("frames.jsonl", "application/x-ndjson", FRAME_SCHEMA),
"worker-gpu-telemetry": ("gpu-telemetry.jsonl", "application/x-ndjson", None),
"perception-run-report": ("run-report.json", "application/json", REPORT_SCHEMA),
}
if not isinstance(raw, list) or len(raw) != len(expected):
raise SessionIntegrityError("full-epoch perception artifact set is invalid")
artifacts: list[PerceptionArtifact] = []
seen: set[str] = set()
for value in raw:
if not isinstance(value, dict):
raise SessionIntegrityError("full-epoch perception artifact is invalid")
kind = value.get("kind")
if not isinstance(kind, str) or kind in seen or kind not in expected:
raise SessionIntegrityError("full-epoch perception artifact kind is invalid")
seen.add(kind)
expected_path, expected_media_type, expected_schema = expected[kind]
path = root / expected_path
metadata = _confined_regular_file(path, root)
byte_length = value.get("byte_length")
digest = value.get("sha256")
if (
value.get("path") != expected_path
or value.get("media_type") != expected_media_type
or value.get("schema_version") != expected_schema
or not isinstance(byte_length, int)
or isinstance(byte_length, bool)
or not 0 < byte_length <= MAX_SAFE_INTEGER
or metadata.st_size != byte_length
or not isinstance(digest, str)
or _SHA256.fullmatch(digest) is None
or _sha256_file(path) != digest
):
raise SessionIntegrityError("full-epoch perception artifact identity changed")
artifacts.append(
PerceptionArtifact(
kind=kind,
path=path,
media_type=expected_media_type,
byte_length=byte_length,
sha256=digest,
schema_version=expected_schema,
)
)
return tuple(artifacts)
def _artifact(artifacts: tuple[PerceptionArtifact, ...], kind: str) -> PerceptionArtifact:
matches = tuple(item for item in artifacts if item.kind == kind)
if len(matches) != 1:
raise SessionIntegrityError("full-epoch perception artifact is unavailable")
return matches[0]
def _validate_frame_metadata(artifact: PerceptionArtifact, job: CameraComputeJob) -> None:
previous = -math.inf
count = 0
try:
with artifact.path.open("rb") as stream:
for expected_index, line in enumerate(stream):
if not line or len(line) > MAX_FRAME_LINE_BYTES:
raise SessionIntegrityError("panoptic frame metadata line is outside bounds")
value = json.loads(line)
session_seconds = value.get("session_seconds") if isinstance(value, dict) else None
if (
not isinstance(value, dict)
or value.get("schema_version") != FRAME_SCHEMA
or value.get("frame_index") != expected_index
or value.get("sequence") != expected_index + 1
or not isinstance(session_seconds, (int, float))
or isinstance(session_seconds, bool)
or not math.isfinite(float(session_seconds))
or float(session_seconds) <= previous
or float(session_seconds) < job.timeline_start_seconds - 0.001
or float(session_seconds) > job.timeline_end_seconds + 0.001
or not isinstance(value.get("instances"), list)
or not isinstance(value.get("semantic_classes"), list)
):
raise SessionIntegrityError("panoptic frame metadata is inconsistent")
previous = float(session_seconds)
count += 1
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SessionIntegrityError("panoptic frame metadata is unavailable") from exc
if count != job.segment_count:
raise SessionIntegrityError("panoptic frame metadata count changed")
def _read_json_object(path: Path, root: Path) -> dict[str, Any]:
metadata = _confined_regular_file(path, root)
if not 0 < metadata.st_size <= MAX_JSON_BYTES:
raise SessionIntegrityError("full-epoch perception JSON is outside bounds")
try:
value = json.loads(path.read_text(encoding="utf-8-sig"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SessionIntegrityError("full-epoch perception JSON is unavailable") from exc
if not isinstance(value, dict):
raise SessionIntegrityError("full-epoch perception JSON is not an object")
return value
def _confined_regular_file(path: Path, root: Path) -> os.stat_result:
try:
resolved_root = root.resolve(strict=True)
resolved = path.resolve(strict=True)
metadata = path.lstat()
except OSError as exc:
raise SessionIntegrityError("full-epoch perception artifact is unavailable") from exc
if (
stat.S_ISLNK(metadata.st_mode)
or not stat.S_ISREG(metadata.st_mode)
or not resolved.is_relative_to(resolved_root)
):
raise SessionIntegrityError("full-epoch perception artifact is not confined")
return metadata
def _result_file_fingerprint(
result: RecordedPerceptionEpochResult,
) -> tuple[tuple[str, int, int, int, int], ...]:
paths = (result.job.manifest_path, result.result_root / "result.json") + tuple(
artifact.path for artifact in result.artifacts
)
values: list[tuple[str, int, int, int, int]] = []
for path in paths:
root = result.job.job_root if path == result.job.manifest_path else result.result_root
metadata = _confined_regular_file(path, root)
values.append(
(
str(path),
metadata.st_dev,
metadata.st_ino,
metadata.st_size,
metadata.st_mtime_ns,
)
)
return tuple(values)
def _sha256_file(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 _canonical_json(value: object) -> bytes:
try:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
except (TypeError, ValueError) as exc:
raise SessionIntegrityError("full-epoch perception identity cannot be encoded") from exc
+401
View File
@@ -0,0 +1,401 @@
from __future__ import annotations
import hashlib
import json
import os
import re
import secrets
import shutil
import stat
from dataclasses import dataclass
from pathlib import Path
from typing import Any, cast
from k1link.artifacts import utc_now_iso, write_json_atomic
from k1link.sessions import SessionIntegrityError
from .jobs import CameraComputeJob, validate_camera_compute_job
QUALIFICATION_SLICE_SCHEMA = "missioncore.recorded-qualification-slice/v1"
QUALIFICATION_SLICE_IDENTITY_SCHEMA = "missioncore.recorded-qualification-slice-identity/v1"
QUALIFICATION_POLICY = "uniform-frame-index-full-epoch/v1"
DEFAULT_QUALIFICATION_FRAME_COUNT = 256
MAX_QUALIFICATION_FRAME_COUNT = 4096
MAX_SLICE_MANIFEST_BYTES = 16 * 1024 * 1024
MAX_INDEX_LINE_BYTES = 16 * 1024
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
_SAFE_GENERATION = re.compile(r"^qualification-slice-[a-f0-9]{64}$")
class RecordedQualificationSliceError(RuntimeError):
"""A deterministic recorded-camera qualification slice is invalid."""
@dataclass(frozen=True, slots=True)
class QualificationFrame:
frame_index: int
sequence: int
segment_sha256: str
host_epoch_ns: int
host_monotonic_ns: int
archive_session_monotonic_ns: int
@dataclass(frozen=True, slots=True)
class RecordedQualificationSlice:
generation_id: str
root: Path
manifest_path: Path
job_id: str
input_sha256: str
policy: str
source_frame_count: int
frames: tuple[QualificationFrame, ...]
def prepare_recorded_qualification_slice(
*,
job_root: Path,
output_root: Path,
sample_count: int = DEFAULT_QUALIFICATION_FRAME_COUNT,
) -> RecordedQualificationSlice:
"""Seal an evenly distributed, exact-repeat frame-index slice.
The selection includes both ends of the epoch and is keyed by the complete
compute-job input identity. It deliberately selects by decoded frame index;
exact PTS are attached by the worker after decoding, while archive arrival
timestamps remain diagnostic metadata only.
"""
job = validate_camera_compute_job(job_root)
if (
not isinstance(sample_count, int)
or isinstance(sample_count, bool)
or not 1 <= sample_count <= MAX_QUALIFICATION_FRAME_COUNT
):
raise RecordedQualificationSliceError("qualification sample count is outside bounds")
admitted_count = min(sample_count, job.segment_count)
selected_indices = _uniform_indices(job.segment_count, admitted_count)
index_rows = _read_archive_index(job)
frames = tuple(_frame_from_index(index_rows[index], index) for index in selected_indices)
identity = {
"schema_version": QUALIFICATION_SLICE_IDENTITY_SCHEMA,
"job_id": job.job_id,
"input_sha256": job.input_sha256,
"session_id": job.session_id,
"source_id": job.source_id,
"codec_epoch": job.codec_epoch,
"source_frame_count": job.segment_count,
"policy": QUALIFICATION_POLICY,
"requested_sample_count": sample_count,
"admitted_sample_count": len(frames),
"selected_frames": [
{
"frame_index": frame.frame_index,
"sequence": frame.sequence,
"segment_sha256": frame.segment_sha256,
}
for frame in frames
],
}
identity_sha256 = hashlib.sha256(_canonical_json(identity)).hexdigest()
generation_id = f"qualification-slice-{identity_sha256}"
root = _prepare_private_directory(output_root)
final = root / generation_id
if final.exists():
existing = validate_recorded_qualification_slice(final, job_root=job.job_root)
if existing.input_sha256 != job.input_sha256:
raise RecordedQualificationSliceError(
"qualification generation collides with another input"
)
return existing
staging = root / f".{generation_id}.{secrets.token_hex(12)}.incomplete"
published = False
try:
staging.mkdir(mode=0o700)
manifest = {
"schema_version": QUALIFICATION_SLICE_SCHEMA,
"generation_id": generation_id,
"identity_sha256": identity_sha256,
"identity": identity,
"created_at_utc": utc_now_iso(),
"source_timeline": {
"basis": "session-time-seconds",
"start_seconds": job.timeline_start_seconds,
"end_seconds": job.timeline_end_seconds,
"duration_seconds": job.timeline_end_seconds - job.timeline_start_seconds,
},
"frames": [
{
"frame_index": frame.frame_index,
"sequence": frame.sequence,
"segment_sha256": frame.segment_sha256,
"archive_host_epoch_ns": frame.host_epoch_ns,
"archive_host_monotonic_ns": frame.host_monotonic_ns,
"archive_session_monotonic_ns": frame.archive_session_monotonic_ns,
"decoded_session_seconds": None,
}
for frame in frames
],
"usage": {
"selection_basis": "zero-based decoded frame index",
"worker_timestamp_binding": (
"resolve exact best_effort_timestamp_time after full stream decode"
),
"archive_timestamp_status": "host-arrival-best-effort-diagnostic",
"comparison_contract": (
"all E1 variants must use this exact ordered frame list and the same input "
"and calibration generations"
),
},
}
write_json_atomic(staging / "manifest.json", manifest)
os.chmod(staging / "manifest.json", 0o600)
_fsync_directory(staging)
os.replace(staging, final)
_fsync_directory(root)
published = True
finally:
if not published and staging.exists():
shutil.rmtree(staging)
return validate_recorded_qualification_slice(final, job_root=job.job_root)
def validate_recorded_qualification_slice(
slice_root: Path,
*,
job_root: Path,
) -> RecordedQualificationSlice:
root = slice_root.expanduser().resolve(strict=True)
if not root.is_dir() or _SAFE_GENERATION.fullmatch(root.name) is None:
raise RecordedQualificationSliceError("qualification slice root is invalid")
manifest = _read_json_object(root / "manifest.json", root)
identity = manifest.get("identity")
identity_sha256 = manifest.get("identity_sha256")
if (
manifest.get("schema_version") != QUALIFICATION_SLICE_SCHEMA
or manifest.get("generation_id") != root.name
or not isinstance(identity, dict)
or identity.get("schema_version") != QUALIFICATION_SLICE_IDENTITY_SCHEMA
or not isinstance(identity_sha256, str)
or _SHA256.fullmatch(identity_sha256) is None
or root.name != f"qualification-slice-{identity_sha256}"
or hashlib.sha256(_canonical_json(identity)).hexdigest() != identity_sha256
):
raise RecordedQualificationSliceError("qualification slice identity is inconsistent")
job = validate_camera_compute_job(job_root)
if (
identity.get("job_id") != job.job_id
or identity.get("input_sha256") != job.input_sha256
or identity.get("session_id") != job.session_id
or identity.get("source_id") != job.source_id
or identity.get("codec_epoch") != job.codec_epoch
or identity.get("source_frame_count") != job.segment_count
or identity.get("policy") != QUALIFICATION_POLICY
):
raise RecordedQualificationSliceError("qualification slice is not bound to the job")
rows = manifest.get("frames")
selected = identity.get("selected_frames")
admitted_count = identity.get("admitted_sample_count")
requested_count = identity.get("requested_sample_count")
if (
not isinstance(rows, list)
or not isinstance(selected, list)
or rows == []
or not isinstance(admitted_count, int)
or isinstance(admitted_count, bool)
or admitted_count != len(rows)
or len(selected) != len(rows)
or not isinstance(requested_count, int)
or isinstance(requested_count, bool)
or not 1 <= requested_count <= MAX_QUALIFICATION_FRAME_COUNT
or admitted_count != min(requested_count, job.segment_count)
):
raise RecordedQualificationSliceError("qualification slice frame count is invalid")
archive_rows = _read_archive_index(job)
frames: list[QualificationFrame] = []
previous_index = -1
for row, selected_row in zip(rows, selected, strict=True):
if not isinstance(row, dict) or not isinstance(selected_row, dict):
raise RecordedQualificationSliceError("qualification frame descriptor is invalid")
frame_index = row.get("frame_index")
if (
not isinstance(frame_index, int)
or isinstance(frame_index, bool)
or not previous_index < frame_index < job.segment_count
):
raise RecordedQualificationSliceError("qualification frame order is invalid")
expected = _frame_from_index(archive_rows[frame_index], frame_index)
expected_identity = {
"frame_index": expected.frame_index,
"sequence": expected.sequence,
"segment_sha256": expected.segment_sha256,
}
expected_row = {
**expected_identity,
"archive_host_epoch_ns": expected.host_epoch_ns,
"archive_host_monotonic_ns": expected.host_monotonic_ns,
"archive_session_monotonic_ns": expected.archive_session_monotonic_ns,
"decoded_session_seconds": None,
}
if selected_row != expected_identity or row != expected_row:
raise RecordedQualificationSliceError("qualification frame binding changed")
frames.append(expected)
previous_index = frame_index
if tuple(frame.frame_index for frame in frames) != _uniform_indices(
job.segment_count,
admitted_count,
):
raise RecordedQualificationSliceError("qualification selection policy changed")
return RecordedQualificationSlice(
generation_id=root.name,
root=root,
manifest_path=root / "manifest.json",
job_id=job.job_id,
input_sha256=job.input_sha256,
policy=QUALIFICATION_POLICY,
source_frame_count=job.segment_count,
frames=tuple(frames),
)
def _uniform_indices(source_count: int, sample_count: int) -> tuple[int, ...]:
if source_count < 1 or not 1 <= sample_count <= source_count:
raise RecordedQualificationSliceError("uniform selection bounds are invalid")
if sample_count == 1:
return (source_count // 2,)
denominator = sample_count - 1
indices = tuple(
(position * (source_count - 1) + denominator // 2) // denominator
for position in range(sample_count)
)
if len(set(indices)) != sample_count or indices[0] != 0 or indices[-1] != source_count - 1:
raise RecordedQualificationSliceError("uniform selection is not exact")
return indices
def _read_archive_index(job: CameraComputeJob) -> list[dict[str, Any]]:
index_path = (
job.job_root
/ "input"
/ "camera"
/ job.source_id
/ f"epoch-{job.codec_epoch}"
/ "index.jsonl"
)
try:
metadata = index_path.lstat()
resolved = index_path.resolve(strict=True)
except OSError as exc:
raise RecordedQualificationSliceError("camera archive index is unavailable") from exc
if (
stat.S_ISLNK(metadata.st_mode)
or not stat.S_ISREG(metadata.st_mode)
or not resolved.is_relative_to(job.job_root)
):
raise RecordedQualificationSliceError("camera archive index is not confined")
rows: list[dict[str, Any]] = []
try:
with index_path.open("r", encoding="utf-8") as stream:
for expected_index, line in enumerate(stream):
if len(line.encode("utf-8")) > MAX_INDEX_LINE_BYTES:
raise RecordedQualificationSliceError("camera archive index row is too large")
value = json.loads(line)
if not isinstance(value, dict):
raise RecordedQualificationSliceError("camera archive index row is invalid")
if value.get("sequence") != expected_index + 1 or value.get("kind") != "media":
raise RecordedQualificationSliceError("camera archive index order changed")
rows.append(value)
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise RecordedQualificationSliceError("camera archive index could not be read") from exc
if len(rows) != job.segment_count:
raise RecordedQualificationSliceError("camera archive index count changed")
return rows
def _frame_from_index(row: dict[str, Any], frame_index: int) -> QualificationFrame:
sequence = row.get("sequence")
segment_sha256 = row.get("sha256")
host_epoch_ns = row.get("host_epoch_ns")
host_monotonic_ns = row.get("host_monotonic_ns")
session_monotonic_ns = row.get("session_monotonic_ns")
integers = (sequence, host_epoch_ns, host_monotonic_ns, session_monotonic_ns)
if (
not all(
isinstance(value, int) and not isinstance(value, bool) and value >= 0
for value in integers
)
or sequence != frame_index + 1
or not isinstance(segment_sha256, str)
or _SHA256.fullmatch(segment_sha256) is None
):
raise RecordedQualificationSliceError("camera archive frame identity is invalid")
return QualificationFrame(
frame_index=frame_index,
sequence=sequence,
segment_sha256=segment_sha256,
host_epoch_ns=cast(int, host_epoch_ns),
host_monotonic_ns=cast(int, host_monotonic_ns),
archive_session_monotonic_ns=cast(int, session_monotonic_ns),
)
def _prepare_private_directory(path: Path) -> Path:
candidate = path.expanduser()
candidate.mkdir(mode=0o700, parents=True, exist_ok=True)
metadata = candidate.lstat()
if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):
raise RecordedQualificationSliceError("qualification output root must be a real directory")
root = candidate.resolve(strict=True)
os.chmod(root, 0o700)
return root
def _read_json_object(path: Path, root: Path) -> dict[str, Any]:
try:
metadata = path.lstat()
resolved = path.resolve(strict=True)
except OSError as exc:
raise RecordedQualificationSliceError("qualification manifest is unavailable") from exc
if (
stat.S_ISLNK(metadata.st_mode)
or not stat.S_ISREG(metadata.st_mode)
or not resolved.is_relative_to(root)
or not 0 < metadata.st_size <= MAX_SLICE_MANIFEST_BYTES
):
raise RecordedQualificationSliceError("qualification manifest is outside bounds")
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise RecordedQualificationSliceError("qualification manifest could not be read") from exc
if not isinstance(value, dict):
raise RecordedQualificationSliceError("qualification manifest is not an object")
return value
def _canonical_json(value: object) -> bytes:
try:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
except (TypeError, ValueError) as exc:
raise SessionIntegrityError("qualification identity is not canonical JSON") from exc
def _fsync_directory(path: Path) -> None:
flags = os.O_RDONLY
if hasattr(os, "O_DIRECTORY"):
flags |= os.O_DIRECTORY
descriptor = os.open(path, flags)
try:
os.fsync(descriptor)
finally:
os.close(descriptor)
@@ -0,0 +1,466 @@
"""Validation for immutable LAB E8 source-paced tracking results."""
from __future__ import annotations
import hashlib
import json
import math
import os
import re
import stat
from dataclasses import dataclass
from pathlib import Path
from typing import Any, TypeGuard
from k1link.sessions import SessionIntegrityError
from .jobs import CameraComputeJob, validate_camera_compute_job
RESULT_SCHEMA = "missioncore.e8-realtime-tracking-result/v1"
IDENTITY_SCHEMA = "missioncore.e8-realtime-tracking-identity/v1"
REPORT_SCHEMA = "missioncore.e8-realtime-tracking-report/v1"
FRAME_SCHEMA = "missioncore.e8-realtime-tracking-frame/v1"
TELEMETRY_SCHEMA = "missioncore.e8-realtime-tracking-telemetry/v1"
PROFILE_SCHEMA = "missioncore.e8-realtime-tracking-profile/v1"
MAX_JSON_BYTES = 64 * 1024 * 1024
MAX_LINE_BYTES = 2 * 1024 * 1024
MAX_SAFE_INTEGER = (1 << 53) - 1
_SAFE_RESULT_ID = re.compile(r"^e8-realtime-tracking-[a-f0-9]{64}$")
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
@dataclass(frozen=True, slots=True)
class RealtimeTrackingArtifact:
kind: str
path: Path
media_type: str
byte_length: int
sha256: str
schema_version: str | None
@dataclass(frozen=True, slots=True)
class RealtimeTrackingQualificationResult:
result_id: str
result_root: Path
job: CameraComputeJob
mode: str
expected_frame_count: int
processed_frame_count: int
dropped_frame_count: int
accepted: bool
artifacts: tuple[RealtimeTrackingArtifact, ...]
def artifact(self, kind: str) -> RealtimeTrackingArtifact:
matches = tuple(item for item in self.artifacts if item.kind == kind)
if len(matches) != 1:
raise SessionIntegrityError(f"realtime tracking artifact is unavailable: {kind}")
return matches[0]
def validate_realtime_tracking_qualification_result(
job_root: Path,
result_root: Path,
) -> RealtimeTrackingQualificationResult:
job = validate_camera_compute_job(job_root)
root = result_root.expanduser().resolve(strict=True)
if not root.is_dir() or _SAFE_RESULT_ID.fullmatch(root.name) is None:
raise SessionIntegrityError("realtime tracking result root is invalid")
result = _read_object(root / "result.json", root)
identity = result.get("identity")
identity_sha256 = result.get("identity_sha256")
if (
result.get("schema_version") != RESULT_SCHEMA
or result.get("result_id") != root.name
or not isinstance(identity, dict)
or identity.get("schema_version") != IDENTITY_SCHEMA
or not isinstance(identity_sha256, str)
or _SHA256.fullmatch(identity_sha256) is None
or root.name != f"e8-realtime-tracking-{identity_sha256}"
or hashlib.sha256(_canonical_json(identity)).hexdigest() != identity_sha256
or identity.get("job_id") != job.job_id
or identity.get("input_sha256") != job.input_sha256
or identity.get("session_id") != job.session_id
or identity.get("source_id") != job.source_id
or result.get("ground_truth") is not False
or result.get("publication_scope") != "recorded-realtime-qualification-only"
or result.get("acceptance_state") not in {"accepted", "rejected"}
):
raise SessionIntegrityError("realtime tracking identity is inconsistent")
selection = identity.get("selection")
configuration = identity.get("configuration")
if not isinstance(selection, dict) or not isinstance(configuration, dict):
raise SessionIntegrityError("realtime tracking selection is unavailable")
expected = selection.get("frame_count")
source_start = selection.get("source_start_frame_index")
source_end = selection.get("source_end_frame_index")
timeline_start = selection.get("timeline_start_seconds")
timeline_end = selection.get("timeline_end_seconds")
profile = configuration.get("profile")
if (
not isinstance(expected, int)
or isinstance(expected, bool)
or expected < 2
or not isinstance(source_start, int)
or isinstance(source_start, bool)
or not isinstance(source_end, int)
or isinstance(source_end, bool)
or source_start < 0
or source_end - source_start + 1 != expected
or source_end >= job.segment_count
or not _finite_number(timeline_start)
or not _finite_number(timeline_end)
or float(timeline_start) < job.timeline_start_seconds
or float(timeline_end) > job.timeline_end_seconds
or float(timeline_end) <= float(timeline_start)
or not isinstance(profile, dict)
or profile.get("schema_version") != PROFILE_SCHEMA
):
raise SessionIntegrityError("realtime tracking selection is invalid")
mode = profile.get("mode")
realtime = profile.get("realtime")
if (
mode not in {"pilot", "qualification", "overload-negative-control"}
or not isinstance(realtime, dict)
or realtime.get("queue_policy") != "bounded-latest-wins"
or not isinstance(realtime.get("queue_capacity"), int)
or not 1 <= int(realtime["queue_capacity"]) <= 8
):
raise SessionIntegrityError("realtime tracking profile is invalid")
artifacts = _validate_artifacts(root, result.get("artifacts"))
report = _read_object(_artifact(artifacts, "realtime-tracking-report").path, root)
metrics = report.get("metrics")
acceptance = report.get("acceptance")
if (
report.get("schema_version") != REPORT_SCHEMA
or report.get("result_id") != root.name
or report.get("identity") != identity
or report.get("ground_truth") is not False
or report.get("state") != result.get("acceptance_state")
or not isinstance(metrics, dict)
or not isinstance(acceptance, dict)
or not isinstance(acceptance.get("accepted"), bool)
or acceptance.get("navigation_or_safety_accepted") is not False
or (result.get("acceptance_state") == "accepted") != acceptance["accepted"]
):
raise SessionIntegrityError("realtime tracking report is inconsistent")
processed = metrics.get("frames_processed")
dropped = metrics.get("frames_dropped")
queue = metrics.get("queue")
health = metrics.get("health_counts")
if (
metrics.get("frames_expected") != expected
or metrics.get("frames_published") != expected
or not isinstance(processed, int)
or isinstance(processed, bool)
or not 1 <= processed <= expected
or not isinstance(dropped, int)
or isinstance(dropped, bool)
or dropped < 0
or processed + dropped != expected
or result.get("frames_processed") != processed
or metrics.get("frames_failed") != 0
or not isinstance(queue, dict)
or queue.get("capacity") != realtime["queue_capacity"]
or queue.get("published") != expected
or queue.get("consumed") != processed
or queue.get("dropped_overflow") != dropped
or queue.get("final_depth") != 0
or not isinstance(queue.get("maximum_depth"), int)
or not 0 <= queue["maximum_depth"] <= queue["capacity"]
or queue.get("closed") is not True
or not isinstance(health, dict)
or sum(health.values()) != processed
):
raise SessionIntegrityError("realtime tracking accounting is inconsistent")
indices = _validate_realtime_frames(
_artifact(artifacts, "realtime-tracking-frames").path,
root,
expected_count=processed,
selection_count=expected,
source_start=source_start,
timeline_start=float(timeline_start),
timeline_end=float(timeline_end),
)
_validate_realtime_telemetry(
_artifact(artifacts, "realtime-tracking-telemetry").path,
root,
expected_indices=indices,
)
return RealtimeTrackingQualificationResult(
result_id=root.name,
result_root=root,
job=job,
mode=str(mode),
expected_frame_count=expected,
processed_frame_count=processed,
dropped_frame_count=dropped,
accepted=bool(acceptance["accepted"]),
artifacts=artifacts,
)
def _validate_realtime_frames(
path: Path,
root: Path,
*,
expected_count: int,
selection_count: int,
source_start: int,
timeline_start: float,
timeline_end: float,
) -> tuple[int, ...]:
_confined_regular_file(path, root)
indices: list[int] = []
previous_time = -math.inf
try:
with path.open("rb") as stream:
for line in stream:
if not line or len(line) > MAX_LINE_BYTES:
raise SessionIntegrityError("realtime tracking frame line is outside bounds")
value = json.loads(line)
frame_index = value.get("frame_index") if isinstance(value, dict) else None
session_seconds = value.get("session_seconds") if isinstance(value, dict) else None
detections = value.get("detections") if isinstance(value, dict) else None
tracks = value.get("tracks") if isinstance(value, dict) else None
delivery = value.get("delivery") if isinstance(value, dict) else None
if (
not isinstance(value, dict)
or value.get("schema_version") != FRAME_SCHEMA
or not isinstance(frame_index, int)
or isinstance(frame_index, bool)
or not 0 <= frame_index < selection_count
or (indices and frame_index <= indices[-1])
or value.get("sequence") != frame_index + 1
or value.get("source_frame_index") != source_start + frame_index
or value.get("source_sequence") != source_start + frame_index + 1
or not _finite_number(session_seconds)
or not timeline_start - 0.001 <= float(session_seconds) <= timeline_end + 0.001
or float(session_seconds) <= previous_time
or not isinstance(detections, list)
or not isinstance(tracks, list)
or not isinstance(delivery, dict)
or delivery.get("health") not in {"healthy", "degraded", "stale", "unavailable"}
or not _finite_number(delivery.get("result_age_ms"))
or float(delivery["result_age_ms"]) < 0
):
raise SessionIntegrityError("realtime tracking frame metadata is inconsistent")
_validate_observations(detections, tracks)
indices.append(frame_index)
previous_time = float(session_seconds)
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SessionIntegrityError("realtime tracking frames are unavailable") from exc
if len(indices) != expected_count:
raise SessionIntegrityError("realtime tracking frame count changed")
return tuple(indices)
def _validate_realtime_telemetry(
path: Path,
root: Path,
*,
expected_indices: tuple[int, ...],
) -> None:
_confined_regular_file(path, root)
indices: list[int] = []
try:
with path.open("rb") as stream:
for line in stream:
if not line or len(line) > MAX_LINE_BYTES:
raise SessionIntegrityError("realtime telemetry line is outside bounds")
value = json.loads(line)
if (
not isinstance(value, dict)
or value.get("schema_version") != TELEMETRY_SCHEMA
or not isinstance(value.get("frame_index"), int)
or value.get("health") not in {"healthy", "degraded", "stale", "unavailable"}
or not _finite_number(value.get("result_age_ms"))
or float(value["result_age_ms"]) < 0
or not _finite_number(value.get("processing_ms"))
or float(value["processing_ms"]) < 0
):
raise SessionIntegrityError("realtime tracking telemetry is inconsistent")
indices.append(int(value["frame_index"]))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SessionIntegrityError("realtime tracking telemetry is unavailable") from exc
if tuple(indices) != expected_indices:
raise SessionIntegrityError("realtime tracking telemetry binding changed")
def _validate_observations(detections: list[object], tracks: list[object]) -> None:
for detection in detections:
if not isinstance(detection, dict):
raise SessionIntegrityError("realtime tracking detection is invalid")
_validate_box(detection.get("bbox_xyxy"))
if (
not isinstance(detection.get("class_id"), int)
or not isinstance(detection.get("label"), str)
or not _finite_number(detection.get("score"))
or not 0 <= float(detection["score"]) <= 1
):
raise SessionIntegrityError("realtime tracking detection fields are invalid")
track_ids: set[int] = set()
for track in tracks:
if not isinstance(track, dict):
raise SessionIntegrityError("realtime tracking observation is invalid")
_validate_box(track.get("bbox_xyxy"))
track_id = track.get("track_id")
if (
not isinstance(track_id, int)
or isinstance(track_id, bool)
or track_id < 1
or track_id in track_ids
or not isinstance(track.get("label"), str)
or not _finite_number(track.get("score"))
or not isinstance(track.get("hits"), int)
or track["hits"] < 1
):
raise SessionIntegrityError("realtime tracking observation fields are invalid")
track_ids.add(track_id)
def _validate_box(value: object) -> None:
if (
not isinstance(value, list)
or len(value) != 4
or not all(_finite_number(item) for item in value)
or not 0 <= float(value[0]) < float(value[2]) <= 800
or not 0 <= float(value[1]) < float(value[3]) <= 600
):
raise SessionIntegrityError("realtime tracking box is invalid")
def _validate_artifacts(
root: Path,
raw: object,
) -> tuple[RealtimeTrackingArtifact, ...]:
expected = {
"realtime-tracking-frames": (
"frames.jsonl",
"application/x-ndjson",
FRAME_SCHEMA,
),
"realtime-tracking-telemetry": (
"telemetry.jsonl",
"application/x-ndjson",
TELEMETRY_SCHEMA,
),
"worker-gpu-telemetry": ("gpu-telemetry.jsonl", "application/x-ndjson", None),
"realtime-tracking-report": (
"run-report.json",
"application/json",
REPORT_SCHEMA,
),
}
if not isinstance(raw, list) or len(raw) != len(expected):
raise SessionIntegrityError("realtime tracking artifact set is invalid")
artifacts: list[RealtimeTrackingArtifact] = []
seen: set[str] = set()
for value in raw:
if not isinstance(value, dict):
raise SessionIntegrityError("realtime tracking artifact descriptor is invalid")
kind = value.get("kind")
if not isinstance(kind, str) or kind in seen or kind not in expected:
raise SessionIntegrityError("realtime tracking artifact kind is invalid")
seen.add(kind)
expected_path, expected_media_type, expected_schema = expected[kind]
path = root / expected_path
metadata = _confined_regular_file(path, root)
byte_length = value.get("byte_length")
digest = value.get("sha256")
if (
value.get("path") != expected_path
or value.get("media_type") != expected_media_type
or value.get("schema_version") != expected_schema
or not isinstance(byte_length, int)
or isinstance(byte_length, bool)
or not 0 < byte_length <= MAX_SAFE_INTEGER
or metadata.st_size != byte_length
or not isinstance(digest, str)
or _SHA256.fullmatch(digest) is None
or _sha256_file(path) != digest
):
raise SessionIntegrityError("realtime tracking artifact identity changed")
artifacts.append(
RealtimeTrackingArtifact(
kind=kind,
path=path,
media_type=expected_media_type,
byte_length=byte_length,
sha256=digest,
schema_version=expected_schema,
)
)
return tuple(artifacts)
def _artifact(
artifacts: tuple[RealtimeTrackingArtifact, ...],
kind: str,
) -> RealtimeTrackingArtifact:
matches = tuple(item for item in artifacts if item.kind == kind)
if len(matches) != 1:
raise SessionIntegrityError(f"realtime tracking artifact is unavailable: {kind}")
return matches[0]
def _read_object(path: Path, root: Path) -> dict[str, Any]:
metadata = _confined_regular_file(path, root)
if not 0 < metadata.st_size <= MAX_JSON_BYTES:
raise SessionIntegrityError("realtime tracking JSON is outside bounds")
try:
value = json.loads(path.read_text(encoding="utf-8-sig"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SessionIntegrityError("realtime tracking JSON is unavailable") from exc
if not isinstance(value, dict):
raise SessionIntegrityError("realtime tracking JSON root is invalid")
return value
def _confined_regular_file(path: Path, root: Path) -> os.stat_result:
try:
resolved_root = root.resolve(strict=True)
resolved = path.resolve(strict=True)
metadata = path.lstat()
except OSError as exc:
raise SessionIntegrityError("realtime tracking artifact is unavailable") from exc
if (
stat.S_ISLNK(metadata.st_mode)
or not stat.S_ISREG(metadata.st_mode)
or not resolved.is_relative_to(resolved_root)
):
raise SessionIntegrityError("realtime tracking artifact is not confined")
return metadata
def _sha256_file(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 _canonical_json(value: object) -> bytes:
try:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
except (TypeError, ValueError) as exc:
raise SessionIntegrityError("realtime tracking identity is not canonical JSON") from exc
def _finite_number(value: object) -> TypeGuard[int | float]:
return (
isinstance(value, (int, float))
and not isinstance(value, bool)
and math.isfinite(float(value))
)
@@ -0,0 +1,425 @@
"""Validation for immutable LAB E6 tracked LiDAR qualification results."""
from __future__ import annotations
import hashlib
import json
import math
import os
import re
import stat
from dataclasses import dataclass
from pathlib import Path
from typing import Any, TypeGuard
import numpy as np
from k1link.sessions import SessionIntegrityError
from .jobs import CameraComputeJob, validate_camera_compute_job
from .perception_epoch import (
RecordedPerceptionEpochResult,
validate_recorded_perception_epoch_result,
)
from .tracking_qualification import (
TrackingQualificationResult,
validate_tracking_qualification_result,
)
RESULT_SCHEMA = "missioncore.e6-tracked-lidar-fusion-result/v1"
IDENTITY_SCHEMA = "missioncore.e6-tracked-lidar-fusion-identity/v1"
FRAME_SCHEMA = "missioncore.e6-tracked-lidar-frame/v1"
REPORT_SCHEMA = "missioncore.e6-tracked-lidar-run-report/v1"
MAX_JSON_BYTES = 64 * 1024 * 1024
MAX_FRAME_LINE_BYTES = 4 * 1024 * 1024
MAX_POINTS_PER_FRAME = 100_000
MAX_BOXES_PER_FRAME = 10_000
_SAFE_RESULT_ID = re.compile(r"^e6-fusion-[a-f0-9]{64}$")
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
@dataclass(frozen=True, slots=True)
class TrackedFusionQualificationResult:
result_id: str
result_root: Path
job: CameraComputeJob
tracking: TrackingQualificationResult
semantic: RecordedPerceptionEpochResult
frame_count: int
artifacts: tuple[dict[str, Any], ...]
def validate_tracked_fusion_qualification_result(
job_root: Path,
tracking_root: Path,
semantic_root: Path,
result_root: Path,
) -> TrackedFusionQualificationResult:
job = validate_camera_compute_job(job_root)
tracking = validate_tracking_qualification_result(job_root, tracking_root)
semantic = validate_recorded_perception_epoch_result(job_root, semantic_root)
root = result_root.expanduser().resolve(strict=True)
if not root.is_dir() or _SAFE_RESULT_ID.fullmatch(root.name) is None:
raise SessionIntegrityError("tracked fusion result root is invalid")
result = _read_object(root / "result.json", root)
identity = result.get("identity")
identity_sha256 = result.get("identity_sha256")
if (
result.get("schema_version") != RESULT_SCHEMA
or result.get("result_id") != root.name
or not isinstance(identity, dict)
or identity.get("schema_version") != IDENTITY_SCHEMA
or not isinstance(identity_sha256, str)
or _SHA256.fullmatch(identity_sha256) is None
or root.name != f"e6-fusion-{identity_sha256}"
or hashlib.sha256(_canonical_json(identity)).hexdigest() != identity_sha256
or identity.get("job_id") != job.job_id
or identity.get("input_sha256") != job.input_sha256
or identity.get("tracking_result_id") != tracking.result_id
or identity.get("semantic_result_id") != semantic.result_id
or identity.get("calibration_sha256") != semantic.calibration_sha256
or identity.get("camera_slot") != semantic.calibration_slot
or result.get("session_id") != job.session_id
or result.get("source_id") != job.source_id
or result.get("ground_truth") is not False
or result.get("publication_scope") != "qualification-clip-only"
or result.get("frames_processed") != tracking.frame_count
):
raise SessionIntegrityError("tracked fusion identity is inconsistent")
clip = identity.get("clip")
if (
not isinstance(clip, dict)
or clip.get("source_start_frame_index") != tracking.source_start_frame_index
or clip.get("source_end_frame_index") != tracking.source_end_frame_index
or clip.get("clip_frame_count") != tracking.frame_count
or clip.get("timeline_start_seconds") != tracking.timeline_start_seconds
or clip.get("timeline_end_seconds") != tracking.timeline_end_seconds
):
raise SessionIntegrityError("tracked fusion clip binding changed")
artifacts = _validate_artifacts(root, result.get("artifacts"))
labels = _read_labels(_artifact_path(artifacts, "tracked-lidar-box-labels", root))
_validate_arrays(
_artifact_path(artifacts, "tracked-lidar-arrays", root),
frame_count=tracking.frame_count,
label_count=len(labels),
timeline_start=tracking.timeline_start_seconds,
timeline_end=tracking.timeline_end_seconds,
)
_validate_frames(
_artifact_path(artifacts, "tracked-lidar-frame-metadata", root),
tracking=tracking,
)
report = _read_object(
_artifact_path(artifacts, "tracked-lidar-run-report", root), root
)
metrics = report.get("metrics")
if (
report.get("schema_version") != REPORT_SCHEMA
or report.get("result_id") != root.name
or report.get("identity") != identity
or not isinstance(metrics, dict)
or metrics.get("frames_expected") != tracking.frame_count
or metrics.get("frames_processed") != tracking.frame_count
or metrics.get("frames_fused", 0) + metrics.get("frames_depth_unavailable", 0)
!= tracking.frame_count
or result.get("metrics") != metrics
):
raise SessionIntegrityError("tracked fusion report is inconsistent")
rrd = _artifact_path(artifacts, "tracked-lidar-rerun", root)
if rrd.read_bytes()[:4] != b"RRF2":
raise SessionIntegrityError("tracked fusion Rerun stream is invalid")
return TrackedFusionQualificationResult(
result_id=root.name,
result_root=root,
job=job,
tracking=tracking,
semantic=semantic,
frame_count=tracking.frame_count,
artifacts=artifacts,
)
def _validate_artifacts(root: Path, raw: object) -> tuple[dict[str, Any], ...]:
expected = {
"tracked-lidar-arrays": ("fusion.npz", "application/x-npz", None),
"tracked-lidar-frame-metadata": (
"fusion-frames.jsonl",
"application/x-ndjson",
FRAME_SCHEMA,
),
"tracked-lidar-box-labels": ("box-labels.json", "application/json", None),
"tracked-lidar-overlay-video": ("fusion-overlay.mp4", "video/mp4", None),
"tracked-lidar-rerun": ("fusion.rrd", "application/vnd.rerun.rrd", None),
"tracked-lidar-contact-sheet": ("contact-sheet.png", "image/png", None),
"tracked-lidar-run-report": ("run-report.json", "application/json", REPORT_SCHEMA),
}
if not isinstance(raw, list) or len(raw) != len(expected):
raise SessionIntegrityError("tracked fusion artifact set is invalid")
artifacts: list[dict[str, Any]] = []
seen: set[str] = set()
for value in raw:
if not isinstance(value, dict):
raise SessionIntegrityError("tracked fusion artifact descriptor is invalid")
kind = value.get("kind")
if not isinstance(kind, str) or kind in seen or kind not in expected:
raise SessionIntegrityError("tracked fusion artifact kind is invalid")
seen.add(kind)
name, media_type, schema = expected[kind]
path = root / name
metadata = _confined_regular_file(path, root)
if (
value.get("path") != name
or value.get("media_type") != media_type
or value.get("schema_version") != schema
or value.get("byte_length") != metadata.st_size
or not isinstance(value.get("sha256"), str)
or _SHA256.fullmatch(str(value["sha256"])) is None
or _sha256(path) != value["sha256"]
):
raise SessionIntegrityError("tracked fusion artifact identity changed")
artifacts.append(value)
return tuple(artifacts)
def _validate_arrays(
path: Path,
*,
frame_count: int,
label_count: int,
timeline_start: float,
timeline_end: float,
) -> None:
required = {
"frame_times_ns",
"cloud_offsets",
"cloud_points",
"support_offsets",
"support_points",
"support_colors",
"box_offsets",
"box_centers",
"box_half_sizes",
"box_quaternions",
"box_colors",
"box_track_ids",
"box_distances_m",
"box_support_counts",
}
try:
with np.load(path, allow_pickle=False) as arrays:
if set(arrays.files) != required:
raise SessionIntegrityError("tracked fusion array set is invalid")
times = arrays["frame_times_ns"]
cloud_offsets = arrays["cloud_offsets"]
cloud_points = arrays["cloud_points"]
support_offsets = arrays["support_offsets"]
support_points = arrays["support_points"]
support_colors = arrays["support_colors"]
box_offsets = arrays["box_offsets"]
centers = arrays["box_centers"]
half_sizes = arrays["box_half_sizes"]
quaternions = arrays["box_quaternions"]
colors = arrays["box_colors"]
track_ids = arrays["box_track_ids"]
distances = arrays["box_distances_m"]
support_counts = arrays["box_support_counts"]
boxes = centers.shape[0]
if (
times.dtype != np.int64
or times.shape != (frame_count,)
or cloud_offsets.dtype != np.int64
or cloud_offsets.shape != (frame_count + 1,)
or cloud_points.dtype != np.float32
or cloud_points.ndim != 2
or cloud_points.shape[1:] != (3,)
or support_offsets.dtype != np.int64
or support_offsets.shape != (frame_count + 1,)
or support_points.dtype != np.float32
or support_points.ndim != 2
or support_points.shape[1:] != (3,)
or support_colors.dtype != np.uint8
or support_colors.shape != support_points.shape
or box_offsets.dtype != np.int64
or box_offsets.shape != (frame_count + 1,)
or centers.dtype != np.float32
or centers.ndim != 2
or centers.shape[1:] != (3,)
or half_sizes.dtype != np.float32
or half_sizes.shape != centers.shape
or quaternions.dtype != np.float32
or quaternions.shape != (boxes, 4)
or colors.dtype != np.uint8
or colors.shape != (boxes, 4)
or track_ids.dtype != np.int64
or track_ids.shape != (boxes,)
or distances.dtype != np.float32
or distances.shape != (boxes,)
or support_counts.dtype != np.int64
or support_counts.shape != (boxes,)
or label_count != boxes
):
raise SessionIntegrityError("tracked fusion array shapes changed")
_validate_offsets(cloud_offsets, cloud_points.shape[0], MAX_POINTS_PER_FRAME)
_validate_offsets(support_offsets, support_points.shape[0], MAX_POINTS_PER_FRAME)
_validate_offsets(box_offsets, boxes, MAX_BOXES_PER_FRAME)
if (
np.any(np.diff(times) <= 0)
or times[0] < round(timeline_start * 1e9) - 1_000_000
or times[-1] > round(timeline_end * 1e9) + 1_000_000
or not np.isfinite(cloud_points).all()
or not np.isfinite(support_points).all()
or not np.isfinite(centers).all()
or not np.isfinite(half_sizes).all()
or not np.isfinite(quaternions).all()
or not np.isfinite(distances).all()
or np.any(half_sizes <= 0)
or np.any(np.linalg.norm(quaternions, axis=1) < 1e-6)
or np.any(track_ids < 1)
or np.any(distances <= 0)
or np.any(support_counts < 2)
):
raise SessionIntegrityError("tracked fusion arrays are inconsistent")
except (OSError, ValueError, KeyError) as exc:
raise SessionIntegrityError("tracked fusion arrays are unavailable") from exc
def _validate_frames(path: Path, *, tracking: TrackingQualificationResult) -> None:
count = 0
previous = -math.inf
try:
with path.open("rb") as stream:
for frame_index, line in enumerate(stream):
if not line or len(line) > MAX_FRAME_LINE_BYTES:
raise SessionIntegrityError("tracked fusion frame line is outside bounds")
value = json.loads(line)
session_seconds = value.get("session_seconds") if isinstance(value, dict) else None
objects = value.get("objects") if isinstance(value, dict) else None
if (
not isinstance(value, dict)
or value.get("schema_version") != FRAME_SCHEMA
or value.get("frame_index") != frame_index
or value.get("source_frame_index")
!= tracking.source_start_frame_index + frame_index
or not _finite(session_seconds)
or float(session_seconds) <= previous
or not isinstance(objects, list)
or value.get("state")
not in {"fused", "depth-unavailable-sync-gate"}
or not isinstance(value.get("accepted_cuboids"), int)
or value["accepted_cuboids"] < 0
):
raise SessionIntegrityError("tracked fusion frame metadata changed")
accepted = 0
for item in objects:
if not isinstance(item, dict):
raise SessionIntegrityError("tracked fusion object is invalid")
status = item.get("cuboid_status")
if status == "accepted-point-supported-oriented-p05-p95":
accepted += 1
if (
not _finite(item.get("distance_smoothed_m"))
or not isinstance(item.get("clustered_points"), int)
or item["clustered_points"] < 2
):
raise SessionIntegrityError("accepted tracked fusion object is invalid")
if accepted != value["accepted_cuboids"]:
raise SessionIntegrityError("tracked fusion frame cuboid count changed")
previous = float(session_seconds)
count += 1
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SessionIntegrityError("tracked fusion frame metadata is unavailable") from exc
if count != tracking.frame_count:
raise SessionIntegrityError("tracked fusion frame count changed")
def _validate_offsets(
offsets: np.ndarray[Any, np.dtype[np.int64]],
total: int,
maximum: int,
) -> None:
if (
offsets[0] != 0
or offsets[-1] != total
or np.any(np.diff(offsets) < 0)
or np.any(np.diff(offsets) > maximum)
):
raise SessionIntegrityError("tracked fusion offsets are inconsistent")
def _read_labels(path: Path) -> list[str]:
value = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(value, list) or any(
not isinstance(item, str) or not 1 <= len(item) <= 256 for item in value
):
raise SessionIntegrityError("tracked fusion labels are invalid")
return value
def _artifact_path(
artifacts: tuple[dict[str, Any], ...],
kind: str,
root: Path,
) -> Path:
matches = [item for item in artifacts if item["kind"] == kind]
if len(matches) != 1:
raise SessionIntegrityError(f"tracked fusion artifact is unavailable: {kind}")
return root / str(matches[0]["path"])
def _read_object(path: Path, root: Path) -> dict[str, Any]:
metadata = _confined_regular_file(path, root)
if not 0 < metadata.st_size <= MAX_JSON_BYTES:
raise SessionIntegrityError("tracked fusion JSON is outside bounds")
try:
value = json.loads(path.read_text(encoding="utf-8-sig"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SessionIntegrityError("tracked fusion JSON is unavailable") from exc
if not isinstance(value, dict):
raise SessionIntegrityError("tracked fusion JSON root is invalid")
return value
def _confined_regular_file(path: Path, root: Path) -> os.stat_result:
try:
resolved_root = root.resolve(strict=True)
resolved = path.resolve(strict=True)
metadata = path.lstat()
except OSError as exc:
raise SessionIntegrityError("tracked fusion artifact is unavailable") from exc
if (
stat.S_ISLNK(metadata.st_mode)
or not stat.S_ISREG(metadata.st_mode)
or not resolved.is_relative_to(resolved_root)
):
raise SessionIntegrityError("tracked fusion artifact is not confined")
return metadata
def _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 _canonical_json(value: object) -> bytes:
try:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode()
except (TypeError, ValueError) as exc:
raise SessionIntegrityError("tracked fusion identity cannot be encoded") from exc
def _finite(value: object) -> TypeGuard[int | float]:
return (
isinstance(value, (int, float))
and not isinstance(value, bool)
and math.isfinite(float(value))
)
@@ -0,0 +1,364 @@
"""Validation for immutable LAB E5 tracking-qualification results."""
from __future__ import annotations
import hashlib
import json
import math
import os
import re
import stat
from dataclasses import dataclass
from pathlib import Path
from typing import Any, TypeGuard
from k1link.sessions import SessionIntegrityError
from .jobs import CameraComputeJob, validate_camera_compute_job
RESULT_SCHEMA = "missioncore.e5-tracking-result/v1"
IDENTITY_SCHEMA = "missioncore.e5-tracking-identity/v1"
REPORT_SCHEMA = "missioncore.e5-tracking-report/v1"
FRAME_SCHEMA = "missioncore.e5-tracking-frame/v1"
MAX_JSON_BYTES = 64 * 1024 * 1024
MAX_FRAME_LINE_BYTES = 1024 * 1024
MAX_SAFE_INTEGER = (1 << 53) - 1
_SAFE_RESULT_ID = re.compile(r"^e5-tracking-[a-f0-9]{64}$")
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
@dataclass(frozen=True, slots=True)
class TrackingQualificationArtifact:
kind: str
path: Path
media_type: str
byte_length: int
sha256: str
schema_version: str | None
@dataclass(frozen=True, slots=True)
class TrackingQualificationResult:
result_id: str
result_root: Path
job: CameraComputeJob
frame_count: int
source_start_frame_index: int
source_end_frame_index: int
timeline_start_seconds: float
timeline_end_seconds: float
artifacts: tuple[TrackingQualificationArtifact, ...]
def artifact(self, kind: str) -> TrackingQualificationArtifact:
matches = tuple(item for item in self.artifacts if item.kind == kind)
if len(matches) != 1:
raise SessionIntegrityError(f"tracking artifact is unavailable: {kind}")
return matches[0]
def validate_tracking_qualification_result(
job_root: Path,
result_root: Path,
) -> TrackingQualificationResult:
job = validate_camera_compute_job(job_root)
root = result_root.expanduser().resolve(strict=True)
if not root.is_dir() or _SAFE_RESULT_ID.fullmatch(root.name) is None:
raise SessionIntegrityError("tracking result root is invalid")
result = _read_object(root / "result.json", root)
identity = result.get("identity")
identity_sha256 = result.get("identity_sha256")
if (
result.get("schema_version") != RESULT_SCHEMA
or result.get("result_id") != root.name
or not isinstance(identity, dict)
or identity.get("schema_version") != IDENTITY_SCHEMA
or not isinstance(identity_sha256, str)
or _SHA256.fullmatch(identity_sha256) is None
or root.name != f"e5-tracking-{identity_sha256}"
or hashlib.sha256(_canonical_json(identity)).hexdigest() != identity_sha256
or result.get("job_id") != job.job_id
or result.get("input_sha256") != job.input_sha256
or result.get("session_id") != job.session_id
or result.get("source_id") != job.source_id
or result.get("timestamp_basis") != "session-time-seconds"
or result.get("ground_truth") is not False
or result.get("publication_scope") != "qualification-clip-only"
or identity.get("job_id") != job.job_id
or identity.get("input_sha256") != job.input_sha256
):
raise SessionIntegrityError("tracking result identity is inconsistent")
clip = identity.get("clip")
if not isinstance(clip, dict):
raise SessionIntegrityError("tracking clip identity is unavailable")
frame_count = clip.get("clip_frame_count")
start_index = clip.get("source_start_frame_index")
end_index = clip.get("source_end_frame_index")
timeline_start = clip.get("timeline_start_seconds")
timeline_end = clip.get("timeline_end_seconds")
if (
not isinstance(frame_count, int)
or isinstance(frame_count, bool)
or frame_count < 2
or not isinstance(start_index, int)
or isinstance(start_index, bool)
or not isinstance(end_index, int)
or isinstance(end_index, bool)
or start_index < 0
or end_index - start_index + 1 != frame_count
or end_index >= job.segment_count
or not _finite_number(timeline_start)
or not _finite_number(timeline_end)
or float(timeline_start) < job.timeline_start_seconds
or float(timeline_end) > job.timeline_end_seconds
or float(timeline_end) <= float(timeline_start)
or result.get("frames_processed") != frame_count
):
raise SessionIntegrityError("tracking clip identity is invalid")
artifacts = _validate_artifacts(root, result.get("artifacts"))
report = _read_object(_artifact(artifacts, "tracking-run-report").path, root)
metrics = report.get("metrics")
report_input = report.get("input")
if (
report.get("schema_version") != REPORT_SCHEMA
or report.get("state") != "published-qualification"
or report.get("result_id") != root.name
or report.get("ground_truth") is not False
or not isinstance(metrics, dict)
or metrics.get("frames_expected") != frame_count
or metrics.get("frames_processed") != frame_count
or metrics.get("frames_failed") != 0
or metrics.get("frames_skipped") != 0
or not isinstance(report_input, dict)
or report_input.get("job_id") != job.job_id
or report_input.get("input_sha256") != job.input_sha256
or report_input.get("source_start_frame_index") != start_index
or report_input.get("source_end_frame_index") != end_index
):
raise SessionIntegrityError("tracking report is inconsistent")
_validate_frames(
_artifact(artifacts, "tracking-frame-metadata"),
frame_count=frame_count,
source_start=start_index,
timeline_start=float(timeline_start),
timeline_end=float(timeline_end),
)
return TrackingQualificationResult(
result_id=root.name,
result_root=root,
job=job,
frame_count=frame_count,
source_start_frame_index=start_index,
source_end_frame_index=end_index,
timeline_start_seconds=float(timeline_start),
timeline_end_seconds=float(timeline_end),
artifacts=artifacts,
)
def _validate_artifacts(
root: Path,
raw: object,
) -> tuple[TrackingQualificationArtifact, ...]:
expected = {
"tracking-overlay-video": ("tracking.mp4", "video/mp4", None),
"tracking-frame-metadata": ("frames.jsonl", "application/x-ndjson", FRAME_SCHEMA),
"worker-gpu-telemetry": ("gpu-telemetry.jsonl", "application/x-ndjson", None),
"tracking-contact-sheet": ("contact-sheet.png", "image/png", None),
"tracking-run-report": ("run-report.json", "application/json", REPORT_SCHEMA),
}
if not isinstance(raw, list) or len(raw) != len(expected):
raise SessionIntegrityError("tracking artifact set is invalid")
artifacts: list[TrackingQualificationArtifact] = []
seen: set[str] = set()
for value in raw:
if not isinstance(value, dict):
raise SessionIntegrityError("tracking artifact descriptor is invalid")
kind = value.get("kind")
if not isinstance(kind, str) or kind in seen or kind not in expected:
raise SessionIntegrityError("tracking artifact kind is invalid")
seen.add(kind)
expected_path, expected_media_type, expected_schema = expected[kind]
path = root / expected_path
metadata = _confined_regular_file(path, root)
byte_length = value.get("byte_length")
digest = value.get("sha256")
if (
value.get("path") != expected_path
or value.get("media_type") != expected_media_type
or value.get("schema_version") != expected_schema
or not isinstance(byte_length, int)
or isinstance(byte_length, bool)
or not 0 < byte_length <= MAX_SAFE_INTEGER
or metadata.st_size != byte_length
or not isinstance(digest, str)
or _SHA256.fullmatch(digest) is None
or _sha256_file(path) != digest
):
raise SessionIntegrityError("tracking artifact identity changed")
artifacts.append(
TrackingQualificationArtifact(
kind=kind,
path=path,
media_type=expected_media_type,
byte_length=byte_length,
sha256=digest,
schema_version=expected_schema,
)
)
return tuple(artifacts)
def _validate_frames(
artifact: TrackingQualificationArtifact,
*,
frame_count: int,
source_start: int,
timeline_start: float,
timeline_end: float,
) -> None:
previous = -math.inf
count = 0
try:
with artifact.path.open("rb") as stream:
for frame_index, line in enumerate(stream):
if not line or len(line) > MAX_FRAME_LINE_BYTES:
raise SessionIntegrityError("tracking frame line is outside bounds")
value = json.loads(line)
session_seconds = value.get("session_seconds") if isinstance(value, dict) else None
detections = value.get("detections") if isinstance(value, dict) else None
tracks = value.get("tracks") if isinstance(value, dict) else None
if (
not isinstance(value, dict)
or value.get("schema_version") != FRAME_SCHEMA
or value.get("frame_index") != frame_index
or value.get("sequence") != frame_index + 1
or value.get("source_frame_index") != source_start + frame_index
or value.get("source_sequence") != source_start + frame_index + 1
or not _finite_number(session_seconds)
or float(session_seconds) <= previous
or float(session_seconds) < timeline_start - 0.001
or float(session_seconds) > timeline_end + 0.001
or not isinstance(detections, list)
or not isinstance(tracks, list)
):
raise SessionIntegrityError("tracking frame metadata is inconsistent")
_validate_observations(detections, tracks)
previous = float(session_seconds)
count += 1
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SessionIntegrityError("tracking frame metadata is unavailable") from exc
if count != frame_count:
raise SessionIntegrityError("tracking frame metadata count changed")
def _validate_observations(detections: list[object], tracks: list[object]) -> None:
for detection in detections:
if not isinstance(detection, dict):
raise SessionIntegrityError("tracking detection is invalid")
_validate_box(detection.get("bbox_xyxy"))
if (
not isinstance(detection.get("class_id"), int)
or not isinstance(detection.get("label"), str)
or not _finite_number(detection.get("score"))
or not 0 <= float(detection["score"]) <= 1
):
raise SessionIntegrityError("tracking detection fields are invalid")
track_ids: set[int] = set()
for track in tracks:
if not isinstance(track, dict):
raise SessionIntegrityError("tracking observation is invalid")
_validate_box(track.get("bbox_xyxy"))
track_id = track.get("track_id")
if (
not isinstance(track_id, int)
or isinstance(track_id, bool)
or track_id < 1
or track_id in track_ids
or not isinstance(track.get("class_id"), int)
or not isinstance(track.get("label"), str)
or not _finite_number(track.get("score"))
or not isinstance(track.get("hits"), int)
or track["hits"] < 1
):
raise SessionIntegrityError("tracking observation fields are invalid")
track_ids.add(track_id)
def _validate_box(value: object) -> None:
if (
not isinstance(value, list)
or len(value) != 4
or not all(_finite_number(item) for item in value)
or not 0 <= float(value[0]) < float(value[2]) <= 800
or not 0 <= float(value[1]) < float(value[3]) <= 600
):
raise SessionIntegrityError("tracking box is invalid")
def _artifact(
artifacts: tuple[TrackingQualificationArtifact, ...],
kind: str,
) -> TrackingQualificationArtifact:
matches = tuple(item for item in artifacts if item.kind == kind)
if len(matches) != 1:
raise SessionIntegrityError(f"tracking artifact is unavailable: {kind}")
return matches[0]
def _read_object(path: Path, root: Path) -> dict[str, Any]:
metadata = _confined_regular_file(path, root)
if not 0 < metadata.st_size <= MAX_JSON_BYTES:
raise SessionIntegrityError("tracking JSON is outside bounds")
try:
value = json.loads(path.read_text(encoding="utf-8-sig"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise SessionIntegrityError("tracking JSON is unavailable") from exc
if not isinstance(value, dict):
raise SessionIntegrityError("tracking JSON root is invalid")
return value
def _confined_regular_file(path: Path, root: Path) -> os.stat_result:
try:
resolved_root = root.resolve(strict=True)
resolved = path.resolve(strict=True)
metadata = path.lstat()
except OSError as exc:
raise SessionIntegrityError("tracking artifact is unavailable") from exc
if (
stat.S_ISLNK(metadata.st_mode)
or not stat.S_ISREG(metadata.st_mode)
or not resolved.is_relative_to(resolved_root)
):
raise SessionIntegrityError("tracking artifact is not confined")
return metadata
def _sha256_file(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 _canonical_json(value: object) -> bytes:
try:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
except (TypeError, ValueError) as exc:
raise SessionIntegrityError("tracking identity is not canonical JSON") from exc
def _finite_number(value: object) -> TypeGuard[int | float]:
return (
isinstance(value, (int, float))
and not isinstance(value, bool)
and math.isfinite(float(value))
)