fix(m4): retain compact obstacle evidence

This commit is contained in:
DCCONSTRUCTIONS
2026-08-05 22:16:33 +03:00
parent 13ed096f80
commit aacc6dc43b
24 changed files with 981 additions and 410 deletions
+50 -95
View File
@@ -46,18 +46,15 @@ TEMPORAL_REPLAY_REPORT_NAME: Final = "report.json"
TEMPORAL_REPLAY_MANIFEST_NAME: Final = "manifest.json"
E34_RESULT_ID: Final = (
"e34-temporal-occupied-"
"8d9abb3f2cc072cfdbb16cc4e55798e05c35a0abe0b8f691096770e091573a73"
"e34-temporal-occupied-8d9abb3f2cc072cfdbb16cc4e55798e05c35a0abe0b8f691096770e091573a73"
)
E34_MANIFEST_SHA256: Final = "88285f44d0316913881cc0948a4dfb300d56b460c51be36d51b4dd717cbf4170"
E51_RESULT_ID: Final = (
"e51-motion-semantic-"
"1abb7eb9940608fc5af95a1f318cadfbc42ac2412a8662b6622e000e03da1555"
"e51-motion-semantic-1abb7eb9940608fc5af95a1f318cadfbc42ac2412a8662b6622e000e03da1555"
)
E51_MANIFEST_SHA256: Final = "a38ecad59765d1439ac432a34c053362195ec4b493d8e8f1d4a1731f99836437"
E46B_RESULT_ID: Final = (
"e46b-temporal-motion-"
"78d038912273364e36f996401873a8ee178a94641350021c2cd35bcb301ba36d"
"e46b-temporal-motion-78d038912273364e36f996401873a8ee178a94641350021c2cd35bcb301ba36d"
)
E46B_MANIFEST_SHA256: Final = "b0d0b6bdfa23f0475106c1870771ee90dd6de85719396a665dd7311f93da3d59"
E46B_CASES_SHA256: Final = "95b58300f10dc796f7576bffbcc670ac76b74d6f25b13c0768f57331cba49074"
@@ -100,9 +97,7 @@ def build_temporal_replay(
store = RecordedGeometryStore.from_repository(repository)
temporal = BoundedSpatialTemporalProvider(point_resolver=store, profile=profile)
motion = ClassIndependentMotionEstimator(profile=profile)
rolling_profile = load_rolling_map_profile(
repository / DEFAULT_ROLLING_MAP_PROFILE_PATH
)
rolling_profile = load_rolling_map_profile(repository / DEFAULT_ROLLING_MAP_PROFILE_PATH)
rolling = RollingLocalObstacleMapProvider(
pose_resolver=store,
profile=rolling_profile,
@@ -147,9 +142,7 @@ def build_temporal_replay(
obstacles = motion.estimate(packet, temporal_obstacles)
rolling_retained = rolling.update(packet, obstacles)
latencies_ms.append((time.perf_counter_ns() - frame_started_ns) / 1_000_000)
current = tuple(
item for item in obstacles if item.state is TemporalState.CURRENT
)
current = tuple(item for item in obstacles if item.state is TemporalState.CURRENT)
held = tuple(item for item in obstacles if item.state is TemporalState.HELD)
expired = tuple(item for item in obstacles if item.state is TemporalState.EXPIRED)
motion_counts = _motion_counts(current)
@@ -158,13 +151,10 @@ def build_temporal_replay(
"sequence": frame_count,
"frame_id": packet.envelope.frame_id,
"source_time_ns": packet.envelope.timestamps.source_ns,
"source_available": (
packet.envelope.registered_point_increment.available
),
"source_available": (packet.envelope.registered_point_increment.available),
"input_observation_count": len(observations),
"current_occupied_input_count": sum(
item.occupied_support
and item.currentness.value == "current"
item.occupied_support and item.currentness.value == "current"
for item in observations
),
"nonmetric_uncertainty_input_count": sum(
@@ -173,13 +163,10 @@ def build_temporal_replay(
"current": [item.to_dict() for item in current],
"held": [item.to_dict() for item in held],
"expired": [item.to_dict() for item in expired],
"rolling_retained": [
item.to_dict() for item in rolling_retained
],
"rolling_retained": [item.to_dict() for item in rolling_retained],
"motion_counts": motion_counts,
"map_frame_jump_candidate": any(
item.association_basis == "map-frame-discontinuity"
for item in current
item.association_basis == "map-frame-discontinuity" for item in current
),
"policy": _frame_policy(),
"authority": _false_authority(),
@@ -216,9 +203,7 @@ def build_temporal_replay(
identity = {
"schema_version": TEMPORAL_REPLAY_SCHEMA_V2,
"geometry_result_id": geometry.result_id,
"geometry_manifest_sha256": _file_sha256(
geometry.result_root / "manifest.json"
),
"geometry_manifest_sha256": _file_sha256(geometry.result_root / "manifest.json"),
"geometry_frames_sha256": profile.geometry_frames_sha256,
"profile_id": profile.profile_id,
"profile_sha256": profile.profile_sha256,
@@ -398,18 +383,11 @@ def read_temporal_replay_result(root: Path) -> TemporalReplayResult:
if is_v2
else _requirements(metrics, ttl_ns / 1_000_000_000)
)
if (
ttl_ns != 750_000_000
or requirements != expected_requirements
):
if ttl_ns != 750_000_000 or requirements != expected_requirements:
raise TemporalReplayError("temporal replay acceptance was not derived from metrics")
if (
report.get("schema_version")
!= (
TEMPORAL_REPLAY_REPORT_SCHEMA_V2
if is_v2
else TEMPORAL_REPLAY_REPORT_SCHEMA
)
!= (TEMPORAL_REPLAY_REPORT_SCHEMA_V2 if is_v2 else TEMPORAL_REPLAY_REPORT_SCHEMA)
or report.get("result_id") != resolved.name
or report.get("identity_sha256") != identity_sha256
or report.get("metrics") != metrics
@@ -490,9 +468,7 @@ def _requirements_v2(
requirements.update(
{
"registered_increment_is_not_treated_as_complete_scan": True,
"rolling_map_processed_every_source_frame": (
rolling.get("input_frames") == 4489
),
"rolling_map_processed_every_source_frame": (rolling.get("input_frames") == 4489),
"rolling_map_materialized_retained_occupancy": (
_integer(
rolling.get("retained_component_publications"),
@@ -506,8 +482,7 @@ def _requirements_v2(
"maximum rolling retained age",
)
<= round(rolling_retention_seconds * 1_000_000_000)
and rolling.get("retention_ns")
== round(rolling_retention_seconds * 1_000_000_000)
and rolling.get("retention_ns") == round(rolling_retention_seconds * 1_000_000_000)
and rolling.get("maximum_cells") == rolling_maximum_cells
and _integer(
rolling.get("capacity_evicted_cells"),
@@ -571,22 +546,24 @@ def _requirements(metrics: dict[str, object], ttl_seconds: float) -> dict[str, b
)
return {
"full_frame_accounting": frames == {"total": 4489, "failed": 0},
"geometry_observation_accounting": metrics.get("input_observations") == 37457,
"geometry_observation_accounting": (
metrics.get("input_observations") == temporal_input
and temporal.get("input_frames") == frames.get("total")
and temporal_input > 0
),
"metric_and_nonmetric_partition_closed": (
temporal.get("current_occupied_observations") == 27299
and temporal.get("nonmetric_uncertainty_observations") == 10158
temporal_input == current_input + uncertainty_input
and current_input > 0
and uncertainty_input > 0
),
"camera_uncertainty_never_created_occupied_state": (
temporal_input == current_input + uncertainty_input
),
"detector_identity_changes_survive_spatial_reassociation": identity_changes > 0,
"temporal_state_is_bounded": (
peak_components <= 256
and peak_cells <= 4096
and maximum_history <= 8
peak_components <= 256 and peak_cells <= 4096 and maximum_history <= 8
),
"held_and_expired_states_materialized": held_publications > 0
and expired_publications > 0,
"held_and_expired_states_materialized": held_publications > 0 and expired_publications > 0,
"no_occupied_cells_survive_ttl": (
retention.get("past_ttl_occupied_publications") == 0
and retention.get("ghost_occupancy_past_ttl_count") == 0
@@ -600,8 +577,7 @@ def _requirements(metrics: dict[str, object], ttl_seconds: float) -> dict[str, b
value > 0 for value in (moving, stationary, unknown)
),
"motion_accounting_closed": (
motion_input
== current_publications + held_publications + expired_publications
motion_input == current_publications + held_publications + expired_publications
),
"bounded_labeled_engineering_checks_recorded": (
clips
@@ -651,9 +627,8 @@ def _validate_frame_ledger(
frame.get("nonmetric_uncertainty_input_count"),
"frame nonmetric uncertainty inputs",
)
if (
frame_current_inputs + frame_uncertainty_inputs
!= frame.get("input_observation_count")
if frame_current_inputs + frame_uncertainty_inputs != frame.get(
"input_observation_count"
):
raise TemporalReplayError("temporal frame input partition is open")
current_inputs += frame_current_inputs
@@ -673,11 +648,7 @@ def _validate_frame_ledger(
groups[state] = items
for item in items:
motion_counts[item.motion.value] += 1
component_ids = [
item.component_id
for group in groups.values()
for item in group
]
component_ids = [item.component_id for group in groups.values() for item in group]
if len(component_ids) != len(set(component_ids)):
raise TemporalReplayError("temporal frame duplicated a component")
if is_v2:
@@ -689,13 +660,9 @@ def _validate_frame_ledger(
)
)
if any(item.state is not TemporalState.RETAINED for item in rolling):
raise TemporalReplayError(
"rolling map published a non-retained component"
)
raise TemporalReplayError("rolling map published a non-retained component")
if set(component_ids) & {item.component_id for item in rolling}:
raise TemporalReplayError(
"rolling and temporal component identities overlap"
)
raise TemporalReplayError("rolling and temporal component identities overlap")
rolling_publications += len(rolling)
current_publications += len(groups[TemporalState.CURRENT])
held_publications += len(groups[TemporalState.HELD])
@@ -706,11 +673,7 @@ def _validate_frame_ledger(
frame_metrics = _object(metrics.get("frames"), "temporal frames")
temporal = _object(metrics.get("temporal"), "temporal metrics")
motion = _object(metrics.get("motion"), "motion metrics")
rolling_metrics = (
_object(metrics.get("rolling_map"), "rolling map metrics")
if is_v2
else None
)
rolling_metrics = _object(metrics.get("rolling_map"), "rolling map metrics") if is_v2 else None
if (
frames != frame_metrics.get("total")
or observations != metrics.get("input_observations")
@@ -724,8 +687,7 @@ def _validate_frame_ledger(
or motion_counts[MotionState.UNKNOWN.value] != motion.get("unknown")
or (
rolling_metrics is not None
and rolling_publications
!= rolling_metrics.get("retained_component_publications")
and rolling_publications != rolling_metrics.get("retained_component_publications")
)
):
raise TemporalReplayError("temporal frame ledger and metrics disagree")
@@ -801,10 +763,7 @@ def _clip_check(
def _motion_counts(obstacles: tuple[TemporalObstacle, ...]) -> dict[str, int]:
return {
state.value: sum(item.motion is state for item in obstacles)
for state in MotionState
}
return {state.value: sum(item.motion is state for item in obstacles) for state in MotionState}
def _frame_policy() -> dict[str, object]:
@@ -836,9 +795,7 @@ def _read_geometry_frame(line: bytes, sequence: int) -> dict[str, object]:
try:
frame = _object(json.loads(line), "geometry replay frame")
except json.JSONDecodeError as exc:
raise TemporalReplayError(
f"geometry replay frame {sequence + 1} is invalid JSON"
) from exc
raise TemporalReplayError(f"geometry replay frame {sequence + 1} is invalid JSON") from exc
if frame.get("sequence") != sequence:
raise TemporalReplayError("geometry replay frame sequence is incomplete")
return frame
@@ -853,25 +810,23 @@ def _read_frame(
try:
frame = _object(json.loads(line), "temporal replay frame")
except json.JSONDecodeError as exc:
raise TemporalReplayError(
f"temporal replay frame {sequence + 1} is invalid JSON"
) from exc
raise TemporalReplayError(f"temporal replay frame {sequence + 1} is invalid JSON") from exc
expected_keys = {
"schema_version",
"sequence",
"frame_id",
"source_time_ns",
"source_available",
"input_observation_count",
"current_occupied_input_count",
"nonmetric_uncertainty_input_count",
"current",
"held",
"expired",
"motion_counts",
"map_frame_jump_candidate",
"policy",
"authority",
"schema_version",
"sequence",
"frame_id",
"source_time_ns",
"source_available",
"input_observation_count",
"current_occupied_input_count",
"nonmetric_uncertainty_input_count",
"current",
"held",
"expired",
"motion_counts",
"map_frame_jump_candidate",
"policy",
"authority",
}
if is_v2:
expected_keys.add("rolling_retained")
+149 -45
View File
@@ -66,7 +66,7 @@ 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, 1880)
VISUAL_GEOMETRY_REGRESSION_SEQUENCES: Final = (138, 274, 1880, 2584)
FRAME_1880_ENGINEERING_ANCHORS: Final = (
{
"anchor_id": "near-concrete-hemisphere",
@@ -83,6 +83,22 @@ FRAME_1880_ENGINEERING_ANCHORS: Final = (
"must_assert_threat": False,
},
)
FRAME_2584_ENGINEERING_ANCHORS: Final = (
{
"anchor_id": "near-compact-concrete-hemisphere",
"x_bounds_m": (1.5, 2.4),
"y_bounds_m": (-0.7, 0.2),
"z_bounds_m": (-0.1, 0.9),
"must_assert_threat": True,
},
{
"anchor_id": "far-concrete-hemisphere-occupancy",
"x_bounds_m": (2.8, 4.0),
"y_bounds_m": (1.2, 2.4),
"z_bounds_m": (-0.1, 1.0),
"must_assert_threat": False,
},
)
class ThreatReplayError(RuntimeError):
@@ -143,6 +159,7 @@ def build_threat_replay(
latencies_ms: list[float] = []
visual_count = 0
frame_1880_regression: dict[str, object] | None = None
frame_2584_regression: dict[str, object] | None = None
try:
temporal_frames_path = temporal.result_root / "frames.jsonl"
geometry_frames_path = geometry.result_root / "frames.jsonl"
@@ -188,13 +205,8 @@ def build_threat_replay(
"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"
)
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"
)
@@ -229,11 +241,8 @@ def build_threat_replay(
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, *rolling_retained, *unknown)
} | {
item.proposal_id for item in camera_uncertainty
}
item.component_id for item in (*current, *rolling_retained, *unknown)
} | {item.proposal_id for item in camera_uncertainty}
if set(by_id) != expected_ids:
raise ThreatReplayError("threat assessment coverage is incomplete")
camera_rows = _camera_rows(
@@ -248,9 +257,13 @@ def build_threat_replay(
if frame_count == 1880:
frame_1880_regression = _frame_1880_regression(
metric_rows,
body_frame_resolver.body_frame_for_frame(
packet.envelope.frame_id
),
body_frame_resolver.body_frame_for_frame(packet.envelope.frame_id),
)
if frame_count == 2584:
frame_2584_regression = _frame_2584_regression(
metric_rows,
body_frame_resolver.body_frame_for_frame(packet.envelope.frame_id),
voxel_size_m=profile.corridor.occupied_voxel_size_m,
)
for item in assessments:
assessment_counts[item.decision.value] += 1
@@ -327,6 +340,7 @@ def build_threat_replay(
fixtures=fixtures,
body_frame=body_frame_resolver.qualification_summary(),
frame_1880_regression=frame_1880_regression,
frame_2584_regression=frame_2584_regression,
)
requirements = _requirements_v2(metrics, fixtures)
accepted = all(value is True for value in requirements.values())
@@ -488,9 +502,7 @@ def read_threat_replay_result(root: Path) -> ThreatReplayResult:
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)
_requirements_v2(metrics, fixtures) if is_v2 else _requirements_v1(metrics, fixtures)
)
if (
report.get("schema_version")
@@ -687,8 +699,7 @@ def _visual_frame(
"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
row.get("state") == TemporalState.RETAINED.value for row in metric_rows
),
"metric_obstacles": metric_visuals,
"camera_proposals": camera_rows,
@@ -778,22 +789,15 @@ def _frame_1880_regression(
"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])
),
"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")
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
)
and (item["must_assert_threat"] is False or item["decision"] == ThreatDecision.THREAT.value)
for item in anchors
)
return {
@@ -808,6 +812,100 @@ def _frame_1880_regression(
}
def _frame_2584_regression(
metric_rows: list[dict[str, object]],
body_frame: ReplayBodyFrame | None,
*,
voxel_size_m: float,
) -> dict[str, object]:
"""Bind both visible hemispheres to produced occupancy without injecting it."""
if body_frame is None:
raise ThreatReplayError("frame 2584 has no qualified body frame")
candidates: list[
tuple[dict[str, object], tuple[float, float, float], tuple[float, float, float]]
] = []
for row in metric_rows:
assessment = _object(row.get("assessment"), "frame 2584 assessment")
centroid_map = _array(row.get("centroid_map_xyz_m"), "frame 2584 centroid")
if len(centroid_map) != 3:
raise ThreatReplayError("frame 2584 centroid is invalid")
centroid_body = body_frame.map_point_to_body(
tuple(_number_value(value, "frame 2584 centroid") for value in centroid_map)
)
for raw_cell in _array(row.get("cells"), "frame 2584 cells"):
cell = _object(raw_cell, "frame 2584 cell")
point_map = tuple(
(_signed_integer(cell.get(key), f"frame 2584 cell {key}") + 0.5) * voxel_size_m
for key in ("x", "y", "z")
)
candidates.append(
(
row,
centroid_body,
body_frame.map_point_to_body(point_map),
)
)
if not row.get("cells"):
raise ThreatReplayError("frame 2584 metric component has no occupied cells")
if assessment.get("component_id") != row.get("component_id"):
raise ThreatReplayError("frame 2584 assessment identity changed")
anchors: list[dict[str, object]] = []
matched_ids: set[str] = set()
for raw_anchor in FRAME_2584_ENGINEERING_ANCHORS:
anchor = _object(raw_anchor, "frame 2584 engineering anchor")
x_bounds = _bounds(anchor.get("x_bounds_m"), "frame 2584 x bounds")
y_bounds = _bounds(anchor.get("y_bounds_m"), "frame 2584 y bounds")
z_bounds = _bounds(anchor.get("z_bounds_m"), "frame 2584 z bounds")
match = next(
(
(row, centroid, cell)
for row, centroid, cell in candidates
if row.get("component_id") not in matched_ids
and x_bounds[0] <= cell[0] <= x_bounds[1]
and y_bounds[0] <= cell[1] <= y_bounds[1]
and z_bounds[0] <= cell[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)
assessment = (
None
if match is None
else _object(match[0].get("assessment"), "frame 2584 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,
"component_state": None if match is None else match[0].get("state"),
"centroid_body_xyz_m": None if match is None else list(match[1]),
"matched_cell_body_xyz_m": None if match is None else list(match[2]),
"decision": None if assessment is None else 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": 2584,
"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,
"matching_basis": "produced-occupied-cell-inside-camera-reviewed-body-window",
"gate": "compact-and-merged-hemisphere-occupancy-regression",
}
class _FixtureBodyFrames:
def body_frame_for_frame(self, frame_id: str) -> ReplayBodyFrame:
return ReplayBodyFrame(
@@ -982,11 +1080,7 @@ def _fixture_obstacle(
component_id=component_id,
identity_scope="ephemeral",
state=state,
ttl_ns=(
3_000_000_000
if state is TemporalState.RETAINED
else 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",
@@ -1096,6 +1190,7 @@ def _metrics(
fixtures: dict[str, object],
body_frame: dict[str, object],
frame_1880_regression: dict[str, object] | None,
frame_2584_regression: dict[str, object] | None,
) -> dict[str, object]:
values = np.asarray(latencies_ms, dtype=np.float64)
return {
@@ -1116,6 +1211,7 @@ def _metrics(
"qualified_base_footprint_available": True,
"geometry_regression_sequences": list(VISUAL_GEOMETRY_REGRESSION_SEQUENCES),
"frame_1880_regression": frame_1880_regression,
"frame_2584_regression": frame_2584_regression,
},
"fixtures": {
"passed": fixtures["passed_count"],
@@ -1156,8 +1252,7 @@ def _requirements_v1(
stale_cases = [
_object(item, "fixture")
for item in cases
if isinstance(item, dict)
and item.get("name") in {"occluded-held", "stale-expired"}
if isinstance(item, dict) and item.get("name") in {"occluded-held", "stale-expired"}
]
return {
"full_ravnoves00_replay_completed": (
@@ -1168,8 +1263,7 @@ def _requirements_v1(
),
"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)
len(stale_cases) == 2 and all(item.get("actual") == "unknown" for item in stale_cases)
),
"geometry_only_evidence_is_assessed": (
_integer(
@@ -1206,13 +1300,10 @@ def _requirements_v1(
== _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
)
>= 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 body_frame.get("forward") == "smoothed-slam-trajectory-validated-by-camera-axis"
and _number_value(
_object(
body_frame.get("camera_forward_alignment_deg"),
@@ -1308,6 +1399,19 @@ def _requirements_v2(
).get("required_threats_passed")
is True
),
"frame_2584_retains_compact_hemisphere_and_accounts_for_far_occupancy": (
isinstance(visual.get("frame_2584_regression"), dict)
and _object(
visual.get("frame_2584_regression"),
"frame 2584 regression",
).get("matched_anchor_count")
== len(FRAME_2584_ENGINEERING_ANCHORS)
and _object(
visual.get("frame_2584_regression"),
"frame 2584 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")