feat(perception): integrate vegetation policy review
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import json
|
||||
import tarfile
|
||||
from pathlib import Path
|
||||
|
||||
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
|
||||
EVIDENCE_PATH = (
|
||||
REPOSITORY_ROOT
|
||||
/ "experiments/perception/worker/m49_t3_travel/"
|
||||
"build_vegetation_integrated_graph_evidence.py"
|
||||
)
|
||||
ARTIFACT_PATH = (
|
||||
REPOSITORY_ROOT / "scripts/build_lab_v1_vegetation_integrated_worker_artifact.py"
|
||||
)
|
||||
RUNNER_PATH = (
|
||||
REPOSITORY_ROOT
|
||||
/ "experiments/perception/worker/lab_v1_vegetation_goose/"
|
||||
"run_vegetation_integrated_load.py"
|
||||
)
|
||||
POWERSHELL_PATH = (
|
||||
REPOSITORY_ROOT
|
||||
/ "experiments/perception/worker/Invoke-M49TgsIntegratedGraphShadow.ps1"
|
||||
)
|
||||
|
||||
|
||||
def load_module(name: str, path: Path):
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
assert spec is not None and spec.loader is not None
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
EVIDENCE = load_module("vegetation_integrated_evidence", EVIDENCE_PATH)
|
||||
ARTIFACT = load_module("vegetation_integrated_artifact", ARTIFACT_PATH)
|
||||
|
||||
|
||||
def test_three_layer_gate_joins_exact_frames_and_preserves_false_authority(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
profile = tmp_path / "profile.json"
|
||||
profile.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"schema_version": EVIDENCE.PROFILE_SCHEMA,
|
||||
"profile_id": "test",
|
||||
"source": {"source_id": "RAVNOVES00", "requested_source_rate_hz": 12.0},
|
||||
"stages": {
|
||||
"m49_graph_tgs": {"profile_sha256": "a" * 64},
|
||||
"vegetation": {
|
||||
"checkpoint_sha256": "b" * 64,
|
||||
"config_sha256": "c" * 64,
|
||||
"policy_sha256": "d" * 64,
|
||||
"provider_map_sha256": "e" * 64,
|
||||
},
|
||||
},
|
||||
"acceptance": {
|
||||
"minimum_graph_world_state_fps": 11.2,
|
||||
"minimum_vegetation_fps": 11.2,
|
||||
"maximum_vegetation_completion_p95_ms": 125.0,
|
||||
"maximum_combined_output_age_p99_ms": 125.0,
|
||||
},
|
||||
"authority": {
|
||||
"commands_enabled": False,
|
||||
"actuation_allowed": False,
|
||||
"navigation_or_safety_accepted": False,
|
||||
"production_accepted": False,
|
||||
},
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
m49 = tmp_path / "m49.json"
|
||||
m49.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"schema_version": EVIDENCE.M49_SCHEMA,
|
||||
"status": "passed",
|
||||
"integrated_runtime_gate_passed": True,
|
||||
"result_id": "m49-test",
|
||||
"identity": {"profile_sha256": "a" * 64},
|
||||
"performance": {"effective_world_state_fps": 11.8},
|
||||
"accounting": {
|
||||
"graph_admitted": EVIDENCE.FRAME_COUNT,
|
||||
"graph_delivered": EVIDENCE.FRAME_COUNT,
|
||||
"tgs_timeline_frames": EVIDENCE.FRAME_COUNT,
|
||||
"tgs_capacity_drops": 0,
|
||||
},
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
vegetation = tmp_path / "vegetation.json"
|
||||
vegetation.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"schema_version": EVIDENCE.VEGETATION_SCHEMA,
|
||||
"result_id": "vegetation-test",
|
||||
"source": {"requested_source_rate_hz": 12.0},
|
||||
"candidate": {"candidate_key": "ddrnet", "checkpoint_sha256": "b" * 64},
|
||||
"identity": {
|
||||
"config_sha256": "c" * 64,
|
||||
"policy_sha256": "d" * 64,
|
||||
"provider_map_sha256": "e" * 64,
|
||||
},
|
||||
"execution": {
|
||||
"frame_count": EVIDENCE.FRAME_COUNT,
|
||||
"effective_fps": 11.75,
|
||||
"capacity_drop_count": 0,
|
||||
},
|
||||
"timing": {
|
||||
"completion_age_ms": {"p95": 25.0},
|
||||
"stage_ms": {"p95": 20.0},
|
||||
"inference_ms": {"p95": 18.0},
|
||||
},
|
||||
"resource": {"gpu_name": "test"},
|
||||
"authority": {
|
||||
"commands_enabled": False,
|
||||
"actuation_allowed": False,
|
||||
"navigation_or_safety_accepted": False,
|
||||
"production_accepted": False,
|
||||
},
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
graph_frames = tmp_path / "graph.jsonl"
|
||||
graph_frames.write_text(
|
||||
"".join(
|
||||
json.dumps(
|
||||
{"source_envelope": {"sequence": index}, "completion_age_ns": 40_000_000}
|
||||
)
|
||||
+ "\n"
|
||||
for index in range(EVIDENCE.FRAME_COUNT)
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
tgs_frames = tmp_path / "tgs.tsv"
|
||||
tgs_frames.write_text(
|
||||
"timeline_frame_index\tcompletion_age_ms\n"
|
||||
+ "".join(f"{index}\t5.0\n" for index in range(EVIDENCE.FRAME_COUNT)),
|
||||
encoding="utf-8",
|
||||
)
|
||||
vegetation_frames = tmp_path / "vegetation.jsonl"
|
||||
vegetation_frames.write_text(
|
||||
"".join(
|
||||
json.dumps(
|
||||
{
|
||||
"schema_version": "missioncore.lab-v1-vegetation-integrated-frame/v1",
|
||||
"sequence": index,
|
||||
"completion_age_ms": 20.0,
|
||||
}
|
||||
)
|
||||
+ "\n"
|
||||
for index in range(EVIDENCE.FRAME_COUNT)
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
telemetry = tmp_path / "telemetry.jsonl"
|
||||
telemetry.write_text(
|
||||
"".join(
|
||||
json.dumps(
|
||||
{
|
||||
"role": role,
|
||||
"cpu_percent": "10.0%",
|
||||
"memory_usage": "1GiB / 64GiB",
|
||||
"memory_percent": "1.56%",
|
||||
}
|
||||
)
|
||||
+ "\n"
|
||||
for role in ("graph", "tgs", "triton", "vegetation")
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
output = tmp_path / "result.json"
|
||||
|
||||
result = EVIDENCE.build(
|
||||
profile_path=profile,
|
||||
m49_result_path=m49,
|
||||
graph_frames_path=graph_frames,
|
||||
tgs_timing_path=tgs_frames,
|
||||
vegetation_result_path=vegetation,
|
||||
vegetation_frames_path=vegetation_frames,
|
||||
telemetry_path=telemetry,
|
||||
output_path=output,
|
||||
release_sha256="f" * 64,
|
||||
)
|
||||
|
||||
assert result["status"] == "passed"
|
||||
assert result["source"]["joined_frame_count"] == EVIDENCE.FRAME_COUNT
|
||||
assert result["performance"]["three_layer_output_age_ms"]["p99"] == 40.0
|
||||
assert result["checks"]["authority_remains_false"] is True
|
||||
assert result["production_accepted"] is False
|
||||
|
||||
|
||||
def test_integrated_release_is_deterministic_and_contains_one_vegetation_candidate(
|
||||
monkeypatch, tmp_path: Path
|
||||
) -> None:
|
||||
def fake_wheel(_source_root: Path, output: Path) -> Path:
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
wheel = output / ARTIFACT.WHEEL_NAME
|
||||
wheel.write_bytes(b"clean committed wheel\n")
|
||||
return wheel
|
||||
|
||||
monkeypatch.setattr(ARTIFACT, "build_wheel", fake_wheel)
|
||||
revision = "f" * 40
|
||||
first = ARTIFACT.build_artifact(
|
||||
"mission-core-vegetation-integrated-unit-001",
|
||||
tmp_path / "first",
|
||||
revision=revision,
|
||||
source_root=REPOSITORY_ROOT,
|
||||
)
|
||||
second = ARTIFACT.build_artifact(
|
||||
"mission-core-vegetation-integrated-unit-001",
|
||||
tmp_path / "second",
|
||||
revision=revision,
|
||||
source_root=REPOSITORY_ROOT,
|
||||
)
|
||||
|
||||
assert Path(first["artifact"]).read_bytes() == Path(second["artifact"]).read_bytes()
|
||||
with tarfile.open(first["artifact"], "r:gz") as archive:
|
||||
names = set(archive.getnames())
|
||||
release_stream = archive.extractfile("payload/release.json")
|
||||
assert release_stream is not None
|
||||
release = json.loads(release_stream.read())
|
||||
assert "payload/run_vegetation_integrated_load.py" in names
|
||||
assert "payload/build_vegetation_integrated_graph_evidence.py" in names
|
||||
assert release["scope"]["heavy_vegetation_candidates"] == ["ddrnet"]
|
||||
assert all(value is False for value in release["authority"].values())
|
||||
|
||||
|
||||
def test_worker_gate_reuses_shared_barrier_and_keeps_canonical_triton_unchanged() -> None:
|
||||
runner = RUNNER_PATH.read_text(encoding="utf-8")
|
||||
wrapper = POWERSHELL_PATH.read_text(encoding="utf-8")
|
||||
assert '"source-paced-integrated-shadow/v1"' in runner
|
||||
assert "wait_for_shared_start(" in runner
|
||||
assert '"camera_semantics_can_clear_rigid_geometry": False' in runner
|
||||
assert "$VegetationLoadGate" in wrapper
|
||||
assert '"vegetation"' in wrapper
|
||||
assert "if ($canonicalAfter.Id -cne $canonicalId" not in wrapper
|
||||
assert "$canonicalAfter.Id -cne $canonicalId" in wrapper
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import shutil
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
@@ -9,9 +10,11 @@ from types import SimpleNamespace
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from k1link.laboratory import LaboratoryEvidenceRegistry
|
||||
import k1link.laboratory.vegetation_policy_review as policy_review_module
|
||||
import k1link.laboratory.vegetation_shadow_lab as vegetation_lab_module
|
||||
from k1link.laboratory import LaboratoryEvidenceRegistry
|
||||
from k1link.laboratory.evidence_report import verify_laboratory_evidence_result
|
||||
from k1link.laboratory.vegetation_policy_review import seal_vegetation_policy_review
|
||||
from k1link.laboratory.vegetation_shadow_lab import seal_vegetation_shadow_lab
|
||||
from k1link.web.vegetation_shadow_lab_api import build_vegetation_shadow_lab_router
|
||||
|
||||
@@ -229,3 +232,95 @@ def test_vegetation_shadow_lab_seals_autonomous_visual_evidence(
|
||||
client.get(f"/api/v1/laboratory/vegetation-shadow/{result_root.name}").status_code
|
||||
== 503
|
||||
)
|
||||
|
||||
|
||||
def test_policy_review_reuses_sealed_video_and_links_yolox_tgs(
|
||||
tmp_path: Path,
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
roots = {}
|
||||
for candidate, vegetation_iou in (("ddrnet", 0.64), ("ppliteseg", 0.61)):
|
||||
for mode in ("goose", "ravnoves"):
|
||||
root = tmp_path / "worker" / f"{candidate}-{mode}"
|
||||
_worker_result(root, candidate=candidate, mode=mode, vegetation_iou=vegetation_iou)
|
||||
roots[(candidate, mode)] = root
|
||||
video_root = tmp_path / "worker" / "ddrnet-ravnoves-video"
|
||||
_video_worker_result(video_root)
|
||||
m47_root = tmp_path / f"m47-reference-graph-lab-{'a' * 64}"
|
||||
m47_root.mkdir()
|
||||
base_m4_result_id = f"m4-threat-replay-{'f' * 64}"
|
||||
monkeypatch.setattr(
|
||||
vegetation_lab_module,
|
||||
"read_m47_reference_graph_lab",
|
||||
lambda _root: SimpleNamespace(
|
||||
result_id=m47_root.name,
|
||||
report={
|
||||
"source": {"source_id": "RAVNOVES00"},
|
||||
"visual_evidence": {
|
||||
"linked_result_id": base_m4_result_id,
|
||||
"timeline_frames": 4489,
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
base_root = seal_vegetation_shadow_lab(
|
||||
ddrnet_goose_root=roots[("ddrnet", "goose")],
|
||||
ppliteseg_goose_root=roots[("ppliteseg", "goose")],
|
||||
ddrnet_ravnoves_root=roots[("ddrnet", "ravnoves")],
|
||||
ppliteseg_ravnoves_root=roots[("ppliteseg", "ravnoves")],
|
||||
output_root=tmp_path / "results",
|
||||
ddrnet_ravnoves_video_root=video_root,
|
||||
m47_reference_graph_lab_root=m47_root,
|
||||
)
|
||||
tgs_result_id = f"m49-tgs-full-shadow-{'9' * 64}"
|
||||
monkeypatch.setattr(
|
||||
policy_review_module,
|
||||
"read_m49_tgs_full_shadow",
|
||||
lambda _root: SimpleNamespace(
|
||||
result_id=tgs_result_id,
|
||||
report={
|
||||
"source": {
|
||||
"source_id": "RAVNOVES00",
|
||||
"linked_visual_result_id": base_m4_result_id,
|
||||
},
|
||||
"timeline": {"frame_count": 4489},
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
def fake_policy_archive(**kwargs) -> list[int]:
|
||||
shutil.copyfile(kwargs["source_archive"], kwargs["destination_archive"])
|
||||
return [4489 * 800 * 600, *([0] * 8)]
|
||||
|
||||
monkeypatch.setattr(policy_review_module, "build_policy_mask_archive", fake_policy_archive)
|
||||
result_root = seal_vegetation_policy_review(
|
||||
base_lab_root=base_root,
|
||||
mission_policy_path=REPOSITORY_ROOT
|
||||
/ "config/perception/lab-v1-vegetation-mission-policy-v1.json",
|
||||
provider_label_map_path=REPOSITORY_ROOT
|
||||
/ "config/perception/lab-v1-vegetation-provider-label-map-v1.json",
|
||||
m49_tgs_full_shadow_root=tmp_path / "sealed-tgs",
|
||||
output_root=tmp_path / "results",
|
||||
created_at_utc="2026-08-28T08:00:00+00:00",
|
||||
)
|
||||
manifest = json.loads((result_root / "result.json").read_text("utf-8"))
|
||||
route = manifest["route_video"]
|
||||
assert route["view_kind"] == "coarse-material-policy-review"
|
||||
assert route["linked_tgs_result_id"] == tgs_result_id
|
||||
assert route["fusion"]["pixel_raster_fusion"] is False
|
||||
assert route["fusion"]["camera_semantic_temporal_filter"] == "none"
|
||||
assert route["taxonomy"]["schema_version"] == (
|
||||
"missioncore.lab-v1-terrain-policy-taxonomy/v1"
|
||||
)
|
||||
assert len(route["taxonomy"]["classes"]) == 9
|
||||
assert len(manifest["artifacts"]) == 79
|
||||
assert manifest["authority"]["commands_enabled"] is False
|
||||
assert manifest["decision"]["multilayer_policy_review_ready"] is True
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(build_vegetation_shadow_lab_router(root_provider=lambda: result_root.parent))
|
||||
response = TestClient(app).get(
|
||||
f"/api/v1/laboratory/vegetation-shadow/{result_root.name}/masks/0"
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.content == b"\x89PNG\r\n\x1a\n"
|
||||
|
||||
Reference in New Issue
Block a user