feat(perception): add integrated TGS graph shadow gate
This commit is contained in:
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
import importlib.util
|
||||
import time
|
||||
from pathlib import Path
|
||||
from threading import Thread
|
||||
from unittest.mock import patch
|
||||
|
||||
from k1link.perception.detector import DetectorFrameTiming
|
||||
@@ -157,3 +158,33 @@ def test_cyclic_gc_policy_collects_outside_hot_loop_and_restores_state() -> None
|
||||
"pre_collected": 3,
|
||||
"post_collected": 0,
|
||||
}
|
||||
|
||||
|
||||
def test_shared_start_barrier_publishes_readiness_and_waits_for_release(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
ready = tmp_path / "graph.ready"
|
||||
start = tmp_path / "start.signal"
|
||||
completed: list[bool] = []
|
||||
|
||||
thread = Thread(
|
||||
target=lambda: (
|
||||
RUNNER.wait_for_shared_start(
|
||||
ready_file=ready,
|
||||
start_file=start,
|
||||
timeout_seconds=1.0,
|
||||
),
|
||||
completed.append(True),
|
||||
)
|
||||
)
|
||||
thread.start()
|
||||
deadline = time.monotonic() + 1.0
|
||||
while not ready.exists() and time.monotonic() < deadline:
|
||||
time.sleep(0.005)
|
||||
assert ready.read_text(encoding="utf-8") == "ready\n"
|
||||
assert completed == []
|
||||
|
||||
start.write_text("start\n", encoding="utf-8")
|
||||
thread.join(timeout=1.0)
|
||||
|
||||
assert completed == [True]
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
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_tgs_integrated_graph_evidence.py"
|
||||
)
|
||||
ARTIFACT_PATH = REPOSITORY_ROOT / "scripts/build_m49_tgs_integrated_graph_worker_artifact.py"
|
||||
|
||||
|
||||
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("m49_tgs_integrated_evidence", EVIDENCE_PATH)
|
||||
ARTIFACT = load_module("m49_tgs_integrated_artifact", ARTIFACT_PATH)
|
||||
|
||||
|
||||
def test_integrated_gate_joins_all_frames_and_preserves_false_authority(tmp_path: Path) -> None:
|
||||
profile_path = tmp_path / "profile.json"
|
||||
profile_path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"schema_version": EVIDENCE.PROFILE_SCHEMA,
|
||||
"profile_id": "test",
|
||||
"source": {
|
||||
"source_id": "RAVNOVES00",
|
||||
"source_pack_sha256": "a" * 64,
|
||||
"requested_source_rate_hz": 12.0,
|
||||
},
|
||||
"stages": {
|
||||
"reference_graph": {
|
||||
"graph_config_sha256": "b" * 64,
|
||||
"detector_profile_sha256": "z" * 64,
|
||||
},
|
||||
"tgs": {
|
||||
"profile_sha256": "c" * 64,
|
||||
"linked_accepted_result_id": "m49-tgs-full-shadow-" + "d" * 64,
|
||||
},
|
||||
},
|
||||
"acceptance": {
|
||||
"minimum_delivery_ratio": 1.0,
|
||||
"reference_world_state_fps": 11.79902,
|
||||
"maximum_world_state_fps_regression_fraction": 0.05,
|
||||
"minimum_effective_world_state_fps": 11.209069,
|
||||
"maximum_world_state_completion_p95_ms": 125.0,
|
||||
"candidate_stage_p95_ms_max": 25.0,
|
||||
"candidate_stage_p99_ms_max": 50.0,
|
||||
"combined_output_age_p99_ms_max": 125.0,
|
||||
"capacity_drop_count_max": 0,
|
||||
},
|
||||
"authority": {
|
||||
"visual_quality_accepted": False,
|
||||
"traversability_accepted": False,
|
||||
"physical_free_space_accepted": False,
|
||||
"commands_enabled": False,
|
||||
"actuation_allowed": False,
|
||||
"navigation_or_safety_accepted": False,
|
||||
"production_accepted": False,
|
||||
},
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
graph_result = tmp_path / "graph-result.json"
|
||||
graph_result.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"schema_version": EVIDENCE.GRAPH_SCHEMA,
|
||||
"execution": {
|
||||
"admitted_frames": EVIDENCE.FRAME_COUNT,
|
||||
"delivered_world_states": EVIDENCE.FRAME_COUNT,
|
||||
"effective_world_state_fps": 11.75,
|
||||
"delivery_ratio": 1.0,
|
||||
"terminal_outcomes": {"delivered": EVIDENCE.FRAME_COUNT},
|
||||
"requested_source_rate_hz": 12.0,
|
||||
},
|
||||
"identity": {
|
||||
"inputs": {
|
||||
"graph_config": "b" * 64,
|
||||
"detector_profile": "z" * 64,
|
||||
}
|
||||
},
|
||||
"metrics": {
|
||||
"world_state_completion_age_ms": {"p95": 40.0},
|
||||
"gpu": {"sample_count": 2},
|
||||
},
|
||||
"evidence_integrity_gate_passed": True,
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
graph_frames = tmp_path / "graph-frames.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_result = tmp_path / "tgs-result.json"
|
||||
tgs_result.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"schema_version": EVIDENCE.TGS_SCHEMA,
|
||||
"status": "passed",
|
||||
"config_sha256": "c" * 64,
|
||||
"timeline": {
|
||||
"frame_count": EVIDENCE.FRAME_COUNT,
|
||||
"available_lidar_frame_count": 3928,
|
||||
},
|
||||
"performance": {
|
||||
"candidate_tgs_ms": {"p95": 2.0, "p99": 3.0},
|
||||
"completion_age_ms": {"p99": 5.0},
|
||||
"capacity_drop_count": 0,
|
||||
},
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
tgs_timing = tmp_path / "tgs-timing.tsv"
|
||||
tgs_timing.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",
|
||||
)
|
||||
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")
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
output = tmp_path / "result.json"
|
||||
|
||||
result = EVIDENCE.build(
|
||||
profile_path=profile_path,
|
||||
graph_result_path=graph_result,
|
||||
graph_frames_path=graph_frames,
|
||||
tgs_result_path=tgs_result,
|
||||
tgs_timing_path=tgs_timing,
|
||||
telemetry_path=telemetry,
|
||||
output_path=output,
|
||||
release_sha256="e" * 64,
|
||||
)
|
||||
|
||||
assert result["status"] == "passed"
|
||||
assert result["source"]["joined_frame_count"] == EVIDENCE.FRAME_COUNT
|
||||
assert result["performance"]["combined_output_age_ms"]["p99"] == 40.0
|
||||
assert result["checks"]["authority_remains_false"] is True
|
||||
assert result["production_accepted"] is False
|
||||
|
||||
|
||||
def test_worker_artifact_is_deterministic_and_excludes_gauss(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-m49-integrated-unit-001",
|
||||
tmp_path / "first",
|
||||
revision=revision,
|
||||
source_root=REPOSITORY_ROOT,
|
||||
)
|
||||
second = ARTIFACT.build_artifact(
|
||||
"mission-core-m49-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 not any("gauss" in name.lower() or "playcanvas" in name.lower() for name in names)
|
||||
assert release["scope"]["gauss_or_playcanvas_action"] == "none"
|
||||
assert all(value is False for value in release["authority"].values())
|
||||
Reference in New Issue
Block a user