feat(lab): complete E30 evidence review gate

This commit is contained in:
DCCONSTRUCTIONS
2026-07-27 11:00:32 +03:00
parent a44d7627fd
commit 001d597a89
55 changed files with 15897 additions and 1548 deletions
+259
View File
@@ -0,0 +1,259 @@
"""Exact camera-frame evidence for an E30 materialization.
The E30 point substrate is useful only when a reviewer can see the camera
observation that caused the semantic claim. This module binds an immutable
camera compute job and decodes only the exact source-frame indices selected by
the E30 review pack. The canonical camera archive remains unchanged.
"""
from __future__ import annotations
import hashlib
import os
import shutil
import subprocess
import threading
from dataclasses import dataclass
from pathlib import Path
from typing import Final
from PIL import Image
from k1link.compute.jobs import CameraComputeJob, validate_camera_compute_job
_READ_CHUNK_BYTES: Final = 1024 * 1024
_FFMPEG_TIMEOUT_SECONDS: Final = 600.0
class E30CameraEvidenceError(ValueError):
"""The bound camera job or a decoded evidence frame is invalid."""
@dataclass(frozen=True, slots=True)
class E30CameraEvidenceSource:
job: CameraComputeJob
epoch_root: Path
ffmpeg_path: Path
encoder_identity: str
def identity(self) -> dict[str, object]:
return {
"schema_version": "missioncore.e30-camera-evidence-source/v1",
"job_id": self.job.job_id,
"input_sha256": self.job.input_sha256,
"session_id": self.job.session_id,
"source_id": self.job.source_id,
"codec_epoch": self.job.codec_epoch,
"segment_count": self.job.segment_count,
"source_frame_basis": "zero-based-decoded-video-frame",
"camera_sequence_basis": "source-frame-index-plus-one",
"decoder": {
"name": "ffmpeg",
"identity": self.encoder_identity,
"output": "jpeg-q2-yuvj420p",
},
}
def open_e30_camera_evidence_source(
*,
camera_job_root: Path,
ffmpeg_path: Path,
expected_session_id: str,
expected_source_id: str,
) -> E30CameraEvidenceSource:
"""Validate the immutable camera job and bind its decoder identity."""
try:
job = validate_camera_compute_job(camera_job_root)
except (OSError, ValueError) as exc:
raise E30CameraEvidenceError("E30 camera compute job is invalid") from exc
if (
job.session_id != expected_session_id
or job.source_id != expected_source_id
):
raise E30CameraEvidenceError(
"E30 camera job does not match the LiDAR/projection source"
)
decoder = ffmpeg_path.expanduser().resolve(strict=True)
if not decoder.is_file():
raise E30CameraEvidenceError("ffmpeg is unavailable")
try:
version = subprocess.run(
[str(decoder), "-version"],
check=True,
capture_output=True,
text=True,
timeout=10,
)
except (OSError, subprocess.SubprocessError) as exc:
raise E30CameraEvidenceError("ffmpeg identity is unavailable") from exc
version_line = version.stdout.splitlines()[0].strip()
if not version_line:
raise E30CameraEvidenceError("ffmpeg identity is empty")
epoch = (
job.job_root
/ "input"
/ "camera"
/ job.source_id
/ f"epoch-{job.codec_epoch}"
).resolve(strict=True)
if not epoch.is_dir() or not epoch.is_relative_to(job.job_root):
raise E30CameraEvidenceError("E30 camera epoch escaped its compute job")
return E30CameraEvidenceSource(
job=job,
epoch_root=epoch,
ffmpeg_path=decoder,
encoder_identity=hashlib.sha256(version_line.encode()).hexdigest(),
)
def materialize_e30_camera_frames(
*,
source: E30CameraEvidenceSource,
source_frame_indices: tuple[int, ...],
destination_root: Path,
width: int,
height: int,
) -> dict[int, dict[str, object]]:
"""Decode exact source frames once and return verified artifact metadata."""
frames = tuple(sorted(set(source_frame_indices)))
if (
not frames
or any(
isinstance(frame, bool)
or frame < 0
or frame >= source.job.segment_count
for frame in frames
)
or width < 1
or height < 1
):
raise E30CameraEvidenceError("E30 camera frame selection is invalid")
destination = destination_root.resolve()
destination.mkdir(mode=0o700, parents=True, exist_ok=False)
init_path = source.epoch_root / "init.mp4"
segments = tuple(
source.epoch_root / "segments" / f"{sequence}.m4s"
for sequence in range(1, source.job.segment_count + 1)
)
select = "+".join(f"eq(n\\,{frame})" for frame in frames)
temporary_pattern = destination / ".decoded-%06d.jpg"
read_fd, write_fd = os.pipe()
process = subprocess.Popen(
[
str(source.ffmpeg_path),
"-hide_banner",
"-loglevel",
"error",
"-i",
"pipe:0",
"-vf",
f"select={select}",
"-fps_mode",
"passthrough",
"-frames:v",
str(len(frames)),
"-q:v",
"2",
"-pix_fmt",
"yuvj420p",
"-start_number",
"0",
str(temporary_pattern),
],
stdin=read_fd,
stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE,
shell=False,
)
os.close(read_fd)
feeder_errors: list[BaseException] = []
def feed_archive() -> None:
try:
with os.fdopen(write_fd, "wb", buffering=0) as sink:
for path in (init_path, *segments):
with path.open("rb") as stream:
shutil.copyfileobj(
stream,
sink,
length=_READ_CHUNK_BYTES,
)
except BrokenPipeError:
return
except BaseException as exc: # pragma: no cover - child pipe boundary
feeder_errors.append(exc)
feeder = threading.Thread(
target=feed_archive,
name="e30-camera-evidence-ffmpeg",
daemon=True,
)
feeder.start()
try:
_, stderr = process.communicate(timeout=_FFMPEG_TIMEOUT_SECONDS)
except subprocess.TimeoutExpired as exc:
process.kill()
process.communicate()
raise E30CameraEvidenceError("E30 camera extraction timed out") from exc
feeder.join(timeout=10.0)
if feeder.is_alive():
raise E30CameraEvidenceError("E30 camera archive feeder did not stop")
if feeder_errors:
raise E30CameraEvidenceError(
"E30 camera archive streaming failed"
) from feeder_errors[0]
if process.returncode != 0:
message = (stderr or b"").decode(errors="replace").strip()[-1000:]
raise E30CameraEvidenceError(
f"ffmpeg rejected the E30 camera archive: {message}"
)
artifacts: dict[int, dict[str, object]] = {}
for ordinal, frame_index in enumerate(frames):
decoded = destination / f".decoded-{ordinal:06d}.jpg"
final = destination / f"frame-{frame_index:06d}.jpg"
if decoded.is_symlink() or not decoded.is_file():
raise E30CameraEvidenceError(
"ffmpeg returned an incomplete E30 camera frame set"
)
try:
with Image.open(decoded) as image:
image.verify()
with Image.open(decoded) as image:
decoded_size = image.size
decoded_format = image.format
except (OSError, ValueError) as exc:
raise E30CameraEvidenceError(
"an E30 camera frame is not a valid image"
) from exc
if decoded_size != (width, height) or decoded_format != "JPEG":
raise E30CameraEvidenceError(
"an E30 camera frame has an unexpected format"
)
os.replace(decoded, final)
artifacts[frame_index] = {
"role": "camera-frame",
"path": f"frames/{final.name}",
"media_type": "image/jpeg",
"byte_length": final.stat().st_size,
"sha256": _sha256_file(final),
"width": width,
"height": height,
"source_frame_index": frame_index,
"camera_sequence": frame_index + 1,
"exact_source_frame": True,
}
if any(destination.glob(".decoded-*.jpg")):
raise E30CameraEvidenceError("ffmpeg returned undeclared E30 frames")
return artifacts
def _sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
while chunk := stream.read(_READ_CHUNK_BYTES):
digest.update(chunk)
return digest.hexdigest()
@@ -0,0 +1,645 @@
"""Immutable AI-assisted engineering generations over E30 evidence.
This contract is intentionally separate from ``e30_human_review``. It records
an explicitly AI-assisted engineering assessment, retains uncertainty, and
routes only bounded exceptions to a human. It never claims human ground truth
or navigation/safety acceptance.
"""
from __future__ import annotations
import hashlib
import json
import math
import os
import re
import shutil
import tempfile
from collections import Counter
from pathlib import Path
from typing import Any, Final
from k1link.artifacts import utc_now_iso, write_json_atomic
E30_ENGINEERING_DECISION_SCHEMA_V1: Final = (
"missioncore.e30-engineering-decision/v1"
)
E30_ENGINEERING_DECISION_SCHEMA: Final = (
"missioncore.e30-engineering-decision/v2"
)
E30_ENGINEERING_DECISION_SCHEMAS: Final = (
E30_ENGINEERING_DECISION_SCHEMA_V1,
E30_ENGINEERING_DECISION_SCHEMA,
)
E30_ENGINEERING_GENERATION_SCHEMA: Final = (
"missioncore.e30-engineering-generation/v1"
)
E30_ENGINEERING_SUMMARY_SCHEMA: Final = (
"missioncore.e30-engineering-summary/v1"
)
E30_ENGINEERING_CAUSES_SCHEMA: Final = (
"missioncore.e30-engineering-causes/v1"
)
E30_ENGINEERING_EXCEPTION_SCHEMA_V1: Final = (
"missioncore.e30-engineering-exception/v1"
)
E30_ENGINEERING_EXCEPTION_SCHEMA: Final = (
"missioncore.e30-engineering-exception/v2"
)
E30_ENGINEERING_EXCEPTION_SCHEMAS: Final = (
E30_ENGINEERING_EXCEPTION_SCHEMA_V1,
E30_ENGINEERING_EXCEPTION_SCHEMA,
)
E30_ENGINEERING_DECISIONS_NAME: Final = "engineering-decisions.jsonl"
E30_ENGINEERING_EXCEPTIONS_NAME: Final = "human-exceptions.jsonl"
E30_ENGINEERING_CAUSES_NAME: Final = "cause-distribution.json"
E30_ENGINEERING_SUMMARY_NAME: Final = "summary.json"
E30_ENGINEERING_MANIFEST_NAME: Final = "manifest.json"
STRATA: Final = (
"conflict",
"agree",
"camera-only",
"unknown",
"geometry-only",
)
VERDICTS: Final = ("confirmed", "corrected", "insufficient-evidence")
DETECTOR_ASSESSMENTS: Final = (
"valid",
"class-mismatch",
"false-positive",
"missed-object",
"not-applicable",
"insufficient-evidence",
)
PROJECTION_ASSESSMENTS: Final = ("aligned", "misaligned", "not-assessable")
OWNERSHIP_ASSESSMENTS: Final = (
"object",
"surface-or-background",
"static-environment",
"self",
"insufficient-support",
"not-applicable",
"mixed",
"insufficient-evidence",
)
EXCEPTION_DISPOSITIONS: Final = (
"object-present",
"background-or-noise",
"insufficient-evidence",
)
_MATERIALIZATION_ID = re.compile(r"^e30-materialization-[a-f0-9]{64}$")
_GENERATION_ID = re.compile(r"^e30-engineering-generation-[a-f0-9]{64}$")
_ITEM_ID = re.compile(r"^e30-review-item-[a-f0-9]{64}$")
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
_SAFE_PRODUCER = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,159}$")
class E30EngineeringGenerationError(RuntimeError):
"""An engineering generation or its immutable source is invalid."""
def _canonical_json(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
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 _regular_file(root: Path, relative: object) -> Path:
if not isinstance(relative, str) or not relative or relative.startswith("/"):
raise E30EngineeringGenerationError("artifact path is invalid")
path = root / relative
if (
path.is_symlink()
or not path.is_file()
or root.resolve() not in path.resolve().parents
):
raise E30EngineeringGenerationError("artifact is unavailable")
return path
def _read_json(path: Path) -> dict[str, Any]:
if path.is_symlink() or not path.is_file():
raise E30EngineeringGenerationError("required JSON is unavailable")
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise E30EngineeringGenerationError("required JSON is invalid") from exc
if not isinstance(value, dict):
raise E30EngineeringGenerationError("required JSON must be an object")
return value
def _read_jsonl(path: Path) -> list[dict[str, Any]]:
if path.is_symlink() or not path.is_file():
raise E30EngineeringGenerationError("required JSONL is unavailable")
values: list[dict[str, Any]] = []
with path.open("r", encoding="utf-8") as stream:
for line_number, line in enumerate(stream, start=1):
try:
value = json.loads(line)
except json.JSONDecodeError as exc:
raise E30EngineeringGenerationError(
f"invalid JSONL line {line_number}"
) from exc
if not isinstance(value, dict):
raise E30EngineeringGenerationError(
f"JSONL line {line_number} must be an object"
)
values.append(value)
return values
def _validate_artifact(root: Path, value: object) -> Path:
if not isinstance(value, dict):
raise E30EngineeringGenerationError("artifact metadata is invalid")
path = _regular_file(root, value.get("path"))
digest = value.get("sha256")
byte_length = value.get("byte_length")
if (
not isinstance(digest, str)
or _SHA256.fullmatch(digest) is None
or not isinstance(byte_length, int)
or isinstance(byte_length, bool)
or byte_length <= 0
or path.stat().st_size != byte_length
or _sha256(path) != digest
):
raise E30EngineeringGenerationError("artifact content changed")
return path
def _materialization(
root: Path,
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
root = root.resolve()
if (
root.is_symlink()
or not root.is_dir()
or _MATERIALIZATION_ID.fullmatch(root.name) is None
):
raise E30EngineeringGenerationError("materialization root is invalid")
manifest_path = root / "manifest.json"
index_path = root / "materialized-items.jsonl"
manifest = _read_json(manifest_path)
identity = manifest.get("identity")
identity_sha256 = manifest.get("identity_sha256")
if (
manifest.get("schema_version")
!= "missioncore.e30-evidence-materialization/v2"
or manifest.get("result_id") != root.name
or not isinstance(identity, dict)
or not isinstance(identity_sha256, str)
or _SHA256.fullmatch(identity_sha256) is None
or hashlib.sha256(_canonical_json(identity)).hexdigest()
!= identity_sha256
or root.name != f"e30-materialization-{identity_sha256}"
or manifest.get("camera_evidence_available") is not True
or manifest.get("human_review_complete") is not False
or manifest.get("lab_published") is not False
):
raise E30EngineeringGenerationError("materialization identity changed")
authority = manifest.get("authority")
if (
not isinstance(authority, dict)
or authority.get("commands_enabled") is not False
or authority.get("navigation_or_safety_accepted") is not False
):
raise E30EngineeringGenerationError("materialization authority differs")
items = _read_jsonl(index_path)
if (
len(items) != manifest.get("item_count")
or len(items) != 486
or len({item.get("item_id") for item in items}) != len(items)
):
raise E30EngineeringGenerationError("materialization item count differs")
for expected_sequence, item in enumerate(items):
if (
item.get("schema_version")
!= "missioncore.e30-evidence-materialization-item/v2"
or item.get("sequence") != expected_sequence
or _ITEM_ID.fullmatch(str(item.get("item_id"))) is None
or item.get("stratum") not in STRATA
):
raise E30EngineeringGenerationError("materialization item changed")
_validate_artifact(root, item.get("artifact"))
_validate_artifact(root, item.get("camera_frame"))
return manifest, items
def _reason_taxonomy(
root: Path,
*,
expected_binding: object,
) -> tuple[str, ...]:
root = root.resolve()
if root.is_symlink() or not root.is_dir():
raise E30EngineeringGenerationError("review pack root is invalid")
manifest = _read_json(root / "manifest.json")
identity = manifest.get("identity")
if not isinstance(expected_binding, dict) or not isinstance(identity, dict):
raise E30EngineeringGenerationError("review pack binding is invalid")
if (
manifest.get("schema_version")
!= "missioncore.e30-evidence-review-pack/v1"
or manifest.get("result_id") != expected_binding.get("result_id")
or root.name != expected_binding.get("result_id")
or manifest.get("identity_sha256")
!= expected_binding.get("identity_sha256")
or manifest.get("selected_item_count")
!= expected_binding.get("item_count")
):
raise E30EngineeringGenerationError("review pack identity changed")
reason_taxonomy = identity.get("reason_taxonomy")
if (
not isinstance(reason_taxonomy, list)
or not reason_taxonomy
or not all(isinstance(value, str) and value for value in reason_taxonomy)
):
raise E30EngineeringGenerationError("reason taxonomy is unavailable")
return tuple(reason_taxonomy)
def _sheets(
root: Path,
*,
materialization_root: Path,
) -> tuple[dict[str, Any], dict[str, dict[str, object]]]:
root = root.resolve()
if root.is_symlink() or not root.is_dir():
raise E30EngineeringGenerationError("review sheet root is invalid")
manifest = _read_json(root / "manifest.json")
identity_sha256 = manifest.get("identity_sha256")
identity = {
key: value
for key, value in manifest.items()
if key
not in {
"identity_sha256",
"sheet_count",
"item_count",
"authority",
}
}
if (
manifest.get("schema_version")
!= "missioncore.e30-engineering-review-sheets/v1"
or manifest.get("materialization_id") != materialization_root.name
or not isinstance(identity_sha256, str)
or _SHA256.fullmatch(identity_sha256) is None
or hashlib.sha256(_canonical_json(identity)).hexdigest()
!= identity_sha256
or manifest.get("item_count") != 486
or manifest.get("sheet_count") != len(manifest.get("sheets", []))
):
raise E30EngineeringGenerationError("review sheet identity changed")
by_item: dict[str, dict[str, object]] = {}
sheets = manifest.get("sheets")
if not isinstance(sheets, list):
raise E30EngineeringGenerationError("review sheet list is invalid")
for sheet in sheets:
if not isinstance(sheet, dict):
raise E30EngineeringGenerationError("review sheet metadata is invalid")
path = _regular_file(root, sheet.get("path"))
if (
path.stat().st_size != sheet.get("byte_length")
or _sha256(path) != sheet.get("sha256")
or not isinstance(sheet.get("entries"), list)
):
raise E30EngineeringGenerationError("review sheet changed")
for entry in sheet["entries"]:
if (
not isinstance(entry, dict)
or _ITEM_ID.fullmatch(str(entry.get("item_id"))) is None
or entry["item_id"] in by_item
):
raise E30EngineeringGenerationError("review sheet entry is invalid")
by_item[str(entry["item_id"])] = {
"path": sheet["path"],
"sha256": sheet["sha256"],
"ordinal": entry.get("ordinal"),
}
if len(by_item) != 486:
raise E30EngineeringGenerationError("review sheet coverage differs")
return manifest, by_item
def _validate_decision(
*,
value: dict[str, Any],
item: dict[str, Any],
reason_taxonomy: tuple[str, ...],
expected_sheet: dict[str, object],
) -> dict[str, Any]:
confidence = value.get("confidence")
verdict = value.get("verdict")
effective_stratum = value.get("effective_stratum")
cause_code = value.get("cause_code")
exception_required = value.get("human_exception_required")
exception_reason = value.get("exception_reason")
note = value.get("evidence_note")
review_prompt = value.get("review_prompt")
if (
value.get("schema_version") != E30_ENGINEERING_DECISION_SCHEMA
or value.get("sequence") != item["sequence"]
or value.get("item_id") != item["item_id"]
or value.get("review_key") != item["review_key"]
or value.get("source_stratum") != item["stratum"]
or verdict not in VERDICTS
or effective_stratum not in (*STRATA, None)
or value.get("detector_assessment") not in DETECTOR_ASSESSMENTS
or value.get("projection_assessment") not in PROJECTION_ASSESSMENTS
or value.get("point_ownership") not in OWNERSHIP_ASSESSMENTS
or not isinstance(confidence, (int, float))
or isinstance(confidence, bool)
or not math.isfinite(float(confidence))
or not 0.0 <= float(confidence) <= 1.0
or not isinstance(exception_required, bool)
or exception_reason not in (None, "ambiguity", "high-impact")
or exception_required != (exception_reason is not None)
or not isinstance(note, str)
or not note.strip()
or len(note) > 1_000
or value.get("review_sheet") != expected_sheet
):
raise E30EngineeringGenerationError("engineering decision is invalid")
normalized_review_prompt: dict[str, object] | None = None
if exception_required:
if (
not isinstance(review_prompt, dict)
or set(review_prompt) != {"question", "focus", "effects"}
or not isinstance(review_prompt.get("question"), str)
or not review_prompt["question"].strip()
or len(review_prompt["question"]) > 240
or not isinstance(review_prompt.get("focus"), str)
or not review_prompt["focus"].strip()
or len(review_prompt["focus"]) > 500
or not isinstance(review_prompt.get("effects"), dict)
or set(review_prompt["effects"]) != set(EXCEPTION_DISPOSITIONS)
or any(
not isinstance(review_prompt["effects"].get(disposition), str)
or not review_prompt["effects"][disposition].strip()
or len(review_prompt["effects"][disposition]) > 500
for disposition in EXCEPTION_DISPOSITIONS
)
):
raise E30EngineeringGenerationError(
"engineering exception review prompt is invalid"
)
normalized_review_prompt = {
"question": review_prompt["question"].strip(),
"focus": review_prompt["focus"].strip(),
"effects": {
disposition: review_prompt["effects"][disposition].strip()
for disposition in EXCEPTION_DISPOSITIONS
},
}
elif review_prompt is not None:
raise E30EngineeringGenerationError(
"non-exception decision carries a review prompt"
)
if cause_code is not None and cause_code not in reason_taxonomy:
raise E30EngineeringGenerationError("decision cause is outside taxonomy")
if verdict == "confirmed" and effective_stratum != item["stratum"]:
raise E30EngineeringGenerationError("confirmed decision changes stratum")
if verdict == "corrected" and effective_stratum == item["stratum"]:
raise E30EngineeringGenerationError("corrected decision keeps stratum")
if verdict == "insufficient-evidence" and (
effective_stratum is not None or not exception_required
):
raise E30EngineeringGenerationError(
"insufficient decision must route an exception"
)
if verdict != "insufficient-evidence" and confidence < 0.5:
raise E30EngineeringGenerationError("issued verdict confidence is too low")
return {
**value,
"confidence": round(float(confidence), 4),
"evidence_note": note.strip(),
"review_prompt": normalized_review_prompt,
}
def build_e30_engineering_generation(
*,
materialization_root: Path,
review_pack_root: Path,
review_sheets_root: Path,
decisions_path: Path,
output_root: Path,
producer_id: str,
method_id: str,
) -> dict[str, Any]:
"""Validate 486 decisions and publish one immutable engineering generation."""
if (
_SAFE_PRODUCER.fullmatch(producer_id) is None
or _SAFE_PRODUCER.fullmatch(method_id) is None
):
raise E30EngineeringGenerationError("producer identity is invalid")
materialization_root = materialization_root.resolve()
materialization_manifest, items = _materialization(
materialization_root
)
taxonomy = _reason_taxonomy(
review_pack_root,
expected_binding=materialization_manifest["identity"].get("review_pack"),
)
sheet_manifest, sheets_by_item = _sheets(
review_sheets_root,
materialization_root=materialization_root,
)
raw_decisions = _read_jsonl(decisions_path)
if len(raw_decisions) != len(items):
raise E30EngineeringGenerationError("engineering coverage is incomplete")
decisions = [
_validate_decision(
value=value,
item=item,
reason_taxonomy=taxonomy,
expected_sheet=sheets_by_item[str(item["item_id"])],
)
for item, value in zip(items, raw_decisions, strict=True)
]
if len({decision["item_id"] for decision in decisions}) != len(decisions):
raise E30EngineeringGenerationError("engineering decision ids repeat")
source = {
"materialization_id": materialization_root.name,
"materialization_identity_sha256": materialization_manifest[
"identity_sha256"
],
"materialization_manifest_sha256": _sha256(
materialization_root / "manifest.json"
),
"materialization_index_sha256": _sha256(
materialization_root / "materialized-items.jsonl"
),
"review_pack_id": materialization_manifest["identity"]["review_pack"][
"result_id"
],
"item_count": len(items),
}
producer = {
"kind": "ai-assisted-engineering-review",
"producer_id": producer_id,
"method_id": method_id,
"review_sheet_identity_sha256": sheet_manifest["identity_sha256"],
"claims_human_ground_truth": False,
}
decisions_input_sha256 = _sha256(decisions_path)
identity = {
"schema_version": E30_ENGINEERING_GENERATION_SCHEMA,
"source": source,
"producer": producer,
"decisions_input_sha256": decisions_input_sha256,
"decision_content_sha256": hashlib.sha256(
b"".join(_canonical_json(value) + b"\n" for value in decisions)
).hexdigest(),
}
identity_sha256 = hashlib.sha256(_canonical_json(identity)).hexdigest()
generation_id = f"e30-engineering-generation-{identity_sha256}"
if _GENERATION_ID.fullmatch(generation_id) is None:
raise AssertionError("generated E30 identity is invalid")
output_root.mkdir(parents=True, exist_ok=True)
output_root = output_root.resolve()
destination = output_root / generation_id
if destination.exists():
existing = _read_json(destination / E30_ENGINEERING_MANIFEST_NAME)
if (
destination.is_symlink()
or not destination.is_dir()
or existing.get("identity") != identity
or existing.get("identity_sha256") != identity_sha256
):
raise E30EngineeringGenerationError("generation identity collision")
return existing
verdicts = Counter(str(value["verdict"]) for value in decisions)
causes = Counter(
str(value["cause_code"])
for value in decisions
if value.get("cause_code") is not None
)
detector = Counter(str(value["detector_assessment"]) for value in decisions)
projection = Counter(str(value["projection_assessment"]) for value in decisions)
ownership = Counter(str(value["point_ownership"]) for value in decisions)
exceptions = [value for value in decisions if value["human_exception_required"]]
summary = {
"schema_version": E30_ENGINEERING_SUMMARY_SCHEMA,
"item_count": len(decisions),
"reviewed_item_count": len(decisions),
"verdict_distribution": dict(sorted(verdicts.items())),
"detector_distribution": dict(sorted(detector.items())),
"projection_distribution": dict(sorted(projection.items())),
"point_ownership_distribution": dict(sorted(ownership.items())),
"human_exception_count": len(exceptions),
"mean_confidence": round(
sum(float(value["confidence"]) for value in decisions) / len(decisions),
4,
),
}
cause_document = {
"schema_version": E30_ENGINEERING_CAUSES_SCHEMA,
"item_count_with_cause": sum(causes.values()),
"reasons": [
{"reason_code": reason, "count": count}
for reason, count in sorted(causes.items())
],
}
staging = Path(tempfile.mkdtemp(prefix=f".{generation_id}.", dir=output_root))
try:
decisions_output = staging / E30_ENGINEERING_DECISIONS_NAME
with decisions_output.open("wb") as stream:
for decision in decisions:
stream.write(_canonical_json(decision) + b"\n")
stream.flush()
os.fsync(stream.fileno())
exception_output = staging / E30_ENGINEERING_EXCEPTIONS_NAME
with exception_output.open("wb") as stream:
for decision in exceptions:
exception = {
"schema_version": E30_ENGINEERING_EXCEPTION_SCHEMA,
"sequence": decision["sequence"],
"item_id": decision["item_id"],
"review_key": decision["review_key"],
"source_stratum": decision["source_stratum"],
"exception_reason": decision["exception_reason"],
"confidence": decision["confidence"],
"review_sheet": decision["review_sheet"],
"evidence_note": decision["evidence_note"],
"review_prompt": decision["review_prompt"],
}
stream.write(_canonical_json(exception) + b"\n")
stream.flush()
os.fsync(stream.fileno())
write_json_atomic(staging / E30_ENGINEERING_SUMMARY_NAME, summary)
write_json_atomic(staging / E30_ENGINEERING_CAUSES_NAME, cause_document)
artifacts = [
{
"role": "engineering-decisions",
"path": E30_ENGINEERING_DECISIONS_NAME,
},
{
"role": "human-exceptions",
"path": E30_ENGINEERING_EXCEPTIONS_NAME,
},
{
"role": "engineering-summary",
"path": E30_ENGINEERING_SUMMARY_NAME,
},
{
"role": "cause-distribution",
"path": E30_ENGINEERING_CAUSES_NAME,
},
]
artifact_documents = []
for artifact in artifacts:
path = staging / artifact["path"]
artifact_documents.append(
{
**artifact,
"sha256": _sha256(path),
"byte_length": path.stat().st_size,
}
)
created_at_utc = utc_now_iso()
manifest = {
"schema_version": E30_ENGINEERING_GENERATION_SCHEMA,
"result_id": generation_id,
"identity": identity,
"identity_sha256": identity_sha256,
"created_at_utc": created_at_utc,
"summary": summary,
"cause_distribution": cause_document,
"artifacts": artifact_documents,
"ai_review_complete": True,
"human_exception_complete": len(exceptions) == 0,
"human_review_complete": False,
"lab_published": False,
"authority": {
"commands_enabled": False,
"navigation_or_safety_accepted": False,
},
}
write_json_atomic(staging / E30_ENGINEERING_MANIFEST_NAME, manifest)
staging.rename(destination)
return manifest
except Exception:
shutil.rmtree(staging, ignore_errors=True)
raise
+759
View File
@@ -0,0 +1,759 @@
from __future__ import annotations
import hashlib
import json
import os
import re
import shutil
import tempfile
from collections import Counter
from collections.abc import Iterator
from contextlib import contextmanager
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Final, Literal
from k1link.artifacts import utc_now_iso, write_json_atomic
E30_HUMAN_REVIEW_DRAFT_SCHEMA: Final = "missioncore.e30-human-review-draft/v2"
E30_HUMAN_REVIEW_EVENT_SCHEMA: Final = "missioncore.e30-human-review-event/v2"
E30_HUMAN_REVIEW_DECISION_SCHEMA: Final = (
"missioncore.e30-human-review-decision/v2"
)
E30_HUMAN_REVIEW_GENERATION_SCHEMA: Final = (
"missioncore.e30-human-review-generation/v2"
)
E30_HUMAN_REVIEW_FINALIZATION_SCHEMA: Final = (
"missioncore.e30-human-review-finalization/v2"
)
E30_HUMAN_REVIEW_DECISIONS_NAME: Final = "review-decisions.jsonl"
E30_HUMAN_REVIEW_DISTRIBUTION_NAME: Final = "disposition-distribution.json"
E30_HUMAN_REVIEW_MANIFEST_NAME: Final = "manifest.json"
E30_STRATA: Final = (
"conflict",
"agree",
"camera-only",
"unknown",
"geometry-only",
)
E30_DISPOSITIONS: Final = (
"object-present",
"background-or-noise",
"insufficient-evidence",
)
E30Stratum = Literal[
"conflict",
"agree",
"camera-only",
"unknown",
"geometry-only",
]
E30ExceptionDisposition = Literal[
"object-present",
"background-or-noise",
"insufficient-evidence",
]
_DRAFT_ID = re.compile(r"^e30-human-draft-[a-f0-9]{64}$")
_GENERATION_ID = re.compile(r"^e30-review-generation-[a-f0-9]{64}$")
_EVENT_ID = re.compile(r"^e30-review-event-[a-f0-9]{64}$")
_CONTENT_ID = re.compile(r"^[a-z0-9][a-z0-9-]*-[a-f0-9]{64}$")
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
_REVIEWER_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}$")
_IDEMPOTENCY_KEY = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$")
_MAX_JSON_BYTES: Final = 512 * 1024
_MAX_EVENT_LOG_BYTES: Final = 32 * 1024 * 1024
_MAX_NOTES_LENGTH: Final = 2_000
class E30HumanReviewError(RuntimeError):
"""Base error for the exception-review lifecycle."""
class E30HumanReviewNotFoundError(E30HumanReviewError):
"""The requested draft or generation does not exist."""
class E30HumanReviewConflictError(E30HumanReviewError):
"""The request conflicts with the current append-only revision."""
class E30HumanReviewValidationError(E30HumanReviewError):
"""A reviewer decision violates the frozen review protocol."""
class E30HumanReviewIntegrityError(E30HumanReviewError):
"""Stored reviewer evidence is incomplete, changed or incompatible."""
@dataclass(frozen=True)
class E30ReviewSubject:
item_id: str
sequence: int
source_stratum: E30Stratum
@dataclass(frozen=True)
class E30ReviewSubstrate:
materialization_id: str
materialization_identity_sha256: str
review_pack_id: str
review_items_sha256: str
reason_taxonomy: tuple[str, ...]
subjects: tuple[E30ReviewSubject, ...]
engineering_generation_id: str | None = None
def __post_init__(self) -> None:
if (
_CONTENT_ID.fullmatch(self.materialization_id) is None
or _CONTENT_ID.fullmatch(self.review_pack_id) is None
or _SHA256.fullmatch(self.materialization_identity_sha256) is None
or _SHA256.fullmatch(self.review_items_sha256) is None
or (
self.engineering_generation_id is not None
and _CONTENT_ID.fullmatch(self.engineering_generation_id) is None
)
):
raise E30HumanReviewValidationError("E30 source binding is invalid")
if not self.subjects:
raise E30HumanReviewValidationError("E30 review substrate is empty")
if tuple(subject.sequence for subject in self.subjects) != tuple(
range(len(self.subjects))
):
raise E30HumanReviewValidationError("E30 subjects are out of sequence")
if len({subject.item_id for subject in self.subjects}) != len(self.subjects):
raise E30HumanReviewValidationError("E30 subject ids are not unique")
if any(
subject.source_stratum not in E30_STRATA
or _CONTENT_ID.fullmatch(subject.item_id) is None
for subject in self.subjects
):
raise E30HumanReviewValidationError("E30 subject is invalid")
@property
def item_set_sha256(self) -> str:
return hashlib.sha256(
_canonical_json(
[
{
"item_id": subject.item_id,
"sequence": subject.sequence,
"source_stratum": subject.source_stratum,
}
for subject in self.subjects
]
)
).hexdigest()
def binding(self) -> dict[str, object]:
return {
"materialization_id": self.materialization_id,
"materialization_identity_sha256": self.materialization_identity_sha256,
"review_pack_id": self.review_pack_id,
"review_items_sha256": self.review_items_sha256,
"engineering_generation_id": self.engineering_generation_id,
"item_count": len(self.subjects),
"item_set_sha256": self.item_set_sha256,
}
def _canonical_json(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
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 _read_json(path: Path) -> dict[str, Any]:
if (
path.is_symlink()
or not path.is_file()
or not 0 < path.stat().st_size <= _MAX_JSON_BYTES
):
raise E30HumanReviewIntegrityError("E30 human-review JSON is unavailable")
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise E30HumanReviewIntegrityError("E30 human-review JSON is invalid") from exc
if not isinstance(value, dict):
raise E30HumanReviewIntegrityError("E30 human-review JSON must be an object")
return value
def _safe_root(root: Path) -> Path:
root.mkdir(parents=True, exist_ok=True)
if root.is_symlink() or not root.is_dir():
raise E30HumanReviewIntegrityError("E30 human-review root is invalid")
return root.resolve()
def _normalized_reviewer_id(value: str) -> str:
value = value.strip()
if _REVIEWER_ID.fullmatch(value) is None:
raise E30HumanReviewValidationError("reviewer_id is invalid")
return value
def _normalized_notes(value: str | None) -> str | None:
if value is None:
return None
value = value.strip()
if len(value) > _MAX_NOTES_LENGTH:
raise E30HumanReviewValidationError("review notes are too long")
return value or None
@contextmanager
def _exclusive_lock(path: Path) -> Iterator[None]:
path.parent.mkdir(parents=True, exist_ok=True)
descriptor = os.open(path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600)
try:
yield
finally:
os.close(descriptor)
path.unlink(missing_ok=True)
class E30HumanReviewStore:
"""Append-only human decisions over AI-selected exception items."""
def __init__(self, *, draft_root: Path, generation_root: Path) -> None:
self.draft_root = _safe_root(draft_root)
self.generation_root = _safe_root(generation_root)
def create_or_resume(
self,
*,
substrate: E30ReviewSubstrate,
reviewer_id: str,
) -> dict[str, object]:
if substrate.engineering_generation_id is None:
raise E30HumanReviewValidationError(
"engineering generation binding is required"
)
reviewer_id = _normalized_reviewer_id(reviewer_id)
identity = {
"schema_version": E30_HUMAN_REVIEW_DRAFT_SCHEMA,
"protocol": "exception-disposition-review/v1",
"source": substrate.binding(),
"reviewer_id": reviewer_id,
}
identity_sha256 = hashlib.sha256(_canonical_json(identity)).hexdigest()
draft_id = f"e30-human-draft-{identity_sha256}"
root = self.draft_root / draft_id
with _exclusive_lock(self.draft_root / ".locks" / f"{draft_id}.lock"):
if root.is_symlink():
raise E30HumanReviewIntegrityError("E30 draft must not be a symlink")
root.mkdir(mode=0o700, parents=False, exist_ok=True)
manifest_path = root / "manifest.json"
if not manifest_path.exists():
write_json_atomic(
manifest_path,
{
"schema_version": E30_HUMAN_REVIEW_DRAFT_SCHEMA,
"draft_id": draft_id,
"identity": identity,
"identity_sha256": identity_sha256,
"created_at_utc": utc_now_iso(),
"source": substrate.binding(),
"authority": {
"commands_enabled": False,
"navigation_or_safety_accepted": False,
},
},
)
return self._summary(root, substrate)
def get(
self,
*,
draft_id: str,
substrate: E30ReviewSubstrate,
) -> dict[str, object]:
return self._summary(self._draft_root(draft_id), substrate)
def record_decision(
self,
*,
draft_id: str,
substrate: E30ReviewSubstrate,
item_id: str,
expected_revision: int,
idempotency_key: str,
disposition: E30ExceptionDisposition,
notes: str | None,
) -> dict[str, object]:
if _IDEMPOTENCY_KEY.fullmatch(idempotency_key) is None:
raise E30HumanReviewValidationError("idempotency_key is invalid")
if expected_revision < 0:
raise E30HumanReviewValidationError("expected_revision is invalid")
subject = next(
(value for value in substrate.subjects if value.item_id == item_id),
None,
)
if subject is None:
raise E30HumanReviewNotFoundError("E30 review item was not found")
decision = self._validate_decision(disposition=disposition, notes=notes)
fingerprint = hashlib.sha256(
_canonical_json({"item_id": item_id, **decision})
).hexdigest()
root = self._draft_root(draft_id)
with _exclusive_lock(self.draft_root / ".locks" / f"{draft_id}.lock"):
manifest, events, current = self._load(root, substrate)
if self._finalization(root) is not None:
raise E30HumanReviewConflictError("E30 review draft is finalized")
prior = next(
(
event
for event in events
if event.get("idempotency_key") == idempotency_key
),
None,
)
if prior is not None:
if prior.get("request_fingerprint") != fingerprint:
raise E30HumanReviewConflictError(
"idempotency_key is already bound to another decision"
)
return self._project_summary(
manifest, substrate, events, current, None
)
if len(events) != expected_revision:
raise E30HumanReviewConflictError(
f"E30 review revision is {len(events)}, not {expected_revision}"
)
superseded = current.get(item_id)
payload: dict[str, object] = {
"schema_version": E30_HUMAN_REVIEW_EVENT_SCHEMA,
"draft_id": draft_id,
"sequence": len(events) + 1,
"item_id": item_id,
"source_stratum": subject.source_stratum,
**decision,
"reviewer_id": manifest["identity"]["reviewer_id"],
"decided_at_utc": utc_now_iso(),
"idempotency_key": idempotency_key,
"request_fingerprint": fingerprint,
"supersedes_event_id": (
superseded.get("event_id") if superseded else None
),
"previous_event_sha256": (
events[-1]["event_sha256"] if events else None
),
}
event_sha256 = hashlib.sha256(_canonical_json(payload)).hexdigest()
event = {
**payload,
"event_id": f"e30-review-event-{event_sha256}",
"event_sha256": event_sha256,
}
with (root / "events.jsonl").open("ab", buffering=0) as stream:
stream.write(_canonical_json(event) + b"\n")
os.fsync(stream.fileno())
events.append(event)
current[item_id] = event
return self._project_summary(manifest, substrate, events, current, None)
def finalize(
self,
*,
draft_id: str,
substrate: E30ReviewSubstrate,
expected_revision: int,
) -> dict[str, object]:
root = self._draft_root(draft_id)
with _exclusive_lock(self.draft_root / ".locks" / f"{draft_id}.lock"):
manifest, events, current = self._load(root, substrate)
existing = self._finalization(root)
if existing is not None:
return self._generation_summary(existing, substrate)
if len(events) != expected_revision:
raise E30HumanReviewConflictError(
f"E30 review revision is {len(events)}, not {expected_revision}"
)
if len(current) != len(substrate.subjects):
raise E30HumanReviewConflictError(
"E30 review coverage is incomplete: "
f"{len(substrate.subjects) - len(current)} decisions remain"
)
generation = self._write_generation(manifest, substrate, current)
finalization = {
"schema_version": E30_HUMAN_REVIEW_FINALIZATION_SCHEMA,
"draft_id": draft_id,
"generation_id": generation["result_id"],
"generation_identity_sha256": generation["identity_sha256"],
"final_revision": len(events),
"finalized_at_utc": generation["created_at_utc"],
}
write_json_atomic(root / "finalized.json", finalization)
return self._generation_summary(finalization, substrate)
@staticmethod
def _validate_decision(
*,
disposition: object,
notes: object,
) -> dict[str, object]:
if disposition not in E30_DISPOSITIONS:
raise E30HumanReviewValidationError("review disposition is invalid")
if notes is not None and not isinstance(notes, str):
raise E30HumanReviewValidationError("review notes are invalid")
return {
"disposition": disposition,
"notes": _normalized_notes(notes),
}
def _draft_root(self, draft_id: str) -> Path:
root = self.draft_root / draft_id
if (
_DRAFT_ID.fullmatch(draft_id) is None
or root.is_symlink()
or not root.is_dir()
):
raise E30HumanReviewNotFoundError("E30 human-review draft was not found")
return root
def _summary(
self,
root: Path,
substrate: E30ReviewSubstrate,
) -> dict[str, object]:
manifest, events, current = self._load(root, substrate)
return self._project_summary(
manifest,
substrate,
events,
current,
self._finalization(root),
)
def _load(
self,
root: Path,
substrate: E30ReviewSubstrate,
) -> tuple[dict[str, Any], list[dict[str, Any]], dict[str, dict[str, Any]]]:
manifest = _read_json(root / "manifest.json")
identity = manifest.get("identity")
identity_sha256 = manifest.get("identity_sha256")
if (
manifest.get("schema_version") != E30_HUMAN_REVIEW_DRAFT_SCHEMA
or manifest.get("draft_id") != root.name
or not isinstance(identity, dict)
or not isinstance(identity_sha256, str)
or hashlib.sha256(_canonical_json(identity)).hexdigest()
!= identity_sha256
or root.name != f"e30-human-draft-{identity_sha256}"
or manifest.get("source") != substrate.binding()
or not isinstance(manifest.get("created_at_utc"), str)
or not self._diagnostic_authority(manifest.get("authority"))
):
raise E30HumanReviewIntegrityError("E30 human-review draft changed")
events_path = root / "events.jsonl"
if not events_path.exists():
return manifest, [], {}
if (
events_path.is_symlink()
or not events_path.is_file()
or events_path.stat().st_size > _MAX_EVENT_LOG_BYTES
):
raise E30HumanReviewIntegrityError("E30 event log is invalid")
subject_by_id = {value.item_id: value for value in substrate.subjects}
events: list[dict[str, Any]] = []
current: dict[str, dict[str, Any]] = {}
previous_sha256: str | None = None
with events_path.open("r", encoding="utf-8") as stream:
for expected_sequence, line in enumerate(stream, start=1):
try:
event = json.loads(line)
except json.JSONDecodeError as exc:
raise E30HumanReviewIntegrityError(
"E30 event is invalid"
) from exc
subject = subject_by_id.get(event.get("item_id"))
event_sha256 = event.get("event_sha256")
payload = {
key: value
for key, value in event.items()
if key not in {"event_id", "event_sha256"}
}
if (
event.get("schema_version") != E30_HUMAN_REVIEW_EVENT_SCHEMA
or event.get("draft_id") != root.name
or event.get("sequence") != expected_sequence
or subject is None
or event.get("source_stratum") != subject.source_stratum
or event.get("reviewer_id") != identity.get("reviewer_id")
or event.get("previous_event_sha256") != previous_sha256
or not isinstance(event_sha256, str)
or hashlib.sha256(_canonical_json(payload)).hexdigest()
!= event_sha256
or event.get("event_id")
!= f"e30-review-event-{event_sha256}"
):
raise E30HumanReviewIntegrityError("E30 event chain changed")
self._validate_decision(
disposition=event.get("disposition"),
notes=event.get("notes"),
)
superseded = current.get(subject.item_id)
if event.get("supersedes_event_id") != (
superseded.get("event_id") if superseded else None
):
raise E30HumanReviewIntegrityError(
"E30 supersession chain changed"
)
events.append(event)
current[subject.item_id] = event
previous_sha256 = event_sha256
return manifest, events, current
def _project_summary(
self,
manifest: dict[str, Any],
substrate: E30ReviewSubstrate,
events: list[dict[str, Any]],
current: dict[str, dict[str, Any]],
finalization: dict[str, Any] | None,
) -> dict[str, object]:
distribution = Counter(
str(event["disposition"]) for event in current.values()
)
decisions = [
{
"item_id": subject.item_id,
"source_stratum": subject.source_stratum,
"disposition": event["disposition"],
"notes": event["notes"],
"event_id": event["event_id"],
"decided_at_utc": event["decided_at_utc"],
}
for subject in substrate.subjects
if (event := current.get(subject.item_id)) is not None
]
return {
"schema_version": E30_HUMAN_REVIEW_DRAFT_SCHEMA,
"draft_id": manifest["draft_id"],
"materialization_id": substrate.materialization_id,
"engineering_generation_id": substrate.engineering_generation_id,
"reviewer_id": manifest["identity"]["reviewer_id"],
"created_at_utc": manifest["created_at_utc"],
"state": "finalized" if finalization else "active",
"revision": len(events),
"item_count": len(substrate.subjects),
"reviewed_item_count": len(decisions),
"remaining_item_count": len(substrate.subjects) - len(decisions),
"disposition_distribution": dict(sorted(distribution.items())),
"generation_id": (
finalization["generation_id"] if finalization else None
),
"decisions": decisions,
"lab_published": False,
"access": "read-only" if finalization else "review-write",
}
def _write_generation(
self,
manifest: dict[str, Any],
substrate: E30ReviewSubstrate,
current: dict[str, dict[str, Any]],
) -> dict[str, Any]:
decisions = [
{
"schema_version": E30_HUMAN_REVIEW_DECISION_SCHEMA,
"sequence": subject.sequence,
"item_id": subject.item_id,
"source_stratum": subject.source_stratum,
"disposition": current[subject.item_id]["disposition"],
"notes": current[subject.item_id]["notes"],
"reviewer_id": manifest["identity"]["reviewer_id"],
"decision_event_id": current[subject.item_id]["event_id"],
"decided_at_utc": current[subject.item_id]["decided_at_utc"],
}
for subject in substrate.subjects
]
identity = {
"schema_version": E30_HUMAN_REVIEW_GENERATION_SCHEMA,
"protocol": "exception-disposition-review/v1",
"source": substrate.binding(),
"reviewer_id": manifest["identity"]["reviewer_id"],
"decision_event_ids": [
decision["decision_event_id"] for decision in decisions
],
}
identity_sha256 = hashlib.sha256(_canonical_json(identity)).hexdigest()
generation_id = f"e30-review-generation-{identity_sha256}"
destination = self.generation_root / generation_id
if destination.exists():
existing = _read_json(destination / E30_HUMAN_REVIEW_MANIFEST_NAME)
if existing.get("identity") != identity:
raise E30HumanReviewIntegrityError(
"E30 reviewer generation identity collision"
)
return existing
staging = Path(
tempfile.mkdtemp(prefix=f".{generation_id}.", dir=self.generation_root)
)
try:
decisions_path = staging / E30_HUMAN_REVIEW_DECISIONS_NAME
with decisions_path.open("wb") as stream:
for decision in decisions:
stream.write(_canonical_json(decision) + b"\n")
stream.flush()
os.fsync(stream.fileno())
distribution = {
"schema_version": "missioncore.e30-human-review-dispositions/v1",
"dispositions": dict(
sorted(Counter(
str(decision["disposition"]) for decision in decisions
).items())
),
}
distribution_path = staging / E30_HUMAN_REVIEW_DISTRIBUTION_NAME
write_json_atomic(distribution_path, distribution)
created_at_utc = utc_now_iso()
generation_manifest: dict[str, Any] = {
"schema_version": E30_HUMAN_REVIEW_GENERATION_SCHEMA,
"result_id": generation_id,
"identity": identity,
"identity_sha256": identity_sha256,
"created_at_utc": created_at_utc,
"reviewer_id": manifest["identity"]["reviewer_id"],
"item_count": len(decisions),
"disposition_distribution": distribution["dispositions"],
"coverage": {
"expected_item_count": len(substrate.subjects),
"reviewed_item_count": len(decisions),
"complete": True,
},
"human_review_complete": True,
"lab_published": False,
"artifacts": [
self._artifact("review-decisions", decisions_path),
self._artifact("disposition-distribution", distribution_path),
],
"authority": {
"commands_enabled": False,
"navigation_or_safety_accepted": False,
},
}
write_json_atomic(
staging / E30_HUMAN_REVIEW_MANIFEST_NAME,
generation_manifest,
)
os.replace(staging, destination)
return generation_manifest
finally:
if staging.exists():
shutil.rmtree(staging)
@staticmethod
def _artifact(role: str, path: Path) -> dict[str, object]:
return {
"role": role,
"path": path.name,
"byte_length": path.stat().st_size,
"sha256": _sha256(path),
}
def _finalization(self, root: Path) -> dict[str, Any] | None:
path = root / "finalized.json"
if not path.exists():
return None
value = _read_json(path)
if (
value.get("schema_version") != E30_HUMAN_REVIEW_FINALIZATION_SCHEMA
or value.get("draft_id") != root.name
or _GENERATION_ID.fullmatch(str(value.get("generation_id"))) is None
or _SHA256.fullmatch(
str(value.get("generation_identity_sha256"))
) is None
):
raise E30HumanReviewIntegrityError("E30 finalization marker changed")
return value
def _generation_summary(
self,
finalization: dict[str, Any],
substrate: E30ReviewSubstrate,
) -> dict[str, object]:
generation_id = str(finalization["generation_id"])
root = self.generation_root / generation_id
if root.is_symlink() or not root.is_dir():
raise E30HumanReviewIntegrityError(
"E30 reviewer generation is unavailable"
)
manifest = _read_json(root / E30_HUMAN_REVIEW_MANIFEST_NAME)
identity = manifest.get("identity")
identity_sha256 = manifest.get("identity_sha256")
if (
manifest.get("schema_version") != E30_HUMAN_REVIEW_GENERATION_SCHEMA
or manifest.get("result_id") != generation_id
or not isinstance(identity, dict)
or not isinstance(identity_sha256, str)
or hashlib.sha256(_canonical_json(identity)).hexdigest()
!= identity_sha256
or generation_id != f"e30-review-generation-{identity_sha256}"
or identity.get("source") != substrate.binding()
or manifest.get("human_review_complete") is not True
or manifest.get("lab_published") is not False
or not self._diagnostic_authority(manifest.get("authority"))
):
raise E30HumanReviewIntegrityError("E30 reviewer generation changed")
expected = {
"review-decisions": E30_HUMAN_REVIEW_DECISIONS_NAME,
"disposition-distribution": E30_HUMAN_REVIEW_DISTRIBUTION_NAME,
}
artifacts = manifest.get("artifacts")
if not isinstance(artifacts, list) or len(artifacts) != len(expected):
raise E30HumanReviewIntegrityError("E30 generation artifacts are invalid")
for artifact in artifacts:
if not isinstance(artifact, dict):
raise E30HumanReviewIntegrityError(
"E30 generation artifact metadata is invalid"
)
path = root / str(artifact.get("path"))
if (
expected.get(str(artifact.get("role"))) != path.name
or path.is_symlink()
or not path.is_file()
or path.stat().st_size != artifact.get("byte_length")
or _sha256(path) != artifact.get("sha256")
):
raise E30HumanReviewIntegrityError(
"E30 generation artifact content changed"
)
return {
"schema_version": E30_HUMAN_REVIEW_GENERATION_SCHEMA,
"generation_id": generation_id,
"materialization_id": substrate.materialization_id,
"engineering_generation_id": substrate.engineering_generation_id,
"reviewer_id": manifest["reviewer_id"],
"created_at_utc": manifest["created_at_utc"],
"item_count": manifest["item_count"],
"disposition_distribution": manifest["disposition_distribution"],
"coverage": manifest["coverage"],
"human_review_complete": True,
"lab_published": False,
"access": "read-only",
}
@staticmethod
def _diagnostic_authority(value: object) -> bool:
return (
isinstance(value, dict)
and value.get("commands_enabled") is False
and value.get("navigation_or_safety_accepted") is False
)
File diff suppressed because it is too large Load Diff
+734
View File
@@ -0,0 +1,734 @@
"""Deterministic E30 review selection over one immutable E29 result.
The pack is a review substrate, not a human-reviewed LAB result. It binds each
selected item to the exact E29 frame and its camera/LiDAR/pose/surface source
identities, preserves the E29 observation or geometry-cluster snapshot and
leaves the reviewer decision empty. No perception threshold is changed.
"""
from __future__ import annotations
import hashlib
import json
import math
import os
import shutil
from collections import Counter
from dataclasses import asdict, dataclass
from datetime import UTC, datetime
from pathlib import Path
from typing import Any, Final
from .semantic_geometry_fusion import (
CAMERA_GEOMETRY_FRAME_SCHEMA,
CAMERA_GEOMETRY_FRAMES_NAME,
CAMERA_GEOMETRY_FUSION_SCHEMA,
CAMERA_GEOMETRY_MANIFEST_NAME,
CAMERA_GEOMETRY_REPORT_NAME,
CAMERA_GEOMETRY_REPORT_SCHEMA,
)
E30_REVIEW_PACK_SCHEMA: Final = "missioncore.e30-evidence-review-pack/v1"
E30_REVIEW_ITEM_SCHEMA: Final = "missioncore.e30-evidence-review-item/v1"
E30_REVIEW_ITEMS_NAME: Final = "review-items.jsonl"
E30_REVIEW_MANIFEST_NAME: Final = "manifest.json"
E30_REASON_TAXONOMY: Final = (
"no_lidar_observation",
"outside_lidar_support",
"outside_camera_fov",
"time_mismatch",
"pose_age",
"calibration_residual",
"surface_rejection",
"ground_leakage",
"foreground_occlusion",
"background_leakage",
"component_split",
"component_merge",
"self_points",
"sparse_support",
"semantic_mask_error",
"detector_error",
"track_identity_error",
"post_lio_motion_artifact",
"unknown",
)
_STATUS_TO_STRATUM: Final = {
"agree": "agree",
"single-source-camera": "camera-only",
"conflict": "conflict",
"unknown": "unknown",
}
class E30ReviewPackError(ValueError):
"""An E29 source or E30 review-pack derivative violates the contract."""
@dataclass(frozen=True, slots=True)
class E30ReviewSelectionProfile:
"""Bounded deterministic sample sizes for non-conflict E29 strata."""
profile_id: str = "e30-e29-stratified-review/v1"
agree_maximum: int = 96
camera_only_maximum: int = 128
unknown_maximum: int = 96
geometry_only_maximum: int = 128
temporal_bins: int = 12
def __post_init__(self) -> None:
if (
not self.profile_id
or len(self.profile_id) > 160
or not 1 <= self.agree_maximum <= 4096
or not 1 <= self.camera_only_maximum <= 4096
or not 1 <= self.unknown_maximum <= 4096
or not 1 <= self.geometry_only_maximum <= 4096
or not 2 <= self.temporal_bins <= 128
):
raise E30ReviewPackError("E30 review selection profile is invalid")
def to_dict(self) -> dict[str, object]:
return {
"schema_version": "missioncore.e30-review-selection-profile/v1",
**asdict(self),
"conflict_selection": "all",
"sample_selection": "round-robin-class-range-time-strata-sha256",
}
def maximum_for(self, stratum: str) -> int:
values = {
"agree": self.agree_maximum,
"camera-only": self.camera_only_maximum,
"unknown": self.unknown_maximum,
"geometry-only": self.geometry_only_maximum,
}
try:
return values[stratum]
except KeyError as exc:
raise E30ReviewPackError("E30 review stratum is unknown") from exc
DEFAULT_E30_REVIEW_SELECTION_PROFILE: Final = E30ReviewSelectionProfile()
@dataclass(frozen=True, slots=True)
class E30ReviewPack:
result_root: Path
result_id: str
manifest: dict[str, Any]
@dataclass(frozen=True, slots=True)
class _ReviewCandidate:
review_key: str
stratum: str
group_key: tuple[str, ...]
document: dict[str, object]
def build_e30_review_pack(
*,
e29_result_root: Path,
output_root: Path,
profile: E30ReviewSelectionProfile = DEFAULT_E30_REVIEW_SELECTION_PROFILE,
) -> E30ReviewPack:
"""Build one immutable, bounded selection pack over a complete E29 result."""
source_root = e29_result_root.expanduser()
if source_root.is_symlink():
raise E30ReviewPackError("E29 result root must not be a symlink")
source_root = source_root.resolve(strict=True)
if not source_root.is_dir():
raise E30ReviewPackError("E29 result root must be a directory")
source_manifest_path = _regular_file(
source_root,
CAMERA_GEOMETRY_MANIFEST_NAME,
)
source_manifest = _read_json(source_manifest_path, "E29 manifest")
if source_manifest.get("schema_version") != CAMERA_GEOMETRY_FUSION_SCHEMA:
raise E30ReviewPackError("E29 manifest schema is incompatible")
result_id = _required_string(source_manifest, "result_id")
if not result_id.startswith("e29-camera-geometry-"):
raise E30ReviewPackError("E29 result id is incompatible")
if source_root.name != result_id:
raise E30ReviewPackError("E29 result directory and manifest id disagree")
if source_manifest.get("ground_truth") is not False:
raise E30ReviewPackError("E29 diagnostic result must not claim ground truth")
identity = _required_object(source_manifest, "identity")
source_identity_sha256 = _required_string(source_manifest, "identity_sha256")
if (
hashlib.sha256(_canonical_json(identity)).hexdigest()
!= source_identity_sha256
or result_id != f"e29-camera-geometry-{source_identity_sha256}"
):
raise E30ReviewPackError("E29 manifest identity digest is invalid")
_reject_authority(_required_object(identity, "authority"), "E29 identity")
frame_record = _artifact_record(source_manifest, "camera-geometry-frames")
report_record = _artifact_record(source_manifest, "camera-geometry-report")
frames_path = _verified_artifact(
source_root,
frame_record,
CAMERA_GEOMETRY_FRAMES_NAME,
)
report_path = _verified_artifact(
source_root,
report_record,
CAMERA_GEOMETRY_REPORT_NAME,
)
report = _read_json(report_path, "E29 report")
_validate_report(report, result_id, identity)
frame_count = _required_int(identity, "frame_count")
timeline_start = _required_float(identity, "timeline_start_seconds")
timeline_end = _required_float(identity, "timeline_end_seconds")
if frame_count < 1 or timeline_end < timeline_start:
raise E30ReviewPackError("E29 identity timeline is invalid")
source_bindings: dict[str, object] = {
"e29_result_id": result_id,
"e29_identity_sha256": source_identity_sha256,
"e29_frames_sha256": _required_string(frame_record, "sha256"),
"e29_report_sha256": _required_string(report_record, "sha256"),
"camera_result_id": _required_string(identity, "source_result_id"),
"lidar_pack_id": _required_string(identity, "source_pack_id"),
"local_surface_model_id": _required_string(
identity,
"local_surface_model_id",
),
"fusion_profile_id": _required_string(
_required_object(identity, "profile"),
"profile_id",
),
}
candidates, observed_counts = _read_candidates(
frames_path,
source_bindings=source_bindings,
expected_frame_count=frame_count,
timeline_start=timeline_start,
timeline_end=timeline_end,
temporal_bins=profile.temporal_bins,
)
_validate_observed_counts(report, observed_counts)
profile_document = profile.to_dict()
identity_document = {
"schema_version": E30_REVIEW_PACK_SCHEMA,
"source": source_bindings,
"selection_profile": profile_document,
"reason_taxonomy": list(E30_REASON_TAXONOMY),
"human_review_complete": False,
"producer_sha256": _sha256_file(Path(__file__).resolve(strict=True)),
"authority": {
"commands_enabled": False,
"navigation_or_safety_accepted": False,
},
}
identity_sha256 = hashlib.sha256(_canonical_json(identity_document)).hexdigest()
review_result_id = f"e30-review-pack-{identity_sha256}"
selected = _select_candidates(candidates, profile, identity_sha256)
selected_counts = Counter(candidate.stratum for candidate in selected)
output = output_root.expanduser().absolute()
output.mkdir(mode=0o700, parents=True, exist_ok=True)
result_root = output / review_result_id
if result_root.exists():
manifest = _read_existing_pack(result_root, identity_document)
return E30ReviewPack(result_root, review_result_id, manifest)
staging = output / f".{review_result_id}.{os.getpid()}.incomplete"
staging.mkdir(mode=0o700, exist_ok=False)
items_path = staging / E30_REVIEW_ITEMS_NAME
try:
with items_path.open("x", encoding="utf-8") as stream:
for sequence, candidate in enumerate(selected):
document = {
"schema_version": E30_REVIEW_ITEM_SCHEMA,
"sequence": sequence,
"item_id": _item_id(identity_sha256, candidate.review_key),
**candidate.document,
"review": {
"state": "unreviewed",
"reason_code": None,
"notes": None,
},
"authority": {
"commands_enabled": False,
"navigation_or_safety_accepted": False,
},
}
stream.write(_canonical_json(document).decode("utf-8") + "\n")
items_artifact = _artifact("review-items", items_path, "application/x-ndjson")
manifest = {
"schema_version": E30_REVIEW_PACK_SCHEMA,
"result_id": review_result_id,
"identity_sha256": identity_sha256,
"identity": identity_document,
"created_at_utc": datetime.now(UTC).isoformat(),
"classification": "private-derived-perception-review-substrate",
"human_review_complete": False,
"lab_published": False,
"source_counts": dict(sorted(observed_counts.items())),
"selected_counts": dict(sorted(selected_counts.items())),
"selected_item_count": len(selected),
"artifacts": [items_artifact],
"limitations": [
"the pack contains no human decisions",
"E29 stores support counts but not point-index ownership",
"selected and rejected point visualization requires source-bound reprojection",
"selection is not object-detection or association accuracy",
],
"authority": {
"commands_enabled": False,
"navigation_or_safety_accepted": False,
},
}
_write_json(staging / E30_REVIEW_MANIFEST_NAME, manifest)
os.replace(staging, result_root)
except BaseException:
shutil.rmtree(staging, ignore_errors=True)
raise
return E30ReviewPack(result_root, review_result_id, manifest)
def _read_candidates(
frames_path: Path,
*,
source_bindings: dict[str, object],
expected_frame_count: int,
timeline_start: float,
timeline_end: float,
temporal_bins: int,
) -> tuple[list[_ReviewCandidate], Counter[str]]:
candidates: list[_ReviewCandidate] = []
counts: Counter[str] = Counter()
observed_frame_count = 0
with frames_path.open("r", encoding="utf-8") as stream:
for expected_frame_index, line in enumerate(stream):
observed_frame_count += 1
try:
value = json.loads(line)
except json.JSONDecodeError as exc:
raise E30ReviewPackError("E29 frame JSON is invalid") from exc
frame = _object(value, "E29 frame")
if frame.get("schema_version") != CAMERA_GEOMETRY_FRAME_SCHEMA:
raise E30ReviewPackError("E29 frame schema is incompatible")
frame_index = _required_int(frame, "frame_index")
if frame_index != expected_frame_index:
raise E30ReviewPackError("E29 frame order is not contiguous")
source_frame_index = _required_int(frame, "source_frame_index")
if source_frame_index < 0:
raise E30ReviewPackError("E29 source frame index is invalid")
session_seconds = _required_float(frame, "session_seconds")
temporal_bin = _temporal_bin(
session_seconds,
timeline_start,
timeline_end,
temporal_bins,
)
binding = {
**source_bindings,
"frame_index": frame_index,
"source_frame_index": source_frame_index,
"session_seconds": session_seconds,
}
observations = _required_array(frame, "semantic_observations")
for observation_index, raw_observation in enumerate(observations):
observation = _object(raw_observation, "E29 semantic observation")
status = _required_string(observation, "geometry_status")
try:
stratum = _STATUS_TO_STRATUM[status]
except KeyError as exc:
raise E30ReviewPackError(
"E29 semantic geometry status is incompatible"
) from exc
counts[stratum] += 1
label = _required_string(observation, "label")
association_group = _required_string(
observation,
"association_group",
)
range_bucket = _range_bucket(
_optional_float(observation.get("range_m"))
)
review_key = f"semantic:{frame_index}:{observation_index}"
candidates.append(
_ReviewCandidate(
review_key=review_key,
stratum=stratum,
group_key=(
association_group,
label,
range_bucket,
f"time-{temporal_bin:03d}",
),
document={
"review_key": review_key,
"stratum": stratum,
"range_bucket": range_bucket,
"evidence_binding": binding,
"e29_locator": {
"kind": "semantic-observation",
"observation_index": observation_index,
},
"e29_snapshot": observation,
"materialization": {
"camera_frame_bound": True,
"lidar_frame_bound": True,
"pose_frame_bound": True,
"surface_frame_bound": True,
"point_indices_available_in_e29": False,
"source_reprojection_required": True,
},
},
)
)
geometry_clusters = _required_array(frame, "geometry_only_occupied")
for cluster_index, raw_cluster in enumerate(geometry_clusters):
cluster = _object(raw_cluster, "E29 geometry-only cluster")
if cluster.get("geometry_status") != "single-source-geometry":
raise E30ReviewPackError(
"E29 geometry-only status is incompatible"
)
counts["geometry-only"] += 1
range_bucket = _range_bucket(
_optional_float(cluster.get("nearest_range_m"))
)
review_key = f"geometry:{frame_index}:{cluster_index}"
candidates.append(
_ReviewCandidate(
review_key=review_key,
stratum="geometry-only",
group_key=(
range_bucket,
f"time-{temporal_bin:03d}",
),
document={
"review_key": review_key,
"stratum": "geometry-only",
"range_bucket": range_bucket,
"evidence_binding": binding,
"e29_locator": {
"kind": "geometry-only-cluster",
"cluster_index": cluster_index,
},
"e29_snapshot": cluster,
"materialization": {
"camera_frame_bound": True,
"lidar_frame_bound": True,
"pose_frame_bound": True,
"surface_frame_bound": True,
"point_indices_available_in_e29": False,
"source_reprojection_required": True,
},
},
)
)
if observed_frame_count != expected_frame_count:
raise E30ReviewPackError("E29 frame artifact is incomplete")
return candidates, counts
def _select_candidates(
candidates: list[_ReviewCandidate],
profile: E30ReviewSelectionProfile,
seed: str,
) -> list[_ReviewCandidate]:
by_stratum: dict[str, list[_ReviewCandidate]] = {}
for candidate in candidates:
by_stratum.setdefault(candidate.stratum, []).append(candidate)
admitted_strata = {
"agree",
"camera-only",
"conflict",
"unknown",
"geometry-only",
}
if not set(by_stratum).issubset(admitted_strata):
raise E30ReviewPackError("E29 result contains an unknown E30 review stratum")
selected = list(by_stratum.get("conflict", []))
for stratum in ("agree", "camera-only", "unknown", "geometry-only"):
selected.extend(
_round_robin_sample(
by_stratum.get(stratum, []),
profile.maximum_for(stratum),
seed,
)
)
selected.sort(key=lambda candidate: candidate.review_key)
if len({candidate.review_key for candidate in selected}) != len(selected):
raise E30ReviewPackError("E30 selection contains duplicate review items")
return selected
def _round_robin_sample(
candidates: list[_ReviewCandidate],
maximum: int,
seed: str,
) -> list[_ReviewCandidate]:
if len(candidates) <= maximum:
return list(candidates)
groups: dict[tuple[str, ...], list[_ReviewCandidate]] = {}
for candidate in candidates:
groups.setdefault(candidate.group_key, []).append(candidate)
for group in groups.values():
group.sort(key=lambda candidate: _selection_rank(seed, candidate.review_key))
selected: list[_ReviewCandidate] = []
active = sorted(groups)
while active and len(selected) < maximum:
next_active: list[tuple[str, ...]] = []
for key in active:
group = groups[key]
if group and len(selected) < maximum:
selected.append(group.pop(0))
if group:
next_active.append(key)
active = next_active
if len(selected) != maximum:
raise E30ReviewPackError("E30 stratified selector did not fill its quota")
return selected
def _selection_rank(seed: str, review_key: str) -> str:
return hashlib.sha256(f"{seed}:{review_key}".encode()).hexdigest()
def _item_id(identity_sha256: str, review_key: str) -> str:
digest = hashlib.sha256(f"{identity_sha256}:{review_key}".encode()).hexdigest()
return f"e30-review-item-{digest}"
def _temporal_bin(
session_seconds: float,
start: float,
end: float,
bin_count: int,
) -> int:
if not start <= session_seconds <= end:
raise E30ReviewPackError("E29 frame escaped the identity timeline")
if end == start:
return 0
fraction = (session_seconds - start) / (end - start)
return min(bin_count - 1, max(0, int(fraction * bin_count)))
def _range_bucket(value: float | None) -> str:
if value is None:
return "unavailable"
if value < 3.0:
return "near"
if value < 7.0:
return "middle"
return "far"
def _validate_report(
report: dict[str, Any],
result_id: str,
identity: dict[str, Any],
) -> None:
if (
report.get("schema_version") != CAMERA_GEOMETRY_REPORT_SCHEMA
or report.get("result_id") != result_id
or report.get("status") != "diagnostic-replay-complete"
or report.get("ground_truth") is not False
or report.get("identity") != identity
):
raise E30ReviewPackError("E29 report is not a complete diagnostic replay")
_reject_authority(_required_object(report, "authority"), "E29 report")
def _validate_observed_counts(
report: dict[str, Any],
observed: Counter[str],
) -> None:
metrics = _required_object(report, "metrics")
semantic = _required_object(metrics, "semantic_observations")
statuses = _required_object(semantic, "geometry_status")
expected = {
"agree": _required_int(statuses, "agree"),
"camera-only": _required_int(statuses, "single-source-camera"),
"conflict": _required_int(statuses, "conflict"),
"unknown": _required_int(statuses, "unknown"),
"geometry-only": _required_int(
_required_object(metrics, "geometry_only_occupied"),
"cluster_count",
),
}
actual = {stratum: observed[stratum] for stratum in expected}
if actual != expected:
raise E30ReviewPackError("E29 frame and report stratum counts disagree")
def _artifact_record(
manifest: dict[str, Any],
role: str,
) -> dict[str, Any]:
artifacts = _required_array(manifest, "artifacts")
matches = [
_object(item, "E29 artifact")
for item in artifacts
if isinstance(item, dict) and item.get("role") == role
]
if len(matches) != 1:
raise E30ReviewPackError(f"E29 manifest has no unique {role} artifact")
return matches[0]
def _verified_artifact(
root: Path,
record: dict[str, Any],
expected_name: str,
) -> Path:
relative = _required_string(record, "path")
if Path(relative).is_absolute() or relative != expected_name:
raise E30ReviewPackError("E29 artifact path is incompatible")
path = _regular_file(root, relative)
if path.stat().st_size != _required_int(record, "byte_length"):
raise E30ReviewPackError("E29 artifact byte length changed")
if _sha256_file(path) != _required_string(record, "sha256"):
raise E30ReviewPackError("E29 artifact digest changed")
return path
def _regular_file(root: Path, relative: str) -> Path:
raw = root / relative
if raw.is_symlink():
raise E30ReviewPackError("evidence artifact must not be a symlink")
path = raw.resolve(strict=True)
try:
path.relative_to(root)
except ValueError as exc:
raise E30ReviewPackError("evidence artifact escaped its result root") from exc
if not path.is_file():
raise E30ReviewPackError("evidence artifact must be a regular file")
return path
def _artifact(role: str, path: Path, media_type: str) -> dict[str, object]:
return {
"role": role,
"path": path.name,
"media_type": media_type,
"byte_length": path.stat().st_size,
"sha256": _sha256_file(path),
}
def _read_existing_pack(
result_root: Path,
expected_identity: dict[str, object],
) -> dict[str, Any]:
manifest_path = _regular_file(result_root, E30_REVIEW_MANIFEST_NAME)
manifest = _read_json(manifest_path, "E30 review manifest")
if (
manifest.get("schema_version") != E30_REVIEW_PACK_SCHEMA
or manifest.get("identity") != expected_identity
or manifest.get("human_review_complete") is not False
or manifest.get("lab_published") is not False
):
raise E30ReviewPackError("existing E30 review pack identity differs")
_reject_authority(_required_object(manifest, "authority"), "E30 review pack")
artifact = _artifact_record(manifest, "review-items")
_verified_artifact(result_root, artifact, E30_REVIEW_ITEMS_NAME)
return manifest
def _reject_authority(authority: dict[str, Any], label: str) -> None:
if (
authority.get("commands_enabled") is not False
or authority.get("navigation_or_safety_accepted") is not False
):
raise E30ReviewPackError(f"{label} must remain diagnostic-only")
def _read_json(path: Path, label: str) -> dict[str, Any]:
try:
return _object(json.loads(path.read_text(encoding="utf-8")), label)
except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc:
raise E30ReviewPackError(f"{label} is invalid") from exc
def _write_json(path: Path, value: dict[str, object] | dict[str, Any]) -> None:
with path.open("x", encoding="utf-8") as stream:
stream.write(_canonical_json(value).decode("utf-8"))
stream.write("\n")
def _canonical_json(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode()
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 _object(value: object, label: str) -> dict[str, Any]:
if not isinstance(value, dict) or any(not isinstance(key, str) for key in value):
raise E30ReviewPackError(f"{label} must be an object")
return value
def _required_object(document: dict[str, Any], key: str) -> dict[str, Any]:
return _object(document.get(key), key)
def _required_array(document: dict[str, Any], key: str) -> list[object]:
value = document.get(key)
if not isinstance(value, list):
raise E30ReviewPackError(f"{key} must be an array")
return value
def _required_string(document: dict[str, Any], key: str) -> str:
value = document.get(key)
if not isinstance(value, str) or not value:
raise E30ReviewPackError(f"{key} must be a nonempty string")
return value
def _required_int(document: dict[str, Any], key: str) -> int:
value = document.get(key)
if not isinstance(value, int) or isinstance(value, bool):
raise E30ReviewPackError(f"{key} must be an integer")
return value
def _required_float(document: dict[str, Any], key: str) -> float:
value = document.get(key)
if not isinstance(value, int | float) or isinstance(value, bool):
raise E30ReviewPackError(f"{key} must be numeric")
parsed = float(value)
if not math.isfinite(parsed):
raise E30ReviewPackError(f"{key} must be finite")
return parsed
def _optional_float(value: object) -> float | None:
if value is None:
return None
if not isinstance(value, int | float) or isinstance(value, bool):
raise E30ReviewPackError("optional range must be numeric")
parsed = float(value)
if not math.isfinite(parsed):
raise E30ReviewPackError("optional range must be finite")
return parsed
+58 -25
View File
@@ -132,6 +132,12 @@ class _SemanticSupport:
occupied_source_indices: IntArray
@dataclass(frozen=True, slots=True)
class _GeometryClusterSupport:
document: dict[str, object]
occupied_source_indices: IntArray
def build_camera_geometry_fusion(
*,
fusion_frames_path: Path,
@@ -552,6 +558,28 @@ def _geometry_clusters(
claimed_source_indices: set[int],
profile: CameraGeometryFusionProfile,
) -> list[dict[str, object]]:
return [
support.document
for support in _geometry_cluster_supports(
points_map=points_map,
point_class=point_class,
point_height_m=point_height_m,
sensor_position_map=sensor_position_map,
claimed_source_indices=claimed_source_indices,
profile=profile,
)
]
def _geometry_cluster_supports(
*,
points_map: FloatArray,
point_class: npt.NDArray[np.uint8],
point_height_m: npt.NDArray[np.float32],
sensor_position_map: FloatArray,
claimed_source_indices: set[int],
profile: CameraGeometryFusionProfile,
) -> list[_GeometryClusterSupport]:
occupied = np.flatnonzero(point_class == POINT_OCCUPIED).astype(np.int64)
if occupied.size == 0:
return []
@@ -564,7 +592,7 @@ def _geometry_clusters(
occupied,
profile.geometry_voxel_size_m,
)
documents: list[dict[str, object]] = []
supports: list[_GeometryClusterSupport] = []
for indices, voxel_count in components:
if (
indices.size < profile.geometry_minimum_cluster_points
@@ -574,33 +602,38 @@ def _geometry_clusters(
continue
values = points_map[indices]
distances = np.linalg.norm(values - sensor_position_map, axis=1)
documents.append(
{
"geometry_status": "single-source-geometry",
"semantic_class": None,
"point_count": int(indices.size),
"voxel_count": voxel_count,
"centroid_map_xyz_m": np.median(values, axis=0).astype(np.float64).tolist(),
"bounds_map_xyz_m": [
np.min(values, axis=0).astype(np.float64).tolist(),
np.max(values, axis=0).astype(np.float64).tolist(),
],
"height_range_m": [
float(np.min(point_height_m[indices])),
float(np.max(point_height_m[indices])),
],
"nearest_range_m": float(np.min(distances)),
"unknown_is_occupied": True,
"navigation_or_safety_accepted": False,
}
supports.append(
_GeometryClusterSupport(
document={
"geometry_status": "single-source-geometry",
"semantic_class": None,
"point_count": int(indices.size),
"voxel_count": voxel_count,
"centroid_map_xyz_m": np.median(values, axis=0)
.astype(np.float64)
.tolist(),
"bounds_map_xyz_m": [
np.min(values, axis=0).astype(np.float64).tolist(),
np.max(values, axis=0).astype(np.float64).tolist(),
],
"height_range_m": [
float(np.min(point_height_m[indices])),
float(np.max(point_height_m[indices])),
],
"nearest_range_m": float(np.min(distances)),
"unknown_is_occupied": True,
"navigation_or_safety_accepted": False,
},
occupied_source_indices=indices.astype(np.int64, copy=False),
)
)
documents.sort(
key=lambda item: (
_required_float(item["nearest_range_m"]),
-_required_int(item["point_count"]),
supports.sort(
key=lambda support: (
_required_float(support.document["nearest_range_m"]),
-_required_int(support.document["point_count"]),
)
)
return documents[: profile.maximum_geometry_clusters_per_frame]
return supports[: profile.maximum_geometry_clusters_per_frame]
def _voxel_components(
+484
View File
@@ -0,0 +1,484 @@
"""Provider-neutral sensor representation capabilities and algorithm admission.
This module does not reinterpret source evidence. It records which physical
properties a representation actually provides and rejects algorithms whose
declared requirements are not satisfied. Compatibility never grants command,
navigation or safety authority.
"""
from __future__ import annotations
import re
from dataclasses import dataclass
from enum import StrEnum
from typing import Final
SENSOR_REPRESENTATION_CAPABILITIES_SCHEMA: Final = (
"missioncore.sensor-representation-capabilities/v1"
)
SENSOR_ALGORITHM_REQUIREMENTS_SCHEMA: Final = (
"missioncore.sensor-algorithm-requirements/v1"
)
SENSOR_ALGORITHM_ADMISSION_SCHEMA: Final = "missioncore.sensor-algorithm-admission/v1"
_IDENTIFIER = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:/-]{0,159}$")
class SensorRepresentationContractError(ValueError):
"""A representation, requirement or admission violates the v1 contract."""
class SensorRepresentationKind(StrEnum):
NATIVE_SENSOR_SCAN = "native-sensor-scan"
REGISTERED_MAP_INCREMENT = "registered-map-increment"
ACCUMULATED_MAP = "accumulated-map"
DEPTH_IMAGE = "depth-image"
class SensorSourceCurrentness(StrEnum):
CURRENT_OBSERVATION = "current-observation"
FRAME_INCREMENT = "frame-increment"
PERSISTENT_RECONSTRUCTION = "persistent-reconstruction"
class SensorCapability(StrEnum):
METRIC_XYZ = "metric_xyz"
METRIC_INTENSITY = "metric_intensity"
MAP_REGISTERED = "map_registered"
SENSOR_POSE_AVAILABLE = "sensor_pose_available"
PER_POINT_TIME = "per_point_time"
RING_OR_CHANNEL = "ring_or_channel"
SEPARATE_IMU = "separate_imu"
SHARED_HARDWARE_CLOCK = "shared_hardware_clock"
NATIVE_RAY_MODEL = "native_ray_model"
SENSOR_ORIGIN_PER_POINT = "sensor_origin_per_point"
RAY_CLEARING_VALID = "ray_clearing_valid"
MOTION_COMPENSATION_VALID = "motion_compensation_valid"
FREE_SPACE_EVIDENCE_VALID = "free_space_evidence_valid"
PERSISTENT_RECONSTRUCTION = "persistent_reconstruction"
@dataclass(frozen=True, slots=True)
class SensorRepresentationCapabilities:
"""State the admitted physical meaning of one versioned sensor product."""
profile_id: str
source_profile_id: str
representation_kind: SensorRepresentationKind
coordinate_frame: str
source_currentness: SensorSourceCurrentness
capabilities: frozenset[SensorCapability]
def __post_init__(self) -> None:
_safe_identifier(self.profile_id, "representation profile id")
_safe_identifier(self.source_profile_id, "source profile id")
_safe_identifier(self.coordinate_frame, "coordinate frame")
if not self.capabilities:
raise SensorRepresentationContractError(
"representation must declare at least one admitted capability"
)
capabilities = self.capabilities
point_capabilities = {
SensorCapability.METRIC_INTENSITY,
SensorCapability.MAP_REGISTERED,
SensorCapability.PER_POINT_TIME,
SensorCapability.RING_OR_CHANNEL,
SensorCapability.NATIVE_RAY_MODEL,
SensorCapability.SENSOR_ORIGIN_PER_POINT,
SensorCapability.RAY_CLEARING_VALID,
SensorCapability.MOTION_COMPENSATION_VALID,
SensorCapability.FREE_SPACE_EVIDENCE_VALID,
}
if (
capabilities.intersection(point_capabilities)
and SensorCapability.METRIC_XYZ not in capabilities
):
raise SensorRepresentationContractError(
"point-level capabilities require metric xyz"
)
if (
SensorCapability.RAY_CLEARING_VALID in capabilities
and (
SensorCapability.NATIVE_RAY_MODEL not in capabilities
or SensorCapability.SENSOR_ORIGIN_PER_POINT not in capabilities
)
):
raise SensorRepresentationContractError(
"ray clearing requires a native ray model and per-point sensor origin"
)
if (
SensorCapability.MOTION_COMPENSATION_VALID in capabilities
and (
SensorCapability.PER_POINT_TIME not in capabilities
or SensorCapability.SENSOR_POSE_AVAILABLE not in capabilities
)
):
raise SensorRepresentationContractError(
"motion compensation requires point time and sensor pose"
)
if (
SensorCapability.FREE_SPACE_EVIDENCE_VALID in capabilities
and SensorCapability.RAY_CLEARING_VALID not in capabilities
):
raise SensorRepresentationContractError(
"free-space evidence requires admitted ray clearing"
)
persistent = SensorCapability.PERSISTENT_RECONSTRUCTION in capabilities
currentness_is_persistent = (
self.source_currentness
is SensorSourceCurrentness.PERSISTENT_RECONSTRUCTION
)
if persistent is not currentness_is_persistent:
raise SensorRepresentationContractError(
"persistent reconstruction capability and currentness disagree"
)
if self.representation_kind is SensorRepresentationKind.REGISTERED_MAP_INCREMENT:
required = {
SensorCapability.METRIC_XYZ,
SensorCapability.MAP_REGISTERED,
}
if not required.issubset(capabilities):
raise SensorRepresentationContractError(
"registered map increments require metric map-registered xyz"
)
if self.source_currentness is not SensorSourceCurrentness.FRAME_INCREMENT:
raise SensorRepresentationContractError(
"registered map increments must declare frame-increment currentness"
)
elif self.representation_kind is SensorRepresentationKind.ACCUMULATED_MAP:
required = {
SensorCapability.METRIC_XYZ,
SensorCapability.MAP_REGISTERED,
SensorCapability.PERSISTENT_RECONSTRUCTION,
}
if not required.issubset(capabilities):
raise SensorRepresentationContractError(
"accumulated maps require persistent map-registered metric xyz"
)
def supports(self, capability: SensorCapability) -> bool:
return capability in self.capabilities
def to_dict(self) -> dict[str, object]:
return {
"schema_version": SENSOR_REPRESENTATION_CAPABILITIES_SCHEMA,
"profile_id": self.profile_id,
"source_profile_id": self.source_profile_id,
"representation_kind": self.representation_kind.value,
"coordinate_frame": self.coordinate_frame,
"source_currentness": self.source_currentness.value,
"capabilities": {
capability.value: capability in self.capabilities
for capability in SensorCapability
},
"semantics": {
"absence_of_endpoints_means_free": False,
"unknown_remains_unknown": True,
},
"authority": {
"compatibility_only": True,
"commands_enabled": False,
"navigation_or_safety_accepted": False,
},
}
@classmethod
def from_dict(cls, value: object) -> SensorRepresentationCapabilities:
document = _object(value, "sensor representation capabilities")
_exact_keys(
document,
{
"schema_version",
"profile_id",
"source_profile_id",
"representation_kind",
"coordinate_frame",
"source_currentness",
"capabilities",
"semantics",
"authority",
},
"sensor representation capabilities",
)
if (
document.get("schema_version")
!= SENSOR_REPRESENTATION_CAPABILITIES_SCHEMA
):
raise SensorRepresentationContractError(
"sensor representation capabilities schema is incompatible"
)
capability_document = _object(
document.get("capabilities"),
"sensor capabilities",
)
expected_capability_keys = {capability.value for capability in SensorCapability}
_exact_keys(
capability_document,
expected_capability_keys,
"sensor capabilities",
)
capabilities = frozenset(
capability
for capability in SensorCapability
if _bool(capability_document, capability.value)
)
semantics = _object(document.get("semantics"), "sensor semantics")
_exact_keys(
semantics,
{"absence_of_endpoints_means_free", "unknown_remains_unknown"},
"sensor semantics",
)
if (
_bool(semantics, "absence_of_endpoints_means_free")
or not _bool(semantics, "unknown_remains_unknown")
):
raise SensorRepresentationContractError(
"v1 sensor semantics cannot infer free space from missing endpoints"
)
authority = _object(document.get("authority"), "sensor authority")
_exact_keys(
authority,
{
"compatibility_only",
"commands_enabled",
"navigation_or_safety_accepted",
},
"sensor authority",
)
if (
not _bool(authority, "compatibility_only")
or _bool(authority, "commands_enabled")
or _bool(authority, "navigation_or_safety_accepted")
):
raise SensorRepresentationContractError(
"v1 sensor capability admission cannot grant authority"
)
try:
return cls(
profile_id=_string(document, "profile_id"),
source_profile_id=_string(document, "source_profile_id"),
representation_kind=SensorRepresentationKind(
_string(document, "representation_kind")
),
coordinate_frame=_string(document, "coordinate_frame"),
source_currentness=SensorSourceCurrentness(
_string(document, "source_currentness")
),
capabilities=capabilities,
)
except ValueError as exc:
raise SensorRepresentationContractError(
"sensor representation enum is unknown"
) from exc
@dataclass(frozen=True, slots=True)
class SensorAlgorithmRequirements:
"""Declare the minimum representation properties required by an algorithm."""
algorithm_id: str
accepted_representations: tuple[SensorRepresentationKind, ...]
required_capabilities: frozenset[SensorCapability]
def __post_init__(self) -> None:
_safe_identifier(self.algorithm_id, "algorithm id")
if not self.accepted_representations:
raise SensorRepresentationContractError(
"algorithm must declare at least one accepted representation"
)
if len(self.accepted_representations) != len(
set(self.accepted_representations)
):
raise SensorRepresentationContractError(
"accepted algorithm representations must be unique"
)
if not self.required_capabilities:
raise SensorRepresentationContractError(
"algorithm must declare at least one required capability"
)
def to_dict(self) -> dict[str, object]:
return {
"schema_version": SENSOR_ALGORITHM_REQUIREMENTS_SCHEMA,
"algorithm_id": self.algorithm_id,
"accepted_representations": [
representation.value for representation in self.accepted_representations
],
"required_capabilities": sorted(
capability.value for capability in self.required_capabilities
),
"on_capability_mismatch": "reject",
}
@classmethod
def from_dict(cls, value: object) -> SensorAlgorithmRequirements:
document = _object(value, "sensor algorithm requirements")
_exact_keys(
document,
{
"schema_version",
"algorithm_id",
"accepted_representations",
"required_capabilities",
"on_capability_mismatch",
},
"sensor algorithm requirements",
)
if document.get("schema_version") != SENSOR_ALGORITHM_REQUIREMENTS_SCHEMA:
raise SensorRepresentationContractError(
"sensor algorithm requirements schema is incompatible"
)
if document.get("on_capability_mismatch") != "reject":
raise SensorRepresentationContractError(
"sensor algorithm capability mismatch must reject"
)
try:
accepted_representations = tuple(
SensorRepresentationKind(_string_value(item, "representation kind"))
for item in _array(document, "accepted_representations")
)
required_capabilities = frozenset(
SensorCapability(_string_value(item, "sensor capability"))
for item in _array(document, "required_capabilities")
)
return cls(
algorithm_id=_string(document, "algorithm_id"),
accepted_representations=accepted_representations,
required_capabilities=required_capabilities,
)
except ValueError as exc:
raise SensorRepresentationContractError(
"sensor algorithm requirement enum is unknown"
) from exc
@dataclass(frozen=True, slots=True)
class SensorAlgorithmAdmission:
profile_id: str
algorithm_id: str
admitted: bool
reasons: tuple[str, ...]
def to_dict(self) -> dict[str, object]:
return {
"schema_version": SENSOR_ALGORITHM_ADMISSION_SCHEMA,
"profile_id": self.profile_id,
"algorithm_id": self.algorithm_id,
"admitted": self.admitted,
"reasons": list(self.reasons),
"authority": {
"compatibility_only": True,
"commands_enabled": False,
"navigation_or_safety_accepted": False,
},
}
def assess_sensor_algorithm(
profile: SensorRepresentationCapabilities,
requirements: SensorAlgorithmRequirements,
) -> SensorAlgorithmAdmission:
"""Return a deterministic binary admission without inferring capabilities."""
reasons: list[str] = []
if profile.representation_kind not in requirements.accepted_representations:
reasons.append(
f"representation-not-accepted:{profile.representation_kind.value}"
)
missing_capabilities = requirements.required_capabilities.difference(
profile.capabilities
)
reasons.extend(
f"missing-capability:{capability.value}"
for capability in sorted(missing_capabilities, key=lambda item: item.value)
)
return SensorAlgorithmAdmission(
profile_id=profile.profile_id,
algorithm_id=requirements.algorithm_id,
admitted=not reasons,
reasons=tuple(reasons),
)
def require_sensor_algorithm_admission(
profile: SensorRepresentationCapabilities,
requirements: SensorAlgorithmRequirements,
) -> SensorAlgorithmAdmission:
"""Return admission or reject the algorithm/representation pairing."""
admission = assess_sensor_algorithm(profile, requirements)
if not admission.admitted:
raise SensorRepresentationContractError(
f"{requirements.algorithm_id} rejected for {profile.profile_id}: "
+ ", ".join(admission.reasons)
)
return admission
def _safe_identifier(value: str, label: str) -> str:
if not isinstance(value, str) or _IDENTIFIER.fullmatch(value) is None:
raise SensorRepresentationContractError(f"{label} is not a safe identifier")
return value
def _object(value: object, label: str) -> dict[str, object]:
if not isinstance(value, dict) or any(not isinstance(key, str) for key in value):
raise SensorRepresentationContractError(f"{label} must be an object")
return value
def _exact_keys(
document: dict[str, object],
expected: set[str],
label: str,
) -> None:
if set(document) != expected:
raise SensorRepresentationContractError(f"{label} fields are incompatible")
def _array(document: dict[str, object], key: str) -> list[object]:
value = document.get(key)
if not isinstance(value, list):
raise SensorRepresentationContractError(f"{key} must be an array")
return value
def _string(document: dict[str, object], key: str) -> str:
return _string_value(document.get(key), key)
def _string_value(value: object, label: str) -> str:
if not isinstance(value, str) or not value:
raise SensorRepresentationContractError(f"{label} must be a nonempty string")
return value
def _bool(document: dict[str, object], key: str) -> bool:
value = document.get(key)
if not isinstance(value, bool):
raise SensorRepresentationContractError(f"{key} must be a boolean")
return value
K1_LIO_PCL_CAPABILITIES: Final = SensorRepresentationCapabilities(
profile_id="xgrids-k1-live-lio-pcl-capabilities/v1",
source_profile_id="xgrids-k1-live-lio-pcl/v1",
representation_kind=SensorRepresentationKind.REGISTERED_MAP_INCREMENT,
coordinate_frame="map",
source_currentness=SensorSourceCurrentness.FRAME_INCREMENT,
capabilities=frozenset(
{
SensorCapability.METRIC_XYZ,
SensorCapability.METRIC_INTENSITY,
SensorCapability.MAP_REGISTERED,
SensorCapability.SENSOR_POSE_AVAILABLE,
}
),
)