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
+158
View File
@@ -0,0 +1,158 @@
from __future__ import annotations
import pytest
from k1link.compute.e30_engineering_generation import (
E30_ENGINEERING_DECISION_SCHEMA,
E30EngineeringGenerationError,
_validate_decision,
)
from k1link.web.e30_engineering_api import _validate_decisions
def _item() -> dict[str, object]:
return {
"sequence": 0,
"item_id": f"e30-review-item-{'a' * 64}",
"review_key": "geometry:100:0",
"stratum": "geometry-only",
}
def _decision(**overrides: object) -> dict[str, object]:
value: dict[str, object] = {
"schema_version": E30_ENGINEERING_DECISION_SCHEMA,
"sequence": 0,
"item_id": f"e30-review-item-{'a' * 64}",
"review_key": "geometry:100:0",
"source_stratum": "geometry-only",
"verdict": "confirmed",
"effective_stratum": "geometry-only",
"detector_assessment": "missed-object",
"projection_assessment": "aligned",
"point_ownership": "object",
"cause_code": "detector_error",
"confidence": 0.88,
"human_exception_required": False,
"exception_reason": None,
"review_prompt": None,
"evidence_note": "Visible object has geometry but no semantic observation.",
"review_sheet": {
"path": "geometry-only-01.jpg",
"sha256": "b" * 64,
"ordinal": 1,
},
}
value.update(overrides)
return value
def test_engineering_decision_supports_detector_miss_without_rewriting_a2() -> None:
validated = _validate_decision(
value=_decision(),
item=_item(),
reason_taxonomy=("detector_error", "unknown"),
expected_sheet={
"path": "geometry-only-01.jpg",
"sha256": "b" * 64,
"ordinal": 1,
},
)
assert validated["verdict"] == "confirmed"
assert validated["effective_stratum"] == "geometry-only"
assert validated["detector_assessment"] == "missed-object"
def test_engineering_uncertainty_must_route_a_bounded_exception() -> None:
with pytest.raises(
E30EngineeringGenerationError,
match="insufficient decision must route an exception",
):
_validate_decision(
value=_decision(
verdict="insufficient-evidence",
effective_stratum=None,
detector_assessment="insufficient-evidence",
confidence=0.48,
),
item=_item(),
reason_taxonomy=("detector_error", "unknown"),
expected_sheet={
"path": "geometry-only-01.jpg",
"sha256": "b" * 64,
"ordinal": 1,
},
)
def test_engineering_api_recomputes_distributions_from_all_486_decisions() -> None:
decisions = []
for sequence in range(486):
decision = _decision(
sequence=sequence,
item_id=f"e30-review-item-{sequence:064x}",
review_key=f"geometry:{sequence}:0",
)
decisions.append(decision)
decisions[-1] = {
**decisions[-1],
"verdict": "insufficient-evidence",
"effective_stratum": None,
"detector_assessment": "insufficient-evidence",
"projection_assessment": "not-assessable",
"point_ownership": "insufficient-evidence",
"cause_code": "unknown",
"confidence": 0.48,
"human_exception_required": True,
"exception_reason": "ambiguity",
"review_prompt": {
"question": "Is this an occupied physical object?",
"focus": "Inspect the selected white LiDAR cluster.",
"effects": {
"object-present": "Retain occupied geometry.",
"background-or-noise": "Reject the cluster.",
"insufficient-evidence": "Keep the item unknown.",
},
},
}
summary, causes = _validate_decisions(decisions)
assert summary["reviewed_item_count"] == 486
assert summary["human_exception_count"] == 1
assert summary["verdict_distribution"] == {
"confirmed": 485,
"insufficient-evidence": 1,
}
assert causes["reasons"] == [
{"reason_code": "detector_error", "count": 485},
{"reason_code": "unknown", "count": 1},
]
def test_engineering_exception_requires_a_specific_review_prompt() -> None:
with pytest.raises(
E30EngineeringGenerationError,
match="exception review prompt is invalid",
):
_validate_decision(
value=_decision(
verdict="insufficient-evidence",
effective_stratum=None,
detector_assessment="insufficient-evidence",
projection_assessment="not-assessable",
point_ownership="insufficient-evidence",
cause_code="unknown",
confidence=0.48,
human_exception_required=True,
exception_reason="ambiguity",
),
item=_item(),
reason_taxonomy=("detector_error", "unknown"),
expected_sheet={
"path": "geometry-only-01.jpg",
"sha256": "b" * 64,
"ordinal": 1,
},
)
+344
View File
@@ -0,0 +1,344 @@
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
import pytest
from fastapi import APIRouter
from k1link.compute.e30_human_review import (
E30HumanReviewConflictError,
E30HumanReviewIntegrityError,
E30HumanReviewStore,
E30HumanReviewValidationError,
E30ReviewSubject,
E30ReviewSubstrate,
)
from k1link.web import e30_human_review_api
from k1link.web.e30_human_review_api import build_e30_human_review_router
def _endpoint(router: APIRouter, path: str, method: str) -> Any:
for route in router.routes:
if (
getattr(route, "path", None) == path
and method in getattr(route, "methods", set())
):
return route.endpoint
raise AssertionError(f"{method} endpoint {path} not found")
def _substrate() -> E30ReviewSubstrate:
return E30ReviewSubstrate(
materialization_id=f"e30-materialization-{'a' * 64}",
materialization_identity_sha256="a" * 64,
review_pack_id=f"e30-review-pack-{'b' * 64}",
review_items_sha256="c" * 64,
reason_taxonomy=(),
subjects=(
E30ReviewSubject(
item_id=f"e30-review-item-{'d' * 64}",
sequence=0,
source_stratum="geometry-only",
),
E30ReviewSubject(
item_id=f"e30-review-item-{'e' * 64}",
sequence=1,
source_stratum="unknown",
),
),
engineering_generation_id=f"e30-engineering-generation-{'f' * 64}",
)
def _store(tmp_path: Path) -> E30HumanReviewStore:
return E30HumanReviewStore(
draft_root=tmp_path / "drafts",
generation_root=tmp_path / "generations",
)
def test_exception_review_is_generation_bound_and_resumable(
tmp_path: Path,
) -> None:
substrate = _substrate()
store = _store(tmp_path)
created = store.create_or_resume(substrate=substrate, reviewer_id="DC")
resumed = store.create_or_resume(substrate=substrate, reviewer_id="DC")
assert resumed["draft_id"] == created["draft_id"]
assert resumed["engineering_generation_id"] == (
substrate.engineering_generation_id
)
assert resumed["item_count"] == 2
assert resumed["reviewed_item_count"] == 0
def test_decisions_are_append_only_idempotent_and_superseding(
tmp_path: Path,
) -> None:
substrate = _substrate()
store = _store(tmp_path)
draft_id = str(
store.create_or_resume(substrate=substrate, reviewer_id="DC")["draft_id"]
)
subject = substrate.subjects[0]
first = store.record_decision(
draft_id=draft_id,
substrate=substrate,
item_id=subject.item_id,
expected_revision=0,
idempotency_key="decision-001",
disposition="object-present",
notes=None,
)
replay = store.record_decision(
draft_id=draft_id,
substrate=substrate,
item_id=subject.item_id,
expected_revision=0,
idempotency_key="decision-001",
disposition="object-present",
notes=None,
)
changed = store.record_decision(
draft_id=draft_id,
substrate=substrate,
item_id=subject.item_id,
expected_revision=1,
idempotency_key="decision-002",
disposition="background-or-noise",
notes="Static façade points.",
)
assert first["revision"] == replay["revision"] == 1
assert changed["revision"] == 2
assert changed["reviewed_item_count"] == 1
assert changed["disposition_distribution"] == {
"background-or-noise": 1
}
assert len(
(
tmp_path / "drafts" / draft_id / "events.jsonl"
).read_text().splitlines()
) == 2
def test_decision_rejects_unknown_disposition(tmp_path: Path) -> None:
substrate = _substrate()
store = _store(tmp_path)
draft_id = str(
store.create_or_resume(substrate=substrate, reviewer_id="DC")["draft_id"]
)
with pytest.raises(E30HumanReviewValidationError):
store.record_decision(
draft_id=draft_id,
substrate=substrate,
item_id=substrate.subjects[0].item_id,
expected_revision=0,
idempotency_key="decision-invalid",
disposition="invented", # type: ignore[arg-type]
notes=None,
)
def test_finalization_requires_every_exception_and_freezes_generation(
tmp_path: Path,
) -> None:
substrate = _substrate()
store = _store(tmp_path)
draft_id = str(
store.create_or_resume(substrate=substrate, reviewer_id="DC")["draft_id"]
)
first = store.record_decision(
draft_id=draft_id,
substrate=substrate,
item_id=substrate.subjects[0].item_id,
expected_revision=0,
idempotency_key="decision-001",
disposition="object-present",
notes=None,
)
with pytest.raises(E30HumanReviewConflictError, match="coverage is incomplete"):
store.finalize(
draft_id=draft_id,
substrate=substrate,
expected_revision=int(first["revision"]),
)
complete = store.record_decision(
draft_id=draft_id,
substrate=substrate,
item_id=substrate.subjects[1].item_id,
expected_revision=1,
idempotency_key="decision-002",
disposition="insufficient-evidence",
notes="Occluded.",
)
generation = store.finalize(
draft_id=draft_id,
substrate=substrate,
expected_revision=int(complete["revision"]),
)
assert generation["human_review_complete"] is True
assert generation["lab_published"] is False
assert generation["engineering_generation_id"] == (
substrate.engineering_generation_id
)
assert generation["disposition_distribution"] == {
"insufficient-evidence": 1,
"object-present": 1,
}
generation_id = str(generation["generation_id"])
decision_rows = [
json.loads(line)
for line in (
tmp_path
/ "generations"
/ generation_id
/ "review-decisions.jsonl"
).read_text().splitlines()
]
assert [row["disposition"] for row in decision_rows] == [
"object-present",
"insufficient-evidence",
]
repeated = store.finalize(
draft_id=draft_id,
substrate=substrate,
expected_revision=2,
)
assert repeated["generation_id"] == generation_id
with pytest.raises(E30HumanReviewConflictError, match="finalized"):
store.record_decision(
draft_id=draft_id,
substrate=substrate,
item_id=substrate.subjects[0].item_id,
expected_revision=2,
idempotency_key="decision-after-finalize",
disposition="object-present",
notes=None,
)
def test_event_and_generation_tampering_fail_closed(tmp_path: Path) -> None:
substrate = _substrate()
store = _store(tmp_path)
draft_id = str(
store.create_or_resume(substrate=substrate, reviewer_id="DC")["draft_id"]
)
store.record_decision(
draft_id=draft_id,
substrate=substrate,
item_id=substrate.subjects[0].item_id,
expected_revision=0,
idempotency_key="decision-001",
disposition="object-present",
notes=None,
)
events_path = tmp_path / "drafts" / draft_id / "events.jsonl"
event = json.loads(events_path.read_text())
event["disposition"] = "background-or-noise"
events_path.write_text(json.dumps(event) + "\n")
with pytest.raises(E30HumanReviewIntegrityError):
store.get(draft_id=draft_id, substrate=substrate)
def test_http_lifecycle_is_bound_to_the_engineering_exception_queue(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
) -> None:
source_substrate = _substrate()
generation_id = str(source_substrate.engineering_generation_id)
monkeypatch.setattr(
e30_human_review_api,
"load_verified_e30_review",
lambda **_: ({}, (), source_substrate),
)
monkeypatch.setattr(
e30_human_review_api,
"load_verified_e30_engineering_generation",
lambda **_: (
{
"human_exceptions": [
{"item_id": subject.item_id}
for subject in source_substrate.subjects
]
},
(),
),
)
router = build_e30_human_review_router(
materialization_root_provider=lambda: tmp_path / "materializations",
review_pack_root_provider=lambda: tmp_path / "review-packs",
engineering_generation_root_provider=lambda: (
tmp_path / "engineering-generations"
),
draft_root_provider=lambda: tmp_path / "drafts",
generation_root_provider=lambda: tmp_path / "generations",
)
create = _endpoint(
router,
"/api/v1/laboratory/e30/reviews/{result_id}/human-review",
"POST",
)
decide = _endpoint(
router,
(
"/api/v1/laboratory/e30/reviews/{result_id}/human-review/"
"{draft_id}/decisions/{item_id}"
),
"PUT",
)
finalize = _endpoint(
router,
(
"/api/v1/laboratory/e30/reviews/{result_id}/human-review/"
"{draft_id}/finalize"
),
"POST",
)
created = create(
result_id=source_substrate.materialization_id,
request=e30_human_review_api.E30HumanReviewCreateRequest(
reviewer_id="DC",
engineering_generation_id=generation_id,
),
)
draft = created
for revision, subject in enumerate(source_substrate.subjects):
draft = decide(
result_id=source_substrate.materialization_id,
draft_id=draft["draft_id"],
item_id=subject.item_id,
engineering_generation_id=generation_id,
request=e30_human_review_api.E30HumanReviewDecisionRequest(
expected_revision=revision,
idempotency_key=f"http-{revision}",
disposition=(
"object-present"
if revision == 0
else "insufficient-evidence"
),
notes=None,
),
)
finalized = finalize(
result_id=source_substrate.materialization_id,
draft_id=draft["draft_id"],
engineering_generation_id=generation_id,
request=e30_human_review_api.E30HumanReviewFinalizeRequest(
expected_revision=draft["revision"],
confirm_generation=True,
),
)
assert finalized["draft"]["state"] == "finalized"
+394
View File
@@ -0,0 +1,394 @@
from __future__ import annotations
import hashlib
import json
from pathlib import Path
from typing import Any
import numpy as np
from fastapi import APIRouter
from fastapi.routing import APIRoute
from k1link.compute import e30_materialization as materialization
from k1link.compute.e30_materialization import build_e30_materialization
from k1link.compute.semantic_geometry_fusion import (
CameraGeometryFusionProfile,
_projection_profile,
_semantic_support,
)
from k1link.device_plugins.xgrids_k1.analyze.calibrated_projection import (
project_map_points_kb4,
)
from k1link.web.e30_review_api import build_e30_review_router
def _canonical(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode()
def _sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def _endpoint(router: APIRouter, path: str) -> object:
for route in router.routes:
if (
isinstance(route, APIRoute)
and route.path == path
and route.methods is not None
and "GET" in route.methods
):
return route.endpoint
raise AssertionError(f"GET {path} route is missing")
class _FakeSource:
def __init__(self, root: Path) -> None:
self.root = root
self.pack_id = root.name
self.arrays = {
"cloud_offsets": np.asarray([0, 4], dtype=np.int64),
"cloud_points_map": np.asarray(
[
[0.00, 0.00, 2.0],
[0.10, 0.00, 2.0],
[0.20, 0.00, 2.0],
[1.50, 1.50, 2.0],
],
dtype=np.float32,
),
"pose_positions_map": np.zeros((1, 3), dtype=np.float64),
"pose_quaternions_map_from_lidar": np.asarray(
[[0.0, 0.0, 0.0, 1.0]],
dtype=np.float64,
),
"sample_available": np.asarray([True], dtype=np.bool_),
"source_frame_indices": np.asarray([10], dtype=np.int64),
"session_seconds": np.asarray([12.5], dtype=np.float64),
"intrinsic_fx_fy_cx_cy": np.asarray(
[100.0, 100.0, 50.0, 50.0],
dtype=np.float64,
),
"distortion_kb4": np.zeros(4, dtype=np.float64),
"t_camera_from_lidar": np.eye(4, dtype=np.float64),
}
self.identity: dict[str, Any] = {
"session_id": "source-session",
"frame_count": 1,
"point_count": 4,
"source_id": "sensor.camera.right",
"camera_slot": "camera_1",
"projection": {"width": 100, "height": 100},
}
self.manifest = {"artifact": {"sha256": "1" * 64}}
@property
def frame_count(self) -> int:
return 1
@property
def point_count(self) -> int:
return 4
def close(self) -> None:
pass
class _FakeSurface:
def __init__(self, root: Path) -> None:
self.root = root
self.model_id = root.name
self.identity: dict[str, Any] = {
"source_pack_id": "e10-lidar-pack-" + "b" * 64,
"frame_count": 1,
"point_count": 4,
}
self.arrays = {
"frame_valid": np.asarray([True], dtype=np.bool_),
"point_class": np.asarray([2, 2, 2, 1], dtype=np.uint8),
"point_height_m": np.asarray([0.5, 0.5, 0.5, 0.0], dtype=np.float32),
}
self.manifest = {
"artifacts": [
{"role": "local-surface", "sha256": "2" * 64},
]
}
def close(self) -> None:
pass
def _write_source_tree(tmp_path: Path) -> tuple[dict[str, Path], str]:
source_pack_id = "e10-lidar-pack-" + "b" * 64
local_surface_id = "k1-local-surface-" + "c" * 64
source_result_identity = {"schema_version": "test-source/v1"}
source_result_identity_sha256 = hashlib.sha256(
_canonical(source_result_identity)
).hexdigest()
source_result_id = (
f"e10-integrated-perception-{source_result_identity_sha256}"
)
roots = {
"e29": tmp_path / "e29",
"source_results": tmp_path / "source-results",
"source_packs": tmp_path / "source-packs",
"surfaces": tmp_path / "surfaces",
"reviews": tmp_path / "reviews",
"output": tmp_path / "output",
}
for root in roots.values():
root.mkdir()
(roots["source_packs"] / source_pack_id).mkdir()
(roots["surfaces"] / local_surface_id).mkdir()
source_result = roots["source_results"] / source_result_id
source_result.mkdir()
result_document = {
"result_id": source_result_id,
"identity_sha256": source_result_identity_sha256,
"identity": source_result_identity,
}
(source_result / "result.json").write_bytes(_canonical(result_document))
fusion_frame = {
"schema_version": "missioncore.e10-fusion-frame/v1",
"frame_index": 0,
"source_frame_index": 10,
"session_seconds": 12.5,
"objects": [
{
"source_track_id": 7,
"track_id": 70,
"label": "car",
"association_group": "vehicle",
"score": 0.9,
"bbox_xyxy": [40.0, 40.0, 70.0, 60.0],
"cuboid_status": "observed",
"camera_motion_state": "static",
"camera_motion_confidence": 0.8,
"motion_state": "unknown",
"motion_status": "test",
}
],
}
fusion_path = source_result / "fusion-frames.jsonl"
fusion_path.write_bytes(_canonical(fusion_frame) + b"\n")
source = _FakeSource(roots["source_packs"] / source_pack_id)
profile = CameraGeometryFusionProfile()
points = np.asarray(source.arrays["cloud_points_map"], dtype=np.float64)
projected = project_map_points_kb4(
points,
position_map_xyz=(0.0, 0.0, 0.0),
orientation_map_from_lidar_xyzw=(0.0, 0.0, 0.0, 1.0),
profile=_projection_profile(source), # type: ignore[arg-type]
)
snapshot = _semantic_support(
fusion_frame["objects"][0],
projected=projected,
frame_points_map=points,
point_class=np.asarray([2, 2, 2, 1], dtype=np.uint8),
point_height_m=np.asarray([0.5, 0.5, 0.5, 0.0], dtype=np.float32),
source_available=True,
surface_valid=True,
profile=profile,
).document
assert snapshot["geometry_status"] == "agree"
e29_identity = {
"schema_version": "missioncore.e29-camera-geometry-fusion/v1",
"source_result_id": source_result_id,
"source_fusion_frames_sha256": _sha256(fusion_path),
"source_pack_id": source_pack_id,
"local_surface_model_id": local_surface_id,
"frame_count": 1,
"profile": profile.to_dict(),
"authority": {
"commands_enabled": False,
"navigation_or_safety_accepted": False,
},
}
e29_identity_sha256 = hashlib.sha256(_canonical(e29_identity)).hexdigest()
e29_result_id = f"e29-camera-geometry-{e29_identity_sha256}"
e29_result = roots["e29"] / e29_result_id
e29_result.mkdir()
e29_manifest = {
"schema_version": "missioncore.e29-camera-geometry-fusion/v1",
"result_id": e29_result_id,
"identity_sha256": e29_identity_sha256,
"identity": e29_identity,
}
(e29_result / "manifest.json").write_bytes(_canonical(e29_manifest))
review_item_id = "e30-review-item-" + "d" * 64
review_item = {
"schema_version": "missioncore.e30-evidence-review-item/v1",
"sequence": 0,
"item_id": review_item_id,
"review_key": "semantic:0:0",
"stratum": "agree",
"range_bucket": "near",
"evidence_binding": {
"frame_index": 0,
"source_frame_index": 10,
"session_seconds": 12.5,
},
"e29_locator": {
"kind": "semantic-observation",
"observation_index": 0,
},
"e29_snapshot": snapshot,
"review": {"state": "unreviewed", "reason_code": None, "notes": None},
"authority": {
"commands_enabled": False,
"navigation_or_safety_accepted": False,
},
}
review_items = _canonical(review_item) + b"\n"
review_identity = {
"source": {
"e29_result_id": e29_result_id,
"e29_identity_sha256": e29_identity_sha256,
"camera_result_id": source_result_id,
"lidar_pack_id": source_pack_id,
"local_surface_model_id": local_surface_id,
},
"reason_taxonomy": [
"no_lidar_observation",
"outside_lidar_support",
"outside_camera_fov",
"time_mismatch",
"semantic_mismatch",
"geometry_mismatch",
"insufficient_evidence",
"other",
],
}
review_identity_sha256 = hashlib.sha256(_canonical(review_identity)).hexdigest()
review_result_id = f"e30-review-pack-{review_identity_sha256}"
review_root = roots["reviews"] / review_result_id
review_root.mkdir()
items_path = review_root / "review-items.jsonl"
items_path.write_bytes(review_items)
review_manifest = {
"schema_version": "missioncore.e30-evidence-review-pack/v1",
"result_id": review_result_id,
"identity_sha256": review_identity_sha256,
"identity": review_identity,
"human_review_complete": False,
"lab_published": False,
"selected_item_count": 1,
"artifacts": [
{
"role": "review-items",
"path": items_path.name,
"byte_length": items_path.stat().st_size,
"sha256": _sha256(items_path),
}
],
"authority": {
"commands_enabled": False,
"navigation_or_safety_accepted": False,
},
}
(review_root / "manifest.json").write_bytes(_canonical(review_manifest))
roots["review_root"] = review_root
return roots, review_item_id
def test_materialization_replays_exact_support_and_publishes_point_indices(
tmp_path: Path,
monkeypatch: Any,
) -> None:
roots, review_item_id = _write_source_tree(tmp_path)
monkeypatch.setattr(materialization, "E10LidarFieldSource", _FakeSource)
monkeypatch.setattr(materialization, "K1LocalSurfaceV1", _FakeSurface)
result = build_e30_materialization(
review_pack_root=roots["review_root"],
e29_root=roots["e29"],
source_result_root=roots["source_results"],
source_pack_root=roots["source_packs"],
local_surface_root=roots["surfaces"],
output_root=roots["output"],
)
assert result.manifest["item_count"] == 1
assert result.manifest["human_review_complete"] is False
index = json.loads(
(result.result_root / "materialized-items.jsonl").read_text()
)
assert index["item_id"] == review_item_id
assert index["materialization"]["selected_point_count"] == 3
assert index["materialization"]["source_reprojection_required"] is False
artifact = result.result_root / index["artifact"]["path"]
with np.load(artifact, allow_pickle=False) as arrays:
assert arrays["selected_source_indices"].tolist() == [0, 1, 2]
assert arrays["projected_selected_mask"].sum() == 3
assert arrays["projected_candidate_mask"].sum() >= 3
def test_e30_review_api_exposes_verified_read_only_evidence(
tmp_path: Path,
monkeypatch: Any,
) -> None:
roots, review_item_id = _write_source_tree(tmp_path)
monkeypatch.setattr(materialization, "E10LidarFieldSource", _FakeSource)
monkeypatch.setattr(materialization, "K1LocalSurfaceV1", _FakeSurface)
result = build_e30_materialization(
review_pack_root=roots["review_root"],
e29_root=roots["e29"],
source_result_root=roots["source_results"],
source_pack_root=roots["source_packs"],
local_surface_root=roots["surfaces"],
output_root=roots["output"],
)
router = build_e30_review_router(
materialization_root_provider=lambda: roots["output"],
review_pack_root_provider=lambda: roots["reviews"],
)
catalog_route = _endpoint(router, "/api/v1/laboratory/e30/reviews")
items_route = _endpoint(
router,
"/api/v1/laboratory/e30/reviews/{result_id}/items",
)
detail_route = _endpoint(
router,
"/api/v1/laboratory/e30/reviews/{result_id}/items/{item_id}",
)
catalog = catalog_route(limit=1) # type: ignore[operator]
items = items_route( # type: ignore[operator]
result_id=result.result_id,
stratum="agree",
limit=48,
cursor=0,
)
detail = detail_route( # type: ignore[operator]
result_id=result.result_id,
item_id=review_item_id,
)
assert catalog["configured"] is True
assert catalog["items"][0]["access"] == "read-only"
assert catalog["items"][0]["stratum_counts"]["agree"] == 1
assert items["total"] == 1
assert items["items"][0]["item_id"] == review_item_id
assert detail["item"]["selected"]["source_indices"] == [0, 1, 2]
assert detail["item"]["projection"]["selected_mask"] == [1, 1, 1]
assert str(tmp_path) not in repr(catalog)
assert str(tmp_path) not in repr(items)
assert str(tmp_path) not in repr(detail)
artifact = result.result_root / "items" / f"{review_item_id}.npz"
artifact.write_bytes(artifact.read_bytes() + b"changed")
tampered = catalog_route(limit=1) # type: ignore[operator]
assert tampered["candidate_total"] == 1
assert tampered["invalid_total"] == 1
assert tampered["items"] == []
+228
View File
@@ -0,0 +1,228 @@
from __future__ import annotations
import hashlib
import json
from pathlib import Path
from typing import Any
import pytest
from k1link.compute.e30_review_pack import (
E30_REASON_TAXONOMY,
E30ReviewPackError,
E30ReviewSelectionProfile,
build_e30_review_pack,
)
from k1link.compute.semantic_geometry_fusion import (
CAMERA_GEOMETRY_FRAME_SCHEMA,
CAMERA_GEOMETRY_FUSION_SCHEMA,
CAMERA_GEOMETRY_REPORT_SCHEMA,
)
def _canonical(value: object) -> bytes:
return json.dumps(
value,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode()
def _sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def _observation(status: str, index: int) -> dict[str, object]:
return {
"track_id": index,
"label": "car" if index % 2 else "person",
"association_group": "vehicle" if index % 2 else "person",
"geometry_status": status,
"geometry_reason": f"reason-{status}",
"range_m": None if status in {"single-source-camera", "unknown"} else 2.5 + index,
"support": {"connected_occupied_points": 0},
}
def _source_result(root: Path) -> Path:
result_id = "e29-camera-geometry-" + "a" * 64
result = root / result_id
result.mkdir()
frames = [
{
"schema_version": CAMERA_GEOMETRY_FRAME_SCHEMA,
"frame_index": 0,
"source_frame_index": 10,
"session_seconds": 1.0,
"semantic_observations": [
_observation("agree", 1),
_observation("single-source-camera", 2),
_observation("conflict", 3),
_observation("unknown", 4),
],
"geometry_only_occupied": [
{
"geometry_status": "single-source-geometry",
"nearest_range_m": 8.0,
"point_count": 10,
}
],
},
{
"schema_version": CAMERA_GEOMETRY_FRAME_SCHEMA,
"frame_index": 1,
"source_frame_index": 11,
"session_seconds": 2.0,
"semantic_observations": [
_observation("agree", 5),
_observation("single-source-camera", 6),
_observation("conflict", 7),
_observation("unknown", 8),
],
"geometry_only_occupied": [
{
"geometry_status": "single-source-geometry",
"nearest_range_m": 2.0,
"point_count": 12,
}
],
},
]
frames_path = result / "camera-geometry-frames.jsonl"
frames_path.write_bytes(b"".join(_canonical(frame) + b"\n" for frame in frames))
identity: dict[str, Any] = {
"frame_count": 2,
"timeline_start_seconds": 1.0,
"timeline_end_seconds": 2.0,
"source_result_id": "e10-integrated-perception-" + "b" * 64,
"source_pack_id": "e10-lidar-pack-" + "c" * 64,
"local_surface_model_id": "k1-local-surface-" + "d" * 64,
"profile": {"profile_id": "camera-first-local-surface-validation/v1"},
"authority": {
"commands_enabled": False,
"navigation_or_safety_accepted": False,
},
}
report = {
"schema_version": CAMERA_GEOMETRY_REPORT_SCHEMA,
"result_id": result_id,
"status": "diagnostic-replay-complete",
"ground_truth": False,
"identity": identity,
"metrics": {
"semantic_observations": {
"geometry_status": {
"agree": 2,
"single-source-camera": 2,
"conflict": 2,
"unknown": 2,
}
},
"geometry_only_occupied": {"cluster_count": 2},
},
"authority": {
"commands_enabled": False,
"navigation_or_safety_accepted": False,
},
}
report_path = result / "camera-geometry-report.json"
report_path.write_bytes(_canonical(report))
identity_sha256 = hashlib.sha256(_canonical(identity)).hexdigest()
result_id = f"e29-camera-geometry-{identity_sha256}"
result_with_identity = root / result_id
result.rename(result_with_identity)
result = result_with_identity
frames_path = result / frames_path.name
report_path = result / report_path.name
report["result_id"] = result_id
report_path.write_bytes(_canonical(report))
manifest = {
"schema_version": CAMERA_GEOMETRY_FUSION_SCHEMA,
"result_id": result_id,
"identity_sha256": identity_sha256,
"identity": identity,
"ground_truth": False,
"artifacts": [
{
"role": "camera-geometry-frames",
"path": frames_path.name,
"byte_length": frames_path.stat().st_size,
"sha256": _sha256(frames_path),
},
{
"role": "camera-geometry-report",
"path": report_path.name,
"byte_length": report_path.stat().st_size,
"sha256": _sha256(report_path),
},
],
}
(result / "manifest.json").write_bytes(_canonical(manifest))
return result
def test_review_pack_binds_all_strata_and_keeps_human_decision_open(
tmp_path: Path,
) -> None:
source = _source_result(tmp_path)
profile = E30ReviewSelectionProfile(
agree_maximum=1,
camera_only_maximum=1,
unknown_maximum=1,
geometry_only_maximum=1,
temporal_bins=2,
)
first = build_e30_review_pack(
e29_result_root=source,
output_root=tmp_path / "review-packs",
profile=profile,
)
second = build_e30_review_pack(
e29_result_root=source,
output_root=tmp_path / "review-packs",
profile=profile,
)
assert first.result_id == second.result_id
assert first.manifest["human_review_complete"] is False
assert first.manifest["lab_published"] is False
assert first.manifest["source_counts"] == {
"agree": 2,
"camera-only": 2,
"conflict": 2,
"geometry-only": 2,
"unknown": 2,
}
assert first.manifest["selected_counts"] == {
"agree": 1,
"camera-only": 1,
"conflict": 2,
"geometry-only": 1,
"unknown": 1,
}
lines = (first.result_root / "review-items.jsonl").read_text().splitlines()
items = [json.loads(line) for line in lines]
assert len(items) == 6
assert all(item["review"]["state"] == "unreviewed" for item in items)
assert all(item["materialization"]["source_reprojection_required"] for item in items)
assert len({item["item_id"] for item in items}) == 6
def test_review_pack_taxonomy_is_fixed_and_source_tampering_rejects(
tmp_path: Path,
) -> None:
assert E30_REASON_TAXONOMY[0] == "no_lidar_observation"
assert E30_REASON_TAXONOMY[-1] == "unknown"
assert len(E30_REASON_TAXONOMY) == 19
source = _source_result(tmp_path)
frames = source / "camera-geometry-frames.jsonl"
frames.write_bytes(frames.read_bytes() + b"\n")
with pytest.raises(E30ReviewPackError, match="byte length changed"):
build_e30_review_pack(
e29_result_root=source,
output_root=tmp_path / "review-packs",
)
+171
View File
@@ -0,0 +1,171 @@
from __future__ import annotations
from dataclasses import replace
import pytest
from k1link.compute.lidar_contract import K1_LIVE_LIDAR_PROFILE
from k1link.compute.sensor_representation import (
K1_LIO_PCL_CAPABILITIES,
SensorAlgorithmRequirements,
SensorCapability,
SensorRepresentationCapabilities,
SensorRepresentationContractError,
SensorRepresentationKind,
SensorSourceCurrentness,
assess_sensor_algorithm,
require_sensor_algorithm_admission,
)
def _projective_mapper_requirements() -> SensorAlgorithmRequirements:
return SensorAlgorithmRequirements(
algorithm_id="lidar-projective-mapper/v1",
accepted_representations=(SensorRepresentationKind.NATIVE_SENSOR_SCAN,),
required_capabilities=frozenset(
{
SensorCapability.METRIC_XYZ,
SensorCapability.NATIVE_RAY_MODEL,
SensorCapability.SENSOR_ORIGIN_PER_POINT,
SensorCapability.RAY_CLEARING_VALID,
SensorCapability.FREE_SPACE_EVIDENCE_VALID,
}
),
)
def test_k1_lio_pcl_capabilities_round_trip_without_free_space_or_authority() -> None:
document = K1_LIO_PCL_CAPABILITIES.to_dict()
restored = SensorRepresentationCapabilities.from_dict(document)
assert restored == K1_LIO_PCL_CAPABILITIES
assert restored.source_profile_id == K1_LIVE_LIDAR_PROFILE.profile_id
assert document["representation_kind"] == "registered-map-increment"
assert document["source_currentness"] == "frame-increment"
assert document["semantics"] == {
"absence_of_endpoints_means_free": False,
"unknown_remains_unknown": True,
}
assert document["authority"] == {
"compatibility_only": True,
"commands_enabled": False,
"navigation_or_safety_accepted": False,
}
assert not restored.supports(SensorCapability.NATIVE_RAY_MODEL)
assert not restored.supports(SensorCapability.RAY_CLEARING_VALID)
assert not restored.supports(SensorCapability.FREE_SPACE_EVIDENCE_VALID)
def test_k1_lio_pcl_rejects_projective_free_space_mapper() -> None:
requirements = _projective_mapper_requirements()
admission = assess_sensor_algorithm(K1_LIO_PCL_CAPABILITIES, requirements)
assert admission.admitted is False
assert admission.reasons == (
"representation-not-accepted:registered-map-increment",
"missing-capability:free_space_evidence_valid",
"missing-capability:native_ray_model",
"missing-capability:ray_clearing_valid",
"missing-capability:sensor_origin_per_point",
)
with pytest.raises(
SensorRepresentationContractError,
match="lidar-projective-mapper/v1 rejected",
):
require_sensor_algorithm_admission(K1_LIO_PCL_CAPABILITIES, requirements)
def test_k1_lio_pcl_admits_endpoint_marking_without_granting_authority() -> None:
requirements = SensorAlgorithmRequirements(
algorithm_id="endpoint-occupied-marking/v1",
accepted_representations=(
SensorRepresentationKind.REGISTERED_MAP_INCREMENT,
),
required_capabilities=frozenset(
{
SensorCapability.METRIC_XYZ,
SensorCapability.MAP_REGISTERED,
}
),
)
admission = require_sensor_algorithm_admission(
K1_LIO_PCL_CAPABILITIES,
requirements,
)
assert admission.admitted is True
assert admission.reasons == ()
assert admission.to_dict()["authority"] == {
"compatibility_only": True,
"commands_enabled": False,
"navigation_or_safety_accepted": False,
}
def test_complete_native_scan_admits_projective_mapper() -> None:
profile = SensorRepresentationCapabilities(
profile_id="synthetic-native-lidar-capabilities/v1",
source_profile_id="synthetic-native-lidar/v1",
representation_kind=SensorRepresentationKind.NATIVE_SENSOR_SCAN,
coordinate_frame="lidar",
source_currentness=SensorSourceCurrentness.CURRENT_OBSERVATION,
capabilities=frozenset(
{
SensorCapability.METRIC_XYZ,
SensorCapability.METRIC_INTENSITY,
SensorCapability.SENSOR_POSE_AVAILABLE,
SensorCapability.PER_POINT_TIME,
SensorCapability.RING_OR_CHANNEL,
SensorCapability.SEPARATE_IMU,
SensorCapability.SHARED_HARDWARE_CLOCK,
SensorCapability.NATIVE_RAY_MODEL,
SensorCapability.SENSOR_ORIGIN_PER_POINT,
SensorCapability.RAY_CLEARING_VALID,
SensorCapability.MOTION_COMPENSATION_VALID,
SensorCapability.FREE_SPACE_EVIDENCE_VALID,
}
),
)
assert require_sensor_algorithm_admission(
profile,
_projective_mapper_requirements(),
).admitted
def test_contract_rejects_invented_free_space_and_authority() -> None:
with pytest.raises(
SensorRepresentationContractError,
match="free-space evidence requires admitted ray clearing",
):
replace(
K1_LIO_PCL_CAPABILITIES,
capabilities=K1_LIO_PCL_CAPABILITIES.capabilities
| {SensorCapability.FREE_SPACE_EVIDENCE_VALID},
)
document = K1_LIO_PCL_CAPABILITIES.to_dict()
authority = document["authority"]
assert isinstance(authority, dict)
authority["navigation_or_safety_accepted"] = True
with pytest.raises(
SensorRepresentationContractError,
match="cannot grant authority",
):
SensorRepresentationCapabilities.from_dict(document)
def test_algorithm_requirements_are_strict_and_round_trip() -> None:
requirements = _projective_mapper_requirements()
restored = SensorAlgorithmRequirements.from_dict(requirements.to_dict())
assert restored == requirements
document = requirements.to_dict()
document["on_capability_mismatch"] = "degrade"
with pytest.raises(
SensorRepresentationContractError,
match="mismatch must reject",
):
SensorAlgorithmRequirements.from_dict(document)