fix(perception): reconstruct rolling occupancy from K1 increments
This commit is contained in:
@@ -53,6 +53,11 @@ THREAT_REPLAY_FRAME_SCHEMA: Final = "missioncore.perception-threat-replay-frame/
|
||||
THREAT_REPLAY_VISUAL_SCHEMA: Final = "missioncore.perception-threat-visual-frame/v1"
|
||||
THREAT_REPLAY_FIXTURE_SCHEMA: Final = "missioncore.perception-threat-fixtures/v1"
|
||||
THREAT_REPLAY_REPORT_SCHEMA: Final = "missioncore.perception-threat-replay-report/v1"
|
||||
THREAT_REPLAY_SCHEMA_V2: Final = "missioncore.perception-threat-replay-result/v2"
|
||||
THREAT_REPLAY_FRAME_SCHEMA_V2: Final = "missioncore.perception-threat-replay-frame/v2"
|
||||
THREAT_REPLAY_VISUAL_SCHEMA_V2: Final = "missioncore.perception-threat-visual-frame/v2"
|
||||
THREAT_REPLAY_FIXTURE_SCHEMA_V2: Final = "missioncore.perception-threat-fixtures/v2"
|
||||
THREAT_REPLAY_REPORT_SCHEMA_V2: Final = "missioncore.perception-threat-replay-report/v2"
|
||||
THREAT_REPLAY_RESULT_PREFIX: Final = "m4-threat-replay-"
|
||||
THREAT_REPLAY_FRAMES_NAME: Final = "frames.jsonl"
|
||||
THREAT_REPLAY_VISUALS_NAME: Final = "visual-frames.jsonl"
|
||||
@@ -61,7 +66,23 @@ THREAT_REPLAY_REPORT_NAME: Final = "report.json"
|
||||
THREAT_REPLAY_MANIFEST_NAME: Final = "manifest.json"
|
||||
VISUAL_FRAME_COUNT: Final = 32
|
||||
VISUAL_POINT_LIMIT: Final = 4_000
|
||||
VISUAL_GEOMETRY_REGRESSION_SEQUENCES: Final = (138, 274)
|
||||
VISUAL_GEOMETRY_REGRESSION_SEQUENCES: Final = (138, 274, 1880)
|
||||
FRAME_1880_ENGINEERING_ANCHORS: Final = (
|
||||
{
|
||||
"anchor_id": "near-concrete-hemisphere",
|
||||
"x_bounds_m": (0.3, 1.2),
|
||||
"y_bounds_m": (-0.8, 0.2),
|
||||
"z_bounds_m": (-0.1, 0.9),
|
||||
"must_assert_threat": True,
|
||||
},
|
||||
{
|
||||
"anchor_id": "far-concrete-hemisphere",
|
||||
"x_bounds_m": (1.5, 2.7),
|
||||
"y_bounds_m": (0.6, 1.7),
|
||||
"z_bounds_m": (-0.1, 0.9),
|
||||
"must_assert_threat": False,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class ThreatReplayError(RuntimeError):
|
||||
@@ -121,6 +142,7 @@ def build_threat_replay(
|
||||
reason_counts: Counter[str] = Counter()
|
||||
latencies_ms: list[float] = []
|
||||
visual_count = 0
|
||||
frame_1880_regression: dict[str, object] | None = None
|
||||
try:
|
||||
temporal_frames_path = temporal.result_root / "frames.jsonl"
|
||||
geometry_frames_path = geometry.result_root / "frames.jsonl"
|
||||
@@ -159,6 +181,20 @@ def build_threat_replay(
|
||||
for key in ("held", "expired")
|
||||
for value in _array(temporal_frame.get(key), f"{key} obstacles")
|
||||
)
|
||||
rolling_retained = tuple(
|
||||
TemporalObstacle.from_dict(value)
|
||||
for value in _array(
|
||||
temporal_frame.get("rolling_retained"),
|
||||
"rolling retained obstacles",
|
||||
)
|
||||
)
|
||||
if any(
|
||||
item.state is not TemporalState.RETAINED
|
||||
for item in rolling_retained
|
||||
):
|
||||
raise ThreatReplayError(
|
||||
"temporal replay rolling map escaped retained state"
|
||||
)
|
||||
geometry_observations = _array(
|
||||
geometry_frame.get("observations"), "geometry observations"
|
||||
)
|
||||
@@ -183,7 +219,7 @@ def build_threat_replay(
|
||||
graph_id="reference-perception-graph/v1",
|
||||
generated_monotonic_ns=0,
|
||||
output_age_ns=0,
|
||||
occupied=current,
|
||||
occupied=(*current, *rolling_retained),
|
||||
unknown=unknown,
|
||||
camera_uncertainty=camera_uncertainty,
|
||||
accounting=SourceAccounting(1, 1, 0, 0),
|
||||
@@ -192,7 +228,10 @@ def build_threat_replay(
|
||||
assessments = provider.assess(obstacle_map)
|
||||
latencies_ms.append((time.perf_counter_ns() - frame_started_ns) / 1_000_000)
|
||||
by_id = {item.component_id: item for item in assessments}
|
||||
expected_ids = {item.component_id for item in (*current, *unknown)} | {
|
||||
expected_ids = {
|
||||
item.component_id
|
||||
for item in (*current, *rolling_retained, *unknown)
|
||||
} | {
|
||||
item.proposal_id for item in camera_uncertainty
|
||||
}
|
||||
if set(by_id) != expected_ids:
|
||||
@@ -203,12 +242,21 @@ def build_threat_replay(
|
||||
by_id,
|
||||
)
|
||||
metric_rows = [
|
||||
_metric_row(item, by_id[item.component_id]) for item in (*current, *unknown)
|
||||
_metric_row(item, by_id[item.component_id])
|
||||
for item in (*current, *rolling_retained, *unknown)
|
||||
]
|
||||
if frame_count == 1880:
|
||||
frame_1880_regression = _frame_1880_regression(
|
||||
metric_rows,
|
||||
body_frame_resolver.body_frame_for_frame(
|
||||
packet.envelope.frame_id
|
||||
),
|
||||
)
|
||||
for item in assessments:
|
||||
assessment_counts[item.decision.value] += 1
|
||||
reason_counts.update(item.reason_codes)
|
||||
evidence_counts["current-metric"] += len(current)
|
||||
evidence_counts["rolling-map-retained"] += len(rolling_retained)
|
||||
evidence_counts["stale-or-held"] += len(unknown)
|
||||
evidence_counts["camera-only"] += len(camera_uncertainty)
|
||||
for obstacle in current:
|
||||
@@ -216,7 +264,7 @@ def build_threat_replay(
|
||||
f"{obstacle.motion.value}:{by_id[obstacle.component_id].decision.value}"
|
||||
] += 1
|
||||
frame_document = {
|
||||
"schema_version": THREAT_REPLAY_FRAME_SCHEMA,
|
||||
"schema_version": THREAT_REPLAY_FRAME_SCHEMA_V2,
|
||||
"sequence": frame_count,
|
||||
"frame_id": packet.envelope.frame_id,
|
||||
"source_time_ns": packet.envelope.timestamps.source_ns,
|
||||
@@ -232,6 +280,8 @@ def build_threat_replay(
|
||||
"metric_obstacles": len(metric_rows),
|
||||
"camera_proposals": len(proposals),
|
||||
"camera_only": len(camera_uncertainty),
|
||||
"current_increment_metric": len(current),
|
||||
"rolling_map_retained": len(rolling_retained),
|
||||
"assessments": len(assessments),
|
||||
},
|
||||
"authority": _false_authority(),
|
||||
@@ -276,14 +326,15 @@ def build_threat_replay(
|
||||
visual_count=visual_count,
|
||||
fixtures=fixtures,
|
||||
body_frame=body_frame_resolver.qualification_summary(),
|
||||
frame_1880_regression=frame_1880_regression,
|
||||
)
|
||||
requirements = _requirements(metrics, fixtures)
|
||||
requirements = _requirements_v2(metrics, fixtures)
|
||||
accepted = all(value is True for value in requirements.values())
|
||||
frames_sha256 = _file_sha256(frames_path)
|
||||
visuals_sha256 = _file_sha256(visuals_path)
|
||||
fixtures_sha256 = _file_sha256(fixtures_path)
|
||||
identity = {
|
||||
"schema_version": THREAT_REPLAY_SCHEMA,
|
||||
"schema_version": THREAT_REPLAY_SCHEMA_V2,
|
||||
"profile_id": profile.profile_id,
|
||||
"profile_sha256": profile.profile_sha256,
|
||||
"provider_id": provider.provider_id,
|
||||
@@ -319,7 +370,7 @@ def build_threat_replay(
|
||||
identity_sha256 = hashlib.sha256(_canonical_json(identity)).hexdigest()
|
||||
result_id = f"{THREAT_REPLAY_RESULT_PREFIX}{identity_sha256}"
|
||||
report = {
|
||||
"schema_version": THREAT_REPLAY_REPORT_SCHEMA,
|
||||
"schema_version": THREAT_REPLAY_REPORT_SCHEMA_V2,
|
||||
"result_id": result_id,
|
||||
"identity_sha256": identity_sha256,
|
||||
"status": "accepted" if accepted else "rejected",
|
||||
@@ -355,7 +406,7 @@ def build_threat_replay(
|
||||
report_path = staging / THREAT_REPLAY_REPORT_NAME
|
||||
_write_json(report_path, report)
|
||||
manifest = {
|
||||
"schema_version": THREAT_REPLAY_SCHEMA,
|
||||
"schema_version": THREAT_REPLAY_SCHEMA_V2,
|
||||
"result_id": result_id,
|
||||
"identity_sha256": identity_sha256,
|
||||
"identity": identity,
|
||||
@@ -400,11 +451,14 @@ def read_threat_replay_result(root: Path) -> ThreatReplayResult:
|
||||
},
|
||||
"threat replay manifest",
|
||||
)
|
||||
schema_version = manifest.get("schema_version")
|
||||
if schema_version not in {THREAT_REPLAY_SCHEMA, THREAT_REPLAY_SCHEMA_V2}:
|
||||
raise ThreatReplayError("threat replay schema is incompatible")
|
||||
is_v2 = schema_version == THREAT_REPLAY_SCHEMA_V2
|
||||
identity = _object(manifest.get("identity"), "threat replay identity")
|
||||
identity_sha256 = hashlib.sha256(_canonical_json(identity)).hexdigest()
|
||||
if (
|
||||
manifest.get("schema_version") != THREAT_REPLAY_SCHEMA
|
||||
or manifest.get("result_id") != resolved.name
|
||||
manifest.get("result_id") != resolved.name
|
||||
or manifest.get("identity_sha256") != identity_sha256
|
||||
or resolved.name != f"{THREAT_REPLAY_RESULT_PREFIX}{identity_sha256}"
|
||||
):
|
||||
@@ -433,8 +487,14 @@ def read_threat_replay_result(root: Path) -> ThreatReplayResult:
|
||||
requirements = _object(identity.get("acceptance_requirements"), "threat requirements")
|
||||
fixtures = _read_json(paths["threat-deterministic-fixtures"])
|
||||
accepted = all(value is True for value in requirements.values())
|
||||
expected_requirements = (
|
||||
_requirements_v2(metrics, fixtures)
|
||||
if is_v2
|
||||
else _requirements_v1(metrics, fixtures)
|
||||
)
|
||||
if (
|
||||
report.get("schema_version") != THREAT_REPLAY_REPORT_SCHEMA
|
||||
report.get("schema_version")
|
||||
!= (THREAT_REPLAY_REPORT_SCHEMA_V2 if is_v2 else THREAT_REPLAY_REPORT_SCHEMA)
|
||||
or report.get("result_id") != resolved.name
|
||||
or report.get("identity_sha256") != identity_sha256
|
||||
or report.get("metrics") != metrics
|
||||
@@ -443,13 +503,14 @@ def read_threat_replay_result(root: Path) -> ThreatReplayResult:
|
||||
or identity.get("authority") != _false_authority()
|
||||
or manifest.get("accepted") is not accepted
|
||||
or identity.get("accepted") is not accepted
|
||||
or requirements != _requirements(metrics, fixtures)
|
||||
or requirements != expected_requirements
|
||||
):
|
||||
raise ThreatReplayError("threat replay report or acceptance changed")
|
||||
_validate_ledgers(
|
||||
paths["threat-replay-frames"],
|
||||
paths["threat-visual-frames"],
|
||||
metrics,
|
||||
is_v2=is_v2,
|
||||
)
|
||||
return ThreatReplayResult(
|
||||
result_id=resolved.name,
|
||||
@@ -617,13 +678,18 @@ def _visual_frame(
|
||||
}
|
||||
)
|
||||
return {
|
||||
"schema_version": THREAT_REPLAY_VISUAL_SCHEMA,
|
||||
"schema_version": THREAT_REPLAY_VISUAL_SCHEMA_V2,
|
||||
"sequence": packet.envelope.sequence,
|
||||
"frame_id": packet.envelope.frame_id,
|
||||
"source_time_ns": packet.envelope.timestamps.source_ns,
|
||||
"point_cloud_body_xyz_m": np.round(sampled, 6).tolist(),
|
||||
"point_cloud_source_count": int(points.shape[0]),
|
||||
"point_cloud_sample_count": int(sampled.shape[0]),
|
||||
"point_cloud_layer": "current-increment",
|
||||
"rolling_map_component_count": sum(
|
||||
row.get("state") == TemporalState.RETAINED.value
|
||||
for row in metric_rows
|
||||
),
|
||||
"metric_obstacles": metric_visuals,
|
||||
"camera_proposals": camera_rows,
|
||||
"body_frame": {
|
||||
@@ -649,6 +715,99 @@ def _visual_frame(
|
||||
}
|
||||
|
||||
|
||||
def _frame_1880_regression(
|
||||
metric_rows: list[dict[str, object]],
|
||||
body_frame: ReplayBodyFrame | None,
|
||||
) -> dict[str, object]:
|
||||
if body_frame is None:
|
||||
raise ThreatReplayError("frame 1880 has no qualified body frame")
|
||||
retained_threats = 0
|
||||
retained_components = 0
|
||||
retained_rows: list[tuple[dict[str, object], tuple[float, float, float]]] = []
|
||||
for row in metric_rows:
|
||||
if row.get("state") != TemporalState.RETAINED.value:
|
||||
continue
|
||||
retained_components += 1
|
||||
assessment = _object(row.get("assessment"), "frame 1880 assessment")
|
||||
if assessment.get("decision") == ThreatDecision.THREAT.value:
|
||||
retained_threats += 1
|
||||
centroid_map = _array(
|
||||
row.get("centroid_map_xyz_m"),
|
||||
"frame 1880 retained centroid",
|
||||
)
|
||||
if len(centroid_map) != 3:
|
||||
raise ThreatReplayError("frame 1880 retained centroid is invalid")
|
||||
centroid_values = tuple(
|
||||
_number_value(value, "frame 1880 centroid") for value in centroid_map
|
||||
)
|
||||
centroid_body = body_frame.map_point_to_body(
|
||||
(centroid_values[0], centroid_values[1], centroid_values[2])
|
||||
)
|
||||
retained_rows.append((row, centroid_body))
|
||||
|
||||
anchors: list[dict[str, object]] = []
|
||||
matched_ids: set[str] = set()
|
||||
for raw_anchor in FRAME_1880_ENGINEERING_ANCHORS:
|
||||
anchor = _object(raw_anchor, "frame 1880 engineering anchor")
|
||||
x_bounds = _bounds(anchor.get("x_bounds_m"), "frame 1880 x bounds")
|
||||
y_bounds = _bounds(anchor.get("y_bounds_m"), "frame 1880 y bounds")
|
||||
z_bounds = _bounds(anchor.get("z_bounds_m"), "frame 1880 z bounds")
|
||||
match = next(
|
||||
(
|
||||
(row, centroid)
|
||||
for row, centroid in retained_rows
|
||||
if row.get("component_id") not in matched_ids
|
||||
and x_bounds[0] <= centroid[0] <= x_bounds[1]
|
||||
and y_bounds[0] <= centroid[1] <= y_bounds[1]
|
||||
and z_bounds[0] <= centroid[2] <= z_bounds[1]
|
||||
),
|
||||
None,
|
||||
)
|
||||
component_id = None if match is None else str(match[0]["component_id"])
|
||||
if component_id is not None:
|
||||
matched_ids.add(component_id)
|
||||
anchor_assessment = (
|
||||
None
|
||||
if match is None
|
||||
else _object(match[0].get("assessment"), "frame 1880 anchor assessment")
|
||||
)
|
||||
anchors.append(
|
||||
{
|
||||
"anchor_id": anchor["anchor_id"],
|
||||
"bounds_body_xyz_m": [list(x_bounds), list(y_bounds), list(z_bounds)],
|
||||
"must_assert_threat": anchor["must_assert_threat"],
|
||||
"matched": match is not None,
|
||||
"component_id": component_id,
|
||||
"centroid_body_xyz_m": (
|
||||
None if match is None else list(match[1])
|
||||
),
|
||||
"decision": (
|
||||
None
|
||||
if anchor_assessment is None
|
||||
else anchor_assessment.get("decision")
|
||||
),
|
||||
}
|
||||
)
|
||||
required_threats_passed = all(
|
||||
item["matched"] is True
|
||||
and (
|
||||
item["must_assert_threat"] is False
|
||||
or item["decision"] == ThreatDecision.THREAT.value
|
||||
)
|
||||
for item in anchors
|
||||
)
|
||||
return {
|
||||
"sequence": 1880,
|
||||
"retained_components": retained_components,
|
||||
"retained_threat_components": retained_threats,
|
||||
"engineering_anchors": anchors,
|
||||
"matched_anchor_count": sum(item["matched"] is True for item in anchors),
|
||||
"required_threats_passed": required_threats_passed,
|
||||
"camera_visible_hemispheres_independent_truth": False,
|
||||
"gate": "two-visible-hemisphere-regression",
|
||||
}
|
||||
|
||||
|
||||
class _FixtureBodyFrames:
|
||||
def body_frame_for_frame(self, frame_id: str) -> ReplayBodyFrame:
|
||||
return ReplayBodyFrame(
|
||||
@@ -766,6 +925,19 @@ def _fixture_document(profile: ReplayThreatProfile) -> dict[str, object]:
|
||||
),
|
||||
ThreatDecision.UNKNOWN,
|
||||
),
|
||||
_fixture_case(
|
||||
provider,
|
||||
"retained-in-corridor",
|
||||
_fixture_obstacle(
|
||||
"fixture-retained-in",
|
||||
GridCell(6, 0, 0),
|
||||
MotionState.UNKNOWN,
|
||||
((frame_id, 200_000_000, (2.925, 0.225, 0.225)),),
|
||||
state=TemporalState.RETAINED,
|
||||
),
|
||||
ThreatDecision.THREAT,
|
||||
critical=True,
|
||||
),
|
||||
_fixture_camera_case(provider, frame_id),
|
||||
_fixture_case(
|
||||
provider,
|
||||
@@ -784,7 +956,7 @@ def _fixture_document(profile: ReplayThreatProfile) -> dict[str, object]:
|
||||
),
|
||||
]
|
||||
return {
|
||||
"schema_version": THREAT_REPLAY_FIXTURE_SCHEMA,
|
||||
"schema_version": THREAT_REPLAY_FIXTURE_SCHEMA_V2,
|
||||
"cases": cases,
|
||||
"critical_case_count": sum(item["critical"] is True for item in cases),
|
||||
"critical_false_not_threat_count": sum(
|
||||
@@ -810,7 +982,11 @@ def _fixture_obstacle(
|
||||
component_id=component_id,
|
||||
identity_scope="ephemeral",
|
||||
state=state,
|
||||
ttl_ns=750_000_000,
|
||||
ttl_ns=(
|
||||
3_000_000_000
|
||||
if state is TemporalState.RETAINED
|
||||
else 750_000_000
|
||||
),
|
||||
last_hit_ns=last.evidence_time_ns,
|
||||
age_ns=0 if state is TemporalState.CURRENT else 100_000_000,
|
||||
association_basis="deterministic-fixture",
|
||||
@@ -849,8 +1025,12 @@ def _fixture_case(
|
||||
graph_id="reference-perception-graph/v1",
|
||||
generated_monotonic_ns=0,
|
||||
output_age_ns=0,
|
||||
occupied=(obstacle,) if obstacle.state is TemporalState.CURRENT else (),
|
||||
unknown=(obstacle,) if obstacle.state is not TemporalState.CURRENT else (),
|
||||
occupied=(obstacle,)
|
||||
if obstacle.state in {TemporalState.CURRENT, TemporalState.RETAINED}
|
||||
else (),
|
||||
unknown=(obstacle,)
|
||||
if obstacle.state not in {TemporalState.CURRENT, TemporalState.RETAINED}
|
||||
else (),
|
||||
camera_uncertainty=(),
|
||||
accounting=SourceAccounting(1, 1, 0, 0),
|
||||
)
|
||||
@@ -915,6 +1095,7 @@ def _metrics(
|
||||
visual_count: int,
|
||||
fixtures: dict[str, object],
|
||||
body_frame: dict[str, object],
|
||||
frame_1880_regression: dict[str, object] | None,
|
||||
) -> dict[str, object]:
|
||||
values = np.asarray(latencies_ms, dtype=np.float64)
|
||||
return {
|
||||
@@ -934,6 +1115,7 @@ def _metrics(
|
||||
"virtual_corridor_available": True,
|
||||
"qualified_base_footprint_available": True,
|
||||
"geometry_regression_sequences": list(VISUAL_GEOMETRY_REGRESSION_SEQUENCES),
|
||||
"frame_1880_regression": frame_1880_regression,
|
||||
},
|
||||
"fixtures": {
|
||||
"passed": fixtures["passed_count"],
|
||||
@@ -951,7 +1133,102 @@ def _metrics(
|
||||
}
|
||||
|
||||
|
||||
def _requirements(
|
||||
def _requirements_v1(
|
||||
metrics: dict[str, object],
|
||||
fixtures: dict[str, object],
|
||||
) -> dict[str, bool]:
|
||||
frames = _object(metrics.get("frames"), "frame metrics")
|
||||
evidence = _object(metrics.get("evidence"), "evidence metrics")
|
||||
decisions = _object(metrics.get("decisions"), "decision metrics")
|
||||
visual = _object(metrics.get("visual_evidence"), "visual metrics")
|
||||
body_frame = _object(metrics.get("body_frame"), "body frame metrics")
|
||||
total_evidence = sum(_integer(value, "evidence count") for value in evidence.values())
|
||||
total_decisions = sum(_integer(value, "decision count") for value in decisions.values())
|
||||
cases = _array(fixtures.get("cases"), "fixture cases")
|
||||
camera_case = next(
|
||||
(
|
||||
_object(item, "fixture")
|
||||
for item in cases
|
||||
if isinstance(item, dict) and item.get("name") == "camera-only"
|
||||
),
|
||||
{},
|
||||
)
|
||||
stale_cases = [
|
||||
_object(item, "fixture")
|
||||
for item in cases
|
||||
if isinstance(item, dict)
|
||||
and item.get("name") in {"occluded-held", "stale-expired"}
|
||||
]
|
||||
return {
|
||||
"full_ravnoves00_replay_completed": (
|
||||
frames.get("total") == 4489 and frames.get("failed") == 0
|
||||
),
|
||||
"every_metric_or_camera_evidence_received_one_assessment": (
|
||||
total_evidence == total_decisions and total_evidence > 0
|
||||
),
|
||||
"camera_only_is_unknown_never_safe": camera_case.get("actual") == "unknown",
|
||||
"held_and_stale_are_unknown_never_safe": (
|
||||
len(stale_cases) == 2
|
||||
and all(item.get("actual") == "unknown" for item in stale_cases)
|
||||
),
|
||||
"geometry_only_evidence_is_assessed": (
|
||||
_integer(
|
||||
_object(metrics.get("reason_counts"), "reason metrics").get(
|
||||
"geometry-only-evidence", 0
|
||||
),
|
||||
"geometry-only count",
|
||||
)
|
||||
> 0
|
||||
),
|
||||
"deterministic_fixture_matrix_passed": (
|
||||
fixtures.get("passed_count") == fixtures.get("total_count") == 9
|
||||
),
|
||||
"zero_critical_fixture_false_not_threat": (
|
||||
fixtures.get("critical_false_not_threat_count") == 0
|
||||
),
|
||||
"visual_video_camera_cloud_distance_and_corridor_are_available": (
|
||||
visual.get("frame_count") == VISUAL_FRAME_COUNT
|
||||
and all(
|
||||
visual.get(key) is True
|
||||
for key in (
|
||||
"video_overlay_available",
|
||||
"camera_boxes_available",
|
||||
"point_cloud_available",
|
||||
"metric_distance_available",
|
||||
"virtual_corridor_available",
|
||||
"qualified_base_footprint_available",
|
||||
)
|
||||
)
|
||||
and visual.get("geometry_regression_sequences") == [138, 274]
|
||||
),
|
||||
"body_frame_is_grounded_gravity_stable_and_route_aligned": (
|
||||
body_frame.get("available")
|
||||
== _integer(body_frame.get("qualified"), "qualified body frames")
|
||||
+ _integer(body_frame.get("rejected"), "rejected body frames")
|
||||
and _integer(body_frame.get("qualified"), "qualified body frames")
|
||||
>= math.ceil(
|
||||
_integer(body_frame.get("available"), "available body frames") * 0.95
|
||||
)
|
||||
and body_frame.get("origin") == "local-surface-vertical-projection"
|
||||
and body_frame.get("up") == "vendor-slam-map-gravity-axis"
|
||||
and body_frame.get("forward")
|
||||
== "smoothed-slam-trajectory-validated-by-camera-axis"
|
||||
and _number_value(
|
||||
_object(
|
||||
body_frame.get("camera_forward_alignment_deg"),
|
||||
"body alignment metrics",
|
||||
).get("maximum"),
|
||||
"maximum body alignment",
|
||||
)
|
||||
<= 25.0
|
||||
),
|
||||
"physical_collision_and_actuation_authority_remain_false": (
|
||||
fixtures.get("authority") == _false_authority()
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _requirements_v2(
|
||||
metrics: dict[str, object],
|
||||
fixtures: dict[str, object],
|
||||
) -> dict[str, bool]:
|
||||
@@ -997,7 +1274,7 @@ def _requirements(
|
||||
> 0
|
||||
),
|
||||
"deterministic_fixture_matrix_passed": (
|
||||
fixtures.get("passed_count") == fixtures.get("total_count") == 9
|
||||
fixtures.get("passed_count") == fixtures.get("total_count") == 10
|
||||
),
|
||||
"zero_critical_fixture_false_not_threat": (
|
||||
fixtures.get("critical_false_not_threat_count") == 0
|
||||
@@ -1018,6 +1295,19 @@ def _requirements(
|
||||
and visual.get("geometry_regression_sequences")
|
||||
== list(VISUAL_GEOMETRY_REGRESSION_SEQUENCES)
|
||||
),
|
||||
"frame_1880_retains_two_hemispheres_and_blocks_near_corridor": (
|
||||
isinstance(visual.get("frame_1880_regression"), dict)
|
||||
and _object(
|
||||
visual.get("frame_1880_regression"),
|
||||
"frame 1880 regression",
|
||||
).get("matched_anchor_count")
|
||||
== len(FRAME_1880_ENGINEERING_ANCHORS)
|
||||
and _object(
|
||||
visual.get("frame_1880_regression"),
|
||||
"frame 1880 regression",
|
||||
).get("required_threats_passed")
|
||||
is True
|
||||
),
|
||||
"body_frame_is_grounded_gravity_stable_and_route_aligned": (
|
||||
body_frame.get("available")
|
||||
== _integer(body_frame.get("qualified"), "qualified body frames")
|
||||
@@ -1046,12 +1336,15 @@ def _validate_ledgers(
|
||||
frames_path: Path,
|
||||
visuals_path: Path,
|
||||
metrics: dict[str, object],
|
||||
*,
|
||||
is_v2: bool,
|
||||
) -> None:
|
||||
frame_count = 0
|
||||
assessment_count = 0
|
||||
for sequence, frame in enumerate(_read_jsonl(frames_path)):
|
||||
if (
|
||||
frame.get("schema_version") != THREAT_REPLAY_FRAME_SCHEMA
|
||||
frame.get("schema_version")
|
||||
!= (THREAT_REPLAY_FRAME_SCHEMA_V2 if is_v2 else THREAT_REPLAY_FRAME_SCHEMA)
|
||||
or frame.get("sequence") != sequence
|
||||
or frame.get("authority") != _false_authority()
|
||||
):
|
||||
@@ -1071,7 +1364,11 @@ def _validate_ledgers(
|
||||
for value in _object(metrics.get("decisions"), "decisions").values()
|
||||
)
|
||||
or len(visuals) != VISUAL_FRAME_COUNT
|
||||
or any(item.get("schema_version") != THREAT_REPLAY_VISUAL_SCHEMA for item in visuals)
|
||||
or any(
|
||||
item.get("schema_version")
|
||||
!= (THREAT_REPLAY_VISUAL_SCHEMA_V2 if is_v2 else THREAT_REPLAY_VISUAL_SCHEMA)
|
||||
for item in visuals
|
||||
)
|
||||
):
|
||||
raise ThreatReplayError("threat replay ledger and metrics disagree")
|
||||
|
||||
@@ -1233,6 +1530,16 @@ def _number_value(value: object, label: str) -> float:
|
||||
return float(value)
|
||||
|
||||
|
||||
def _bounds(value: object, label: str) -> tuple[float, float]:
|
||||
if not isinstance(value, tuple) or len(value) != 2:
|
||||
raise ThreatReplayError(f"{label} must contain two values")
|
||||
lower = _number_value(value[0], label)
|
||||
upper = _number_value(value[1], label)
|
||||
if lower >= upper:
|
||||
raise ThreatReplayError(f"{label} must be ordered")
|
||||
return lower, upper
|
||||
|
||||
|
||||
def _signed_integer(value: object, label: str) -> int:
|
||||
if not isinstance(value, int) or isinstance(value, bool):
|
||||
raise ThreatReplayError(f"{label} must be an integer")
|
||||
|
||||
Reference in New Issue
Block a user