"""Auditable quality gates for accessibility 3D completion candidates. This module deliberately records compact, inspectable evidence instead of using an opaque free-form reasoning step. It is a *reconstruction quality* gate, not a certification of accessibility, safety, or metric navigation. """ from __future__ import annotations import json from pathlib import Path from typing import Any import cv2 import numpy as np from PIL import Image DECISION_ORDER = {"accept": 0, "manual_review": 1, "reject": 2} CATEGORY_POLICIES: dict[str, dict[str, float]] = { "stairs": { "min_visible_image_ratio": 0.008, "min_visible_amodal_ratio": 0.15, "max_hidden_visible_ratio": 4.0, "max_hidden_amodal_ratio": 0.75, "max_obstacle_amodal_ratio": 0.75, }, "ramp": { "min_visible_image_ratio": 0.006, "min_visible_amodal_ratio": 0.12, "max_hidden_visible_ratio": 3.5, "max_hidden_amodal_ratio": 0.72, "max_obstacle_amodal_ratio": 0.72, }, "walkway": { "min_visible_image_ratio": 0.008, "min_visible_amodal_ratio": 0.12, "max_hidden_visible_ratio": 3.5, "max_hidden_amodal_ratio": 0.72, "max_obstacle_amodal_ratio": 0.72, }, "curb_cut": { "min_visible_image_ratio": 0.004, "min_visible_amodal_ratio": 0.10, "max_hidden_visible_ratio": 3.0, "max_hidden_amodal_ratio": 0.70, "max_obstacle_amodal_ratio": 0.70, }, "tactile_paving": { "min_visible_image_ratio": 0.002, "min_visible_amodal_ratio": 0.10, "max_hidden_visible_ratio": 3.0, "max_hidden_amodal_ratio": 0.70, "max_obstacle_amodal_ratio": 0.70, }, } def _round(value: float, digits: int = 6) -> float: return round(float(value), digits) def _ratio(numerator: int | float, denominator: int | float) -> float: return float(numerator) / float(denominator) if denominator else 0.0 def _policy(category: str) -> dict[str, float]: return CATEGORY_POLICIES.get(category, CATEGORY_POLICIES["walkway"]) def _component_stats(mask: np.ndarray) -> dict[str, Any]: count, _, stats, _ = cv2.connectedComponentsWithStats(mask.astype(np.uint8), 8) areas = stats[1:, cv2.CC_STAT_AREA] if count > 1 else np.empty((0,), dtype=np.int32) total = int(mask.sum()) largest = int(areas.max()) if areas.size else 0 return { "component_count": int(len(areas)), "largest_component_pixels": largest, "largest_component_fraction": _round(_ratio(largest, total)), } def _trace( rule_id: str, decision: str, observed: Any, expected: Any, explanation: str, ) -> dict[str, Any]: return { "rule_id": rule_id, "decision": decision, "observed": observed, "expected": expected, "explanation": explanation, } def _worst_decision(*decisions: str) -> str: return max(decisions, key=lambda value: DECISION_ORDER[value]) def evaluate_mask_preflight( *, category: str, visible_mask: np.ndarray, amodal_mask: np.ndarray, hidden_mask: np.ndarray, obstacle_mask: np.ndarray, sam3_quality: dict[str, Any] | None = None, reviewed_visible_metadata: dict[str, Any] | None = None, ) -> dict[str, Any]: """Evaluate whether masks have enough observed support for automatic 3D.""" policy = _policy(category) input_shapes = { "visible": list(visible_mask.shape), "amodal": list(amodal_mask.shape), "hidden": list(hidden_mask.shape), "obstacle": list(obstacle_mask.shape), } if not ( visible_mask.shape == amodal_mask.shape == hidden_mask.shape == obstacle_mask.shape ): return { "decision": "reject", "policy": policy, "measurements": {"input_mask_shapes": input_shapes}, "audit_trace": [ _trace( "mask_shape_consistency", "reject", input_shapes, "all masks have exactly the same HxW shape", "A gate must not resample mismatched masks, because that can silently change the claimed visible/hidden support.", ) ], } image_area = int(visible_mask.size) visible_pixels = int(visible_mask.sum()) amodal_pixels = int(amodal_mask.sum()) hidden_pixels = int(hidden_mask.sum()) obstacle_pixels = int(obstacle_mask.sum()) obstacle_amodal_pixels = int((obstacle_mask & amodal_mask).sum()) visible_amodal_ratio = _ratio(visible_pixels, amodal_pixels) hidden_visible_ratio = _ratio(hidden_pixels, max(visible_pixels, 1)) hidden_amodal_ratio = _ratio(hidden_pixels, amodal_pixels) obstacle_amodal_ratio = _ratio(obstacle_amodal_pixels, amodal_pixels) trace: list[dict[str, Any]] = [] sam_status = str((sam3_quality or {}).get("review_status") or "missing") reviewed_visible_approved = bool( reviewed_visible_metadata and str(reviewed_visible_metadata.get("source_kind", "")) == "human_reviewed_visible_mask_only" and str(reviewed_visible_metadata.get("review_status", "")) == "approved" and bool(reviewed_visible_metadata.get("review_is_human", False)) and bool(reviewed_visible_metadata.get("visible_confirmed", False)) and str(reviewed_visible_metadata.get("annotator", "")).strip() ) if reviewed_visible_metadata is not None: trace.append( _trace( "reviewed_visible_mask_status", "accept" if reviewed_visible_approved else "reject", { "source_kind": reviewed_visible_metadata.get("source_kind"), "review_status": reviewed_visible_metadata.get("review_status"), "review_is_human": bool(reviewed_visible_metadata.get("review_is_human", False)), "visible_confirmed": bool(reviewed_visible_metadata.get("visible_confirmed", False)), "annotator_present": bool(str(reviewed_visible_metadata.get("annotator", "")).strip()), }, "approved, human-reviewed, visible-confirmed workspace with an annotator", "A reviewed visible boundary can replace the SAM3 proposal only with explicit human approval and provenance." if reviewed_visible_approved else "The reviewed-visible workspace lacks the required approval provenance.", ) ) if sam3_quality is not None and not reviewed_visible_approved: category_verification = dict(sam3_quality.get("category_verification") or {}) selected_category = str(sam3_quality.get("selected_category") or "") best_category = str(category_verification.get("best_scored_category") or "") category_disagreement = bool(category_verification.get("category_disagreement", False)) category_ok = ( (not selected_category or selected_category == category) and (not best_category or best_category == category) and not category_disagreement ) trace.append( _trace( "category_semantic_consistency", "accept" if category_ok else "manual_review", { "requested_category": category, "selected_category": selected_category or None, "best_scored_category": best_category or None, "category_disagreement": category_disagreement, }, "selected and best-scored category agree with the requested category", "The prompt evidence does not consistently identify the requested stairs/ramp/walkway structure; review category and support boundary." if not category_ok else "Prompt-category evidence is consistent with the requested support structure.", ) ) if reviewed_visible_approved: trace.append( _trace( "sam3_proposal_status", "accept", sam_status, "recorded only; superseded by approved reviewed-visible mask", "The original SAM3 proposal is retained for audit but does not override a separately approved visible target boundary.", ) ) elif sam_status in {"reject_or_reprompt", "error"}: trace.append( _trace( "sam3_review_status", "reject", sam_status, "candidate_accept_after_visual_review or manual_review", "The segmentation stage itself rejected this proposal; it must not be sent to automatic 3D generation.", ) ) elif sam_status == "manual_review": trace.append( _trace( "sam3_review_status", "manual_review", sam_status, "candidate_accept_after_visual_review", "The segmentation proposal needs human mask review before any 3D result is used.", ) ) else: trace.append( _trace( "sam3_review_status", "accept" if sam_status == "candidate_accept_after_visual_review" else "manual_review", sam_status, "candidate_accept_after_visual_review", "SAM3 status is recorded as evidence; automatic masks remain proposals rather than ground truth.", ) ) visible_image_ratio = _ratio(visible_pixels, image_area) for rule_id, observed, threshold, comparator, explanation in ( ( "minimum_observed_support", visible_image_ratio, policy["min_visible_image_ratio"], ">=", "The observed target footprint is too small to support a stable scene-level reconstruction.", ), ( "visible_over_amodal_support", visible_amodal_ratio, policy["min_visible_amodal_ratio"], ">=", "Most of the alleged support is hidden, so the completion would be driven by hallucinated geometry rather than observed structure.", ), ( "hidden_over_visible_support", hidden_visible_ratio, policy["max_hidden_visible_ratio"], "<=", "The hidden region is disproportionate to observed support and commonly indicates wall/railing leakage into the amodal mask.", ), ( "hidden_over_amodal_support", hidden_amodal_ratio, policy["max_hidden_amodal_ratio"], "<=", "Automatic 3D must retain enough visible surface evidence rather than invent nearly the whole target.", ), ( "obstacle_dominates_target", obstacle_amodal_ratio, policy["max_obstacle_amodal_ratio"], "<=", "An obstacle that occupies most of the alleged target is likely a side wall, railing, or segmentation leak rather than a compact occluder.", ), ): passed = observed >= threshold if comparator == ">=" else observed <= threshold trace.append( _trace( rule_id, "accept" if passed else "reject", _round(observed), f"{comparator} {_round(threshold)}", "Observed support satisfies the guard." if passed else explanation, ) ) components = _component_stats(visible_mask) fragmented = ( components["component_count"] >= 8 and components["largest_component_fraction"] < 0.35 ) trace.append( _trace( "visible_support_connectedness", "manual_review" if fragmented else "accept", components, "largest component >= 35% when >= 8 components", "Fragmented stair treads can be valid, but need a reviewed support boundary before surface completion." if fragmented else "Observed support is sufficiently connected for automatic processing.", ) ) decision = _worst_decision(*(row["decision"] for row in trace)) return { "decision": decision, "policy": policy, "measurements": { "image_pixels": image_area, "visible_pixels": visible_pixels, "amodal_pixels": amodal_pixels, "hidden_pixels": hidden_pixels, "obstacle_pixels": obstacle_pixels, "visible_image_ratio": _round(visible_image_ratio), "visible_amodal_ratio": _round(visible_amodal_ratio), "hidden_visible_ratio": _round(hidden_visible_ratio), "hidden_amodal_ratio": _round(hidden_amodal_ratio), "obstacle_amodal_ratio": _round(obstacle_amodal_ratio), **components, }, "audit_trace": trace, } def evaluate_geometry_structure( category: str, geometry_manifest: dict[str, Any] | None, ) -> dict[str, Any]: """Check deterministic evidence emitted by accessibilityamodal.reconstruct.""" if not geometry_manifest: return { "decision": "manual_review", "measurements": {}, "audit_trace": [ _trace( "geometry_manifest_present", "manual_review", False, True, "No deterministic geometry manifest is available yet.", ) ], } trace: list[dict[str, Any]] = [] expected_mode = { "stairs": "stairs", "ramp": "ramp", "walkway": "walkable", "curb_cut": "walkable", "tactile_paving": "walkable", }.get(category) observed_mode = str(geometry_manifest.get("geometry_mode") or "") trace.append( _trace( "geometry_category_mode_consistency", "accept" if observed_mode == expected_mode else "manual_review", {"requested_category": category, "geometry_mode": observed_mode or None}, f"geometry_mode={expected_mode}", "The deterministic geometry branch does not match the requested accessibility structure." if observed_mode != expected_mode else "The deterministic geometry branch matches the requested structure.", ) ) log = list(geometry_manifest.get("completion_log") or []) plane_bands = sum( 1 for row in log if str(row.get("mode") or "").startswith("plane") ) fallback_bands = sum( 1 for row in log if "fallback" in str(row.get("mode") or "")) fallback_ratio = _ratio(fallback_bands, max(len(log), 1)) trace.append( _trace( "completion_band_support", "reject" if fallback_ratio > 0.25 else "accept", {"band_count": len(log), "plane_bands": plane_bands, "fallback_bands": fallback_bands, "fallback_ratio": _round(fallback_ratio)}, "fallback_ratio <= 0.25", "Too many stair bands lacked observed points and fell back to image inpainting." if fallback_ratio > 0.25 else "Most completion bands are supported by fitted geometry.", ) ) hidden_pixels = int(geometry_manifest.get("hidden_pixel_count") or 0) confidence = float(geometry_manifest.get("mean_hidden_completion_confidence") or 0.0) hidden_confidence_decision = "accept" if hidden_pixels == 0 else "manual_review" if confidence < 0.50 else "accept" trace.append( _trace( "hidden_geometry_confidence", hidden_confidence_decision, "not_applicable_no_hidden_region" if hidden_pixels == 0 else _round(confidence), "not applicable when hidden_pixel_count=0; otherwise >= 0.50", "No target surface was extrapolated, so hidden-depth confidence is not applicable." if hidden_pixels == 0 else "Hidden depth confidence is low; do not treat the result as a publishable or navigation-ready surface." if hidden_confidence_decision == "manual_review" else "Hidden completion confidence meets the local visualization threshold.", ) ) faces = int(geometry_manifest.get("mesh_faces") or 0) trace.append( _trace( "mesh_nonempty", "reject" if faces <= 0 else "accept", faces, "> 0", "No mesh faces were produced." if faces <= 0 else "Mesh contains triangle faces.", ) ) if category == "stairs": edges = list(geometry_manifest.get("stair_edges_y") or []) edge_confidence = float(geometry_manifest.get("stair_edge_confidence") or 0.0) trace.append( _trace( "stair_repetition_evidence", "manual_review" if len(edges) < 3 or edge_confidence < 0.45 else "accept", {"edge_count": len(edges), "edge_confidence": _round(edge_confidence), "edge_slope": _round(float(geometry_manifest.get("stair_edge_slope") or 0.0))}, "at least 3 edges and confidence >= 0.45", "Too little repeated tread evidence is available for a reliable stair regularization." if len(edges) < 3 or edge_confidence < 0.45 else "Repeated stair-edge evidence supports a segmented stair prior.", ) ) decision = _worst_decision(*(row["decision"] for row in trace)) return { "decision": decision, "measurements": { "completion_band_count": len(log), "plane_band_count": plane_bands, "fallback_band_count": fallback_bands, "fallback_band_ratio": _round(fallback_ratio), "hidden_pixel_count": hidden_pixels, "mean_hidden_completion_confidence": _round(confidence), "mesh_faces": faces, }, "audit_trace": trace, } def _foreground_measurement(path: Path) -> dict[str, float]: image = np.asarray(Image.open(path).convert("RGB")) foreground = np.any(image < 245, axis=2) ys, xs = np.where(foreground) if xs.size == 0: return {"coverage": 0.0, "bbox_width_ratio": 0.0, "bbox_height_ratio": 0.0, "bbox_aspect_ratio": 0.0} width_ratio = _ratio(int(xs.max() - xs.min() + 1), image.shape[1]) height_ratio = _ratio(int(ys.max() - ys.min() + 1), image.shape[0]) return { "coverage": _round(float(foreground.mean())), "bbox_width_ratio": _round(width_ratio), "bbox_height_ratio": _round(height_ratio), "bbox_aspect_ratio": _round(min(width_ratio, height_ratio) / max(width_ratio, height_ratio, 1e-6)), } def evaluate_full_gpu_render_contract(learned_dir: Path) -> dict[str, Any]: """Require evidence that both learned representations were rasterized on CUDA.""" manifest_path = learned_dir / "manifest.json" required_files = ( "sample_gaussian.gif", "sample_mesh.gif", "sample_multi.gif", "multiview_contact_sheet.jpg", "mesh.ply", ) missing_files = [ name for name in required_files if not (learned_dir / name).is_file() or (learned_dir / name).stat().st_size <= 0 ] payload: dict[str, Any] = {} manifest_error = None try: value = json.loads(manifest_path.read_text(encoding="utf-8")) if not isinstance(value, dict): raise ValueError("manifest root is not an object") payload = value except Exception as exc: manifest_error = f"{type(exc).__name__}: {exc}" renderer = payload.get("gpu_renderer_runtime") validation = payload.get("render_validation") gaussian = renderer.get("gaussian") if isinstance(renderer, dict) else None dense_mesh = ( renderer.get("dense_mesh") if isinstance(renderer, dict) else None ) nviews = int(payload.get("nviews") or 0) gaussian_views = sorted(learned_dir.glob("*_gs.png")) mesh_views = sorted(learned_dir.glob("*_mesh.png")) contract_ok = ( manifest_error is None and not missing_files and isinstance(renderer, dict) and isinstance(gaussian, dict) and gaussian.get("available") is True and gaussian.get("required") is True and gaussian.get("device") == "cuda" and gaussian.get("runtime_import_succeeded") is True and isinstance(dense_mesh, dict) and dense_mesh.get("available") is True and dense_mesh.get("required") is True and dense_mesh.get("device") == "cuda" and dense_mesh.get("runtime_import_succeeded") is True and dense_mesh.get("cuda_context_preflight_succeeded") is True and renderer.get("cpu_render_fallback_allowed") is False and renderer.get("gaussian_only_debug_mode") is False and isinstance(validation, dict) and validation.get("validated") is True and validation.get("dense_mesh_rendered_on_gpu") is True and validation.get("cpu_render_fallback_used") is False and int(validation.get("mesh_face_count") or 0) > 0 and nviews > 0 and len(gaussian_views) == nviews and len(mesh_views) == nviews ) return { "decision": "accept" if contract_ok else "reject", "measurements": { "manifest_error": manifest_error, "missing_files": missing_files, "declared_view_count": nviews, "gaussian_view_count": len(gaussian_views), "mesh_view_count": len(mesh_views), "gaussian_cuda": bool( isinstance(gaussian, dict) and gaussian.get("device") == "cuda" ), "dense_mesh_cuda": bool( isinstance(dense_mesh, dict) and dense_mesh.get("device") == "cuda" ), "cpu_render_fallback_used": ( validation.get("cpu_render_fallback_used") if isinstance(validation, dict) else None ), }, "audit_trace": [ _trace( "full_gpu_renderer_contract", "accept" if contract_ok else "reject", { "manifest_present": manifest_path.is_file(), "missing_files": missing_files, "gaussian_views": len(gaussian_views), "mesh_views": len(mesh_views), "declared_views": nviews, }, "CUDA Gaussian and nvdiffrast mesh renders, no CPU fallback", ( "The learned result proves both CUDA render paths and a " "validated dense triangle mesh." if contract_ok else "The learned result is incomplete or does not prove the required CUDA Gaussian + dense-mesh render contract." ), ) ], } def evaluate_learned_multiview( category: str, learned_dir: Path | None, ) -> dict[str, Any]: """Check learned visual candidates without treating renderer coordinates as metric geometry.""" if learned_dir is None or not learned_dir.is_dir(): return { "decision": "manual_review", "measurements": {}, "audit_trace": [ _trace("learned_multiview_present", "manual_review", False, True, "No learned Accessibility3D multiview result is available.") ], } views = sorted(learned_dir.glob("*_gs.png")) if not views: return { "decision": "reject", "measurements": {}, "audit_trace": [ _trace("learned_multiview_present", "reject", False, True, "The learned 3D result has no rendered multiview evidence.") ], } measures = [_foreground_measurement(path) for path in views] gpu_contract = evaluate_full_gpu_render_contract(learned_dir) median_coverage = float(np.median([row["coverage"] for row in measures])) median_aspect = float(np.median([row["bbox_aspect_ratio"] for row in measures])) coverage_decision = ( "reject" if median_coverage < 0.05 else "manual_review" if median_coverage < 0.12 else "accept" ) aspect_decision = ( "reject" if median_aspect < 0.10 else "manual_review" if median_aspect < 0.30 else "accept" ) trace = [ *gpu_contract["audit_trace"], _trace( "learned_multiview_evidence_type", "accept", "Gaussian-splat raster previews (*_gs.png)", "visual reconstruction evidence only", "These views can expose a collapsed visual candidate but do not establish physical dimensions or navigability.", ), _trace( "multiview_foreground_coverage", coverage_decision, _round(median_coverage), ">= 0.12", "The learned object occupies almost none of the rendered views and is likely an empty or fragmentary result." if coverage_decision == "reject" else "The object is small in the rendered views; inspect its requested scale and framing before use." if coverage_decision == "manual_review" else "The rendered object has adequate view coverage.", ), _trace( "multiview_silhouette_thickness", aspect_decision, _round(median_aspect), ">= 0.30", "Most views are nearly one-dimensional, which is inconsistent with a usable support-surface candidate." if aspect_decision == "reject" else "The views are elongated; this can be valid for a long ramp or stair flight, but needs human geometry review." if aspect_decision == "manual_review" else "Rendered silhouettes have a plausible two-dimensional extent.", ), ] mesh_measurements: dict[str, Any] = {} mesh_path = learned_dir / "mesh.ply" if mesh_path.is_file(): try: import trimesh mesh = trimesh.load(mesh_path, process=False) extent = np.asarray(mesh.bounds[1] - mesh.bounds[0], dtype=np.float64) minmax_ratio = float(extent.min() / max(float(extent.max()), 1e-6)) mesh_measurements = { "vertices": int(len(mesh.vertices)), "faces": int(len(mesh.faces)), "extent": [_round(value) for value in extent], "minmax_extent_ratio": _round(minmax_ratio), "watertight": bool(mesh.is_watertight), } extent_decision = ( "reject" if minmax_ratio < 0.02 else "manual_review" if minmax_ratio < 0.08 else "accept" ) trace.append( _trace( "mesh_near_degeneracy", extent_decision, _round(minmax_ratio), ">= 0.08 (manual review below); < 0.02 rejects", "The learned mesh is almost flat in its own normalized coordinates, consistent with a degenerate fragment." if extent_decision == "reject" else "The learned mesh is elongated; normalized extents alone cannot distinguish a valid long ramp/staircase from a fragment." if extent_decision == "manual_review" else "No near-zero mesh axis was detected. This is not a physical-scale check.", ) ) except Exception as exc: # pragma: no cover - optional mesh parsing trace.append(_trace("mesh_near_degeneracy", "manual_review", "unavailable", ">= 0.08", f"Could not inspect learned mesh: {type(exc).__name__}")) decision = _worst_decision(*(row["decision"] for row in trace)) return { "decision": decision, "measurements": { "view_count": len(measures), "median_foreground_coverage": _round(median_coverage), "median_silhouette_aspect": _round(median_aspect), "views": measures, "mesh": mesh_measurements, "gpu_render_contract": gpu_contract["measurements"], }, "audit_trace": trace, } def mobility_interpretation(category: str, decision: str) -> dict[str, Any]: """Return conservative, non-metric audience-specific interpretation.""" withheld = decision != "accept" common = "withheld pending mask/geometry review" if withheld else "requires calibrated clearance and surface survey" if category == "stairs": return { "pedestrian": common, "blind_or_low_vision_pedestrian": "requires surveyed handrail, edge, tactile, lighting, and obstacle information; monocular 3D is insufficient", "wheelchair": "stairs are not an accessible route; require a separately verified ramp/lift/alternate route", "robot_or_robot_dog": "requires metric riser/tread, friction, width, and local obstacle sensing; do not execute from this visual model alone", } return { "pedestrian": common, "blind_or_low_vision_pedestrian": "requires surveyed tactile/edge/obstacle information; monocular 3D is insufficient", "wheelchair": common, "robot_or_robot_dog": "requires metric slope, clearance, friction, and local obstacle sensing; do not execute from this visual model alone", } def build_verification( *, sample_id: str, category: str, visible_mask: np.ndarray, amodal_mask: np.ndarray, hidden_mask: np.ndarray, obstacle_mask: np.ndarray, sam3_quality: dict[str, Any] | None = None, reviewed_visible_metadata: dict[str, Any] | None = None, geometry_manifest: dict[str, Any] | None = None, learned_dir: Path | None = None, ) -> dict[str, Any]: """Build one JSON-ready, auditable accessibility 3D verification record.""" preflight = evaluate_mask_preflight( category=category, visible_mask=visible_mask, amodal_mask=amodal_mask, hidden_mask=hidden_mask, obstacle_mask=obstacle_mask, sam3_quality=sam3_quality, reviewed_visible_metadata=reviewed_visible_metadata, ) geometry = evaluate_geometry_structure(category, geometry_manifest) learned = evaluate_learned_multiview(category, learned_dir) overall = _worst_decision(preflight["decision"], geometry["decision"], learned["decision"]) if geometry_manifest is None and learned_dir is None: overall = preflight["decision"] trace = [ *preflight["audit_trace"], *geometry["audit_trace"], *learned["audit_trace"], ] return { "schema_version": 1, "sample_id": sample_id, "category": category, "decision": overall, "gate": { "allow_automatic_geometry": preflight["decision"] == "accept", "allow_learned_object_3d": ( preflight["decision"] == "accept" and geometry["decision"] == "accept" and learned["decision"] == "accept" ), "allow_publication": False, "publication_note": "Human license/privacy review and calibrated geometry validation remain required.", }, "mask_preflight": preflight, "geometry_structure": geometry, "learned_multiview": learned, "mobility_interpretation": mobility_interpretation(category, overall), "audit_trace": trace, "recommended_action": ( "re_prompt_or_review_masks_before_3d" if preflight["decision"] != "accept" else "review_geometry_before_release" if overall != "accept" else "keep_as_local_visual_candidate_not_navigation_truth" ), "limitations": [ "This is a structured reconstruction-quality gate, not chain-of-thought or a safety certification.", "Monocular/depth-model geometry is not calibrated metric truth unless separately calibrated.", "Do not use this record alone to control a pedestrian aid, wheelchair, robot, or robot dog.", ], }