"""CDSL runtime adapter driven exclusively by profile operation contracts.""" from __future__ import annotations from copy import deepcopy from hashlib import sha256 import json import math from pathlib import Path from typing import Any from app.cad_agent.domain.operation_contract import ( SEMANTIC_PREFLIGHT_NAMES, OperationContractError, canonical_hash, validate_fragment, validate_operation_contract, ) from app.cad_agent.domain.verifier_registry import default_registry from app.services.engine_service import load_engine, topology_snapshot, validate_cdsl from app.services.review_renderer import ReviewRenderError, render_checkpoint from app.settings import Settings from vendor.cdsl_preview_runtime import step_to_glb class RuntimeAdapterError(RuntimeError): pass class ProfileCadRuntime: """Adapter that owns engine imports; application code sees only its port.""" def __init__(self, settings: Settings) -> None: self.settings = settings self.engine = load_engine(settings) self._semantic_preflight_handlers = { "sketch_workplane": self._preflight_sketch_workplane, "profile_non_self_intersecting": self._preflight_profile_non_self_intersecting, "host_face_exists": self._preflight_host_face_exists, "hole_positions_on_host_plane": self._preflight_hole_positions_on_host_plane, "cut_exit_distance": self._preflight_cut_exit_distance, "requires_active_solid": self._preflight_requires_active_solid, "revolve_axis_on_sketch": self._preflight_revolve_axis_on_sketch, "reference_plane_nonzero_normal": self._preflight_reference_plane_nonzero_normal, "reference_axis_nonzero_direction": self._preflight_reference_axis_nonzero_direction, "selected_edges_exist": self._preflight_selected_edges_exist, "source_features_exist": self._preflight_source_features_exist, "mirror_plane_exists": self._preflight_mirror_plane_exists, } schema_path = Path(str(self.engine.__file__)).with_name("profile_schema.json") try: self._profile = json.loads(schema_path.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError) as error: raise RuntimeAdapterError("RUNTIME_CONTRACT_INVALID: profile schema is unavailable") from error self._contracts = self._profile.get("operation_contracts") if not isinstance(self._contracts, dict): raise RuntimeAdapterError("RUNTIME_CONTRACT_INVALID: profile has no v3 operation contracts") declared = {str(item) for item in self._contracts} registered = {str(item) for item in getattr(self.engine, "SUPPORTED_ATOMIC_IDS", ())} if declared != registered: raise RuntimeAdapterError("RUNTIME_CONTRACT_INVALID: runtime and profile operation registries disagree") verifier_kinds = set(default_registry().claim_kinds) for contract in self._contracts.values(): try: validate_operation_contract(contract) except OperationContractError as error: raise RuntimeAdapterError(f"RUNTIME_CONTRACT_INVALID: {error}") from error unknown_preflights = set(contract["semantic_preflight"]) - set(self._semantic_preflight_handlers) unknown_verifiers = set(contract["candidate_verifiers"]) - verifier_kinds if unknown_preflights or unknown_verifiers: raise RuntimeAdapterError( "RUNTIME_CONTRACT_INVALID: operation contract references an unavailable " f"{'preflight' if unknown_preflights else 'candidate verifier'}" ) if set(self._semantic_preflight_handlers) != SEMANTIC_PREFLIGHT_NAMES: raise RuntimeAdapterError("RUNTIME_CONTRACT_INVALID: runtime preflight registry is incomplete") def supported_atomic_ids(self) -> tuple[str, ...]: return tuple(sorted(self._contracts)) def operation_contract(self, atomic_id: str) -> dict[str, Any]: contract = self._contracts.get(atomic_id) if not isinstance(contract, dict): raise RuntimeAdapterError(f"RUNTIME_CONTRACT_INVALID: unknown operation {atomic_id}") result = deepcopy(contract) result["contract_hash"] = canonical_hash(contract) result["registry_revision"] = str(self._profile.get("schema_version") or "") return result def selector_tokens(self, topology: dict[str, Any] | None) -> dict[str, dict[str, Any]]: if not isinstance(topology, dict): return {} snapshot_id = str(topology.get("snapshot_id") or "") if not snapshot_id: return {} result: dict[str, dict[str, Any]] = {} for record in topology.get("records") or (): if not isinstance(record, dict) or not record.get("executable"): continue record_id = str(record.get("record_id") or "") kind = str(record.get("kind") or "") if not record_id or kind not in {"face", "edge", "plane", "axis", "body", "vertex"}: continue token = "sel_" + sha256(f"{snapshot_id}|{record_id}".encode("utf-8")).hexdigest()[:16] geometry = deepcopy(record.get("geometry") or {}) owners = record.get("owner_feature_ids") or [record.get("feature_id") or ""] result[token] = { "token": token, "kind": kind, "snapshot_id": snapshot_id, "selector": {"kind": kind, "stable_id": record_id, "owner_feature_id": str(owners[0] or ""), "geometry": geometry, "source": "runtime_snapshot", "snapshot_id": snapshot_id, "confidence": 1.0}, "geometry": geometry, } return result def reference_tokens(self, cdsl: dict[str, Any] | None) -> dict[str, str]: """Return opaque, head-scoped feature references for pattern contracts.""" result: dict[str, str] = {} document_hash = canonical_hash(cdsl) if isinstance(cdsl, dict) else "root" for feature in (cdsl or {}).get("features") or (): if not isinstance(feature, dict) or not isinstance(feature.get("id"), str) or not feature["id"]: continue feature_id = feature["id"] result["ref_" + sha256(f"{document_hash}|{feature_id}".encode("utf-8")).hexdigest()[:16]] = feature_id return result def materialize_fragment(self, base_cdsl: dict[str, Any] | None, fragment: dict[str, Any], contract: dict[str, Any], selector_tokens: dict[str, dict[str, Any]], reference_tokens: dict[str, str], *, require_through: bool = False) -> tuple[dict[str, Any], dict[str, Any]]: # ActionCommandHandler validates the exposed schema first, but this # adapter is also used during crash recovery. Keep the runtime boundary # self-contained so a corrupted/replayed staged payload cannot produce # a candidate merely by bypassing that handler-level validation. try: validate_operation_contract(contract) except OperationContractError as error: raise RuntimeAdapterError(f"RUNTIME_CONTRACT_INVALID: {error}") from error current = self.operation_contract(str(contract.get("atomic_id") or "")) if ( contract.get("contract_hash") != current["contract_hash"] or contract.get("registry_revision") != current["registry_revision"] ): raise RuntimeAdapterError("RUNTIME_CONTRACT_INVALID: operation contract is not the current verified registry entry") selector_shape = str((contract.get("fragment_shape") or {}).get("selector_tokens") or "forbidden") selector_kind = str((contract.get("selector_policy") or {}).get("token_kind") or "") allowed_selectors = [ token for token, value in selector_tokens.items() if selector_shape == "required" and isinstance(value, dict) and value.get("kind") == selector_kind ] errors = validate_fragment( contract, fragment, selector_tokens=allowed_selectors, reference_tokens=list(reference_tokens), ) if errors: raise RuntimeAdapterError( "RUNTIME_PRECONDITION_FAILED: fragment no longer matches the active operation schema: " + errors[0]["message"] ) materialized = deepcopy(fragment) reference = contract["reference_policy"] if reference["mode"] == "snapshot_bound": slot = str(reference["slot"]).removeprefix("params.") supplied = materialized.get("feature", {}).get("params", {}).get(slot, []) if not isinstance(supplied, list) or not all(token in reference_tokens for token in supplied): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: reference token is absent or stale") materialized["feature"]["params"][slot] = [reference_tokens[token] for token in supplied] self._semantic_preflight(materialized, contract, selector_tokens, base_cdsl, require_through=require_through) document = deepcopy(base_cdsl) if isinstance(base_cdsl, dict) else { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "agent_preflight", "geometry": {"sketches": []}, "features": [], } geometry = document.setdefault("geometry", {}) sketches = geometry.setdefault("sketches", []) if isinstance(geometry, dict) else None features = document.setdefault("features", []) if not isinstance(sketches, list) or not isinstance(features, list): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: base CDSL collections are invalid") index = len(features) + 1 feature = materialized["feature"] output = {"id": f"feature_{index:03d}", "atomic_id": contract["atomic_id"], "params": deepcopy(feature["params"]), "depends_on": [str(features[-1].get("id"))] if features else []} if contract["fragment_shape"]["sketch"] == "required": sketch_id = f"sketch_{len(sketches) + 1:03d}" sketch = materialized["sketch"] sketches.append({"id": sketch_id, "workplane": deepcopy(sketch["workplane"]), "profile": deepcopy(sketch["profile"])}) output["sketch_id"] = sketch_id selected = [selector_tokens[token]["selector"] for token in feature.get("selector_tokens", [])] slot = contract["selector_policy"]["slot"] if slot == "params.host_face": # A topology selector is authoritative while accepting the # action, but a later pattern replays this feature after its own # cut may have split the selected B-rep face. Lower the accepted # planar selector to a concrete, world-aligned host frame and # transform the author-supplied world coordinates into that # frame. The token remains in the fragment audit for provenance; # the materialized CDSL is stable under replay. output["params"]["host_face"], output["params"]["positions"] = self._materialized_hole_host_frame( selector_tokens[str(feature["selector_tokens"][0])], output["params"].get("positions"), ) elif slot == "params.mirror_plane": output["params"]["mirror_plane"] = selected[0] elif slot == "feature.selectors": output["selectors"] = selected features.append(output) try: self._validate_finite_tree(document) self._validate_materialized_runtime_types(output) validate_cdsl(document, self.engine) except Exception as error: message = str(error) # A JSON-schema failure after server materialization is a registry # drift: the author could not have supplied the missing server # field. Engine capability analysis, however, can reject a # schema-valid author sketch (for example an unresolvable analytic # contour). That is a recoverable action precondition failure, # not a deployment defect. if "CDSL engine runtime preflight failed:" in message: raise RuntimeAdapterError( "RUNTIME_PRECONDITION_FAILED: materialized fragment is not executable by the engine: " + message ) from error raise RuntimeAdapterError( "RUNTIME_CONTRACT_INVALID: materialized fragment violates the engine CDSL schema: " + message ) from error return document, {"schema_version": "cad.v3.fragment-audit.v1", "atomic_id": contract["atomic_id"], "fragment_hash": canonical_hash(fragment), "contract_hash": contract["contract_hash"], "assigned_feature_ids": [output["id"]], "assigned_sketch_ids": [output["sketch_id"]] if output.get("sketch_id") else [], "selector_snapshot_id": next(iter(selector_tokens.values()), {}).get("snapshot_id", ""), "selector_tokens": list(feature.get("selector_tokens", [])), "reference_snapshot_id": canonical_hash(reference_tokens), "reference_tokens": list(fragment.get("feature", {}).get("params", {}).get(str(reference.get("slot") or "").removeprefix("params."), [])) if reference["mode"] == "snapshot_bound" else []} def rebuild(self, cdsl: dict[str, Any], output_dir: str, task_id: str, revision_id: str) -> dict[str, Any]: root = Path(output_dir) root.mkdir(parents=True, exist_ok=True) cdsl_copy = deepcopy(cdsl) cdsl_copy["part_id"] = task_id step_path = root / "model.step" glb_path = root / "model.glb" self._write_json(root / "model.cdsl.json", cdsl_copy) try: self._validate_finite_tree(cdsl_copy) validate_cdsl(cdsl_copy, self.engine) except RuntimeAdapterError: raise except Exception as error: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: persisted CDSL failed rebuild preflight: {error}") from error try: engine_result = self.engine.run_cdsl_only(cdsl_copy, step_path) if str(engine_result.get("engine") or "") != "cdsl_only": raise RuntimeAdapterError("engine did not execute CDSL-only rebuild") preview = step_to_glb(step_path, glb_path) health = self._health(engine_result, step_path, glb_path) topology = topology_snapshot(engine_result, task_id=task_id, revision_id=revision_id, preview=preview) self._write_json(root / "model.topology.json", topology) manifest = render_checkpoint(self.settings, step_path=step_path, output_dir=root / "renders") report = {"engine_result": engine_result, "preview": preview, "health": health, "render_manifest": manifest} self._write_json(root / "rebuild-report.json", report) return {"health": health, "topology": topology, "report": report, "render_manifest": manifest, "paths": {"cdsl": "model.cdsl.json", "step": "model.step", "glb": "model.glb", "topology": "model.topology.json", "report": "rebuild-report.json", "render_manifest": "renders/render-manifest.json"}} except OSError: # Artifact writes are a recoverable infrastructure outage. Let the # application handler park the same candidate stage for replay. raise except ReviewRenderError as error: raise RuntimeAdapterError(f"RENDER_SERVICE_UNAVAILABLE: {error}") from error except Exception as error: raise RuntimeAdapterError(f"RUNTIME_EXECUTION_FAILURE: {error}") from error def _semantic_preflight(self, fragment: dict[str, Any], contract: dict[str, Any], selector_tokens: dict[str, dict[str, Any]], base_cdsl: dict[str, Any] | None, *, require_through: bool) -> None: if fragment.get("feature", {}).get("atomic_id") != contract.get("atomic_id"): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: atomic_id does not match active contract") policy = contract["selector_policy"] supplied = fragment.get("feature", {}).get("selector_tokens", []) if contract["fragment_shape"]["selector_tokens"] == "required": if not all(token in selector_tokens and selector_tokens[token]["kind"] == policy["token_kind"] for token in supplied): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: selector token is absent, stale, or has the wrong kind") for name in contract["semantic_preflight"]: self._semantic_preflight_handlers[name](fragment, selector_tokens, base_cdsl, require_through) def _preflight_sketch_workplane(self, fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: sketch = fragment.get("sketch") workplane = sketch.get("workplane") if isinstance(sketch, dict) else None if not isinstance(workplane, dict): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: sketch workplane is unavailable") normal, x_dir = workplane.get("normal"), workplane.get("x_dir") if not isinstance(normal, list) or not isinstance(x_dir, list) or self._norm(normal) <= 1e-9 or self._norm(x_dir) <= 1e-9 or self._norm(self._cross(normal, x_dir)) <= 1e-9: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: sketch workplane vectors are degenerate") def _preflight_profile_non_self_intersecting(self, fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: sketch = fragment.get("sketch") profile = sketch.get("profile") if isinstance(sketch, dict) else None if not isinstance(profile, dict): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: sketch profile is unavailable") if profile.get("type") == "polygon": vertices = profile.get("vertices") if not isinstance(vertices, list) or len(vertices) < 3 or self._polygon_self_intersects(vertices): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: polygon profile self-intersects") elif profile.get("type") == "analytic_contours": self._preflight_analytic_contours(profile) def _preflight_host_face_exists(self, fragment: dict[str, Any], selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: supplied = fragment.get("feature", {}).get("selector_tokens", []) if len(supplied) != 1 or not isinstance(selectors.get(supplied[0]), dict) or selectors[supplied[0]].get("kind") != "face": raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: host face is absent or stale") def _materialized_hole_host_frame( self, selected: dict[str, Any], positions: Any, ) -> tuple[dict[str, Any], list[dict[str, list[float]]]]: """Lower a verified planar host selector into replay-stable CDSL data. Hole position inputs are world coordinates at the author boundary. The engine's frame form uses local coordinates, so both values must be converted together. Keeping only the selector makes a later pattern replay depend on a face that the source cut has already subdivided, which is neither stable nor geometrically meaningful. """ geometry = selected.get("geometry") if isinstance(selected, dict) else None center = geometry.get("center_mm") if isinstance(geometry, dict) else None normal = geometry.get("normal") if isinstance(geometry, dict) else None if not self._valid_vector3(center) or not self._valid_vector3(normal, require_nonzero=True): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: selected host face has no usable plane frame") if not isinstance(positions, list) or not positions: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: hole positions are unavailable for host-frame materialization") origin = [float(value) for value in center] unit_normal = [float(value) / self._norm(normal) for value in normal] seed = [1.0, 0.0, 0.0] if abs(unit_normal[0]) < 0.9 else [0.0, 1.0, 0.0] x_raw = [seed[index] - self._dot(seed, unit_normal) * unit_normal[index] for index in range(3)] x_length = self._norm(x_raw) if x_length <= 1e-9: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: selected host face cannot define a stable x direction") x_dir = [value / x_length for value in x_raw] y_dir = self._cross(unit_normal, x_dir) local_positions: list[dict[str, list[float]]] = [] for position in positions: point = position.get("mm") if isinstance(position, dict) else None if not self._valid_vector3(point): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: hole position is not a finite 3D point") offset = [float(point[index]) - origin[index] for index in range(3)] local_positions.append({"mm": [ self._dot(offset, x_dir), self._dot(offset, y_dir), self._dot(offset, unit_normal), ]}) return ( {"frame": {"origin_mm": origin, "x_dir": x_dir, "y_dir": y_dir, "normal": unit_normal}}, local_positions, ) def _preflight_hole_positions_on_host_plane(self, fragment: dict[str, Any], selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: positions = fragment.get("feature", {}).get("params", {}).get("positions") if not isinstance(positions, list) or not positions or not all(isinstance(item, dict) and isinstance(item.get("mm"), list) and len(item["mm"]) == 3 for item in positions): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: hole positions are invalid for the host plane") supplied = fragment.get("feature", {}).get("selector_tokens", []) host = selectors.get(supplied[0]) if len(supplied) == 1 else None geometry = host.get("geometry") if isinstance(host, dict) else None normal = geometry.get("normal") if isinstance(geometry, dict) else None center = geometry.get("center_mm") if isinstance(geometry, dict) else None bbox = geometry.get("bbox_mm") if isinstance(geometry, dict) else None if not isinstance(geometry, dict) or geometry.get("surface_type") != "plane" or not self._valid_vector3(normal, require_nonzero=True) or not self._valid_vector3(center): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: hole host must be an observable planar face") unit_normal = [float(component) / self._norm(normal) for component in normal] tolerance = 1e-5 if not self._valid_bbox(bbox): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: host face has no usable topology bounds") for position in positions: point = position["mm"] if not self._valid_vector3(point): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: hole position is not a finite 3D point") offset = [float(point[index]) - float(center[index]) for index in range(3)] if abs(self._dot(offset, unit_normal)) > tolerance: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: hole position is not on the selected host plane") if any(float(point[index]) < float(bbox[index]) - tolerance or float(point[index]) > float(bbox[index + 3]) + tolerance for index in range(3)): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: hole position is outside the selected host-face bounds") boundary_loops = geometry.get("boundary_loops_mm") if isinstance(boundary_loops, list) and boundary_loops and not self._point_in_planar_face(point, boundary_loops, unit_normal, tolerance): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: hole position is outside the selected host face's effective boundary") def _preflight_analytic_contours(self, profile: dict[str, Any]) -> None: contours = profile.get("contours") if not isinstance(contours, list) or not contours: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: analytic profile requires at least one contour") tolerance = 1e-7 for contour_index, contour in enumerate(contours): segments = contour.get("segments") if isinstance(contour, dict) else None if not isinstance(segments, list) or not segments: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} is empty") if any(segment.get("type") == "circle" for segment in segments if isinstance(segment, dict)): if len(segments) != 1 or segments[0].get("type") != "circle": raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} cannot mix a full circle with other segments") continue endpoints: list[tuple[tuple[float, float], tuple[float, float]]] = [] for segment_index, segment in enumerate(segments): if not isinstance(segment, dict) or segment.get("type") not in {"line", "arc"}: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} has an unsupported segment") start = self._point2(segment.get("start")) end = self._point2(segment.get("end")) if start is None or end is None: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} segment {segment_index} has non-finite endpoints") if self._distance2(start, end) <= tolerance: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} segment {segment_index} is degenerate") if segment.get("type") == "arc": center = self._point2(segment.get("center")) radius = segment.get("radius_mm") if center is None or not isinstance(radius, (int, float)) or isinstance(radius, bool) or not math.isfinite(float(radius)) or float(radius) <= 0: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} arc {segment_index} has an invalid circle") arc_tolerance = max(tolerance, float(radius) * 1e-7) if abs(self._distance2(start, center) - float(radius)) > arc_tolerance or abs(self._distance2(end, center) - float(radius)) > arc_tolerance: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} arc {segment_index} endpoints are not on the declared circle") endpoints.append((start, end)) for segment_index in range(1, len(endpoints)): if self._distance2(endpoints[segment_index - 1][1], endpoints[segment_index][0]) > tolerance: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} is discontinuous before segment {segment_index}") if contour.get("closed") is True and self._distance2(endpoints[-1][1], endpoints[0][0]) > tolerance: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} is not closed") line_segments = [segment for segment, raw in zip(endpoints, segments) if raw.get("type") == "line"] for first, (a, b) in enumerate(line_segments): for second, (c, d) in enumerate(line_segments): if second <= first + 1 or (first == 0 and second == len(line_segments) - 1 and contour.get("closed") is True): continue if self._segments_intersect(a, b, c, d): raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: analytic contour {contour_index} has an obvious self-intersection") @classmethod def _validate_materialized_runtime_types(cls, feature: dict[str, Any]) -> None: atomic_id = str(feature.get("atomic_id") or "") if not atomic_id.startswith("hole_"): return params = feature.get("params") if isinstance(feature.get("params"), dict) else {} host = params.get("host_face") if isinstance(params.get("host_face"), dict) else None frame = host.get("frame") if isinstance(host, dict) and isinstance(host.get("frame"), dict) else None if not isinstance(frame, dict): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: materialized hole host_face.frame is unavailable") for field in ("origin_mm", "x_dir", "y_dir", "normal"): if not cls._valid_vector3(frame.get(field), require_nonzero=field != "origin_mm"): raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: materialized hole host_face.frame.{field} is invalid") x_dir, y_dir, normal = frame["x_dir"], frame["y_dir"], frame["normal"] if abs(cls._dot(x_dir, y_dir)) > 1e-6 or abs(cls._dot(x_dir, normal)) > 1e-6 or abs(cls._dot(y_dir, normal)) > 1e-6: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: materialized hole host frame is not orthogonal") positions = params.get("positions") if not isinstance(positions, list) or not positions: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: materialized hole positions are unavailable") for index, position in enumerate(positions): point = position.get("mm") if isinstance(position, dict) else None if not cls._valid_vector3(point) or abs(float(point[2])) > 1e-5: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: materialized hole position {index} is invalid in the host frame") @staticmethod def _validate_finite_tree(value: Any, path: str = "$") -> None: if value is None: raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: {path} must not be null") if isinstance(value, float) and not math.isfinite(value): raise RuntimeAdapterError(f"RUNTIME_PRECONDITION_FAILED: {path} must be finite") if isinstance(value, dict): for key, child in value.items(): ProfileCadRuntime._validate_finite_tree(child, f"{path}.{key}") elif isinstance(value, list): for index, child in enumerate(value): ProfileCadRuntime._validate_finite_tree(child, f"{path}[{index}]") @staticmethod def _point2(value: Any) -> tuple[float, float] | None: if not isinstance(value, list) or len(value) != 2: return None if not all(isinstance(item, (int, float)) and not isinstance(item, bool) and math.isfinite(float(item)) for item in value): return None return float(value[0]), float(value[1]) @staticmethod def _distance2(left: tuple[float, float], right: tuple[float, float]) -> float: return math.hypot(left[0] - right[0], left[1] - right[1]) @classmethod def _point_in_planar_face(cls, point: list[float], loops: list[Any], normal: list[float], tolerance: float) -> bool: drop_axis = max(range(3), key=lambda index: abs(float(normal[index]))) def project(value: Any) -> tuple[float, float] | None: if not cls._valid_vector3(value): return None coordinates = [float(value[index]) for index in range(3) if index != drop_axis] return coordinates[0], coordinates[1] projected_point = project(point) if projected_point is None: return False polygons: list[list[tuple[float, float]]] = [] for loop in loops: polygon = [project(vertex) for vertex in loop] if isinstance(loop, list) else [] if len(polygon) >= 3 and all(vertex is not None for vertex in polygon): polygons.append([vertex for vertex in polygon if vertex is not None]) if not polygons: return False def area(polygon: list[tuple[float, float]]) -> float: return abs(sum( polygon[index][0] * polygon[(index + 1) % len(polygon)][1] - polygon[(index + 1) % len(polygon)][0] * polygon[index][1] for index in range(len(polygon)) )) / 2 def contains(polygon: list[tuple[float, float]]) -> bool: x, y = projected_point inside = False for index, first in enumerate(polygon): second = polygon[(index + 1) % len(polygon)] if cls._distance_to_segment_2d(projected_point, first, second) <= tolerance: return True if (first[1] > y) != (second[1] > y): crossing_x = (second[0] - first[0]) * (y - first[1]) / (second[1] - first[1]) + first[0] if x < crossing_x: inside = not inside return inside outer = max(polygons, key=area) return contains(outer) and not any(contains(inner) for inner in polygons if inner is not outer) @staticmethod def _distance_to_segment_2d(point: tuple[float, float], start: tuple[float, float], end: tuple[float, float]) -> float: dx, dy = end[0] - start[0], end[1] - start[1] length_squared = dx * dx + dy * dy if length_squared <= 1e-18: return math.hypot(point[0] - start[0], point[1] - start[1]) fraction = max(0.0, min(1.0, ((point[0] - start[0]) * dx + (point[1] - start[1]) * dy) / length_squared)) return math.hypot(point[0] - (start[0] + fraction * dx), point[1] - (start[1] + fraction * dy)) def _preflight_cut_exit_distance(self, fragment: dict[str, Any], selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, require_through: bool) -> None: if not require_through: return params = fragment.get("feature", {}).get("params", {}) depth = params.get("depth_mm", params.get("distance_mm")) supplied = fragment.get("feature", {}).get("selector_tokens", []) if not isinstance(depth, (int, float)): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: cut depth is unavailable") if supplied: host = selectors[supplied[0]]["geometry"] bbox = host.get("bbox_mm") if isinstance(host, dict) else None normal = host.get("normal") or host.get("plane_normal") if isinstance(host, dict) else None thickness = self._span_from_bbox(bbox, normal) else: bbox = self._active_body_bbox(selectors) normal = (fragment.get("sketch") or {}).get("workplane", {}).get("normal") thickness = self._span_from_bbox(bbox, normal) if thickness is None: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: cannot prove through-cut exit distance from the active topology") if float(depth) <= thickness + 1e-6: raise RuntimeAdapterError( "RUNTIME_PRECONDITION_FAILED: " f"cut depth {float(depth):g} mm must exceed measured host-body thickness {thickness:g} mm" ) def _preflight_requires_active_solid(self, _fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], base: dict[str, Any] | None, _require_through: bool) -> None: # A committed active CDSL document exists only after a candidate with # a solid has been accepted. Reference and subtraction operations # therefore cannot be the first feature of a part. if not isinstance(base, dict) or not base.get("features"): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: operation requires an active solid checkpoint") def _preflight_revolve_axis_on_sketch(self, fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: axis = fragment.get("feature", {}).get("params", {}).get("axis") direction = axis.get("direction") if isinstance(axis, dict) else None origin = axis.get("origin_mm") if isinstance(axis, dict) else None workplane = fragment.get("sketch", {}).get("workplane") if isinstance(fragment.get("sketch"), dict) else None plane_origin = workplane.get("origin_mm") if isinstance(workplane, dict) else None plane_normal = workplane.get("normal") if isinstance(workplane, dict) else None if not self._valid_vector3(direction, require_nonzero=True) or not self._valid_vector3(origin) or not self._valid_vector3(plane_origin) or not self._valid_vector3(plane_normal, require_nonzero=True): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: revolve axis or sketch plane is invalid") unit_direction = [float(component) / self._norm(direction) for component in direction] unit_normal = [float(component) / self._norm(plane_normal) for component in plane_normal] if abs(self._dot(unit_direction, unit_normal)) > 1e-6: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: revolve axis is not parallel to the sketch plane") axis_offset = [float(origin[index]) - float(plane_origin[index]) for index in range(3)] if abs(self._dot(axis_offset, unit_normal)) > 1e-5: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: revolve axis is not on the sketch plane") def _preflight_reference_plane_nonzero_normal(self, fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: plane = fragment.get("feature", {}).get("params", {}).get("plane") normal = plane.get("normal") if isinstance(plane, dict) else None if not isinstance(normal, list) or self._norm(normal) <= 1e-9: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: reference plane normal is degenerate") def _preflight_reference_axis_nonzero_direction(self, fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: axis = fragment.get("feature", {}).get("params", {}).get("axis") direction = axis.get("direction") if isinstance(axis, dict) else None if not isinstance(direction, list) or self._norm(direction) <= 1e-9: raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: reference axis direction is degenerate") def _preflight_selected_edges_exist(self, fragment: dict[str, Any], selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: supplied = fragment.get("feature", {}).get("selector_tokens", []) if not supplied or not all(isinstance(selectors.get(token), dict) and selectors[token].get("kind") == "edge" for token in supplied): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: selected edge is absent or stale") def _preflight_source_features_exist(self, fragment: dict[str, Any], _selectors: dict[str, dict[str, Any]], base: dict[str, Any] | None, _require_through: bool) -> None: params = fragment.get("feature", {}).get("params", {}) known = {str(feature.get("id") or "") for feature in ((base or {}).get("features") or ()) if isinstance(feature, dict)} if not set(params.get("source_feature_ids") or ()).issubset(known): raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: source feature is not part of current head") def _preflight_mirror_plane_exists(self, fragment: dict[str, Any], selectors: dict[str, dict[str, Any]], _base: dict[str, Any] | None, _require_through: bool) -> None: supplied = fragment.get("feature", {}).get("selector_tokens", []) if len(supplied) != 1 or not isinstance(selectors.get(supplied[0]), dict) or selectors[supplied[0]].get("kind") != "plane": raise RuntimeAdapterError("RUNTIME_PRECONDITION_FAILED: mirror plane is absent or stale") @staticmethod def _polygon_self_intersects(vertices: list[Any]) -> bool: points = [(float(point[0]), float(point[1])) for point in vertices if isinstance(point, list) and len(point) == 2] if len(points) != len(vertices): return True segments = list(zip(points, [*points[1:], points[0]])) for first, (a, b) in enumerate(segments): for second, (c, d) in enumerate(segments): if second <= first + 1 or (first == 0 and second == len(segments) - 1): continue if ProfileCadRuntime._segments_intersect(a, b, c, d): return True return False @staticmethod def _segments_intersect(a: tuple[float, float], b: tuple[float, float], c: tuple[float, float], d: tuple[float, float]) -> bool: def orientation(p: tuple[float, float], q: tuple[float, float], r: tuple[float, float]) -> float: return (q[0] - p[0]) * (r[1] - p[1]) - (q[1] - p[1]) * (r[0] - p[0]) left = orientation(a, b, c) right = orientation(a, b, d) low = orientation(c, d, a) high = orientation(c, d, b) return (left > 0) != (right > 0) and (low > 0) != (high > 0) @staticmethod def _active_body_bbox(selector_tokens: dict[str, dict[str, Any]]) -> list[float] | None: for token in selector_tokens.values(): if token.get("kind") != "body": continue geometry = token.get("geometry") bbox = geometry.get("bbox_mm") if isinstance(geometry, dict) else None if isinstance(bbox, list) and len(bbox) == 6 and all(isinstance(value, (int, float)) for value in bbox): return [float(value) for value in bbox] return None @staticmethod def _span_from_bbox(bbox: Any, normal: Any) -> float | None: if not isinstance(bbox, list) or len(bbox) != 6 or not isinstance(normal, list) or len(normal) != 3: return None if not all(isinstance(value, (int, float)) for value in [*bbox, *normal]): return None length = math.sqrt(sum(float(value) ** 2 for value in normal)) if length <= 1e-9: return None return sum( abs(float(normal[index]) / length) * abs(float(bbox[index + 3]) - float(bbox[index])) for index in range(3) ) @staticmethod def _norm(vector: list[float]) -> float: return math.sqrt(sum(float(item) ** 2 for item in vector)) @staticmethod def _dot(left: list[float], right: list[float]) -> float: return sum(float(left[index]) * float(right[index]) for index in range(3)) @staticmethod def _valid_vector3(value: Any, *, require_nonzero: bool = False) -> bool: valid = ( isinstance(value, list) and len(value) == 3 and all(isinstance(item, (int, float)) and not isinstance(item, bool) and math.isfinite(float(item)) for item in value) ) return valid and (not require_nonzero or ProfileCadRuntime._norm(value) > 1e-9) @staticmethod def _valid_bbox(value: Any) -> bool: return ( isinstance(value, list) and len(value) == 6 and all(isinstance(item, (int, float)) and not isinstance(item, bool) and math.isfinite(float(item)) for item in value) and all(float(value[index]) <= float(value[index + 3]) for index in range(3)) ) @staticmethod def _cross(a: list[float], b: list[float]) -> list[float]: return [float(a[1]) * float(b[2]) - float(a[2]) * float(b[1]), float(a[2]) * float(b[0]) - float(a[0]) * float(b[2]), float(a[0]) * float(b[1]) - float(a[1]) * float(b[0])] @staticmethod def _write_json(path: Path, payload: dict[str, Any]) -> None: path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8") @staticmethod def _health(engine_result: dict[str, Any], step_path: Path, glb_path: Path) -> dict[str, Any]: bbox = engine_result.get("bbox_mm") if isinstance(engine_result.get("bbox_mm"), dict) else {} minimum, maximum = bbox.get("min"), bbox.get("max") if not isinstance(minimum, list) or not isinstance(maximum, list) or len(minimum) != 3 or len(maximum) != 3 or not step_path.is_file() or not glb_path.is_file(): raise RuntimeAdapterError("rebuild did not produce complete deterministic artifacts") return {"bbox_mm": {"min": [float(item) for item in minimum], "max": [float(item) for item in maximum], "dimensions": [float(maximum[index]) - float(minimum[index]) for index in range(3)]}, "volume_mm3": float(engine_result["volume_mm3"]), "solid_count": int(engine_result["solid_count"]), "feature_count": len(engine_result.get("feature_results") or [])}