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:
@@ -20,9 +20,9 @@ CAMERA_ARCHIVE_SCHEMA = "missioncore.camera-recording/v1"
|
||||
CAMERA_INDEX_SCHEMA = "missioncore.camera-recording-index/v1"
|
||||
RECORDED_MEDIA_MANIFEST_SCHEMA = "missioncore.observation-recorded-media/v2"
|
||||
RECORDED_MEDIA_PREPARATION_SCHEMA = "missioncore.recorded-media-preparation/v1"
|
||||
MAX_MEDIA_INDEX_BYTES = 32 * 1024 * 1024
|
||||
MAX_MEDIA_SUMMARY_BYTES = 2 * 1024 * 1024
|
||||
MAX_MEDIA_INDEX_LINE_BYTES = 64 * 1024
|
||||
MAX_INIT_BYTES = 8 * 1024 * 1024
|
||||
MAX_MEDIA_SEGMENTS = 500_000
|
||||
MAX_MEDIA_SEGMENT_BYTES = 64 * 1024 * 1024
|
||||
MAX_SAFE_INTEGER = (1 << 53) - 1
|
||||
MAX_MP4_BOXES = 100_000
|
||||
@@ -30,7 +30,6 @@ MAX_MP4_SAMPLES_PER_FRAGMENT = 1_000_000
|
||||
MAX_MP4_FRAGMENT_DURATION_SECONDS = 3_600.0
|
||||
MEDIA_RECORDING_TIMELINE_TOLERANCE_SECONDS = 0.05
|
||||
MAX_MEDIA_EPOCHS = 4_096
|
||||
MAX_PREPARED_MEDIA_SIDECAR_BYTES = 256 * 1024 * 1024
|
||||
|
||||
_EPOCH_PATTERN = re.compile(r"^epoch-([1-9][0-9]*)$")
|
||||
_SHA256_PATTERN = re.compile(r"^[a-f0-9]{64}$")
|
||||
@@ -258,7 +257,8 @@ class RecordedMediaInspector:
|
||||
return None
|
||||
path = root / _sidecar_name(artifact.session_id, artifact.artifact_id)
|
||||
try:
|
||||
payload = _read_confined_file(path, root, MAX_PREPARED_MEDIA_SIDECAR_BYTES)
|
||||
sidecar_stat = _confined_file_stat(path, root)
|
||||
payload = _read_confined_file(path, root, max(1, sidecar_stat.st_size))
|
||||
document = _decode_prepared_sidecar(payload)
|
||||
manifest = _manifest_from_sidecar(document, artifact, epoch_paths)
|
||||
identity = _prepared_source_identity(
|
||||
@@ -583,7 +583,7 @@ def _epoch_from_sidecar(
|
||||
segment_documents = value.get("segments")
|
||||
if (
|
||||
not isinstance(segment_documents, list)
|
||||
or not 1 <= len(segment_documents) <= MAX_MEDIA_SEGMENTS
|
||||
or not segment_documents
|
||||
):
|
||||
raise SessionIntegrityError("recorded media prepared segments are invalid")
|
||||
segments_root = epoch_path / "segments"
|
||||
@@ -652,8 +652,8 @@ def _canonical_json(value: object) -> bytes:
|
||||
).encode("utf-8")
|
||||
except (TypeError, ValueError) as exc:
|
||||
raise SessionIntegrityError("recorded media preparation cannot be encoded") from exc
|
||||
if not 0 < len(encoded) <= MAX_PREPARED_MEDIA_SIDECAR_BYTES:
|
||||
raise SessionIntegrityError("recorded media preparation is outside bounds")
|
||||
if not encoded:
|
||||
raise SessionIntegrityError("recorded media preparation is empty")
|
||||
return encoded
|
||||
|
||||
|
||||
@@ -887,7 +887,7 @@ def _read_epoch(
|
||||
origin_epoch_ns: int,
|
||||
origin_monotonic_ns: int,
|
||||
) -> RecordedMediaEpoch:
|
||||
summary = _read_json_object(epoch / "summary.json", MAX_MEDIA_INDEX_BYTES)
|
||||
summary = _read_json_object(epoch / "summary.json", MAX_MEDIA_SUMMARY_BYTES)
|
||||
if (
|
||||
summary.get("schema_version") != CAMERA_ARCHIVE_SCHEMA
|
||||
or summary.get("source_id") != expected_source_name
|
||||
@@ -895,7 +895,10 @@ def _read_epoch(
|
||||
):
|
||||
raise SessionIntegrityError("recorded media summary is incompatible")
|
||||
segment_count = summary.get("segment_count")
|
||||
if not _non_negative_int(segment_count) or not 1 <= int(segment_count) <= MAX_MEDIA_SEGMENTS:
|
||||
if (
|
||||
not _non_negative_int(segment_count)
|
||||
or not 1 <= int(segment_count) <= MAX_SAFE_INTEGER
|
||||
):
|
||||
raise SessionIntegrityError("recorded media segment count is invalid")
|
||||
match = _EPOCH_PATTERN.fullmatch(epoch.name)
|
||||
if (
|
||||
@@ -913,39 +916,17 @@ def _read_epoch(
|
||||
):
|
||||
raise SessionIntegrityError("recorded media summary aggregate is inconsistent")
|
||||
|
||||
try:
|
||||
raw_index = _read_confined_file(
|
||||
epoch / "index.jsonl",
|
||||
epoch,
|
||||
MAX_MEDIA_INDEX_BYTES,
|
||||
)
|
||||
raw_lines = raw_index.decode("utf-8").splitlines()
|
||||
except UnicodeDecodeError as exc:
|
||||
raise SessionIntegrityError("recorded media index is unavailable") from exc
|
||||
entries, index_sha256 = _read_media_index(
|
||||
epoch / "index.jsonl",
|
||||
epoch,
|
||||
expected_count=int(segment_count),
|
||||
)
|
||||
expected_index_sha256 = summary.get("index_sha256")
|
||||
if (
|
||||
not isinstance(expected_index_sha256, str)
|
||||
or hashlib.sha256(raw_index).hexdigest() != expected_index_sha256
|
||||
or index_sha256 != expected_index_sha256
|
||||
):
|
||||
raise SessionIntegrityError("recorded media index digest changed")
|
||||
if len(raw_lines) != int(segment_count):
|
||||
raise SessionIntegrityError("recorded media index length does not match its summary")
|
||||
entries: list[dict[str, Any]] = []
|
||||
for sequence, line in enumerate(raw_lines, start=1):
|
||||
try:
|
||||
entry = json.loads(line)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise SessionIntegrityError("recorded media index contains invalid JSON") from exc
|
||||
if not isinstance(entry, dict):
|
||||
raise SessionIntegrityError("recorded media index entry is not an object")
|
||||
if (
|
||||
entry.get("schema_version") != CAMERA_INDEX_SCHEMA
|
||||
or entry.get("sequence") != sequence
|
||||
or entry.get("kind") != "media"
|
||||
or entry.get("path") != f"segments/{sequence}.m4s"
|
||||
):
|
||||
raise SessionIntegrityError("recorded media index entry is inconsistent")
|
||||
entries.append(entry)
|
||||
|
||||
use_monotonic_clock = all(
|
||||
_non_negative_int(entry.get("host_monotonic_ns")) for entry in entries
|
||||
@@ -1572,6 +1553,81 @@ def _read_first_confined_line(path: Path, parent: Path, maximum_bytes: int) -> b
|
||||
os.close(parent_fd)
|
||||
|
||||
|
||||
def _read_media_index(
|
||||
path: Path,
|
||||
parent: Path,
|
||||
*,
|
||||
expected_count: int,
|
||||
) -> tuple[list[dict[str, Any]], str]:
|
||||
"""Stream a sealed JSONL index without imposing a recording-duration cap."""
|
||||
|
||||
try:
|
||||
resolved_parent = parent.resolve(strict=True)
|
||||
if path.parent.resolve(strict=True) != resolved_parent:
|
||||
raise SessionIntegrityError("recorded media index escapes its epoch")
|
||||
parent_fd = os.open(
|
||||
resolved_parent,
|
||||
os.O_RDONLY | getattr(os, "O_DIRECTORY", 0) | getattr(os, "O_NOFOLLOW", 0),
|
||||
)
|
||||
except OSError as exc:
|
||||
raise SessionIntegrityError("recorded media index is missing") from exc
|
||||
descriptor = -1
|
||||
try:
|
||||
descriptor = os.open(
|
||||
path.name,
|
||||
os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0),
|
||||
dir_fd=parent_fd,
|
||||
)
|
||||
before = os.fstat(descriptor)
|
||||
if not stat.S_ISREG(before.st_mode) or before.st_size <= 0:
|
||||
raise SessionIntegrityError("recorded media index is not regular")
|
||||
entries: list[dict[str, Any]] = []
|
||||
digest = hashlib.sha256()
|
||||
with os.fdopen(descriptor, "rb") as stream:
|
||||
descriptor = -1
|
||||
for sequence in range(1, expected_count + 1):
|
||||
raw_line = stream.readline(MAX_MEDIA_INDEX_LINE_BYTES + 1)
|
||||
if not raw_line or len(raw_line) > MAX_MEDIA_INDEX_LINE_BYTES:
|
||||
raise SessionIntegrityError(
|
||||
"recorded media index line is missing or outside bounds"
|
||||
)
|
||||
if not raw_line.endswith(b"\n"):
|
||||
raise SessionIntegrityError("recorded media index has an incomplete line")
|
||||
digest.update(raw_line)
|
||||
try:
|
||||
entry = json.loads(raw_line)
|
||||
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
|
||||
raise SessionIntegrityError(
|
||||
"recorded media index contains invalid JSON"
|
||||
) from exc
|
||||
if (
|
||||
not isinstance(entry, dict)
|
||||
or entry.get("schema_version") != CAMERA_INDEX_SCHEMA
|
||||
or entry.get("sequence") != sequence
|
||||
or entry.get("kind") != "media"
|
||||
or entry.get("path") != f"segments/{sequence}.m4s"
|
||||
):
|
||||
raise SessionIntegrityError("recorded media index entry is inconsistent")
|
||||
entries.append(entry)
|
||||
if stream.read(1):
|
||||
raise SessionIntegrityError(
|
||||
"recorded media index length does not match its summary"
|
||||
)
|
||||
after = os.fstat(stream.fileno())
|
||||
if (
|
||||
(after.st_dev, after.st_ino, after.st_size, after.st_mtime_ns)
|
||||
!= (before.st_dev, before.st_ino, before.st_size, before.st_mtime_ns)
|
||||
):
|
||||
raise SessionIntegrityError("recorded media index changed during validation")
|
||||
return entries, digest.hexdigest()
|
||||
except OSError as exc:
|
||||
raise SessionIntegrityError("recorded media index is unavailable") from exc
|
||||
finally:
|
||||
if descriptor >= 0:
|
||||
os.close(descriptor)
|
||||
os.close(parent_fd)
|
||||
|
||||
|
||||
def _read_json_object(path: Path, maximum_bytes: int) -> dict[str, Any]:
|
||||
try:
|
||||
value = json.loads(_read_confined_file(path, path.parent, maximum_bytes))
|
||||
|
||||
@@ -23,15 +23,18 @@ from .plugin_contract import (
|
||||
RecordingExporter,
|
||||
)
|
||||
|
||||
# v9 derives both real RRD boundary rows from the durable capture-clock
|
||||
# envelope. v8 used the first/last MQTT message and could still exclude a
|
||||
# camera fragment produced between source startup/shutdown and those packets.
|
||||
CACHE_SCHEMA = "missioncore.derived-rerun-recording-cache/v9"
|
||||
# v12 publishes a 2 Hz point-cloud operator projection while retaining complete
|
||||
# normal K1 point batches. v11 also spatially thinned every ~2.4k-point frame;
|
||||
# the latest-at AI view therefore looked visibly bald even though the raw
|
||||
# capture was complete. Frames above the explicit emergency threshold remain
|
||||
# bounded, and the native capture stays the source of record and AI input.
|
||||
CACHE_SCHEMA = "missioncore.derived-rerun-recording-cache/v12"
|
||||
COMPATIBLE_CACHE_SCHEMAS = frozenset({CACHE_SCHEMA})
|
||||
RECORDING_CACHE_FILENAME = "scene.operator-v12.rrd"
|
||||
RECORDING_CACHE_SIDECAR_FILENAME = f"{RECORDING_CACHE_FILENAME}.cache.json"
|
||||
RERUN_RECORDING_MEDIA_TYPE = "application/vnd.rerun.rrd"
|
||||
RERUN_SESSION_TIMELINE = "session_time"
|
||||
SESSION_ID_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$")
|
||||
DEFAULT_CACHE_MAX_BYTES = 8 * 1024 * 1024 * 1024
|
||||
DEFAULT_FREE_SPACE_RESERVE_BYTES = 2 * 1024 * 1024 * 1024
|
||||
|
||||
RrdExporter = Callable[..., Mapping[str, object]]
|
||||
@@ -154,10 +157,9 @@ class SessionRecordingMaterializer:
|
||||
self._exporters = dict(exporters or {})
|
||||
if len(self._exporters) != len(set(self._exporters)):
|
||||
raise ValueError("recording exporter plugin ids must be unique")
|
||||
self.cache_max_bytes = _positive_configuration(
|
||||
self.cache_max_bytes = _optional_positive_configuration(
|
||||
cache_max_bytes,
|
||||
environment_name="MISSIONCORE_RRD_CACHE_MAX_BYTES",
|
||||
default=DEFAULT_CACHE_MAX_BYTES,
|
||||
)
|
||||
self.free_space_reserve_bytes = _non_negative_configuration(
|
||||
free_space_reserve_bytes,
|
||||
@@ -198,7 +200,7 @@ class SessionRecordingMaterializer:
|
||||
return False
|
||||
return (
|
||||
path.parent.parent == self.recordings_root
|
||||
and path.name == "scene.rrd"
|
||||
and path.name == RECORDING_CACHE_FILENAME
|
||||
and stat.S_ISREG(path_stat.st_mode)
|
||||
and not stat.S_ISLNK(path_stat.st_mode)
|
||||
and path_stat.st_size == recording.byte_length
|
||||
@@ -425,7 +427,10 @@ class SessionRecordingMaterializer:
|
||||
and name.endswith(".tmp")
|
||||
)
|
||||
or (name.startswith(".source.") and name.endswith(".tmp"))
|
||||
or (name.startswith(".scene.rrd.cache.json.") and name.endswith(".tmp"))
|
||||
or (
|
||||
name.startswith(f".{RECORDING_CACHE_SIDECAR_FILENAME}.")
|
||||
and name.endswith(".tmp")
|
||||
)
|
||||
)
|
||||
if not stale:
|
||||
continue
|
||||
@@ -464,8 +469,8 @@ class SessionRecordingMaterializer:
|
||||
_chmod_best_effort(resolved_session_root, 0o700)
|
||||
return (
|
||||
resolved_session_root,
|
||||
resolved_session_root / "scene.rrd",
|
||||
resolved_session_root / "scene.rrd.cache.json",
|
||||
resolved_session_root / RECORDING_CACHE_FILENAME,
|
||||
resolved_session_root / RECORDING_CACHE_SIDECAR_FILENAME,
|
||||
)
|
||||
|
||||
def _load_cached_recording(
|
||||
@@ -609,7 +614,10 @@ class SessionRecordingMaterializer:
|
||||
candidate_path,
|
||||
"derived recording candidate",
|
||||
)
|
||||
if candidate_stat.st_size > self.cache_max_bytes:
|
||||
if (
|
||||
self.cache_max_bytes is not None
|
||||
and candidate_stat.st_size > self.cache_max_bytes
|
||||
):
|
||||
raise RecordingMaterializationError("derived recording exceeds the cache quota")
|
||||
self._ensure_cache_capacity(
|
||||
required_bytes=4 * 1024,
|
||||
@@ -802,7 +810,10 @@ class SessionRecordingMaterializer:
|
||||
while True:
|
||||
total_bytes, entries = _cache_entries(self.recordings_root)
|
||||
free_bytes = shutil.disk_usage(self.recordings_root).free
|
||||
quota_ok = total_bytes + required_bytes <= self.cache_max_bytes
|
||||
quota_ok = (
|
||||
self.cache_max_bytes is None
|
||||
or total_bytes + required_bytes <= self.cache_max_bytes
|
||||
)
|
||||
reserve_ok = free_bytes >= self.free_space_reserve_bytes + required_bytes
|
||||
if quota_ok and reserve_ok:
|
||||
return
|
||||
@@ -1372,16 +1383,17 @@ def _touch_lru(path: Path) -> None:
|
||||
return
|
||||
|
||||
|
||||
def _positive_configuration(
|
||||
def _optional_positive_configuration(
|
||||
configured: int | None,
|
||||
*,
|
||||
environment_name: str,
|
||||
default: int,
|
||||
) -> int:
|
||||
) -> int | None:
|
||||
value = configured
|
||||
if value is None:
|
||||
raw = os.environ.get(environment_name, "").strip()
|
||||
value = int(raw) if raw else default
|
||||
value = int(raw) if raw else None
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, bool) or value <= 0:
|
||||
raise ValueError(f"{environment_name} must be a positive integer")
|
||||
return value
|
||||
|
||||
Reference in New Issue
Block a user