feat(lab): publish fail-closed TGS evidence
This commit is contained in:
@@ -127,7 +127,7 @@ def test_product_registry_declares_every_advanced_evidence_source() -> None:
|
||||
repository_root / "config" / "laboratories"
|
||||
)
|
||||
|
||||
assert len(registry.definitions) == 39
|
||||
assert len(registry.definitions) == 41
|
||||
assert {item.work_id for item in registry.definitions} >= {
|
||||
"e31-source-binding",
|
||||
"e46j-raw-fisheye-realtime",
|
||||
@@ -144,6 +144,7 @@ def test_product_registry_declares_every_advanced_evidence_source() -> None:
|
||||
"m48-static-occupancy-qualification",
|
||||
"m48s-fixed-class-detector",
|
||||
"m48t-risk-quality-temporal",
|
||||
"m49-tgs-fail-closed-evidence",
|
||||
}
|
||||
m48 = next(
|
||||
item for item in registry.definitions if item.work_id == "m48-object-centric-quality"
|
||||
|
||||
@@ -101,6 +101,7 @@ def test_repository_registry_classifies_every_evidence_definition() -> None:
|
||||
"e47-semantic-slam-shadow",
|
||||
"m48s-fixed-class-detector",
|
||||
"m48t-risk-quality-temporal",
|
||||
"m49-tgs-fail-closed-evidence",
|
||||
}
|
||||
by_work_id = {row.work_id: row for row in execution.definitions}
|
||||
assert by_work_id["m48-small-static-passage-regression"].evidence_contract == (
|
||||
@@ -118,6 +119,8 @@ def test_repository_registry_classifies_every_evidence_definition() -> None:
|
||||
assert by_work_id["m48s-fixed-class-detector"].isolation == "bounded-adapter"
|
||||
assert by_work_id["m48t-risk-quality-temporal"].lifecycle == "experimental"
|
||||
assert by_work_id["m48t-risk-quality-temporal"].isolation == "bounded-adapter"
|
||||
assert by_work_id["m49-tgs-fail-closed-evidence"].lifecycle == "experimental"
|
||||
assert by_work_id["m49-tgs-fail-closed-evidence"].isolation == "bounded-adapter"
|
||||
assert all(
|
||||
row.lifecycle == "canonical"
|
||||
for row in execution.definitions
|
||||
@@ -126,6 +129,7 @@ def test_repository_registry_classifies_every_evidence_definition() -> None:
|
||||
"e47-semantic-slam-shadow",
|
||||
"m48s-fixed-class-detector",
|
||||
"m48t-risk-quality-temporal",
|
||||
"m49-tgs-fail-closed-evidence",
|
||||
}
|
||||
)
|
||||
assert len(execution.definitions) + len(execution.legacy_work_ids) == len(
|
||||
|
||||
@@ -80,7 +80,7 @@ def test_product_value_review_registry_covers_reviewed_laboratory_families() ->
|
||||
root / "config" / "laboratory-value-review.json"
|
||||
)
|
||||
|
||||
assert len(registry.entries) == 38
|
||||
assert len(registry.entries) == 39
|
||||
assert {entry.catalog_id for entry in registry.entries} >= {
|
||||
"e28-local-surface",
|
||||
"e46d-temporal-failure-audit",
|
||||
@@ -91,4 +91,5 @@ def test_product_value_review_registry_covers_reviewed_laboratory_families() ->
|
||||
"m48-static-occupancy-qualification",
|
||||
"m48s-fixed-class-detector",
|
||||
"m48t-risk-quality-temporal",
|
||||
"m49-tgs-fail-closed-evidence",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from k1link.laboratory.m49_tgs_fail_closed import (
|
||||
M49_TGS_ANCHORS,
|
||||
read_m49_tgs_fail_closed,
|
||||
seal_m49_tgs_fail_closed,
|
||||
)
|
||||
from k1link.web.m49_tgs_fail_closed_api import build_m49_tgs_fail_closed_router
|
||||
|
||||
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def _sha(path: Path) -> str:
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def _write_json(path: Path, value: object) -> None:
|
||||
path.write_text(json.dumps(value, sort_keys=True), encoding="utf-8")
|
||||
|
||||
|
||||
def _source(root: Path) -> Path:
|
||||
root.mkdir()
|
||||
centers = np.zeros((2244, 2), dtype=np.float32)
|
||||
arrays: dict[str, np.ndarray] = {"costmap_cell_centers_xy_m": centers}
|
||||
summaries: list[dict[str, object]] = []
|
||||
for profile in ("current_increment", "causal_rolling_1s"):
|
||||
arrays[f"{profile}_points_xyz_m"] = np.asarray(
|
||||
[[float(index), 0.0, 0.1] for index in range(10)], dtype=np.float32
|
||||
)
|
||||
arrays[f"{profile}_point_states"] = np.asarray([1, 2] * 5, dtype=np.uint8)
|
||||
arrays[f"{profile}_point_offsets"] = np.arange(11, dtype=np.int64)
|
||||
arrays[f"{profile}_costmap_states"] = np.zeros((10, 2244), dtype=np.uint8)
|
||||
arrays[f"{profile}_costmap_ground_point_counts"] = np.zeros((10, 2244), dtype=np.int32)
|
||||
arrays[f"{profile}_costmap_nonground_point_counts"] = np.zeros((10, 2244), dtype=np.int32)
|
||||
arrays[f"{profile}_costmap_rejected_point_counts"] = np.zeros((10, 2244), dtype=np.int32)
|
||||
arrays[f"{profile}_costmap_z_bounds_m"] = np.full((10, 2244, 2), np.nan, dtype=np.float32)
|
||||
for slot, anchor in enumerate(M49_TGS_ANCHORS):
|
||||
summaries.append(
|
||||
{
|
||||
"profile_id": profile,
|
||||
"slot": slot,
|
||||
"anchor_frame_index": anchor,
|
||||
"point_count": 1,
|
||||
"ground_point_count": 1 if slot % 2 == 0 else 0,
|
||||
"nonground_point_count": 0 if slot % 2 == 0 else 1,
|
||||
"rejected_point_count": 0,
|
||||
"ground_cell_count": 0,
|
||||
"nonground_cell_count": 0,
|
||||
"rejected_cell_count": 0,
|
||||
"unobserved_cell_count": 2244,
|
||||
"all_points_accounted": True,
|
||||
}
|
||||
)
|
||||
np.savez_compressed(root / "evidence.npz", **arrays)
|
||||
records = [
|
||||
{
|
||||
"profile_id": profile,
|
||||
"slot": slot,
|
||||
"anchor_frame_index": anchor,
|
||||
}
|
||||
for profile in ("current_increment", "causal_rolling_1s")
|
||||
for slot, anchor in enumerate(M49_TGS_ANCHORS)
|
||||
]
|
||||
_write_json(
|
||||
root / "input-manifest.json",
|
||||
{
|
||||
"schema_version": "missioncore.m49-tgs-fail-closed-input/v1",
|
||||
"coordinate_frame": "map-gravity-local",
|
||||
"records": records,
|
||||
},
|
||||
)
|
||||
_write_json(
|
||||
root / "worker-summary.json",
|
||||
{
|
||||
"schema_version": "missioncore.m49-tgs-worker-summary/v1",
|
||||
"code_revision": "a" * 40,
|
||||
"wall_seconds": 1.5,
|
||||
"free_memory_gib_before": 42.0,
|
||||
"canonical_triton_id": "b" * 64,
|
||||
"canonical_triton_health": "healthy",
|
||||
"all_eligible_points_accounted": True,
|
||||
"aos_used": False,
|
||||
},
|
||||
)
|
||||
timing = ["profile\tslot\twall_seconds\tmax_rss_kib"]
|
||||
timing.extend(
|
||||
f"{profile}\t{slot}\t0.02\t7000"
|
||||
for profile in ("current_increment", "causal_rolling_1s")
|
||||
for slot in range(10)
|
||||
)
|
||||
(root / "tgs-timing.tsv").write_text("\n".join(timing) + "\n", encoding="utf-8")
|
||||
_write_json(
|
||||
root / "result.json",
|
||||
{
|
||||
"schema_version": "missioncore.m49-tgs-fail-closed-evidence-result/v1",
|
||||
"status": "passed",
|
||||
"config_sha256": "c" * 64,
|
||||
"source_pack_sha256": "d" * 64,
|
||||
"input_manifest_sha256": _sha(root / "input-manifest.json"),
|
||||
"evidence": {
|
||||
"path": "evidence.npz",
|
||||
"bytes": (root / "evidence.npz").stat().st_size,
|
||||
"sha256": _sha(root / "evidence.npz"),
|
||||
},
|
||||
"costmap": {
|
||||
"coordinate_frame": "map-gravity-local",
|
||||
"cell_size_m": 0.45,
|
||||
"radius_m": 12.0,
|
||||
"cell_count": 2244,
|
||||
},
|
||||
"summary": {
|
||||
"aos_used": False,
|
||||
"all_eligible_points_accounted": True,
|
||||
"primary_profile": "causal_rolling_1s",
|
||||
},
|
||||
"anchors": summaries,
|
||||
},
|
||||
)
|
||||
return root
|
||||
|
||||
|
||||
def test_m49_tgs_seal_and_api_are_immutable_and_fail_closed(tmp_path: Path) -> None:
|
||||
source = _source(tmp_path / "source")
|
||||
destination = tmp_path / "results"
|
||||
linked = "m4-threat-replay-" + "e" * 64
|
||||
sealed = seal_m49_tgs_fail_closed(
|
||||
source_root=source,
|
||||
destination_root=destination,
|
||||
profile_path=(
|
||||
REPOSITORY_ROOT / "config" / "perception" / "m49-tgs-fail-closed-evidence-v1.json"
|
||||
),
|
||||
linked_visual_result_id=linked,
|
||||
created_at_utc="2026-08-26T17:30:00Z",
|
||||
)
|
||||
assert read_m49_tgs_fail_closed(sealed.root).result_id == sealed.result_id
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(build_m49_tgs_fail_closed_router(root_provider=lambda: destination))
|
||||
client = TestClient(app)
|
||||
catalog = client.get("/api/v1/laboratory/m49/tgs-fail-closed/results")
|
||||
assert catalog.status_code == 200
|
||||
assert catalog.json()["items"][0]["result_id"] == sealed.result_id
|
||||
anchors = client.get(f"/api/v1/laboratory/m49/tgs-fail-closed/{sealed.result_id}/anchors")
|
||||
assert anchors.status_code == 200
|
||||
assert anchors.json()["anchor_count"] == 10
|
||||
spatial = client.get(
|
||||
f"/api/v1/laboratory/m49/tgs-fail-closed/{sealed.result_id}/anchors/171/spatial"
|
||||
)
|
||||
assert spatial.status_code == 200
|
||||
assert spatial.json()["point_states"] == [1]
|
||||
assert spatial.json()["costmap"]["states"] == [0] * 2244
|
||||
assert spatial.json()["authority"]["navigation_or_safety_accepted"] is False
|
||||
|
||||
(sealed.root / "worker-summary.json").write_text("{}", encoding="utf-8")
|
||||
assert (
|
||||
client.get(f"/api/v1/laboratory/m49/tgs-fail-closed/{sealed.result_id}").status_code == 404
|
||||
)
|
||||
Reference in New Issue
Block a user