feat(lab): verify and expose real evidence
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi.routing import APIRoute
|
||||
|
||||
from k1link.compute.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,
|
||||
)
|
||||
from k1link.web.laboratory_api import build_laboratory_router
|
||||
|
||||
|
||||
def _canonical_json(value: object) -> bytes:
|
||||
return json.dumps(
|
||||
value,
|
||||
ensure_ascii=False,
|
||||
sort_keys=True,
|
||||
separators=(",", ":"),
|
||||
allow_nan=False,
|
||||
).encode()
|
||||
|
||||
|
||||
def _sha256(path: Path) -> str:
|
||||
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")
|
||||
|
||||
|
||||
def _write_json(path: Path, value: object) -> None:
|
||||
path.write_bytes(_canonical_json(value))
|
||||
|
||||
|
||||
def _laboratory_evidence(tmp_path: Path) -> tuple[Path, Path, Path, Path, str]:
|
||||
hash_a = "a" * 64
|
||||
hash_b = "b" * 64
|
||||
hash_c = "c" * 64
|
||||
model_id = f"k1-local-surface-{hash_a}"
|
||||
source_pack_id = f"e10-lidar-pack-{hash_b}"
|
||||
source_result_id = f"e10-integrated-perception-{hash_c}"
|
||||
identity = {
|
||||
"schema_version": CAMERA_GEOMETRY_FUSION_SCHEMA,
|
||||
"local_surface_model_id": model_id,
|
||||
"source_pack_id": source_pack_id,
|
||||
"source_result_id": source_result_id,
|
||||
"frame_count": 2,
|
||||
}
|
||||
identity_sha256 = hashlib.sha256(_canonical_json(identity)).hexdigest()
|
||||
result_id = f"e29-camera-geometry-{identity_sha256}"
|
||||
|
||||
e29_root = tmp_path / "e29"
|
||||
result_root = e29_root / result_id
|
||||
result_root.mkdir(parents=True)
|
||||
frames = [
|
||||
{
|
||||
"schema_version": CAMERA_GEOMETRY_FRAME_SCHEMA,
|
||||
"frame_index": 0,
|
||||
"source_frame_index": 100,
|
||||
"session_seconds": 10.0,
|
||||
"semantic_observations": [
|
||||
{
|
||||
"track_id": 7,
|
||||
"semantic_class": "car",
|
||||
"geometry_status": "conflict",
|
||||
"range_m": 4.5,
|
||||
}
|
||||
],
|
||||
"geometry_only_occupied": [],
|
||||
},
|
||||
{
|
||||
"schema_version": CAMERA_GEOMETRY_FRAME_SCHEMA,
|
||||
"frame_index": 1,
|
||||
"source_frame_index": 101,
|
||||
"session_seconds": 10.1,
|
||||
"semantic_observations": [],
|
||||
"geometry_only_occupied": [{"cluster_id": 8, "range_m": 2.0}],
|
||||
},
|
||||
]
|
||||
frames_path = result_root / CAMERA_GEOMETRY_FRAMES_NAME
|
||||
frames_path.write_bytes(b"".join(_canonical_json(row) + b"\n" for row in frames))
|
||||
report = {
|
||||
"schema_version": CAMERA_GEOMETRY_REPORT_SCHEMA,
|
||||
"result_id": result_id,
|
||||
"identity": identity,
|
||||
"status": "diagnostic-replay-complete",
|
||||
"ground_truth": False,
|
||||
"metrics": {
|
||||
"frames": {"total": 2},
|
||||
"semantic_observations": {
|
||||
"total": 1,
|
||||
"geometry_agree": 0,
|
||||
"camera_only": 0,
|
||||
"conflict": 1,
|
||||
},
|
||||
"geometry_only": {"cluster_count": 1},
|
||||
"runtime": {"postprocess_p95_ms": 1.2, "build_elapsed_ms": 3.4},
|
||||
},
|
||||
"decision": {"status": "replay-experiment-only"},
|
||||
"limitations": ["synthetic evidence"],
|
||||
"authority": {
|
||||
"commands_enabled": False,
|
||||
"navigation_or_safety_accepted": False,
|
||||
},
|
||||
}
|
||||
report_path = result_root / CAMERA_GEOMETRY_REPORT_NAME
|
||||
_write_json(report_path, report)
|
||||
manifest = {
|
||||
"schema_version": CAMERA_GEOMETRY_FUSION_SCHEMA,
|
||||
"result_id": result_id,
|
||||
"created_at_utc": "2026-07-26T00:00:00Z",
|
||||
"identity": identity,
|
||||
"identity_sha256": identity_sha256,
|
||||
"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),
|
||||
},
|
||||
],
|
||||
}
|
||||
_write_json(result_root / CAMERA_GEOMETRY_MANIFEST_NAME, manifest)
|
||||
|
||||
local_surface_root = tmp_path / "local-surface"
|
||||
model_root = local_surface_root / model_id
|
||||
model_root.mkdir(parents=True)
|
||||
_write_json(model_root / "manifest.json", {"model_id": model_id})
|
||||
_write_json(
|
||||
model_root / "local-surface.json",
|
||||
{
|
||||
"model_id": model_id,
|
||||
"session_id": "20260720T065719Z_viewer_live",
|
||||
},
|
||||
)
|
||||
(model_root / "local-surface.npz").write_bytes(b"verified-model")
|
||||
|
||||
source_pack_root = tmp_path / "source-pack"
|
||||
pack_root = source_pack_root / source_pack_id
|
||||
pack_root.mkdir(parents=True)
|
||||
_write_json(pack_root / "manifest.json", {"pack_id": source_pack_id})
|
||||
(pack_root / "lidar-pack.npz").write_bytes(b"verified-source-pack")
|
||||
|
||||
source_result_root = tmp_path / "source-result"
|
||||
source_root = source_result_root / source_result_id
|
||||
source_root.mkdir(parents=True)
|
||||
_write_json(source_root / "result.json", {"result_id": source_result_id})
|
||||
(source_root / "fusion-frames.jsonl").write_bytes(b"{}\n")
|
||||
return (
|
||||
e29_root,
|
||||
local_surface_root,
|
||||
source_pack_root,
|
||||
source_result_root,
|
||||
result_id,
|
||||
)
|
||||
|
||||
|
||||
def test_laboratory_catalog_and_frame_require_verified_linked_evidence(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
e29_root, local_root, pack_root, source_root, result_id = (
|
||||
_laboratory_evidence(tmp_path)
|
||||
)
|
||||
router = build_laboratory_router(
|
||||
e29_root_provider=lambda: e29_root,
|
||||
local_surface_root_provider=lambda: local_root,
|
||||
source_pack_root_provider=lambda: pack_root,
|
||||
source_result_root_provider=lambda: source_root,
|
||||
)
|
||||
catalog_route = _endpoint(router, "/api/v1/laboratory/e29/results")
|
||||
frame_route = _endpoint(
|
||||
router,
|
||||
"/api/v1/laboratory/e29/results/{result_id}/frames/{frame_index}",
|
||||
)
|
||||
|
||||
catalog = catalog_route(limit=1) # type: ignore[operator]
|
||||
frame = frame_route(result_id=result_id, frame_index=0) # type: ignore[operator]
|
||||
|
||||
assert catalog["configured"] is True
|
||||
assert catalog["candidate_total"] == 1
|
||||
assert catalog["invalid_total"] == 0
|
||||
assert len(catalog["items"]) == 1
|
||||
item = catalog["items"][0]
|
||||
assert item["result_id"] == result_id
|
||||
assert item["linked_evidence"]["source_session_id"] == (
|
||||
"20260720T065719Z_viewer_live"
|
||||
)
|
||||
assert item["linked_evidence"]["published_on_control_plane"] is True
|
||||
assert frame["frame"]["semantic_observations"][0]["geometry_status"] == (
|
||||
"conflict"
|
||||
)
|
||||
assert str(tmp_path) not in repr(catalog)
|
||||
assert str(tmp_path) not in repr(frame)
|
||||
|
||||
|
||||
def test_laboratory_catalog_hides_tampered_or_incomplete_results(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
e29_root, local_root, pack_root, source_root, result_id = (
|
||||
_laboratory_evidence(tmp_path)
|
||||
)
|
||||
frames_path = e29_root / result_id / CAMERA_GEOMETRY_FRAMES_NAME
|
||||
frames_path.write_bytes(frames_path.read_bytes() + b"{}\n")
|
||||
router = build_laboratory_router(
|
||||
e29_root_provider=lambda: e29_root,
|
||||
local_surface_root_provider=lambda: local_root,
|
||||
source_pack_root_provider=lambda: pack_root,
|
||||
source_result_root_provider=lambda: source_root,
|
||||
)
|
||||
catalog_route = _endpoint(router, "/api/v1/laboratory/e29/results")
|
||||
|
||||
tampered = catalog_route(limit=1) # type: ignore[operator]
|
||||
assert tampered["candidate_total"] == 1
|
||||
assert tampered["invalid_total"] == 1
|
||||
assert tampered["items"] == []
|
||||
|
||||
frames_path.write_bytes(frames_path.read_bytes()[:-3])
|
||||
(
|
||||
local_root
|
||||
/ f"k1-local-surface-{'a' * 64}"
|
||||
/ "local-surface.npz"
|
||||
).unlink()
|
||||
incomplete = catalog_route(limit=1) # type: ignore[operator]
|
||||
assert incomplete["candidate_total"] == 1
|
||||
assert incomplete["invalid_total"] == 1
|
||||
assert incomplete["items"] == []
|
||||
Reference in New Issue
Block a user