from __future__ import annotations import hashlib import importlib.util import json import math import sys import tempfile import unittest from copy import deepcopy from pathlib import Path ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT / "backend" / "engine")) from cdsl_engine.batch_rebuild import _failure_category, _verification_classification, batch_analyze # noqa: E402 from cdsl_engine.capabilities import CapabilityAnalyzer # noqa: E402 from cdsl_engine.runtime_types import ( # noqa: E402 BendSpec, HoleSpec, PlaneSpec, TopologyDelta, TopologyDeltaRelation, TopologyRecord, TopologyRegistry, ) from cdsl_engine.sketch_solver import SHAPE_GENERATORS, resolve_all_sketches # noqa: E402 def _workplane() -> dict: return {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 0, 1]} def _rectangle(minimum: list[float], maximum: list[float]) -> dict: return {"type": "polygon", "vertices": [ [minimum[0], minimum[1]], [maximum[0], minimum[1]], [maximum[0], maximum[1]], [minimum[0], maximum[1]], ]} def _two_body_boolean_cdsl(operation: str, left: dict, right: dict) -> dict: return { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": f"boolean-{operation}", "meta": {"unit": "mm"}, "geometry": {"sketches": [ {"id": "left", "workplane": _workplane(), "profile": left}, {"id": "right", "workplane": _workplane(), "profile": right}, ]}, "features": [ { "id": "left_body", "atomic_id": "extrude_add_blind", "depends_on": [], "sketch_id": "left", "params": {"distance_mm": 4, "result_mode": "new_body"}, }, { "id": "right_body", "atomic_id": "extrude_add_blind", "depends_on": ["left_body"], "sketch_id": "right", "params": {"distance_mm": 4, "result_mode": "new_body"}, }, { "id": "boolean", "atomic_id": "boolean_bodies", "depends_on": ["left_body", "right_body"], "params": { "operation": operation, "target_feature_ids": ["left_body"], "tool_feature_ids": ["right_body"], "keep_tools": False, }, }, ], } def _selector_digest(selectors: list[str]) -> str: return hashlib.sha256("\n".join(selectors).encode("utf-8")).hexdigest() def _corpus_manifest(source: Path) -> dict: return json.loads((source / "corpus-manifest.json").read_text(encoding="utf-8")) class EngineRuntimeFoundationTests(unittest.TestCase): def _base_block(self) -> dict: return { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "runtime-block", "meta": {"unit": "mm"}, "geometry": {"sketches": [{ "id": "base", "workplane": _workplane(), "profile": _rectangle([-5, -5], [5, 5]), }]}, "features": [{ "id": "base_add", "atomic_id": "extrude_add_blind", "depends_on": [], "params": {"distance_mm": 10}, "sketch_id": "base", }], } def test_runtime_module_has_no_build123d_import(self) -> None: runtime_source = (ROOT / "backend" / "engine" / "cdsl_engine" / "runtime.py").read_text(encoding="utf-8") self.assertNotIn("from build123d", runtime_source) self.assertNotIn("import build123d", runtime_source) def test_execution_session_declares_a_kernel_neutral_adapter_protocol(self) -> None: from cdsl_engine.runtime import ExecutionSession, GeometryAdapter self.assertIn("adapter", ExecutionSession.__dataclass_fields__) self.assertTrue(getattr(GeometryAdapter, "_is_protocol", False)) self.assertIn("export", GeometryAdapter.__dict__) def test_missing_bend_generator_does_not_block_engine_import(self) -> None: if importlib.util.find_spec("cdsl_engine.parametric_bend") is not None: self.skipTest("bend generator is installed") from cdsl_engine.build123d_adapter import Build123dGeometryAdapter from cdsl_engine.runtime import rebuild_cdsl self.assertTrue(callable(rebuild_cdsl)) spec = BendSpec.from_feature({ "thickness_mm": 1, "width_mm": 10, "chain": [{"leg_mm": 10}], }) with self.assertRaisesRegex(RuntimeError, "bend_add requires cdsl_engine.parametric_bend.build_bend_solid"): Build123dGeometryAdapter.bend_solid(spec) def test_hole_spec_normalizes_wizard_subtypes_without_occ_dependencies(self) -> None: spec = HoleSpec.from_feature("hole_wizard", { "diameter_mm": 2, "depth_mm": 6, "end_condition": {"type": "blind", "solidworks_code": 0}, "positions": [{"mm": [1, 2, 3]}], "countersink": {"diameter_mm": 4, "angle_rad": 1.5707963267948966}, }, wizard=True) self.assertEqual(spec.positions_mm, ((1.0, 2.0, 3.0),)) self.assertEqual(spec.countersink, (4.0, 1.5707963267948966)) with self.assertRaisesRegex(ValueError, "larger than the main hole"): HoleSpec.from_feature("hole_wizard", { "diameter_mm": 2, "depth_mm": 6, "end_condition": {"type": "blind", "solidworks_code": 0}, "positions": [{"mm": [0, 0, 0]}], "counterbore": {"diameter_mm": 2, "depth_mm": 1}, }, wizard=True) def test_analytic_contours_create_a_region_with_hole(self) -> None: cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "two-point-spline", "meta": {"unit": "mm"}, "geometry": {"sketches": [{ "id": "sketch", "workplane": _workplane(), "profile": {"type": "analytic_contours", "contours": [ {"role": "outer", "closed": True, "segments": [ {"type": "line", "start": [0, 0], "end": [10, 0]}, {"type": "line", "start": [10, 0], "end": [10, 10]}, {"type": "line", "start": [10, 10], "end": [0, 10]}, {"type": "line", "start": [0, 10], "end": [0, 0]}, ]}, {"role": "inner", "closed": True, "segments": [ {"type": "circle", "center": [5, 5], "radius_mm": 2}, ]}, ]}, }]}, "features": [{ "id": "two_point_add", "atomic_id": "extrude_add_blind", "depends_on": [], "sketch_id": "two-point-spline", "params": {"distance_mm": 1}, }], } sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0] region = sketch["contour_regions_mm"][0] self.assertEqual(len(region["outer"]), 4) self.assertEqual(len(region["holes"]), 1) self.assertEqual(len(region["holes"][0]), 4) def test_analytic_contours_normalize_disjoint_inner_roles(self) -> None: cdsl = { "schema": "cad.cdsl.llm.v1", "geometry": {"sketches": [{ "id": "sketch", "workplane": _workplane(), "profile": {"type": "analytic_contours", "contours": [ {"role": "outer", "closed": True, "segments": [ {"type": "line", "start": [0, 0], "end": [2, 0]}, {"type": "line", "start": [2, 0], "end": [2, 2]}, {"type": "line", "start": [2, 2], "end": [0, 2]}, {"type": "line", "start": [0, 2], "end": [0, 0]}, ]}, {"role": "inner", "closed": True, "segments": [ {"type": "line", "start": [4, 0], "end": [6, 0]}, {"type": "line", "start": [6, 0], "end": [6, 2]}, {"type": "line", "start": [6, 2], "end": [4, 2]}, {"type": "line", "start": [4, 2], "end": [4, 0]}, ]}, ]}, }]}, "features": [], } sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0] self.assertEqual(len(sketch["contour_regions_mm"]), 2) self.assertTrue(all(not region["holes"] for region in sketch["contour_regions_mm"])) def test_analytic_circle_contours_preserve_single_circular_wire_edges(self) -> None: from cdsl_engine.build123d_adapter import Build123dGeometryAdapter cdsl = { "schema": "cad.cdsl.llm.v1", "geometry": {"sketches": [{ "id": "sketch", "workplane": _workplane(), "profile": {"type": "analytic_contours", "contours": [ {"role": "outer", "closed": True, "segments": [ {"type": "circle", "center": [-10, 0], "radius_mm": 2}, ]}, {"role": "outer", "closed": True, "segments": [ {"type": "circle", "center": [10, 0], "radius_mm": 2}, ]}, ]}, }]}, "features": [], } sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0] faces = Build123dGeometryAdapter().faces_for_sketch(sketch) self.assertEqual(len(faces), 2) self.assertTrue(all(len(face.outer_wire().edges()) == 1 for face in faces)) def test_planar_imprint_selects_an_exact_bounded_split_region(self) -> None: from cdsl_engine.build123d_adapter import Build123dGeometryAdapter from cdsl_engine.runtime import rebuild_cdsl from cdsl_engine.semantic_validation import validate_semantic_cdsl def line(identifier: str, start: list[float], end: list[float]) -> dict: return {"id": identifier, "curve": {"type": "line", "start": start, "end": end}} profile = { "type": "planar_imprint", "source_entities": [ line("bottom", [-5, -5], [5, -5]), line("right", [5, -5], [5, 5]), line("top", [5, 5], [-5, 5]), line("left", [-5, 5], [-5, -5]), line("cut", [-5, 0], [5, 0]), line("divider", [0, -5], [0, 5]), ], "selections": [{ "source_entity_id": "cut", "face_side": 1, "fragment": {"anchor_entity_id": "divider", "side": -1, "intersection_index": 0}, }], } cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "planar-imprint", "meta": {"unit": "mm"}, "geometry": {"sketches": [{"id": "imprint", "workplane": _workplane(), "profile": profile}]}, "features": [{ "id": "imprint_add", "atomic_id": "extrude_add_blind", "depends_on": [], "sketch_id": "imprint", "params": {"distance_mm": 2}, "execution_status": "supported", }], } validate_semantic_cdsl(cdsl) sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0] faces = Build123dGeometryAdapter().faces_for_sketch(sketch) self.assertEqual(len(faces), 1) self.assertAlmostEqual(faces[0].area, 25.0, places=6) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "imprint.step") self.assertAlmostEqual(float(result["volume_mm3"]), 50.0, places=6) def test_planar_imprint_bare_source_collects_its_bounded_split_faces(self) -> None: from cdsl_engine.build123d_adapter import Build123dGeometryAdapter profile = { "type": "planar_imprint", "source_entities": [ {"id": "bottom", "curve": {"type": "line", "start": [-5, -5], "end": [5, -5]}}, {"id": "right", "curve": {"type": "line", "start": [5, -5], "end": [5, 5]}}, {"id": "top", "curve": {"type": "line", "start": [5, 5], "end": [-5, 5]}}, {"id": "left", "curve": {"type": "line", "start": [-5, 5], "end": [-5, -5]}}, {"id": "divider", "curve": {"type": "line", "start": [0, -5], "end": [0, 5]}}, ], "selections": [{"source_entity_id": "top", "face_side": 1}], } sketch = resolve_all_sketches({"geometry": {"sketches": [{ "id": "imprint", "workplane": _workplane(), "profile": profile, }]}})["geometry"]["sketches"][0] faces = Build123dGeometryAdapter().faces_for_sketch(sketch) self.assertEqual(len(faces), 2) self.assertAlmostEqual(sum(face.area for face in faces), 100.0, places=6) def test_planar_imprint_preserves_a_logical_circle_source_through_a_split(self) -> None: from cdsl_engine.build123d_adapter import Build123dGeometryAdapter from cdsl_engine.runtime import rebuild_cdsl profile = { "type": "planar_imprint", "source_entities": [ {"id": "circle", "curve": {"type": "circle", "center": [0, 0], "radius_mm": 10}}, {"id": "divider", "curve": {"type": "line", "start": [-10, 0], "end": [10, 0]}}, ], "selections": [{ "source_entity_id": "circle", "face_side": 1, "fragment": {"anchor_entity_id": "divider", "side": -1, "intersection_index": 0}, }], } cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "circle-imprint", "meta": {"unit": "mm"}, "geometry": {"sketches": [{"id": "imprint", "workplane": _workplane(), "profile": profile}]}, "features": [{ "id": "imprint_add", "atomic_id": "extrude_add_blind", "depends_on": [], "sketch_id": "imprint", "params": {"distance_mm": 2}, "execution_status": "supported", }], } sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0] circle = next(item for item in sketch["imprint_entities_mm"] if item["id"] == "circle") self.assertEqual([edge["type"] for edge in circle["edges"]], ["circle"]) faces = Build123dGeometryAdapter().faces_for_sketch(sketch) self.assertEqual(len(faces), 1) self.assertAlmostEqual(faces[0].area, math.pi * 50.0, places=6) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "circle-imprint.step") self.assertAlmostEqual(float(result["volume_mm3"]), math.pi * 100.0, places=6) def test_planar_imprint_rejects_a_near_miss_fragment_intersection(self) -> None: from cdsl_engine.build123d_adapter import Build123dGeometryAdapter sketch = { "id": "near-miss", "workplane": _workplane(), "profile": { "type": "planar_imprint", "source_entities": [ {"id": "circle", "curve": {"type": "circle", "center": [0, 0], "radius_mm": 10}}, {"id": "nearby", "curve": {"type": "line", "start": [10.001, -5], "end": [10.001, 5]}}, ], "selections": [{ "source_entity_id": "circle", "face_side": 1, "fragment": {"anchor_entity_id": "nearby", "side": -1, "intersection_index": 0}, }], }, } resolved = resolve_all_sketches({"geometry": {"sketches": [sketch]}})["geometry"]["sketches"][0] with self.assertRaisesRegex(ValueError, "source and anchor do not intersect"): Build123dGeometryAdapter().faces_for_sketch(resolved) def test_planar_imprint_rejects_an_unbounded_arrangement_region(self) -> None: from cdsl_engine.build123d_adapter import Build123dGeometryAdapter sketch = { "id": "unbounded", "workplane": _workplane(), "profile": { "type": "planar_imprint", "source_entities": [ {"id": "cut", "curve": {"type": "line", "start": [-5, 0], "end": [5, 0]}}, {"id": "divider", "curve": {"type": "line", "start": [0, -5], "end": [0, 5]}}, ], "selections": [{ "source_entity_id": "cut", "face_side": 1, "fragment": {"anchor_entity_id": "divider", "side": -1, "intersection_index": 0}, }], }, } resolved = resolve_all_sketches({"geometry": {"sketches": [sketch]}})["geometry"]["sketches"][0] with self.assertRaisesRegex(ValueError, "selected region is unbounded"): Build123dGeometryAdapter().faces_for_sketch(resolved) def test_analytic_ellipse_preserves_its_workplane_orientation_and_volume(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"][0] = { "id": "ellipse", "workplane": {"origin_mm": [0, 0, 0], "x_dir": [0, 1, 0], "normal": [0, 0, 1]}, "profile": {"type": "analytic_contours", "contours": [{ "role": "outer", "closed": True, "segments": [{ "type": "ellipse", "center": [0, 0], "major_radius_mm": 5, "minor_radius_mm": 2, "major_axis": [3 / 5, 4 / 5], }], }]}, } cdsl["features"][0]["sketch_id"] = "ellipse" cdsl["features"][0]["params"] = {"distance_mm": 4} sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0] edge = sketch["contour_regions_mm"][0]["outer"][0] self.assertEqual(edge["major_axis_mm"], [-0.8, 0.6, 0.0]) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "ellipse.step") self.assertEqual(result["solid_count"], 1) self.assertAlmostEqual(result["volume_mm3"], math.pi * 5 * 2 * 4, places=5) def test_bspline_profile_preserves_endpoint_tangents(self) -> None: from cdsl_engine.build123d_adapter import Build123dGeometryAdapter cdsl = { "schema": "cad.cdsl.llm.v1", "geometry": {"sketches": [{ "id": "spline", "workplane": _workplane(), "profile": {"type": "analytic_contours", "contours": [{ "role": "outer", "closed": True, "segments": [ {"type": "line", "start": [0, 0], "end": [4, 0]}, {"type": "bspline", "start": [4, 0], "end": [0, 0], "points": [[4, 0], [4, 3], [0, 0]], "start_tangent": [0, 5], "end_tangent": [-5, 0]}, ], }]}, }]}, "features": [], } sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0] spline = next(edge for edge in sketch["contour_regions_mm"][0]["outer"] if edge["type"] == "bspline") self.assertEqual(spline["start_tangent_mm"], [0.0, 5.0, 0.0]) self.assertEqual(spline["end_tangent_mm"], [-5.0, 0.0, 0.0]) edge = Build123dGeometryAdapter()._wire([spline]).edges()[0] self.assertAlmostEqual(edge.tangent_at(0).Y, 1.0, places=6) self.assertAlmostEqual(edge.tangent_at(1).X, -1.0, places=6) def test_two_point_bspline_profile_requires_and_preserves_endpoint_tangents(self) -> None: from cdsl_engine.build123d_adapter import Build123dGeometryAdapter cdsl = { "schema": "cad.cdsl.llm.v1", "geometry": {"sketches": [{ "id": "two-point-spline", "workplane": _workplane(), "profile": {"type": "analytic_contours", "contours": [{ "role": "outer", "closed": True, "segments": [ {"type": "line", "start": [0, 0], "end": [4, 0]}, {"type": "bspline", "start": [4, 0], "end": [0, 0], "points": [[4, 0], [0, 0]], "parameters": [0, 1], "start_tangent": [0, 5], "end_tangent": [-5, 0]}, ], }]}, }]}, "features": [], } sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0] spline = next(edge for edge in sketch["contour_regions_mm"][0]["outer"] if edge["type"] == "bspline") edge = Build123dGeometryAdapter()._wire([spline]).edges()[0] self.assertAlmostEqual(edge.tangent_at(0).Y, 1.0, places=6) self.assertAlmostEqual(edge.tangent_at(1).X, -1.0, places=6) with self.assertRaisesRegex(ValueError, "parameters must be finite and strictly increasing"): Build123dGeometryAdapter()._wire([{**spline, "parameters": [0, 0]}]) coincident = deepcopy(cdsl) coincident_spline = coincident["geometry"]["sketches"][0]["profile"]["contours"][0]["segments"][1] coincident_spline["end"] = [4, 0] coincident_spline["points"][-1] = [4, 0] with self.assertRaisesRegex(ValueError, "two-point bspline endpoints must be distinct"): resolve_all_sketches(coincident) incomplete = {**cdsl, "geometry": {"sketches": [{ **cdsl["geometry"]["sketches"][0], "profile": {"type": "analytic_contours", "contours": [{ "role": "outer", "closed": True, "segments": [ {"type": "line", "start": [0, 0], "end": [4, 0]}, {"type": "bspline", "start": [4, 0], "end": [0, 0], "points": [[4, 0], [0, 0]]}, ], }]}, }]}} with self.assertRaisesRegex(ValueError, "two-point bspline requires both endpoint tangents"): resolve_all_sketches(incomplete) from cdsl_engine.semantic_validation import validate_semantic_cdsl with self.assertRaisesRegex(ValueError, "CDSL schema violation"): validate_semantic_cdsl(incomplete) def test_drafted_ellipse_exports_as_a_solid(self) -> None: from build123d import import_step from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "ellipse", "workplane": {"origin_mm": [0, 0, 10], "x_dir": [1, 0, 0], "normal": [0, 0, 1]}, "profile": {"type": "analytic_contours", "contours": [{ "role": "outer", "closed": True, "segments": [{ "type": "ellipse", "center": [0, 0], "major_radius_mm": 3, "minor_radius_mm": 2, "major_axis": [1, 0], }], }]}, }) cdsl["features"].append({ "id": "drafted_ellipse", "atomic_id": "extrude_add_blind", "depends_on": ["base_add"], "params": {"distance_mm": 4, "draft": {"angle_deg": 1, "pull_direction": False}}, "sketch_id": "ellipse", }) with tempfile.TemporaryDirectory() as directory: output = Path(directory) / "drafted-ellipse.step" result = rebuild_cdsl(cdsl, output) exported = import_step(str(output)) self.assertEqual(result["solid_count"], 1) self.assertEqual(len(exported.solids()), 1) self.assertGreater(float(exported.volume), 1000.0) def test_boolean_bodies_subtracts_explicit_new_body_sources(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "boolean-bodies", "meta": {"unit": "mm"}, "geometry": {"sketches": [ {"id": "outer", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 10}}, {"id": "inner", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 4}}, ]}, "features": [ {"id": "outer_body", "atomic_id": "extrude_add_blind", "depends_on": [], "sketch_id": "outer", "params": {"distance_mm": 10, "result_mode": "new_body"}}, {"id": "inner_body", "atomic_id": "extrude_add_blind", "depends_on": ["outer_body"], "sketch_id": "inner", "params": {"distance_mm": 10, "result_mode": "new_body"}}, {"id": "cut", "atomic_id": "boolean_bodies", "depends_on": ["outer_body", "inner_body"], "params": {"operation": "subtract", "target_feature_ids": ["outer_body"], "tool_feature_ids": ["inner_body"], "keep_tools": False}}, ], } with tempfile.TemporaryDirectory() as tmp: rebuilt = rebuild_cdsl(cdsl, Path(tmp) / "boolean.step") self.assertEqual(rebuilt["solid_count"], 1) self.assertAlmostEqual(rebuilt["volume_mm3"], math.pi * (10 ** 2 - 4 ** 2) * 10) self.assertEqual([item["feature_id"] for item in rebuilt["feature_results"]], ["outer_body", "inner_body", "cut"]) modified_cap = next( item for item in rebuilt["topology_records"] if item["feature_id"] == "cut" and item["kind"] == "face" and item["geometry"].get("surface_type") == "plane" and abs(item["geometry"].get("area_mm2", 0) - math.pi * (10 ** 2 - 4 ** 2)) < 1e-5 ) self.assertEqual(modified_cap["owner_feature_ids"], ["outer_body"]) delta = next(item for item in rebuilt["topology_deltas"] if item["operation"] == "subtract") self.assertTrue(any( item["event"] == "modified" and item["status"] == "unique_exact_continuation" for item in delta["relations"] )) def test_boolean_union_and_intersect_capture_exact_kernel_history(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cases = ( ( "union", _rectangle([-4, -2], [0, 2]), _rectangle([4, -2], [8, 2]), 128.0, 2, ), ( "intersect", _rectangle([-4, -2], [2, 2]), _rectangle([-1, -2], [5, 2]), 48.0, 1, ), ) for operation, left, right, volume, solid_count in cases: with self.subTest(operation=operation): with tempfile.TemporaryDirectory() as directory: rebuilt = rebuild_cdsl( _two_body_boolean_cdsl(operation, left, right), Path(directory) / f"{operation}.step", ) self.assertAlmostEqual(rebuilt["volume_mm3"], volume) self.assertEqual(rebuilt["solid_count"], solid_count) delta = next(item for item in rebuilt["topology_deltas"] if item["operation"] == operation) self.assertTrue(any( item["status"] == "unique_exact_continuation" for item in delta["relations"] )) self.assertTrue(any( item["owner_feature_ids"] == ["left_body"] for item in rebuilt["topology_records"] if item["feature_id"] == "boolean" and item["kind"] in {"face", "edge", "vertex"} )) def test_boolean_intersection_owner_selector_executes_downstream_fillet(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = _two_body_boolean_cdsl( "intersect", _rectangle([-4, -2], [2, 2]), _rectangle([-1, -2], [5, 2]), ) with tempfile.TemporaryDirectory() as directory: root = Path(directory) intersected = rebuild_cdsl(cdsl, root / "intersected.step") edge = next( item for item in intersected["topology_records"] if item["feature_id"] == "boolean" and item["kind"] == "edge" and item["owner_feature_ids"] == ["left_body"] ) filleted = deepcopy(cdsl) filleted["features"].append({ "id": "fillet", "atomic_id": "fillet", "depends_on": ["boolean"], "params": {"radius_mm": 0.25}, "selectors": [{ "kind": "edge", "stable_id": "intersection-left-edge", "source": "runtime_snapshot", "confidence": 1, "owner_feature_id": "left_body", "geometry": edge["geometry"], }], }) rebuilt = rebuild_cdsl(filleted, root / "filleted.step") self.assertEqual([item["feature_id"] for item in rebuilt["feature_results"]], [ "left_body", "right_body", "boolean", "fillet", ]) self.assertLess(rebuilt["volume_mm3"], intersected["volume_mm3"]) def test_single_body_dressups_capture_kernel_history_only_for_direct_builder_paths(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "base.step") edge = next(item for item in baseline["topology_records"] if item["kind"] == "edge") for atomic_id, params in (("fillet", {"radius_mm": 1}), ("chamfer", {"distance_mm": 1})): with self.subTest(atomic_id=atomic_id): dressed = deepcopy(base) dressed["features"].append({ "id": atomic_id, "atomic_id": atomic_id, "depends_on": ["base_add"], "params": params, "selectors": [{ "kind": "edge", "stable_id": "base-edge", "source": "runtime_snapshot", "confidence": 1, "owner_feature_id": "base_add", "geometry": edge["geometry"], }], }) result = rebuild_cdsl(dressed, root / f"{atomic_id}.step") delta = next(item for item in result["topology_deltas"] if item["operation"] == atomic_id) self.assertTrue(any( item["status"] == "unique_exact_continuation" for item in delta["relations"] )) self.assertTrue(any( item["event"] == "generated" and item["status"] == "recorded_without_owner_transfer" for item in delta["relations"] )) self.assertTrue(any( item["owner_feature_ids"] == ["base_add"] for item in result["topology_records"] if item["feature_id"] == atomic_id and item["kind"] in {"face", "edge"} )) angled = deepcopy(base) angled["features"].append({ "id": "angled", "atomic_id": "chamfer", "depends_on": ["base_add"], "params": {"distance_mm": 1, "distance_2_mm": 0.5}, "selectors": [{ "kind": "edge", "stable_id": "base-edge", "source": "runtime_snapshot", "confidence": 1, "owner_feature_id": "base_add", "geometry": edge["geometry"], }], }) angled_result = rebuild_cdsl(angled, root / "angled.step") self.assertFalse(any(item["operation"] == "chamfer" for item in angled_result["topology_deltas"])) def test_shell_captures_exact_kernel_history_for_downstream_selector(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "base.step") removed_face = next( item for item in baseline["topology_records"] if item["kind"] == "face" and item["feature_id"] == "base_add" and item["geometry"].get("surface_type") == "plane" and item["geometry"].get("normal", [0, 0, 0])[2] > 0.9 ) shelled = deepcopy(base) shelled["features"].append({ "id": "shell", "atomic_id": "shell", "depends_on": ["base_add"], "params": {"thickness_mm": 1, "inward": True}, "selectors": [{ "kind": "face", "stable_id": removed_face["record_id"], "snapshot_id": removed_face["record_id"], "source": "runtime_snapshot", "confidence": 1, "owner_feature_id": "base_add", "geometry": removed_face["geometry"], }], }) shell_result = rebuild_cdsl(shelled, root / "shell.step") delta = next(item for item in shell_result["topology_deltas"] if item["operation"] == "shell") continuation = next( item for item in delta["relations"] if item["kind"] == "edge" and item["status"] == "unique_exact_continuation" ) source_id = continuation["source_record_ids"][0] result_id = continuation["result_record_ids"][0] successor = next(item for item in shell_result["topology_records"] if item["record_id"] == result_id) downstream = deepcopy(shelled) downstream["features"].append({ "id": "fillet", "atomic_id": "fillet", "depends_on": ["shell"], "params": {"radius_mm": 0.25}, "selectors": [{ "kind": "edge", "stable_id": source_id, "snapshot_id": result_id, "source": "runtime_snapshot", "confidence": 1, "owner_feature_id": "base_add", "geometry": successor["geometry"], }], }) downstream_result = rebuild_cdsl(downstream, root / "shell-fillet.step") self.assertTrue(any( item["event"] == "generated" and item["status"] == "recorded_without_owner_transfer" for item in delta["relations"] )) self.assertTrue(any( item["event"] == "generated" and item.get("output_role") == "shell.offset_face" for item in delta["relations"] )) self.assertTrue(any( item.get("output_role") == "shell.closing_descendant" for item in delta["relations"] )) self.assertTrue(any( item.get("output_role") == "shell.wall" for item in delta["relations"] )) self.assertTrue(any( item["status"] == "unique_exact_continuation" for item in delta["relations"] )) self.assertEqual(successor["owner_feature_ids"], ["base_add"]) self.assertEqual([item["feature_id"] for item in downstream_result["feature_results"]], [ "base_add", "shell", "fillet", ]) self.assertEqual(downstream_result["runtime_diagnostics"], []) def test_body_transform_copy_and_explicit_delete_preserve_unselected_members(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "body-transform-delete", "meta": {"unit": "mm"}, "geometry": {"sketches": [{ "id": "source", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 2}, }]}, "features": [ {"id": "source_body", "atomic_id": "extrude_add_blind", "depends_on": [], "sketch_id": "source", "params": {"distance_mm": 5, "result_mode": "new_body"}}, {"id": "copied_body", "atomic_id": "transform_bodies", "depends_on": ["source_body"], "params": { "source_feature_ids": ["source_body"], "transform": {"type": "translation", "translation_mm": [10, 0, 0]}, "make_copy": True, }}, {"id": "remove_copy", "atomic_id": "delete_bodies", "depends_on": ["copied_body"], "params": {"target_feature_ids": ["copied_body"]}}, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "body-transform-delete.step") self.assertEqual(result["solid_count"], 1) self.assertAlmostEqual(result["volume_mm3"], math.pi * 2 ** 2 * 5) self.assertEqual(result["bbox_mm"], {"min": [-2.0, -2.0, 0.0], "max": [2.0, 2.0, 5.0]}) self.assertEqual([item["feature_id"] for item in result["feature_results"]], ["source_body", "copied_body", "remove_copy"]) def test_multi_source_transform_copy_preserves_source_qualified_members(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "multi-source-copy", "meta": {"unit": "mm"}, "geometry": {"sketches": [ {"id": "left", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 1}}, {"id": "right", "workplane": _workplane(), "profile": {"type": "circle", "center": [10, 0], "radius_mm": 1}}, ]}, "features": [ {"id": "left_body", "atomic_id": "extrude_add_blind", "depends_on": [], "sketch_id": "left", "params": {"distance_mm": 2, "result_mode": "new_body"}}, {"id": "right_body", "atomic_id": "extrude_add_blind", "depends_on": ["left_body"], "sketch_id": "right", "params": {"distance_mm": 2, "result_mode": "new_body"}}, {"id": "copy_pair", "atomic_id": "transform_bodies", "depends_on": ["left_body", "right_body"], "params": { "source_feature_ids": ["left_body", "right_body"], "transform": {"type": "translation", "translation_mm": [0, 10, 0]}, "make_copy": True, }}, {"id": "move_left_copy", "atomic_id": "transform_bodies", "depends_on": ["copy_pair"], "params": { "transform_copy_refs": [{"transform_feature_id": "copy_pair", "source_feature_id": "left_body"}], "transform": {"type": "translation", "translation_mm": [0, 0, 10]}, "make_copy": False, }}, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "multi-source-copy.step") self.assertEqual(result["solid_count"], 4) self.assertAlmostEqual(result["volume_mm3"], 4 * math.pi * 2, places=5) self.assertEqual(result["bbox_mm"], {"min": [-1.0, -1.0, 0.0], "max": [11.0, 11.0, 12.0]}) self.assertEqual([item["feature_id"] for item in result["feature_results"]], [ "left_body", "right_body", "copy_pair", "move_left_copy", ]) def test_transform_copy_reference_rejects_unselected_source(self) -> None: from cdsl_engine.semantic_validation import validate_semantic_cdsl cdsl = self._base_block() cdsl["features"][0]["params"]["result_mode"] = "new_body" cdsl["features"].extend([ {"id": "second_body", "atomic_id": "extrude_add_blind", "depends_on": ["base_add"], "sketch_id": "base", "params": {"distance_mm": 5, "result_mode": "new_body"}}, {"id": "copy_pair", "atomic_id": "transform_bodies", "depends_on": ["base_add", "second_body"], "params": { "source_feature_ids": ["base_add", "second_body"], "transform": {"type": "translation", "translation_mm": [1, 0, 0]}, "make_copy": True, }}, {"id": "move", "atomic_id": "transform_bodies", "depends_on": ["copy_pair"], "params": { "transform_copy_refs": [{"transform_feature_id": "copy_pair", "source_feature_id": "missing_body"}], "transform": {"type": "translation", "translation_mm": [1, 0, 0]}, "make_copy": False, }}, ]) for feature in cdsl["features"]: feature["execution_status"] = "supported" with self.assertRaisesRegex(ValueError, "names a source outside its transform"): validate_semantic_cdsl(cdsl) def test_body_uniform_scale_uses_its_explicit_center_and_preserves_topology_provenance(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "body-uniform-scale", "meta": {"unit": "mm"}, "geometry": {"sketches": [{ "id": "source", "workplane": _workplane(), "profile": {"type": "circle", "center": [10, 0], "radius_mm": 2}, }]}, "features": [ {"id": "source_body", "atomic_id": "extrude_add_blind", "depends_on": [], "sketch_id": "source", "params": {"distance_mm": 5, "result_mode": "new_body"}}, {"id": "scaled_body", "atomic_id": "transform_bodies", "depends_on": ["source_body"], "params": { "source_feature_ids": ["source_body"], "transform": {"type": "uniform_scale", "center_mm": [10, 0, 0], "scale_factor": 0.5}, "make_copy": False, }}, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "body-uniform-scale.step") self.assertEqual(result["solid_count"], 1) self.assertAlmostEqual(result["volume_mm3"], math.pi * 2 ** 2 * 5 / 8) self.assertEqual(result["bbox_mm"], {"min": [9.0, -1.0, 0.0], "max": [11.0, 1.0, 2.5]}) self.assertTrue(any(item["operation"] == "uniform_scale" for item in result["topology_deltas"])) def test_kernel_transform_delta_preserves_owner_and_rejects_stale_geometry(self) -> None: from build123d import Solid from cdsl_engine.build123d_adapter import Build123dGeometryAdapter adapter = Build123dGeometryAdapter() source = Solid.make_box(2, 3, 4) cases = ( ("translation", {"type": "translation", "translation_mm": [7, -3, 2]}), ("rotation", { "type": "rotation", "axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]}, "angle_deg": 90, }), ("uniform_scale", {"type": "uniform_scale", "center_mm": [1, 1.5, 2], "scale_factor": 0.5}), ) for name, transform in cases: with self.subTest(transform=name): moved, delta = adapter.transform_with_topology_delta(source, transform) registry = TopologyRegistry() source_records = adapter.topology_records(source, "base", "body:base") registry.replace_body_topology("base", "body:base", source_records) registry.replace_body_topology( "move", "body:move", adapter.topology_records(moved, "move", "body:move"), topology_delta=delta, ) moved_face = next(record for record in registry.records_for_feature("move") if record.kind == "face") resolved = registry.resolve({ "kind": "face", "owner_feature_id": "base", "geometry": moved_face.geometry, }, active_body_id="body:move") stale = registry.resolve({ "kind": "face", "owner_feature_id": "base", "geometry": source_records[0].geometry, }, active_body_id="body:move") self.assertEqual(moved_face.owner_feature_ids, ("base",)) self.assertEqual(resolved.status, "resolved") self.assertEqual(stale.status, "not_found") evidence = registry.topology_deltas()[0] self.assertEqual(evidence["operation"], name) self.assertTrue(any( item["status"] == "unique_exact_continuation" for item in evidence["relations"] )) def test_non_unique_or_incomplete_kernel_history_does_not_transfer_owner(self) -> None: first_source = object() second_source = object() result = object() registry = TopologyRegistry() registry.replace_body_topology("base", "body:base", [ TopologyRecord("base:first", "face", "base", "body:base", {"center_mm": [0, 0, 0]}, first_source), TopologyRecord("base:second", "face", "other", "body:base", {"center_mm": [0, 0, 0]}, second_source), ]) registry.replace_body_topology("move", "body:move", [ TopologyRecord("move:face", "face", "move", "body:move", {"center_mm": [7, 0, 0]}, result), ], topology_delta=TopologyDelta("translation", ( TopologyDeltaRelation("modified", "face", first_source, (result,)), TopologyDeltaRelation("modified", "face", second_source, (result,)), ))) moved = registry.records_for_feature("move")[0] statuses = [item["status"] for item in registry.topology_deltas()[0]["relations"]] self.assertEqual(moved.owner_feature_ids, ("move",)) self.assertEqual(statuses, ["ambiguous_exact_continuation", "ambiguous_exact_continuation"]) self.assertEqual(registry.resolve({ "kind": "face", "stable_id": "base:first", "owner_feature_id": "base", }, active_body_id="body:move").status, "not_found") def test_kernel_deleted_history_blocks_geometry_drift_successor_guessing(self) -> None: registry = TopologyRegistry() source = object() registry.replace_body_topology("base", "body:base", [ TopologyRecord("base:edge", "edge", "base", "body:base", { "curve_type": "line", "start_mm": [0, 0, 0], "end_mm": [0, 0, 10], }, source), ]) registry.replace_body_topology("dress_up", "body:dress_up", [ TopologyRecord("dress_up:edge", "edge", "dress_up", "body:dress_up", { "curve_type": "line", "start_mm": [0, 0, 1], "end_mm": [0, 0, 9], }, object()), ], topology_delta=TopologyDelta("fillet", ( TopologyDeltaRelation("deleted", "edge", source), ))) self.assertNotIn("base:edge", registry._successors) self.assertEqual(registry.topology_deltas()[0]["relations"][0]["status"], "recorded_without_owner_transfer") def test_inactive_stable_id_without_geometry_does_not_bind_a_unique_active_candidate(self) -> None: registry = TopologyRegistry() registry.replace_body_topology("base", "body:base", [ TopologyRecord("base:edge", "edge", "base", "body:base", {"center_mm": [0, 0, 0]}), ]) registry.replace_body_topology("later", "body:later", [ TopologyRecord("later:edge", "edge", "later", "body:later", {"center_mm": [5, 0, 0]}), ]) resolution = registry.resolve({"kind": "edge", "stable_id": "base:edge"}, active_body_id="body:later") self.assertEqual(resolution.status, "not_found") self.assertEqual(resolution.diagnostic.code, "selector_stable_id_inactive") def test_output_role_selector_binds_only_unique_active_kernel_evidence(self) -> None: registry = TopologyRegistry() source_face = object() sweep_end = object() registry.replace_body_topology("base", "body:base", [ TopologyRecord("base:face", "face", "base", "body:base", {"center_mm": [0, 0, 0]}, source_face), ]) registry.replace_body_topology("sweep", "body:sweep", [ TopologyRecord("sweep:end", "face", "sweep", "body:sweep", {"center_mm": [0, 0, 10]}, sweep_end), ], topology_delta=TopologyDelta("sweep", ( TopologyDeltaRelation("generated", "face", object(), (sweep_end,), output_role="sweep.end"), ))) resolved = registry.resolve({ "kind": "face", "owner_feature_id": "sweep", "output_role": "sweep.end", }, active_body_id="body:sweep") evidence = registry.topology_deltas()[0]["relations"][0] self.assertEqual(resolved.status, "resolved") self.assertEqual(resolved.record.record_id, "sweep:end") self.assertEqual(resolved.record.output_roles, ("sweep.end",)) self.assertEqual(evidence["output_role_status"], "unique_result_snapshot") registry.replace_body_topology("later", "body:later", [ TopologyRecord("later:end", "face", "later", "body:later", {"center_mm": [0, 0, 10]}, object()), ]) stale = registry.resolve({ "kind": "face", "owner_feature_id": "sweep", "output_role": "sweep.end", }, active_body_id="body:later") self.assertEqual(stale.status, "not_found") self.assertEqual(stale.diagnostic.code, "selector_output_role_not_found") def test_shell_offset_role_source_selects_one_exact_builder_relation(self) -> None: registry = TopologyRegistry() extrude_start = object() extrude_end = object() offset_start = object() offset_end = object() registry.replace_body_topology("extrude", "body:extrude", [ TopologyRecord( "extrude:start", "face", "extrude", "body:extrude", {"center_mm": [0, 0, 0]}, extrude_start, owner_feature_ids=("extrude",), output_roles=("extrude.start",), ), TopologyRecord( "extrude:end", "face", "extrude", "body:extrude", {"center_mm": [0, 0, 10]}, extrude_end, owner_feature_ids=("extrude",), output_roles=("extrude.end",), ), ]) registry.replace_body_topology("shell", "body:shell", [ TopologyRecord("shell:offset-start", "face", "shell", "body:shell", {"center_mm": [0, 0, 1]}, offset_start), TopologyRecord("shell:offset-end", "face", "shell", "body:shell", {"center_mm": [0, 0, 9]}, offset_end), ], topology_delta=TopologyDelta("shell", ( TopologyDeltaRelation("generated", "face", extrude_start, (offset_start,), output_role="shell.offset_face"), TopologyDeltaRelation("generated", "face", extrude_end, (offset_end,), output_role="shell.offset_face"), ))) ambiguous = registry.resolve({ "kind": "face", "owner_feature_id": "shell", "output_role": "shell.offset_face", }, active_body_id="body:shell") selected = registry.resolve({ "kind": "face", "owner_feature_id": "shell", "output_role": "shell.offset_face", "output_role_source": {"owner_feature_id": "extrude", "output_role": "extrude.start"}, }, active_body_id="body:shell") self.assertEqual(ambiguous.status, "ambiguous") self.assertEqual(selected.status, "resolved") self.assertEqual(selected.record.record_id, "shell:offset-start") self.assertEqual( selected.record.output_role_sources, (("shell.offset_face", "extrude", "extrude.start"),), ) def test_tapered_prism_exposes_builder_proven_single_cap_roles(self) -> None: from build123d import Face, Plane, Wire from cdsl_engine.build123d_adapter import Build123dGeometryAdapter face = Face(Wire.make_circle(10, Plane.XY)) result, delta = Build123dGeometryAdapter().extrude_taper_with_topology_delta( face, (0.0, 0.0, 20.0), 10.0, ) self.assertTrue(result.is_valid) self.assertGreater(result.volume, 0.0) self.assertIsNotNone(delta) self.assertEqual( [(relation.event, relation.kind, relation.output_role) for relation in delta.relations], [("generated", "face", "extrude.start"), ("generated", "face", "extrude.end")], ) def test_exact_kernel_history_crosses_a_boolean_member_index_shift(self) -> None: registry = TopologyRegistry() independent = object() tool = object() draft_cap = object() boolean_result_cap = object() registry.replace_body_topologies("draft", [ ("body:draft:0", [TopologyRecord( "draft:independent", "face", "independent", "body:draft:0", {"center_mm": [-5, 0, 0]}, independent, )]), ("body:draft:1", [TopologyRecord( "draft:tool", "face", "tool", "body:draft:1", {"center_mm": [0, 0, 0]}, tool, )]), ("body:draft:2", [TopologyRecord( "draft:cap", "face", "draft", "body:draft:2", {"center_mm": [5, 0, 0]}, draft_cap, owner_feature_ids=("draft",), output_roles=("extrude.start",), )]), ], active_body_id="body:draft") registry.replace_body_topologies("boolean", [ ("body:boolean:0", [TopologyRecord( "boolean:independent", "face", "boolean", "body:boolean:0", {"center_mm": [-5, 0, 0]}, independent, )]), ("body:boolean:1", [TopologyRecord( "boolean:cap", "face", "boolean", "body:boolean:1", {"center_mm": [5, 0, 0]}, boolean_result_cap, )]), ], active_body_id="body:boolean", topology_delta=TopologyDelta("subtract", ( TopologyDeltaRelation("modified", "face", draft_cap, (boolean_result_cap,)), ))) resolution = registry.resolve({ "kind": "face", "owner_feature_id": "draft", "output_role": "extrude.start", }, active_body_id="body:boolean") self.assertEqual(resolution.status, "resolved") self.assertEqual(resolution.record.record_id, "boolean:cap") self.assertEqual(resolution.record.owner_feature_ids, ("draft",)) self.assertEqual(resolution.record.output_roles, ("extrude.start",)) def test_output_role_semantic_contract_requires_face_runtime_evidence(self) -> None: from cdsl_engine.semantic_validation import validate_semantic_cdsl cdsl = self._base_block() cdsl["features"][0]["execution_status"] = "supported" cdsl["features"].append({ "id": "shell", "atomic_id": "shell", "depends_on": ["base_add"], "params": {"thickness_mm": 1}, "execution_status": "supported", "selectors": [{ "kind": "face", "owner_feature_id": "base_add", "output_role": "sweep.end", "source": "runtime_snapshot", "confidence": 1, }], }) self.assertTrue(validate_semantic_cdsl(cdsl)["future_rebuild_ready"]) invalid = deepcopy(cdsl) invalid["features"][-1]["selectors"][0]["stable_id"] = "legacy-face" with self.assertRaisesRegex(ValueError, "cannot mix stable or geometry evidence"): validate_semantic_cdsl(invalid) nested = deepcopy(cdsl) nested["features"][-1]["selectors"][0]["matched_selectors"] = [{ "kind": "face", "owner_feature_id": "base_add", "output_role": "sweep.start", "source": "runtime_snapshot", "confidence": 1, }] with self.assertRaisesRegex(ValueError, "only supported in feature.selectors"): validate_semantic_cdsl(nested) bypassed_semantic_validation = deepcopy(cdsl) bypassed_semantic_validation["features"][-1]["params"]["role_reference"] = { "kind": "face", "owner_feature_id": "base_add", "output_role": "sweep.start", "source": "runtime_snapshot", "confidence": 1, } analysis = CapabilityAnalyzer( atomic_ids={"extrude_add_blind", "shell"}, profile_types=SHAPE_GENERATORS, ).analyze(bypassed_semantic_validation) self.assertIn( "unsupported_output_role_selector_context", [blocker.code for blocker in analysis.feature_results[-1].blockers], ) malformed_source = deepcopy(cdsl) malformed_source["features"][-1]["selectors"][0]["output_role"] = "shell.offset_face" malformed_source["features"][-1]["selectors"][0]["output_role_source"] = {"owner_feature_id": "base_add"} with self.assertRaisesRegex(ValueError, "CDSL schema violation"): validate_semantic_cdsl(malformed_source) unsupported_source = deepcopy(cdsl) unsupported_source["features"][-1]["selectors"][0]["output_role"] = "shell.offset_face" unsupported_source["features"][-1]["selectors"][0]["output_role_source"] = { "owner_feature_id": "base_add", "output_role": "sweep.end", } with self.assertRaisesRegex(ValueError, "must be an extrude cap role"): validate_semantic_cdsl(unsupported_source) invalid_shell_target = deepcopy(cdsl) invalid_shell_target["features"][-1]["params"]["target_feature_id"] = "shell" with self.assertRaisesRegex(ValueError, "requires a preceding body feature"): validate_semantic_cdsl(invalid_shell_target) def test_transformed_owner_selector_executes_a_downstream_fillet(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["features"].append({ "id": "move", "atomic_id": "transform_bodies", "depends_on": ["base_add"], "params": { "source_feature_ids": ["base_add"], "transform": {"type": "translation", "translation_mm": [20, 0, 0]}, "make_copy": False, }, }) with tempfile.TemporaryDirectory() as directory: root = Path(directory) moved = rebuild_cdsl(cdsl, root / "moved.step") edge = next( item for item in moved["topology_records"] if item["kind"] == "edge" and item.get("body_id") == "body:move" ) filleted = deepcopy(cdsl) filleted["features"].append({ "id": "fillet", "atomic_id": "fillet", "depends_on": ["move"], "params": {"radius_mm": 1}, "selectors": [{ "kind": "edge", "stable_id": "moved-edge", "source": "runtime_snapshot", "confidence": 1, "owner_feature_id": "base_add", "geometry": edge["geometry"], }], }) result = rebuild_cdsl(filleted, root / "filleted.step") self.assertEqual([item["feature_id"] for item in result["feature_results"]], ["base_add", "move", "fillet"]) self.assertLess(result["volume_mm3"], moved["volume_mm3"]) self.assertTrue(any( item["operation"] == "translation" for item in result["topology_deltas"] )) def test_body_transform_unavailable_source_is_a_preflight_diagnostic(self) -> None: cdsl = self._base_block() cdsl["features"].append({ "id": "move", "atomic_id": "transform_bodies", "depends_on": ["base_add"], "params": { "source_feature_ids": ["missing_body"], "transform": {"type": "translation", "translation_mm": [1, 0, 0]}, "make_copy": True, }, }) analysis = CapabilityAnalyzer(atomic_ids={"extrude_add_blind", "transform_bodies"}, profile_types=SHAPE_GENERATORS).analyze(cdsl) blocker = next(item for item in analysis.feature_results[-1].blockers if item.code == "body_source_unavailable") self.assertEqual(blocker.code, "body_source_unavailable") self.assertEqual(blocker.detail, {"source_feature_id": "missing_body"}) def test_absorbed_body_source_is_not_selectable_after_a_fused_feature(self) -> None: cdsl = self._base_block() cdsl["features"].extend([ { "id": "fused_add", "atomic_id": "extrude_add_blind", "depends_on": ["base_add"], "params": {"distance_mm": 5}, "sketch_id": "base", }, { "id": "move", "atomic_id": "transform_bodies", "depends_on": ["fused_add"], "params": { "source_feature_ids": ["base_add"], "transform": {"type": "translation", "translation_mm": [1, 0, 0]}, "make_copy": True, }, }, ]) analysis = CapabilityAnalyzer( atomic_ids={"extrude_add_blind", "transform_bodies"}, profile_types=SHAPE_GENERATORS, ).analyze(cdsl) blocker = next(item for item in analysis.feature_results[-1].blockers if item.code == "body_source_unavailable") self.assertEqual(blocker.detail, {"source_feature_id": "base_add"}) def test_boolean_consumed_body_source_is_not_selectable_afterward(self) -> None: cdsl = self._base_block() cdsl["features"][0]["params"]["result_mode"] = "new_body" cdsl["features"].extend([ { "id": "tool_body", "atomic_id": "extrude_add_blind", "depends_on": ["base_add"], "params": {"distance_mm": 5, "result_mode": "new_body"}, "sketch_id": "base", }, { "id": "join", "atomic_id": "boolean_bodies", "depends_on": ["base_add", "tool_body"], "params": { "operation": "union", "target_feature_ids": ["base_add"], "tool_feature_ids": ["tool_body"], "keep_tools": False, }, }, { "id": "move", "atomic_id": "transform_bodies", "depends_on": ["join"], "params": { "source_feature_ids": ["base_add"], "transform": {"type": "translation", "translation_mm": [1, 0, 0]}, "make_copy": True, }, }, ]) analysis = CapabilityAnalyzer( atomic_ids={"extrude_add_blind", "boolean_bodies", "transform_bodies"}, profile_types=SHAPE_GENERATORS, ).analyze(cdsl) blocker = next(item for item in analysis.feature_results[-1].blockers if item.code == "body_source_unavailable") self.assertEqual(blocker.detail, {"source_feature_id": "base_add"}) def test_boolean_rejects_a_body_source_absorbed_by_a_fused_feature(self) -> None: cdsl = self._base_block() cdsl["features"].extend([ { "id": "fused_add", "atomic_id": "extrude_add_blind", "depends_on": ["base_add"], "params": {"distance_mm": 5}, "sketch_id": "base", }, { "id": "selected_tool", "atomic_id": "extrude_add_blind", "depends_on": ["fused_add"], "params": {"distance_mm": 3, "result_mode": "new_body"}, "sketch_id": "base", }, { "id": "boolean", "atomic_id": "boolean_bodies", "depends_on": ["selected_tool"], "params": { "operation": "subtract", "target_feature_ids": ["base_add"], "tool_feature_ids": ["selected_tool"], "keep_tools": False, }, }, ]) analysis = CapabilityAnalyzer( atomic_ids={"extrude_add_blind", "boolean_bodies"}, profile_types=SHAPE_GENERATORS, ).analyze(cdsl) blocker = next(item for item in analysis.feature_results[-1].blockers if item.code == "boolean_body_unavailable") self.assertEqual(blocker.detail, {"source_feature_id": "base_add"}) def test_deleted_last_body_clears_the_active_body_precondition(self) -> None: cdsl = self._base_block() cdsl["features"].extend([ { "id": "delete_base", "atomic_id": "delete_bodies", "depends_on": ["base_add"], "params": {"target_feature_ids": ["base_add"]}, }, { "id": "cut_after_delete", "atomic_id": "extrude_cut_blind", "depends_on": ["delete_base"], "params": {"distance_mm": 1}, "sketch_id": "base", }, ]) analysis = CapabilityAnalyzer( atomic_ids={"extrude_add_blind", "extrude_cut_blind", "delete_bodies"}, profile_types=SHAPE_GENERATORS, ).analyze(cdsl) blockers = {item.code for item in analysis.feature_results[-1].blockers} self.assertIn("missing_active_body", blockers) def test_pattern_replay_does_not_make_its_source_a_body_member(self) -> None: cdsl = self._base_block() cdsl["features"].extend([ { "id": "array", "atomic_id": "pattern_linear", "depends_on": ["base_add"], "params": { "source_feature_ids": ["base_add"], "direction_1": [1, 0, 0], "spacing_1_mm": 20, "pattern_count_1": 2, }, }, { "id": "move", "atomic_id": "transform_bodies", "depends_on": ["array"], "params": { "source_feature_ids": ["base_add"], "transform": {"type": "translation", "translation_mm": [1, 0, 0]}, "make_copy": True, }, }, ]) analysis = CapabilityAnalyzer( atomic_ids={"extrude_add_blind", "pattern_linear", "transform_bodies"}, profile_types=SHAPE_GENERATORS, ).analyze(cdsl) blocker = next(item for item in analysis.feature_results[-1].blockers if item.code == "body_source_unavailable") self.assertEqual(blocker.detail, {"source_feature_id": "base_add"}) def test_deferred_reference_is_currently_executable_without_a_sketch(self) -> None: cdsl = { "schema": "cad.cdsl.llm.v1", "geometry": {"sketches": [{"id": "s", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 1}}]}, "features": [{ "id": "plane", "atomic_id": "reference_plane", "depends_on": [], "execution_status": "deferred", "params": {"plane": _workplane()}, }], } analysis = CapabilityAnalyzer(atomic_ids={"reference_plane"}, profile_types=SHAPE_GENERATORS).analyze(cdsl) self.assertFalse(analysis.runtime_eligible) self.assertEqual(analysis.feature_results[0].resolved_status, "executable") self.assertEqual(analysis.document_blockers[0].code, "no_solid_feature") def test_unused_invalid_profile_does_not_block_runtime_preflight(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "abandoned", "workplane": _workplane(), "profile": {"type": "analytic_contours", "contours": [{ "role": "outer", "closed": True, "segments": [{"type": "line", "start": [0, 0], "end": [1, 0]}], }]}, }) analysis = analyze_cdsl(cdsl) self.assertTrue(analysis.runtime_eligible) def test_used_invalid_profile_is_a_feature_level_blocker(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "broken", "workplane": _workplane(), "profile": {"type": "analytic_contours", "contours": [{ "role": "outer", "closed": True, "segments": [{"type": "line", "start": [0, 0], "end": [1, 0]}], }]}, }) cdsl["features"].append({ "id": "broken_cut", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 1}, "sketch_id": "broken", }) analysis = analyze_cdsl(cdsl) broken = next(item for item in analysis.feature_results if item.feature_id == "broken_cut") self.assertEqual(broken.resolved_status, "blocked") self.assertIn("profile_resolution_failed", [item.code for item in broken.blockers]) def test_used_empty_analytic_profile_is_a_feature_level_blocker(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "construction_only", "workplane": _workplane(), "profile": {"type": "analytic_contours", "contours": []}, }) cdsl["features"].append({ "id": "empty_cut", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 1}, "sketch_id": "construction_only", }) analysis = analyze_cdsl(cdsl) empty = next(item for item in analysis.feature_results if item.feature_id == "empty_cut") self.assertIn("profile_no_closed_region", [item.code for item in empty.blockers]) def test_selector_resolver_refuses_equal_candidates(self) -> None: registry = TopologyRegistry() plane = PlaneSpec.from_mapping(_workplane()) registry.register(TopologyRecord("first", "plane", "f1", geometry=plane.as_dict(), value=plane)) registry.register(TopologyRecord("second", "plane", "f2", geometry=plane.as_dict(), value=plane)) resolution = registry.resolve({"kind": "plane", "geometry": plane.as_dict()}) self.assertEqual(resolution.status, "ambiguous") self.assertEqual(resolution.diagnostic.code, "selector_ambiguous") def test_selector_resolver_prefers_exact_runtime_stable_id(self) -> None: registry = TopologyRegistry() geometry = {"curve_type": "circle", "center_mm": [0, 0, 0]} registry.register(TopologyRecord("body:b:edge:18", "edge", "boss", "body:b", geometry)) registry.register(TopologyRecord("body:b:edge:20", "edge", "boss", "body:b", geometry)) resolution = registry.resolve({ "kind": "edge", "stable_id": "body:b:edge:18", "owner_feature_id": "boss", "snapshot_id": "cad_test/rev_001", "geometry": geometry, }, active_body_id="body:b") self.assertEqual(resolution.status, "resolved") self.assertEqual(resolution.record.record_id, "body:b:edge:18") def test_snapshot_stable_id_rejects_geometry_that_changed(self) -> None: registry = TopologyRegistry() registry.register(TopologyRecord( "body:b:edge:18", "edge", "boss", "body:b", {"curve_type": "circle", "center_mm": [0, 0, 0]}, )) resolution = registry.resolve({ "kind": "edge", "stable_id": "body:b:edge:18", "owner_feature_id": "boss", "snapshot_id": "cad_test/rev_001", "geometry": {"curve_type": "circle", "center_mm": [1, 0, 0]}, }, active_body_id="body:b") self.assertEqual(resolution.status, "not_found") self.assertEqual(resolution.diagnostic.code, "selector_geometry_mismatch") def test_selector_resolver_normalizes_legacy_solidworks_plane_evidence(self) -> None: registry = TopologyRegistry() registry.register(TopologyRecord( "face", "face", "f1", "body:f1", geometry={ "surface_type": "plane", "plane_normal": [1, 0, 0], "plane_offset_mm": 12.0, "bbox_mm": [12, -2, -3, 12, 2, 3], "area_mm2": 24, }, )) resolution = registry.resolve({ "kind": "face", "geometry": { "surface": {"type": "plane", "parameters": [-1, 0, 0, -0.012, 0, 0]}, "box": [0.012, -0.002, -0.003, 0.012, 0.002, 0.003], "area": 0.000024, }, }) self.assertEqual(resolution.status, "resolved") self.assertEqual(resolution.record.record_id, "face") def test_owner_selector_survives_a_later_body_snapshot_when_geometry_is_unchanged(self) -> None: registry = TopologyRegistry() geometry = { "bbox_mm": [0, 0, 0, 0, 0, 10], "center_mm": [0, 0, 5], "length_mm": 10.0, "curve_type": "line", "start_mm": [0, 0, 0], "end_mm": [0, 0, 10], } registry.replace_body_topology("base_add", "body:base_add", [ TopologyRecord("body:base_add:edge:0", "edge", "base_add", "body:base_add", geometry, "old-edge"), ]) registry.replace_body_topology("later_cut", "body:later_cut", [ TopologyRecord("body:later_cut:edge:3", "edge", "later_cut", "body:later_cut", geometry, "current-edge"), ]) resolution = registry.resolve( {"kind": "edge", "owner_feature_id": "base_add", "geometry": geometry}, active_body_id="body:later_cut", ) self.assertEqual(resolution.status, "resolved") self.assertEqual(resolution.record.value, "current-edge") self.assertEqual(resolution.record.feature_id, "later_cut") self.assertEqual(resolution.record.owner_feature_ids, ("base_add",)) def test_ambiguous_predecessors_do_not_invent_topology_ownership(self) -> None: registry = TopologyRegistry() geometry = {"center_mm": [1, 2, 3]} registry.replace_body_topology("base_add", "body:base_add", [ TopologyRecord("body:base_add:vertex:0", "vertex", "base_add", "body:base_add", geometry), TopologyRecord("body:base_add:vertex:1", "vertex", "base_add", "body:base_add", geometry), ]) registry.replace_body_topology("later_cut", "body:later_cut", [ TopologyRecord("body:later_cut:vertex:0", "vertex", "later_cut", "body:later_cut", geometry), ]) resolution = registry.resolve( {"kind": "vertex", "owner_feature_id": "base_add", "geometry": geometry}, active_body_id="body:later_cut", ) self.assertEqual(resolution.status, "not_found") def test_changed_adjacency_prevents_owner_provenance_transfer(self) -> None: registry = TopologyRegistry() unchanged_geometry = { "bbox_mm": [0, 0, 0, 1, 1, 0], "center_mm": [0.5, 0.5, 0], "normal": [0, 0, 1], "area_mm2": 1, "surface_type": "plane", } registry.replace_body_topology("base_add", "body:base_add", [ TopologyRecord( "body:base_add:face:0", "face", "base_add", "body:base_add", {**unchanged_geometry, "adjacency_signature": ["line:1.000000:2"]}, ), ]) registry.replace_body_topology("later_cut", "body:later_cut", [ TopologyRecord( "body:later_cut:face:0", "face", "later_cut", "body:later_cut", {**unchanged_geometry, "adjacency_signature": ["line:1.000000:2", "circle:1.000000:1"]}, ), ]) resolution = registry.resolve( {"kind": "face", "owner_feature_id": "base_add", "geometry": unchanged_geometry}, active_body_id="body:later_cut", ) self.assertEqual(resolution.status, "not_found") def test_batch_analysis_writes_one_report_per_input(self) -> None: document = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "batch-part", "meta": {"unit": "mm"}, "geometry": {"sketches": [{"id": "s", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 1}}]}, "features": [{ "id": "f", "atomic_id": "reference_plane", "depends_on": [], "execution_status": "deferred", "params": {"plane": _workplane()}, }], } with tempfile.TemporaryDirectory() as directory: root = Path(directory) input_path = root / "input" input_path.mkdir() (input_path / "batch-part.cdsl.json").write_text(json.dumps(document), encoding="utf-8") manifest = batch_analyze(input_path, root / "out", atomic_ids=frozenset({"reference_plane"})) report = json.loads((root / "out" / "parts" / "batch-part.report.json").read_text(encoding="utf-8")) self.assertEqual(manifest["part_count"], 1) self.assertTrue(report["semantic_valid"]) self.assertFalse(report["runtime_eligible"]) self.assertEqual(report["first_blocker"]["code"], "no_solid_feature") def test_batch_analysis_part_filter_is_exact_and_rejects_unknown_ids(self) -> None: document = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "meta": {"unit": "mm"}, "geometry": {"sketches": []}, "features": [], } with tempfile.TemporaryDirectory() as directory: root = Path(directory) input_path = root / "input" input_path.mkdir() for part_id in ("first", "second"): (input_path / f"{part_id}.cdsl.json").write_text( json.dumps({**document, "part_id": part_id}), encoding="utf-8", ) manifest = batch_analyze( input_path, root / "out", atomic_ids=frozenset({"reference_plane"}), part_ids=["second"], ) self.assertEqual(manifest["part_count"], 1) self.assertEqual(manifest["results"], [{"part_id": "second", "report": "parts/second.report.json"}]) with self.assertRaisesRegex(ValueError, "do not exist"): batch_analyze(input_path, root / "bad", part_ids=["missing"]) def test_batch_resume_migrates_derived_failure_category_without_rebuilding(self) -> None: document = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "cached", "meta": {"unit": "mm"}, "geometry": {"sketches": []}, "features": [], } with tempfile.TemporaryDirectory() as directory: root = Path(directory) source = root / "input" source.mkdir() (source / "cached.cdsl.json").write_text(json.dumps(document), encoding="utf-8") report_path = root / "out" / "parts" / "cached.report.json" report_path.parent.mkdir(parents=True) report_path.write_text(json.dumps({ "part_id": "cached", "semantic_valid": True, "runtime_eligible": False, "compiled": False, "build_attempted": False, "built": False, "geometry_verified": False, "feature_results": [], "first_blocker": {"code": "missing_host_face"}, }), encoding="utf-8") manifest = batch_analyze(source, root / "out") report = json.loads(report_path.read_text(encoding="utf-8")) self.assertEqual(manifest["failure_category_counts"], {"input_incomplete": 1}) self.assertEqual(report["failure_category"], "input_incomplete") def test_full_export_batch_has_a_machine_readable_report_for_every_input(self) -> None: """Keep the shipped CDSL corpus on the capability-report path. STEP construction is intentionally left to the resumable build job; this CI-sized pass proves that every current export gets a semantic result and a first blocker rather than being silently skipped. """ source = ROOT / "json_to_cdsl" / "output" selectors = [path.name.removesuffix(".cdsl.json") for path in sorted(source.glob("*.cdsl.json"))] corpus = _corpus_manifest(source) input_count = len(selectors) self.assertGreater(input_count, 0) self.assertEqual(input_count, corpus["document_count"]) self.assertEqual(_selector_digest(selectors), corpus["document_stems_sha256"]) with tempfile.TemporaryDirectory() as directory: out = Path(directory) / "baseline" manifest = batch_analyze(source, out) reports = [json.loads(path.read_text(encoding="utf-8")) for path in (out / "parts").glob("*.report.json")] self.assertEqual(manifest["part_count"], input_count) self.assertEqual(manifest["completed_count"], input_count) self.assertTrue(manifest["complete"]) self.assertEqual(manifest["semantic_valid_count"], input_count) self.assertEqual(len(reports), input_count) self.assertTrue(all(report["runtime_eligible"] or report.get("first_blocker") for report in reports)) def test_p3_static_pool_is_explicit_and_reports_missing_capture_separately(self) -> None: from cdsl_engine.phase_pools import select_p3_static_pool from cdsl_engine.runtime import analyze_cdsl source = ROOT / "json_to_cdsl" / "output" part_ids = select_p3_static_pool(source) expected = _corpus_manifest(source)["phase_pools"]["p3"] self.assertEqual(len(part_ids), expected["selector_count"]) self.assertEqual(_selector_digest(part_ids), expected["selectors_sha256"]) # Static pool membership is input-based. Runtime preflight remains a # separate signal and must retain captured blockers for its members. expected_blockers = { "027784": {"missing_revolve_axis"}, "104237": {"missing_extent_reference"}, "239358": {"missing_extent_reference", "dependency_unavailable"}, "241720": {"missing_extent_reference"}, } for part_id, blockers in expected_blockers.items(): self.assertIn(part_id, part_ids) cdsl = json.loads((source / f"{part_id}.cdsl.json").read_text(encoding="utf-8")) result = analyze_cdsl(cdsl) actual = { blocker.code for feature in result.feature_results for blocker in feature.blockers } self.assertTrue(blockers <= actual) def test_p3_phase_pool_can_be_passed_to_batch_rebuild(self) -> None: from cdsl_engine.phase_pools import select_p3_static_pool source = ROOT / "json_to_cdsl" / "output" pool = select_p3_static_pool(source) self.assertIn("Bearing-Spacer-1", pool) with tempfile.TemporaryDirectory() as directory: manifest = batch_analyze(source, Path(directory) / "p3", part_ids=pool, max_parts=1) self.assertEqual(manifest["part_count"], len(pool)) self.assertEqual(manifest["completed_count"], 1) self.assertFalse(manifest["complete"]) def test_phase_pool_membership_is_regressed_for_p4_and_p6(self) -> None: from cdsl_engine.phase_pools import select_static_phase_pool source = ROOT / "json_to_cdsl" / "output" expected_pools = _corpus_manifest(source)["phase_pools"] for phase in ("p4", "p6"): selectors = select_static_phase_pool(source, phase) expected = expected_pools[phase] self.assertEqual(len(selectors), expected["selector_count"]) self.assertEqual(_selector_digest(selectors), expected["selectors_sha256"]) with self.assertRaisesRegex(ValueError, "Unknown CDSL runtime phase"): select_static_phase_pool(source, "p5") def test_construction_bspline_is_auditable_but_not_an_executable_contour(self) -> None: from cdsl_engine.semantic_validation import validate_semantic_cdsl cdsl = self._base_block() cdsl["features"][0]["execution_status"] = "supported" profile = { "type": "analytic_contours", "contours": [{ "role": "outer", "closed": True, "segments": [ {"type": "line", "start": [-5, -5], "end": [5, -5]}, {"type": "line", "start": [5, -5], "end": [5, 5]}, {"type": "line", "start": [5, 5], "end": [-5, 5]}, {"type": "line", "start": [-5, 5], "end": [-5, -5]}, ], }], "construction": [{ "type": "bspline", "degree": 2, "control_points": [[-5, 0], [0, 2], [5, 0]], "knots": [0, 0, 0, 1, 1, 1], "periodic": False, }], } cdsl["geometry"]["sketches"][0]["profile"] = profile validate_semantic_cdsl(cdsl) resolved = resolve_all_sketches(cdsl) entities = resolved["geometry"]["sketches"][0]["entities"] self.assertNotIn("bspline", {entity["type"] for entity in entities}) profile["contours"][0]["segments"][0] = profile["construction"][0] with self.assertRaisesRegex(ValueError, "CDSL schema violation"): validate_semantic_cdsl(cdsl) def test_nested_pattern_source_is_replayable_after_its_first_execution(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["features"].extend([ { "id": "first_pattern", "atomic_id": "pattern_linear", "depends_on": ["base_add"], "params": {"source_feature_ids": ["base_add"], "direction_1": [1, 0, 0], "spacing_1_mm": 20, "pattern_count_1": 2}, }, { "id": "nested_pattern", "atomic_id": "pattern_linear", "depends_on": ["first_pattern"], "params": {"source_feature_ids": ["first_pattern"], "direction_1": [0, 1, 0], "spacing_1_mm": 20, "pattern_count_1": 2}, }, ]) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "nested.step") self.assertEqual(result["engine"], "cdsl_session_runtime") def test_pattern_source_without_body_definition_is_blocked_in_preflight(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["features"].insert(0, { "id": "context", "atomic_id": "reference_plane", "depends_on": [], "params": {"plane": _workplane()}, }) cdsl["features"].append({ "id": "pattern", "atomic_id": "pattern_linear", "depends_on": ["base_add", "context"], "params": {"source_feature_ids": ["context"], "direction_1": [1, 0, 0], "spacing_1_mm": 20, "pattern_count_1": 2}, }) analysis = analyze_cdsl(cdsl) pattern = next(result for result in analysis.feature_results if result.feature_id == "pattern") self.assertEqual(pattern.resolved_status, "unsupported") self.assertIn("unsupported_pattern_source", [blocker.code for blocker in pattern.blockers]) def test_truth_comparison_keeps_frame_mismatch_distinct_from_verified_geometry(self) -> None: truth = {"bounding_box_mm": [-10, -200, -10, 10, 200, 10], "solid_count": 1} common = { "truth": truth, "volume_relative_error": 1e-8, "area_relative_error": 1e-8, "solid_count_matches": True, } self.assertEqual( _verification_classification( actual_box=[-10, 990, 0, 10, 1010, 400], box_delta=1190, **common, ), "coordinate_frame_mismatch_candidate", ) self.assertEqual( _verification_classification( actual_box=[-10, -200, -10, 10, 200, 10], box_delta=0, **common, ), "verified", ) self.assertEqual( _verification_classification( actual_box=[-10, 990, 0, 10, 1010, 400], box_delta=1190, truth=truth, volume_relative_error=0.1, area_relative_error=1e-8, solid_count_matches=True, ), "geometry_mismatch", ) def test_batch_failure_categories_preserve_input_selector_capability_and_occ_boundaries(self) -> None: self.assertEqual(_failure_category({"geometry_verified": True}), "geometry_verified") self.assertEqual(_failure_category({"runtime_eligible": True, "build_attempted": False}), "runtime_eligible_not_built") self.assertEqual(_failure_category({"built": True, "numeric_comparison": {"classification": "coordinate_frame_mismatch_candidate"}}), "coordinate_frame_mismatch_candidate") self.assertEqual(_failure_category({"built": False, "first_blocker": {"code": "missing_host_face"}}), "input_incomplete") self.assertEqual(_failure_category({"built": False, "first_blocker": {"code": "extent_target_not_reached"}}), "input_incomplete") self.assertEqual(_failure_category({"built": False, "first_blocker": {"code": "selector_ambiguous"}}), "selector_resolution") self.assertEqual(_failure_category({"built": False, "first_blocker": {"code": "unsupported_hole_subtype"}}), "unsupported_capability") self.assertEqual(_failure_category({"built": False, "first_blocker": {"code": "build_timeout"}}), "occ_execution_failure") def test_mirror_pattern_replays_a_selector_free_cut(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "mirror-test", "meta": {"unit": "mm"}, "geometry": {"sketches": [ {"id": "base", "workplane": _workplane(), "profile": _rectangle([-5, -5], [5, 5])}, {"id": "cut", "workplane": _workplane(), "profile": {"type": "circle", "center": [2, 0], "radius_mm": 1}}, ]}, "features": [ {"id": "ref", "atomic_id": "reference_plane", "depends_on": [], "params": {"plane": {"origin_mm": [0, 0, 0], "x_dir": [0, 1, 0], "normal": [1, 0, 0]}}}, {"id": "base_add", "atomic_id": "extrude_add_blind", "depends_on": ["ref"], "params": {"distance_mm": 2}, "sketch_id": "base"}, {"id": "cut_1", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 2}, "sketch_id": "cut"}, { "id": "mirror", "atomic_id": "pattern_mirror", "depends_on": ["cut_1", "ref"], "params": {"source_feature_ids": ["cut_1"], "mirror_plane": {"kind": "plane", "stable_id": "source", "source": "solidworks", "confidence": 1, "owner_feature_id": "ref"}}, "selectors": [{"kind": "plane", "stable_id": "source", "source": "solidworks", "confidence": 1, "owner_feature_id": "ref"}], }, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "mirror.step") self.assertAlmostEqual(result["volume_mm3"], 200 - 4 * 3.141592653589793, places=5) self.assertIn("mirror", [item["feature_id"] for item in result["feature_results"]]) def test_circular_pattern_fuses_face_sharing_new_body_instances(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "circular-fuse", "meta": {"unit": "mm"}, "geometry": {"sketches": [{ "id": "wedge", "workplane": _workplane(), "profile": {"type": "polygon", "vertices": [[0, 0], [10, 0], [0, 10]]}, }]}, "features": [ {"id": "wedge_add", "atomic_id": "extrude_add_blind", "depends_on": [], "params": {"distance_mm": 10, "result_mode": "new_body"}, "sketch_id": "wedge"}, {"id": "wedge_pattern", "atomic_id": "pattern_circular", "depends_on": ["wedge_add"], "params": { "source_feature_ids": ["wedge_add"], "axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]}, "pattern_count": 4, "sweep_angle_deg": 360, }}, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "circular-fuse.step") self.assertEqual(result["solid_count"], 1) self.assertAlmostEqual(result["volume_mm3"], 2000.0, places=5) copy_owners = { owner for record in result["topology_records"] for owner in record.get("owner_feature_ids") or () if owner.startswith("wedge_pattern.c") } self.assertEqual( copy_owners, { "wedge_pattern.c1.wedge_add", "wedge_pattern.c2.wedge_add", "wedge_pattern.c3.wedge_add", }, ) def test_circular_pattern_skips_explicitly_deleted_copy_instances(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "circular-delete-copy", "meta": {"unit": "mm"}, "geometry": {"sketches": [{ "id": "wedge", "workplane": _workplane(), "profile": {"type": "polygon", "vertices": [[0, 0], [10, 0], [0, 10]]}, }]}, "features": [ {"id": "wedge_add", "atomic_id": "extrude_add_blind", "depends_on": [], "params": {"distance_mm": 10, "result_mode": "new_body"}, "sketch_id": "wedge"}, {"id": "wedge_pattern", "atomic_id": "pattern_circular", "depends_on": ["wedge_add"], "params": { "source_feature_ids": ["wedge_add"], "axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]}, "pattern_count": 4, "sweep_angle_deg": 360, "excluded_instance_indices": [2], }}, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "circular-delete-copy.step") self.assertAlmostEqual(result["volume_mm3"], 1500.0, places=5) def test_transform_can_move_one_proven_circular_pattern_copy(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "circular-copy-transform", "meta": {"unit": "mm"}, "geometry": {"sketches": [{ "id": "boss", "workplane": _workplane(), "profile": {"type": "circle", "center": [10, 0], "radius_mm": 1}, }]}, "features": [ {"id": "boss_add", "atomic_id": "extrude_add_blind", "depends_on": [], "sketch_id": "boss", "params": {"distance_mm": 2, "result_mode": "new_body"}}, {"id": "boss_pattern", "atomic_id": "pattern_circular", "depends_on": ["boss_add"], "params": { "source_feature_ids": ["boss_add"], "axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]}, "pattern_count": 4, "sweep_angle_deg": 360, }}, {"id": "move_copy", "atomic_id": "transform_bodies", "depends_on": ["boss_pattern"], "params": { "pattern_instance_refs": [{"pattern_feature_id": "boss_pattern", "source_feature_id": "boss_add", "instance_index": 1}], "transform": {"type": "translation", "translation_mm": [40, 0, 0]}, "make_copy": False, }}, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "circular-copy-transform.step") self.assertEqual(result["solid_count"], 4) self.assertAlmostEqual(result["volume_mm3"], 4 * math.pi * 2, places=5) self.assertEqual(result["bbox_mm"]["max"], [41.0, 11.0, 2.0]) self.assertEqual([item["feature_id"] for item in result["feature_results"]], ["boss_add", "boss_pattern", "move_copy"]) def test_boolean_can_union_copies_of_a_proven_fused_body_successor(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "fused-body-pattern-boolean", "meta": {"unit": "mm"}, "geometry": {"sketches": [ {"id": "base", "workplane": _workplane(), "profile": {"type": "circle", "center": [10, 0], "radius_mm": 2}}, {"id": "boss", "workplane": _workplane(), "profile": {"type": "circle", "center": [12, 0], "radius_mm": 1.5}}, ]}, "features": [ {"id": "base_add", "atomic_id": "extrude_add_blind", "depends_on": [], "sketch_id": "base", "params": {"distance_mm": 2, "result_mode": "new_body"}}, {"id": "boss_add", "atomic_id": "extrude_add_blind", "depends_on": ["base_add"], "sketch_id": "boss", "params": {"distance_mm": 2}}, {"id": "boss_pattern", "atomic_id": "pattern_circular", "depends_on": ["boss_add"], "params": { "source_feature_ids": ["boss_add"], "axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]}, "pattern_count": 3, "sweep_angle_deg": 360, }}, {"id": "join", "atomic_id": "boolean_bodies", "depends_on": ["boss_pattern"], "params": { "operation": "union", "target_feature_ids": ["boss_add"], "tool_pattern_instance_refs": [ {"pattern_feature_id": "boss_pattern", "source_feature_id": "boss_add", "instance_index": 1}, {"pattern_feature_id": "boss_pattern", "source_feature_id": "boss_add", "instance_index": 2}, ], "keep_tools": False, }}, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "fused-body-pattern-boolean.step") self.assertEqual(result["solid_count"], 3) self.assertEqual([item["feature_id"] for item in result["feature_results"]], [ "base_add", "boss_add", "boss_pattern", "join", ]) def test_transform_can_move_one_proven_mirror_pattern_copy(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "mirror-copy-transform", "meta": {"unit": "mm"}, "geometry": {"sketches": [{ "id": "boss", "workplane": _workplane(), "profile": {"type": "circle", "center": [10, 0], "radius_mm": 1}, }]}, "features": [ {"id": "mirror_plane", "atomic_id": "reference_plane", "depends_on": [], "params": { "plane": {"origin_mm": [0, 0, 0], "x_dir": [0, 1, 0], "normal": [1, 0, 0]}, }}, {"id": "boss_add", "atomic_id": "extrude_add_blind", "depends_on": [], "sketch_id": "boss", "params": { "distance_mm": 2, "result_mode": "new_body", }}, {"id": "mirror", "atomic_id": "pattern_mirror", "depends_on": ["boss_add", "mirror_plane"], "params": { "source_feature_ids": ["boss_add"], "mirror_current_body": True, "mirror_plane": {"kind": "plane", "owner_feature_id": "mirror_plane"}, }, "selectors": [{"kind": "plane", "owner_feature_id": "mirror_plane"}]}, {"id": "move_copy", "atomic_id": "transform_bodies", "depends_on": ["mirror"], "params": { "pattern_instance_refs": [{"pattern_feature_id": "mirror", "source_feature_id": "boss_add", "instance_index": 1}], "transform": {"type": "translation", "translation_mm": [0, 20, 0]}, "make_copy": False, }}, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "mirror-copy-transform.step") self.assertEqual(result["solid_count"], 2) self.assertAlmostEqual(result["volume_mm3"], 4 * math.pi, places=5) self.assertEqual(result["bbox_mm"], {"min": [-11.0, -1.0, 0.0], "max": [11.0, 21.0, 2.0]}) self.assertEqual([item["feature_id"] for item in result["feature_results"]], [ "mirror_plane", "boss_add", "mirror", "move_copy", ]) def test_boolean_can_union_one_proven_mirror_pattern_copy(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "mirror-copy-boolean", "meta": {"unit": "mm"}, "geometry": {"sketches": [{ "id": "boss", "workplane": _workplane(), "profile": {"type": "circle", "center": [10, 0], "radius_mm": 1}, }]}, "features": [ {"id": "mirror_plane", "atomic_id": "reference_plane", "depends_on": [], "params": { "plane": {"origin_mm": [0, 0, 0], "x_dir": [0, 1, 0], "normal": [1, 0, 0]}, }}, {"id": "boss_add", "atomic_id": "extrude_add_blind", "depends_on": [], "sketch_id": "boss", "params": { "distance_mm": 2, "result_mode": "new_body", }}, {"id": "mirror", "atomic_id": "pattern_mirror", "depends_on": ["boss_add", "mirror_plane"], "params": { "source_feature_ids": ["boss_add"], "mirror_current_body": True, "mirror_plane": {"kind": "plane", "owner_feature_id": "mirror_plane"}, }, "selectors": [{"kind": "plane", "owner_feature_id": "mirror_plane"}]}, {"id": "join", "atomic_id": "boolean_bodies", "depends_on": ["mirror"], "params": { "operation": "union", "target_feature_ids": ["boss_add"], "tool_pattern_instance_refs": [{ "pattern_feature_id": "mirror", "source_feature_id": "boss_add", "instance_index": 1, }], "keep_tools": False, }}, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "mirror-copy-boolean.step") self.assertEqual(result["solid_count"], 2) self.assertAlmostEqual(result["volume_mm3"], 4 * math.pi, places=5) self.assertEqual(result["bbox_mm"], {"min": [-11.0, -1.0, 0.0], "max": [11.0, 1.0, 2.0]}) self.assertEqual([item["feature_id"] for item in result["feature_results"]], [ "mirror_plane", "boss_add", "mirror", "join", ]) def test_boolean_pattern_instance_requires_a_surviving_member(self) -> None: cdsl = self._base_block() cdsl["features"][0]["params"]["result_mode"] = "new_body" cdsl["features"].extend([ { "id": "mirror_plane", "atomic_id": "reference_plane", "depends_on": [], "params": { "plane": {"origin_mm": [0, 0, 0], "x_dir": [0, 1, 0], "normal": [1, 0, 0]}, }, }, { "id": "mirror", "atomic_id": "pattern_mirror", "depends_on": ["base_add", "mirror_plane"], "params": { "source_feature_ids": ["base_add"], "mirror_plane": { "kind": "plane", "owner_feature_id": "mirror_plane", "stable_id": "mirror-plane", "source": "runtime_snapshot", "confidence": 1, }, }, "selectors": [{ "kind": "plane", "owner_feature_id": "mirror_plane", "stable_id": "mirror-plane", "source": "runtime_snapshot", "confidence": 1, }], }, { "id": "join", "atomic_id": "boolean_bodies", "depends_on": ["mirror"], "params": { "operation": "union", "target_feature_ids": ["base_add"], "tool_pattern_instance_refs": [{ "pattern_feature_id": "mirror", "source_feature_id": "base_add", "instance_index": 2, }], "keep_tools": False, }, }, ]) for feature in cdsl["features"]: feature["execution_status"] = "supported" from cdsl_engine.semantic_validation import validate_semantic_cdsl with self.assertRaisesRegex(ValueError, "not a surviving copy"): validate_semantic_cdsl(cdsl) analysis = CapabilityAnalyzer( atomic_ids={"extrude_add_blind", "reference_plane", "pattern_mirror", "boolean_bodies"}, profile_types=SHAPE_GENERATORS, ).analyze(cdsl) blocker = next(item for item in analysis.feature_results[-1].blockers if item.code == "pattern_instance_unavailable") self.assertEqual(blocker.detail, { "pattern_feature_id": "mirror", "source_feature_id": "base_add", "instance_index": 2, }) def test_mirror_pattern_instance_reference_rejects_any_instance_but_one(self) -> None: from cdsl_engine.semantic_validation import validate_semantic_cdsl cdsl = self._base_block() cdsl["features"][0]["params"]["result_mode"] = "new_body" cdsl["features"].extend([ { "id": "mirror_plane", "atomic_id": "reference_plane", "depends_on": [], "params": { "plane": {"origin_mm": [0, 0, 0], "x_dir": [0, 1, 0], "normal": [1, 0, 0]}, }, }, { "id": "mirror", "atomic_id": "pattern_mirror", "depends_on": ["base_add", "mirror_plane"], "params": { "source_feature_ids": ["base_add"], "mirror_plane": { "kind": "plane", "owner_feature_id": "mirror_plane", "stable_id": "mirror-plane", "source": "runtime_snapshot", "confidence": 1, }, }, "selectors": [{ "kind": "plane", "owner_feature_id": "mirror_plane", "stable_id": "mirror-plane", "source": "runtime_snapshot", "confidence": 1, }], }, { "id": "move", "atomic_id": "transform_bodies", "depends_on": ["mirror"], "params": { "pattern_instance_refs": [{"pattern_feature_id": "mirror", "source_feature_id": "base_add", "instance_index": 2}], "transform": {"type": "translation", "translation_mm": [1, 0, 0]}, "make_copy": False, }, }, ]) for feature in cdsl["features"]: feature["execution_status"] = "supported" with self.assertRaisesRegex(ValueError, "not a surviving copy"): validate_semantic_cdsl(cdsl) def test_mirror_pattern_instance_reference_requires_a_new_body_source(self) -> None: from cdsl_engine.semantic_validation import validate_semantic_cdsl cdsl = self._base_block() cdsl["features"].extend([ { "id": "mirror_plane", "atomic_id": "reference_plane", "depends_on": [], "params": { "plane": {"origin_mm": [0, 0, 0], "x_dir": [0, 1, 0], "normal": [1, 0, 0]}, }, }, { "id": "mirror", "atomic_id": "pattern_mirror", "depends_on": ["base_add", "mirror_plane"], "params": { "source_feature_ids": ["base_add"], "mirror_plane": { "kind": "plane", "owner_feature_id": "mirror_plane", "stable_id": "mirror-plane", "source": "runtime_snapshot", "confidence": 1, }, }, "selectors": [{ "kind": "plane", "owner_feature_id": "mirror_plane", "stable_id": "mirror-plane", "source": "runtime_snapshot", "confidence": 1, }], }, { "id": "move", "atomic_id": "transform_bodies", "depends_on": ["mirror"], "params": { "pattern_instance_refs": [{"pattern_feature_id": "mirror", "source_feature_id": "base_add", "instance_index": 1}], "transform": {"type": "translation", "translation_mm": [1, 0, 0]}, "make_copy": False, }, }, ]) for feature in cdsl["features"]: feature["execution_status"] = "supported" with self.assertRaisesRegex(ValueError, "requires a preceding new_body mirror source"): validate_semantic_cdsl(cdsl) def test_pattern_instance_reference_rejects_an_excluded_copy(self) -> None: cdsl = self._base_block() cdsl["features"][0]["params"]["result_mode"] = "new_body" cdsl["features"].extend([ { "id": "pattern", "atomic_id": "pattern_circular", "depends_on": ["base_add"], "params": { "source_feature_ids": ["base_add"], "axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]}, "pattern_count": 3, "sweep_angle_deg": 360, "excluded_instance_indices": [1], }, "execution_status": "supported", }, { "id": "move", "atomic_id": "transform_bodies", "depends_on": ["pattern"], "params": { "pattern_instance_refs": [{"pattern_feature_id": "pattern", "source_feature_id": "base_add", "instance_index": 1}], "transform": {"type": "translation", "translation_mm": [1, 0, 0]}, "make_copy": False, }, "execution_status": "supported", }, ]) cdsl["features"][0]["execution_status"] = "supported" from cdsl_engine.semantic_validation import validate_semantic_cdsl with self.assertRaisesRegex(ValueError, "not a surviving copy"): validate_semantic_cdsl(cdsl) def test_mirror_pattern_can_union_the_active_body(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "mirror-active-body", "meta": {"unit": "mm"}, "geometry": {"sketches": [ {"id": "base", "workplane": _workplane(), "profile": _rectangle([-5, -5], [5, 5])}, {"id": "half_cut", "workplane": _workplane(), "profile": _rectangle([-5, 0], [5, 5])}, {"id": "hole", "workplane": _workplane(), "profile": {"type": "circle", "center": [0, -2], "radius_mm": 1}}, ]}, "features": [ {"id": "mirror_plane", "atomic_id": "reference_plane", "depends_on": [], "params": {"plane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 1, 0]}}, "execution_status": "supported"}, {"id": "base_add", "atomic_id": "extrude_add_blind", "depends_on": ["mirror_plane"], "params": {"distance_mm": 2}, "sketch_id": "base", "execution_status": "supported"}, {"id": "half_remove", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 2}, "sketch_id": "half_cut", "execution_status": "supported"}, {"id": "hole_remove", "atomic_id": "extrude_cut_blind", "depends_on": ["half_remove"], "params": {"distance_mm": 2}, "sketch_id": "hole", "execution_status": "supported"}, { "id": "mirror", "atomic_id": "pattern_mirror", "depends_on": ["hole_remove", "mirror_plane"], "params": {"source_feature_ids": ["base_add"], "mirror_current_body": True, "mirror_plane": {"kind": "plane", "owner_feature_id": "mirror_plane"}}, "selectors": [{"kind": "plane", "owner_feature_id": "mirror_plane"}], "execution_status": "supported", }, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "mirror-active-body.step") self.assertAlmostEqual(result["volume_mm3"], 200 - 4 * 3.141592653589793, places=5) self.assertEqual(result["solid_count"], 1) def test_mirrored_local_circle_preserves_its_reflected_world_position(self) -> None: from cdsl_engine.build123d_adapter import Build123dGeometryAdapter from cdsl_engine.runtime import _mirrored_sketch from cdsl_engine.sketch_solver import resolve_all_sketches cdsl = { "schema": "cad.cdsl.llm.v1", "geometry": {"sketches": [{ "id": "circles", "workplane": _workplane(), "profile": {"type": "circle", "center": [2, 3], "radius_mm": 1}, }]}, "features": [], } sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0] mirror_plane = PlaneSpec.from_mapping({ "origin_mm": [0, 0, 0], "x_dir": [0, 1, 0], "normal": [1, 0, 0], }) mirrored = _mirrored_sketch(sketch, mirror_plane) face = Build123dGeometryAdapter().faces_for_sketch(mirrored)[0] center = face.center() self.assertAlmostEqual(center.X, -2.0) self.assertAlmostEqual(center.Y, 3.0) self.assertAlmostEqual(center.Z, 0.0) def test_circle_profile_retains_one_topological_circle_edge(self) -> None: from cdsl_engine.build123d_adapter import Build123dGeometryAdapter cdsl = { "schema": "cad.cdsl.llm.v1", "geometry": {"sketches": [{ "id": "circle", "workplane": _workplane(), "profile": {"type": "circle", "center": [2, 3], "radius_mm": 1}, }]}, "features": [], } sketch = resolve_all_sketches(cdsl)["geometry"]["sketches"][0] self.assertNotIn("contour_edges_mm", sketch) face = Build123dGeometryAdapter().faces_for_sketch(sketch)[0] self.assertEqual(len(face.outer_wire().edges()), 1) def test_new_body_mode_preserves_an_overlapping_result_body(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "overlap", "workplane": _workplane(), "profile": _rectangle([-2, -5], [8, 5]), }) cdsl["features"].append({ "id": "second_body", "atomic_id": "extrude_add_blind", "depends_on": ["base_add"], "params": {"distance_mm": 10, "result_mode": "new_body"}, "sketch_id": "overlap", }) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "two-bodies.step") self.assertEqual(result["solid_count"], 2) def test_sweep_add_builds_a_solid_from_a_closed_profile_and_bspline_path(self) -> None: from cdsl_engine.runtime import rebuild_cdsl profile_plane = {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 1, 0]} cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "sweep-add", "meta": {"unit": "mm"}, "geometry": {"sketches": [{ "id": "profile", "workplane": profile_plane, "profile": {"type": "circle", "radius_mm": 2}, }]}, "features": [{ "id": "sweep", "atomic_id": "sweep_add", "depends_on": [], "sketch_id": "profile", "params": {"path": { "workplane": _workplane(), "segment": { "type": "bspline", "start": [0, 0], "end": [0, 20], "points": [[0, 0], [0, 10], [0, 20]], "start_tangent": [0, 10], "end_tangent": [0, 10], }, }}, }], } with tempfile.TemporaryDirectory() as directory: root = Path(directory) result = rebuild_cdsl(cdsl, root / "sweep.step") shelled = deepcopy(cdsl) shelled["features"].append({ "id": "shell", "atomic_id": "shell", "depends_on": ["sweep"], "params": {"thickness_mm": 0.25, "inward": True}, "selectors": [{ "kind": "face", "owner_feature_id": "sweep", "output_role": "sweep.end", "source": "runtime_snapshot", "confidence": 1, }], }) downstream = rebuild_cdsl(shelled, root / "sweep-shell.step") self.assertEqual(result["solid_count"], 1) self.assertAlmostEqual(result["volume_mm3"], 80 * math.pi, delta=1e-4) delta = next(item for item in result["topology_deltas"] if item["operation"] == "sweep") self.assertEqual( {item.get("output_role") for item in delta["relations"]}, {"sweep.start", "sweep.end"}, ) self.assertTrue(all( item["event"] == "generated" and item["status"] == "recorded_without_owner_transfer" and item["result_record_ids"] for item in delta["relations"] )) self.assertTrue(any( item["feature_id"] == "sweep" and item.get("output_roles") == ["sweep.end"] for item in result["topology_records"] )) self.assertEqual(downstream["runtime_diagnostics"], []) self.assertTrue(any( item["feature_id"] == "shell" and item["status"] == "resolved" and item["selected"].get("output_roles") == ["sweep.end"] for item in downstream["selector_resolution"] )) def test_sweep_history_falls_back_for_hollow_profiles(self) -> None: from build123d import Edge, Face, Plane, Vector, Wire from cdsl_engine.build123d_adapter import Build123dGeometryAdapter adapter = Build123dGeometryAdapter() outer = Face(Wire.make_circle(4, Plane.XZ)) inner = Face(Wire.make_circle(2, Plane.XZ)) profile = adapter.face_with_holes(outer, [inner]) result, delta = adapter.sweep_with_topology_delta( profile, Edge.make_line(Vector(0, 0, 0), Vector(0, 10, 0)), ) self.assertIsNone(delta) self.assertAlmostEqual(float(result.volume), 120 * math.pi, places=5) def test_sweep_history_matches_native_builder_for_a_curved_path(self) -> None: from build123d import Edge, Face, Plane, Vector, Wire from cdsl_engine.build123d_adapter import Build123dGeometryAdapter adapter = Build123dGeometryAdapter() profile = Face(Wire.make_circle(2, Plane.XZ)) path = Edge.make_spline([ Vector(0, 0, 0), Vector(0, 10, 0), Vector(5, 20, 0), ], scale=False) direct, delta = adapter.sweep_with_topology_delta(profile, path) native = adapter._sweep_without_topology_delta(profile, path) self.assertIsNotNone(delta) self.assertTrue(direct.is_valid) self.assertAlmostEqual(float(direct.volume), float(native.volume), places=5) self.assertEqual(len(direct.faces()), len(native.faces())) self.assertEqual( [relation.output_role for relation in delta.relations], ["sweep.start", "sweep.end"], ) def test_initial_loft_captures_builder_proven_cap_evidence(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "loft-history", "meta": {"unit": "mm"}, "geometry": {"sketches": [ {"id": "lower", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 2}}, { "id": "upper", "workplane": {"origin_mm": [0, 0, 10], "x_dir": [1, 0, 0], "normal": [0, 0, 1]}, "profile": {"type": "circle", "radius_mm": 4}, }, { "id": "extension", "workplane": {"origin_mm": [0, 0, 20], "x_dir": [1, 0, 0], "normal": [0, 0, 1]}, "profile": {"type": "circle", "radius_mm": 3}, }, ]}, "features": [{ "id": "loft", "atomic_id": "loft_add", "depends_on": [], "params": {"profile_sketch_ids": ["lower", "upper"]}, }], } with tempfile.TemporaryDirectory() as directory: root = Path(directory) result = rebuild_cdsl(cdsl, root / "loft.step") extended = deepcopy(cdsl) extended["features"].append({ "id": "loft_extension", "atomic_id": "loft_add_with_cap_face", "depends_on": ["loft"], "params": {"profile_sketch_ids": ["extension"]}, "selectors": [{ "kind": "face", "owner_feature_id": "loft", "output_role": "loft.end", "source": "runtime_snapshot", "confidence": 1, }], }) downstream = rebuild_cdsl(extended, root / "loft-extension.step") self.assertAlmostEqual(result["volume_mm3"], 280 * math.pi / 3, places=5) delta = next(item for item in result["topology_deltas"] if item["operation"] == "loft") self.assertEqual( {item.get("output_role") for item in delta["relations"]}, {"loft.start", "loft.end"}, ) self.assertTrue(all( item["event"] == "generated" and item["status"] == "recorded_without_owner_transfer" and item["result_record_ids"] for item in delta["relations"] )) self.assertTrue(any( item["feature_id"] == "loft" and item.get("output_roles") == ["loft.end"] for item in result["topology_records"] )) self.assertEqual(downstream["runtime_diagnostics"], []) self.assertTrue(any( item["feature_id"] == "loft_extension" and item["status"] == "resolved" and item["selected"].get("output_roles") == ["loft.end"] for item in downstream["selector_resolution"] )) def test_circular_pattern_rotates_a_sweep_profile_and_path(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "sweep-pattern", "meta": {"unit": "mm"}, "geometry": {"sketches": [{ "id": "profile", "workplane": {"origin_mm": [10, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 0, -1]}, "profile": {"type": "circle", "radius_mm": 1}, }]}, "features": [ {"id": "sweep", "atomic_id": "sweep_add", "depends_on": [], "sketch_id": "profile", "params": {"path": { "workplane": {"origin_mm": [0, 0, 0], "x_dir": [1, 0, 0], "normal": [0, 1, 0]}, "segment": {"type": "line", "start": [10, 0], "end": [10, 10]}, }}}, {"id": "pattern", "atomic_id": "pattern_circular", "depends_on": ["sweep"], "params": { "source_feature_ids": ["sweep"], "axis": {"origin_mm": [0, 0, 0], "direction": [0, 0, 1]}, "pattern_count": 3, "sweep_angle_deg": 360, }}, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "sweep-pattern.step") self.assertEqual(result["solid_count"], 3) self.assertAlmostEqual(result["volume_mm3"], 30 * math.pi, delta=1e-4) def test_shell_removes_a_selected_cap_and_offsets_inward(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "baseline.step") top = next( item for item in baseline["topology_records"] if item["kind"] == "face" and item["feature_id"] == "base_add" and item["geometry"].get("surface_type") == "plane" and item["geometry"].get("normal", [0, 0, 0])[2] > 0.9 ) shelled = deepcopy(base) shelled["features"].append({ "id": "shell", "atomic_id": "shell", "depends_on": ["base_add"], "params": {"thickness_mm": 1, "inward": True}, "selectors": [{ "kind": "face", "stable_id": top["record_id"], "snapshot_id": top["record_id"], "source": "runtime_snapshot", "confidence": 1, "owner_feature_id": "base_add", "geometry": top["geometry"], }], }) result = rebuild_cdsl(shelled, root / "shell.step") self.assertEqual(result["solid_count"], 1) self.assertAlmostEqual(result["volume_mm3"], 424.0, places=5) def test_shell_removes_a_selected_cap_and_offsets_outward(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "baseline.step") top = next( item for item in baseline["topology_records"] if item["kind"] == "face" and item["feature_id"] == "base_add" and item["geometry"].get("surface_type") == "plane" and item["geometry"].get("normal", [0, 0, 0])[2] > 0.9 ) shelled = deepcopy(base) shelled["features"].append({ "id": "shell", "atomic_id": "shell", "depends_on": ["base_add"], "params": {"thickness_mm": 1, "inward": False}, "selectors": [{ "kind": "face", "stable_id": top["record_id"], "snapshot_id": top["record_id"], "source": "runtime_snapshot", "confidence": 1, "owner_feature_id": "base_add", "geometry": top["geometry"], }], }) result = rebuild_cdsl(shelled, root / "shell-outward.step") self.assertEqual(result["solid_count"], 1) # A shell is hollow on either side of the source skin. This confirms # the exterior offset reached a distinct OCC result rather than # silently using the inward default (424 mm^3 for this fixture). self.assertGreater(result["volume_mm3"], 424.0) def test_shell_explicit_target_requires_the_live_face_member(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "baseline.step") top = next( item for item in baseline["topology_records"] if item["kind"] == "face" and item["feature_id"] == "base_add" and item["geometry"].get("surface_type") == "plane" and item["geometry"].get("normal", [0, 0, 0])[2] > 0.9 ) shelled = deepcopy(base) shelled["features"].append({ "id": "shell", "atomic_id": "shell", "depends_on": ["base_add"], "params": {"thickness_mm": 1, "inward": True, "target_feature_id": "base_add"}, "selectors": [{ "kind": "face", "stable_id": top["record_id"], "snapshot_id": top["record_id"], "source": "runtime_snapshot", "confidence": 1, "owner_feature_id": "base_add", "geometry": top["geometry"], }], }) result = rebuild_cdsl(shelled, root / "shell.step") self.assertEqual(result["solid_count"], 1) self.assertAlmostEqual(result["volume_mm3"], 424.0, places=5) invalid = deepcopy(shelled) invalid["features"][-1]["params"]["target_feature_id"] = "missing_body" analysis = CapabilityAnalyzer( atomic_ids={"extrude_add_blind", "shell"}, profile_types=SHAPE_GENERATORS, ).analyze(invalid) self.assertIn( "shell_target_body_unavailable", [blocker.code for blocker in analysis.feature_results[-1].blockers], ) def test_fillet_and_hole_wizard_use_resolved_face_edge_selectors(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "baseline.step") edge = next(item for item in baseline["topology_records"] if item["kind"] == "edge") top_face = next( item for item in baseline["topology_records"] if item["kind"] == "face" and item["geometry"]["surface_type"] == "plane" and item["geometry"]["normal"][2] > 0.9 ) with_fillet = deepcopy(base) with_fillet["features"].append({ "id": "fillet", "atomic_id": "fillet", "depends_on": ["base_add"], "params": {"radius_mm": 1}, "selectors": [{"kind": "edge", "stable_id": "edge", "source": "solidworks", "confidence": 1, "owner_feature_id": "base_add", "geometry": edge["geometry"]}], }) filleted = rebuild_cdsl(with_fillet, root / "fillet.step") with_hole = deepcopy(base) with_hole["features"].append({ "id": "hole", "atomic_id": "hole_wizard", "depends_on": ["base_add"], "params": { "hole_type": "plain", "diameter_mm": 2, "depth_mm": 5, "end_condition": {"type": "blind", "solidworks_code": 0}, "positions": [{"mm": [0, 0, 10]}], "host_face": {"kind": "face", "stable_id": "top", "source": "inferred_from_step", "confidence": 1, "geometry": top_face["geometry"]}, }, "selectors": [{"kind": "face", "stable_id": "top", "source": "inferred_from_step", "confidence": 1, "geometry": top_face["geometry"]}], }) holed = rebuild_cdsl(with_hole, root / "hole.step") self.assertLess(filleted["volume_mm3"], baseline["volume_mm3"]) self.assertAlmostEqual(holed["volume_mm3"], 1000 - 5 * 3.141592653589793, places=5) def test_hole_frame_uses_local_coordinates(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() base["features"].append({ "id": "hole", "atomic_id": "hole_blind", "depends_on": ["base_add"], "sketch_id": "base", "params": { "diameter_mm": 2, "depth_mm": 5, "positions": [{"mm": [0, 0, 0]}], "host_face": {"frame": {"origin_mm": [0, 0, 10], "x_dir": [1, 0, 0], "y_dir": [0, 1, 0], "normal": [0, 0, 1]}}, }, }) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(base, Path(directory) / "local-hole.step") self.assertAlmostEqual(result["volume_mm3"], 1000 - 5 * 3.141592653589793, places=5) def test_counterbore_and_countersink_holes_execute_with_explicit_host_frames(self) -> None: """Keep both legacy hole contracts covered by an actual kernel rebuild.""" from cdsl_engine.runtime import rebuild_cdsl host_frame = { "origin_mm": [0, 0, 10], "x_dir": [1, 0, 0], "y_dir": [0, 1, 0], "normal": [0, 0, 1], } for atomic_id, details in ( ("hole_counterbore", {"counterbore_diameter_mm": 4, "counterbore_depth_mm": 2}), ("hole_countersink", {"countersink_diameter_mm": 4, "countersink_angle_rad": 1.5707963267948966}), ): with self.subTest(atomic_id=atomic_id): cdsl = self._base_block() cdsl["features"].append({ "id": atomic_id, "atomic_id": atomic_id, "depends_on": ["base_add"], "params": { "diameter_mm": 2, "depth_mm": 5, "positions": [{"mm": [0, 0, 0]}], "host_face": {"frame": host_frame}, **details, }, }) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / f"{atomic_id}.step") self.assertLess(result["volume_mm3"], 1000.0) self.assertIn(atomic_id, [item["feature_id"] for item in result["feature_results"]]) def test_sphere_add_executes_without_a_sketch(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "sphere", "meta": {"unit": "mm"}, "geometry": {"sketches": []}, "features": [{ "id": "sphere", "atomic_id": "sphere_add", "depends_on": [], "params": {"radius_mm": 2, "center_mm": [0, 0, 0]}, }], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "sphere.step") self.assertAlmostEqual(result["volume_mm3"], 32 * 3.141592653589793 / 3, places=5) self.assertEqual(result["feature_results"][-1]["feature_id"], "sphere") def test_pattern_with_selector_source_is_blocked_before_execution(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["features"].extend([ {"id": "fillet", "atomic_id": "fillet", "depends_on": ["base_add"], "params": {"radius_mm": 1}, "selectors": [{"kind": "edge", "stable_id": "edge", "source": "solidworks", "confidence": 1}]}, {"id": "repeat", "atomic_id": "pattern_linear", "depends_on": ["fillet"], "params": {"source_feature_ids": ["fillet"], "direction_1": [1, 0, 0], "spacing_1_mm": 10, "pattern_count_1": 2}}, ]) analysis = analyze_cdsl(cdsl) pattern = next(item for item in analysis.feature_results if item.feature_id == "repeat") self.assertFalse(pattern.executable) self.assertIn("unsupported_pattern_selector_transform", [item.code for item in pattern.blockers]) def test_chamfer_and_linear_pattern_execute_without_selector_guessing(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() base["geometry"]["sketches"].append({ "id": "cut", "workplane": _workplane(), "profile": {"type": "circle", "center": [-2, 0], "radius_mm": 1}, }) with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "baseline.step") edge = next(item for item in baseline["topology_records"] if item["kind"] == "edge") chamfered = deepcopy(base) chamfered["features"].append({ "id": "chamfer", "atomic_id": "chamfer", "depends_on": ["base_add"], "params": {"distance_mm": 1}, "selectors": [{"kind": "edge", "stable_id": "edge", "source": "solidworks", "confidence": 1, "owner_feature_id": "base_add", "geometry": edge["geometry"]}], }) chamfer_result = rebuild_cdsl(chamfered, root / "chamfer.step") patterned = deepcopy(base) patterned["features"].extend([ {"id": "cut_1", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 10}, "sketch_id": "cut"}, {"id": "repeat", "atomic_id": "pattern_linear", "depends_on": ["cut_1"], "params": {"source_feature_ids": ["cut_1"], "direction_1": [1, 0, 0], "spacing_1_mm": 2, "pattern_count_1": 3}}, ]) pattern_result = rebuild_cdsl(patterned, root / "pattern.step") self.assertLess(chamfer_result["volume_mm3"], baseline["volume_mm3"]) self.assertAlmostEqual(pattern_result["volume_mm3"], 1000 - 3 * 10 * 3.141592653589793, places=5) def test_chamfer_consumes_angle_rad_instead_of_silent_45_degree_fallback(self) -> None: from cdsl_engine.runtime import rebuild_cdsl import math base = self._base_block() with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "baseline.step") edge = next(item for item in baseline["topology_records"] if item["kind"] == "edge") selector = { "kind": "edge", "stable_id": "edge", "source": "solidworks", "confidence": 1, "owner_feature_id": "base_add", "geometry": edge["geometry"], } equal = deepcopy(base) equal["features"].append({ "id": "chamfer_45", "atomic_id": "chamfer", "depends_on": ["base_add"], "params": {"distance_mm": 1, "angle_rad": math.pi / 4}, "selectors": [deepcopy(selector)], }) equal_result = rebuild_cdsl(equal, root / "chamfer-45.step") # 45° Distance-Angle 等价于等距倒角:tan(45°)=1,切掉 0.5*1*1*10=5 mm³。 self.assertAlmostEqual(equal_result["volume_mm3"], 1000 - 5, places=5) slanted = deepcopy(base) slanted["features"].append({ "id": "chamfer_30", "atomic_id": "chamfer", "depends_on": ["base_add"], "params": {"distance_mm": 1, "angle_rad": math.pi / 6}, "selectors": [deepcopy(selector)], }) slanted_result = rebuild_cdsl(slanted, root / "chamfer-30.step") # 30°:第二距离 = 1*tan(30°)≈0.577,切掉 0.5*1*0.577*10≈2.887 mm³, # 体积明显大于 45° 等距倒角(995),验证 angle_rad 被消费而非静默 45°。 self.assertAlmostEqual(slanted_result["volume_mm3"], 1000 - 0.5 * math.tan(math.pi / 6) * 10, places=5) def test_linear_pattern_replays_hole_with_explicit_host_frame(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["features"].extend([ { "id": "hole_1", "atomic_id": "hole_blind", "depends_on": ["base_add"], "sketch_id": "base", "params": { "diameter_mm": 2, "depth_mm": 5, "positions": [{"mm": [0, 0, 0]}], "host_face": {"frame": { "origin_mm": [0, 0, 10], "x_dir": [1, 0, 0], "y_dir": [0, 1, 0], "normal": [0, 0, 1], }}, }, }, { "id": "repeat", "atomic_id": "pattern_linear", "depends_on": ["hole_1"], "params": { "source_feature_ids": ["hole_1"], "direction_1": [1, 0, 0], "spacing_1_mm": 4, "pattern_count_1": 2, }, }, ]) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "patterned-holes.step") self.assertAlmostEqual(result["volume_mm3"], 1000 - 2 * 5 * 3.141592653589793, places=5) def test_mirror_pattern_replays_local_hole_coordinates_in_the_correct_quadrant(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["features"].insert(0, { "id": "mirror_plane", "atomic_id": "reference_plane", "depends_on": [], "params": {"plane": {"origin_mm": [0, 0, 0], "x_dir": [0, 1, 0], "normal": [1, 0, 0]}}, }) cdsl["features"].extend([ { "id": "hole_1", "atomic_id": "hole_blind", "depends_on": ["base_add"], "sketch_id": "base", "params": { "diameter_mm": 2, "depth_mm": 5, "positions": [{"mm": [2, 3, 0]}], "host_face": {"frame": { "origin_mm": [0, 0, 10], "x_dir": [1, 0, 0], "y_dir": [0, 1, 0], "normal": [0, 0, 1], }}, }, }, { "id": "mirror", "atomic_id": "pattern_mirror", "depends_on": ["hole_1", "mirror_plane"], "params": { "source_feature_ids": ["hole_1"], "mirror_plane": {"kind": "plane", "owner_feature_id": "mirror_plane"}, }, "selectors": [{"kind": "plane", "owner_feature_id": "mirror_plane"}], }, ]) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "mirrored-holes.step") mirrored_cylinder = next( item for item in result["topology_records"] if item["record_id"].startswith("body:mirror.m.hole_1:face") and item["geometry"].get("surface_type") == "cylinder" and item["geometry"]["bbox_mm"][0] < -2.9 ) self.assertEqual(mirrored_cylinder["geometry"]["bbox_mm"][:2], [-3.0, 2.0]) self.assertEqual(mirrored_cylinder["geometry"]["bbox_mm"][3:5], [-1.0, 4.0]) def test_pattern_replays_selected_sources_in_history_order(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "cut", "workplane": _workplane(), "profile": {"type": "circle", "center": [0, 0], "radius_mm": 1}, }) cdsl["features"].extend([ { "id": "cut_1", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 10}, "sketch_id": "cut", }, { "id": "repeat", "atomic_id": "pattern_linear", "depends_on": ["cut_1"], # SolidWorks selection exports are not guaranteed to match # history order; the executor must replay the boss before its cut. "params": { "source_feature_ids": ["cut_1", "base_add"], "direction_1": [1, 0, 0], "spacing_1_mm": 20, "pattern_count_1": 2, }, }, ]) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "ordered-pattern.step") self.assertAlmostEqual(result["volume_mm3"], 2 * (1000 - 10 * 3.141592653589793), places=5) def test_through_all_extent_uses_current_body(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "cut", "workplane": _workplane(), "profile": {"type": "circle", "center": [0, 0], "radius_mm": 1}, }) cdsl["features"].append({ "id": "cut_all", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 0, "end_condition": {"type": "through_all", "solidworks_code": 1}}, "sketch_id": "cut", }) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "through.step") self.assertAlmostEqual(result["volume_mm3"], 1000 - 10 * 3.141592653589793, places=5) def test_two_sided_extrude_uses_independent_forward_and_reverse_distances(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() feature = cdsl["features"][0] feature["atomic_id"] = "extrude_add_two_sided" feature["params"] = { "distance_mm": 2, "reverse_distance_mm": 3, "end_condition": {"type": "blind"}, "reverse_end_condition": {"type": "blind"}, } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "two-sided.step") self.assertAlmostEqual(result["volume_mm3"], 500.0) self.assertEqual(result["bbox_mm"]["min"][2], -3.0) self.assertEqual(result["bbox_mm"]["max"][2], 2.0) def test_two_sided_extrude_requires_reverse_distance_contract(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() feature = cdsl["features"][0] feature["atomic_id"] = "extrude_add_two_sided" analysis = analyze_cdsl(cdsl) self.assertIn("missing_parameter", [item.code for item in analysis.feature_results[0].blockers]) def test_two_sided_cut_uses_independent_forward_and_reverse_distances(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "cut", "workplane": {**_workplane(), "origin_mm": [0, 0, 5]}, "profile": {"type": "circle", "center": [0, 0], "radius_mm": 1}, }) cdsl["features"].append({ "id": "cut_1", "atomic_id": "extrude_cut_two_sided", "depends_on": ["base_add"], "params": { "distance_mm": 3, "reverse_distance_mm": 5, "end_condition": {"type": "blind"}, "reverse_end_condition": {"type": "blind"}, }, "sketch_id": "cut", }) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "two-sided-cut.step") self.assertAlmostEqual(result["volume_mm3"], 1000.0 - 8.0 * 3.141592653589793, places=5) def test_revolve_can_resolve_an_owner_qualified_reference_axis(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "axis-revolve", "meta": {"unit": "mm"}, "geometry": {"sketches": [{ "id": "profile", "workplane": _workplane(), "profile": _rectangle([2, 1], [4, 2]), }]}, "features": [ { "id": "axis", "atomic_id": "reference_axis", "depends_on": [], "params": {"axis": {"origin_mm": [0, 0, 0], "direction": [1, 0, 0]}}, }, { "id": "revolve", "atomic_id": "revolve_add", "depends_on": ["axis"], "sketch_id": "profile", "params": { "angle_deg": 360, "axis": {"selector": {"kind": "axis", "owner_feature_id": "axis"}}, }, "selectors": [{"kind": "axis", "owner_feature_id": "axis"}], }, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "axis-revolve.step") self.assertAlmostEqual(result["volume_mm3"], 6 * 3.141592653589793, places=5) def test_revolve_cut_executes_against_an_existing_body(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "revolve_cut_profile", "workplane": _workplane(), "profile": _rectangle([-4, 1], [4, 2]), }) cdsl["features"].append({ "id": "revolve_cut", "atomic_id": "revolve_cut", "depends_on": ["base_add"], "sketch_id": "revolve_cut_profile", "params": {"angle_deg": 360, "axis": {"origin_mm": [0, 0, 0], "direction": [1, 0, 0]}}, }) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "revolve-cut.step") self.assertLess(result["volume_mm3"], 1000.0) self.assertIn("revolve_cut", [item["feature_id"] for item in result["feature_results"]]) def test_revolve_surface_preserves_the_active_solid_and_registers_a_shell(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "surface_profile", "workplane": _workplane(), "profile": _rectangle([2, 1], [4, 2]), }) cdsl["features"].append({ "id": "surface_revolve", "atomic_id": "revolve_surface", "depends_on": ["base_add"], "sketch_id": "surface_profile", "params": {"angle_deg": 360, "axis": {"origin_mm": [0, 0, 0], "direction": [1, 0, 0]}}, }) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "surface-revolve.step") surface = next(item for item in result["feature_results"] if item["feature_id"] == "surface_revolve") self.assertNotIn("body_id", surface) self.assertEqual(surface["surface_id"], "surface:surface_revolve") self.assertEqual(result["solid_count"], 1) self.assertAlmostEqual(result["volume_mm3"], 1000.0) faces = [ item for item in result["topology_records"] if item["feature_id"] == "surface_revolve" and item["kind"] == "face" ] self.assertEqual(len(faces), 4) def test_extrude_surface_preserves_the_active_solid_and_registers_a_shell(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = self._base_block() cdsl["geometry"]["sketches"].append({ "id": "surface_profile", "workplane": _workplane(), "profile": { "type": "analytic_contours", "contours": [{ "role": "unknown", "closed": True, "segments": [{"type": "circle", "center": [5, 5], "radius_mm": 2}], }], }, }) cdsl["features"].append({ "id": "surface_extrude", "atomic_id": "extrude_surface", "depends_on": ["base_add"], "sketch_id": "surface_profile", "params": {"distance_mm": 10}, }) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "surface-extrude.step") surface = next(item for item in result["feature_results"] if item["feature_id"] == "surface_extrude") self.assertNotIn("body_id", surface) self.assertEqual(surface["surface_id"], "surface:surface_extrude") self.assertEqual(result["solid_count"], 1) self.assertAlmostEqual(result["volume_mm3"], 1000.0) faces = [ item for item in result["topology_records"] if item["feature_id"] == "surface_extrude" and item["kind"] == "face" ] self.assertEqual(len(faces), 1) def test_extrude_surface_exports_a_surface_only_step(self) -> None: from build123d import import_step from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "surface-only", "meta": {"unit": "mm"}, "geometry": {"sketches": [{ "id": "surface_profile", "workplane": _workplane(), "profile": { "type": "analytic_contours", "contours": [{ "role": "unknown", "closed": True, "segments": [{"type": "circle", "center": [5, 5], "radius_mm": 2}], }], }, }]}, "features": [{ "id": "surface_extrude", "atomic_id": "extrude_surface", "depends_on": [], "sketch_id": "surface_profile", "params": {"distance_mm": 10}, }], } with tempfile.TemporaryDirectory() as directory: out_step = Path(directory) / "surface-only.step" result = rebuild_cdsl(cdsl, out_step) rebuilt = import_step(str(out_step)) self.assertEqual(result["solid_count"], 0) self.assertEqual(result["surface_count"], 1) self.assertEqual(result["surface_face_count"], 1) self.assertAlmostEqual(result["volume_mm3"], 0.0) self.assertAlmostEqual(result["surface_area_mm2"], 40 * math.pi) self.assertEqual(len(rebuilt.solids()), 0) self.assertEqual(len(rebuilt.faces()), 1) def test_surface_limited_chamfer_requires_an_explicit_shell_boundary(self) -> None: from build123d import Plane, Solid from cdsl_engine.build123d_adapter import Build123dGeometryAdapter adapter = Build123dGeometryAdapter() base = Solid.make_cylinder(19, 20, Plane(origin=(0, 0, -10))).cut( Solid.make_cylinder(16, 20, Plane(origin=(0, 0, -10))), ) lower = Solid.make_cylinder(28.5, 20, Plane(origin=(0, 0, -20))).cut( Solid.make_cylinder(16, 20, Plane(origin=(0, 0, -20))), ) upper = Solid.make_cylinder(25.5, 25, Plane(origin=(0, 0, 10))).cut( Solid.make_cylinder(17.5, 25, Plane(origin=(0, 0, 10))), ) body = adapter.fuse(adapter.fuse(base, lower), upper) selected = [ edge for edge in body.edges() if str(edge.geom_type).split(".")[-1].lower() == "circle" and abs(float(edge.radius) - 28.5) <= 1e-6 and abs(edge.arc_center.Z) <= 1e-6 ] self.assertEqual(len(selected), 1) with self.assertRaisesRegex(ValueError, "explicit surface support"): adapter.surface_limited_chamfer(body, 10, selected, []) wire = adapter._circle_wire([0, 0], 19, PlaneSpec.from_mapping(_workplane())) support = adapter.extrude_surface([wire], [0, 0, 10]) rebuilt = adapter.surface_limited_chamfer(body, 10, selected, [support]) self.assertLess(rebuilt.volume, body.volume) cones = [face for face in rebuilt.faces() if str(face.geom_type).split(".")[-1].lower() == "cone"] self.assertEqual(len(cones), 1) def test_unowned_revolve_feature_selector_is_preflight_blocked(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = json.loads((ROOT / "json_to_cdsl" / "output" / "027784.cdsl.json").read_text(encoding="utf-8")) analysis = analyze_cdsl(cdsl) revolve = next(item for item in analysis.feature_results if item.feature_id == "f_007") self.assertIn("missing_revolve_axis", [item.code for item in revolve.blockers]) def test_tangent_propagation_does_not_expand_to_unrelated_box_edges(self) -> None: from build123d import Box from cdsl_engine.build123d_adapter import Build123dGeometryAdapter body = Box(10, 10, 10) selected = body.edges()[0] expanded = Build123dGeometryAdapter().tangent_edges(body, [selected]) self.assertEqual(len(expanded), 1) self.assertTrue(expanded[0].is_same(selected)) def test_inconsistent_quarter_arc_flags_are_normalized_from_loop_orientation(self) -> None: from cdsl_engine.batch_rebuild import analyze_document fixture = ROOT / "json_to_cdsl" / "output" / "053393.cdsl.json" with tempfile.TemporaryDirectory() as directory: report = analyze_document(fixture, out_step=Path(directory) / "053393.step") self.assertTrue(report["runtime_eligible"]) self.assertTrue(report["built"]) self.assertTrue(report["geometry_verified"]) def test_exported_two_sided_fixture_is_geometry_verified(self) -> None: from cdsl_engine.batch_rebuild import analyze_document fixture = ROOT / "json_to_cdsl" / "output" / "046112.cdsl.json" with tempfile.TemporaryDirectory() as directory: report = analyze_document(fixture, out_step=Path(directory) / "046112.step") self.assertTrue(report["runtime_eligible"]) self.assertTrue(report["built"]) self.assertTrue(report["geometry_verified"]) def test_selector_driven_extrude_extents_require_unique_rebuilt_topology(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() base["geometry"]["sketches"].append({ "id": "cut", "workplane": _workplane(), "profile": {"type": "circle", "center": [0, 0], "radius_mm": 1}, }) with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "baseline.step") top_face = next( item for item in baseline["topology_records"] if item["kind"] == "face" and item["geometry"]["surface_type"] == "plane" and item["geometry"]["normal"][2] > 0.9 ) top_vertex = next( item for item in baseline["topology_records"] if item["kind"] == "vertex" and item["geometry"]["center_mm"][2] > 9.9 ) for name, end_condition, expected_depth in ( ("surface", {"type": "up_to_surface", "reference": {"kind": "face", "owner_feature_id": "base_add", "geometry": top_face["geometry"]}}, 10.0), ("vertex", {"type": "up_to_vertex", "reference": {"kind": "vertex", "owner_feature_id": "base_add", "geometry": top_vertex["geometry"]}}, 10.0), ("offset", {"type": "offset_from_surface", "reference": {"kind": "face", "owner_feature_id": "base_add", "geometry": top_face["geometry"]}}, 8.0), ("next", {"type": "through_next"}, 10.0), ): cdsl = deepcopy(base) cdsl["features"].append({ "id": f"cut_{name}", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 2 if name == "offset" else 0, "end_condition": end_condition}, "sketch_id": "cut", }) result = rebuild_cdsl(cdsl, root / f"{name}.step") self.assertAlmostEqual(result["volume_mm3"], 1000 - expected_depth * 3.141592653589793, places=5) def test_up_to_vertex_intersection_requires_one_shared_current_body_vertex(self) -> None: from cdsl_engine.runtime import RuntimeExecutionError, rebuild_cdsl base = self._base_block() base["geometry"]["sketches"].append({ "id": "cut", "workplane": _workplane(), "profile": {"type": "circle", "center": [0, 0], "radius_mm": 1}, }) with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "intersection-baseline.step") faces = [item for item in baseline["topology_records"] if item["kind"] == "face"] top = next(item for item in faces if item["geometry"]["normal"][2] > 0.9) right = next(item for item in faces if item["geometry"]["normal"][0] > 0.9) back = next(item for item in faces if item["geometry"]["normal"][1] > 0.9) def face_selector(face: dict) -> dict: return { "kind": "face", "owner_feature_id": "base_add", "stable_id": f"intersection-{face['record_id']}", "source": "runtime_snapshot", "confidence": 1.0, "geometry": face["geometry"], } cdsl = deepcopy(base) cdsl["features"].append({ "id": "cut_to_intersection", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 0, "end_condition": { "type": "up_to_vertex", "reference": { "kind": "vertex", "owner_feature_id": "base_add", "stable_id": "top-right-back", "source": "runtime_snapshot", "confidence": 1.0, "intersection_of": [face_selector(top), face_selector(right), face_selector(back)], }, }}, "sketch_id": "cut", }) result = rebuild_cdsl(cdsl, root / "intersection.step") self.assertAlmostEqual(result["volume_mm3"], 1000 - 10 * 3.141592653589793, places=5) ambiguous = deepcopy(cdsl) ambiguous["features"][-1]["params"]["end_condition"]["reference"]["intersection_of"] = [ face_selector(right), face_selector(back), ] with self.assertRaises(RuntimeExecutionError) as error: rebuild_cdsl(ambiguous, root / "ambiguous-intersection.step") self.assertEqual(error.exception.diagnostic.code, "intersection_vertex_unresolved") def test_through_next_trims_a_partially_overlapping_profile_to_the_next_body_face(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() base["geometry"]["sketches"].append({ "id": "partial", "workplane": {"origin_mm": [0, 0, 12], "x_dir": [1, 0, 0], "normal": [0, 0, -1]}, "profile": {"type": "polygon", "vertices": [[3, -3], [9, -3], [9, 3], [3, 3]]}, }) base["features"].append({ "id": "partial_add", "atomic_id": "extrude_add_blind", "depends_on": ["base_add"], "sketch_id": "partial", "params": {"distance_mm": 0, "end_condition": {"type": "through_next"}}, }) with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(base, Path(directory) / "partial-through-next.step") self.assertAlmostEqual(result["volume_mm3"], 1000 + 24, places=5) def test_through_next_keeps_leading_material_before_a_cylindrical_body(self) -> None: from cdsl_engine.runtime import rebuild_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "leading-through-next", "meta": {"unit": "mm"}, "geometry": {"sketches": [ {"id": "base", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 10}}, {"id": "lead", "workplane": { "origin_mm": [0, -50, 25], "x_dir": [1, 0, 0], "normal": [0, 1, 0], }, "profile": {"type": "circle", "radius_mm": 2}}, ]}, "features": [ {"id": "base_add", "atomic_id": "extrude_add_blind", "depends_on": [], "params": {"distance_mm": 50}, "sketch_id": "base"}, {"id": "lead_add", "atomic_id": "extrude_add_blind", "depends_on": ["base_add"], "params": {"distance_mm": 0, "end_condition": {"type": "through_next"}, "result_mode": "new_body"}, "sketch_id": "lead"}, ], } with tempfile.TemporaryDirectory() as directory: result = rebuild_cdsl(cdsl, Path(directory) / "leading-through-next.step") self.assertEqual(result["solid_count"], 2) self.assertAlmostEqual(result["bbox_mm"]["min"][1], -50.0, places=5) def test_up_to_surface_from_a_cylindrical_wall_uses_the_next_body_face(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "wall-hole", "meta": {"unit": "mm"}, "geometry": {"sketches": [ {"id": "tube", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 10}}, {"id": "tube_bore", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 7}}, {"id": "wall_hole", "workplane": { "origin_mm": [10, 0, 5], "x_dir": [0, 1, 0], "normal": [-1, 0, 0], }, "profile": {"type": "circle", "radius_mm": 1}}, ]}, "features": [ {"id": "tube_add", "atomic_id": "extrude_add_blind", "depends_on": [], "params": {"distance_mm": 10}, "sketch_id": "tube"}, {"id": "tube_cut", "atomic_id": "extrude_cut_blind", "depends_on": ["tube_add"], "params": {"distance_mm": 10}, "sketch_id": "tube_bore"}, ], } with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "tube.step") outer_face = next( item for item in baseline["topology_records"] if item["kind"] == "face" and item["geometry"]["surface_type"] == "cylinder" and abs(item["geometry"]["radius_mm"] - 10) <= 1e-6 ) cdsl = deepcopy(base) cdsl["features"].append({ "id": "wall_hole_cut", "atomic_id": "extrude_cut_blind", "depends_on": ["tube_cut"], "params": {"distance_mm": 0, "end_condition": {"type": "up_to_surface", "reference": { "kind": "face", "owner_feature_id": "tube_add", "geometry": outer_face["geometry"], }}}, "sketch_id": "wall_hole", }) result = rebuild_cdsl(cdsl, root / "wall-hole.step") self.assertLess(result["volume_mm3"], baseline["volume_mm3"] - 8) self.assertGreater(result["volume_mm3"], baseline["volume_mm3"] - 12) def test_up_to_body_extent_uses_a_uniquely_resolved_body_record(self) -> None: from cdsl_engine.runtime import rebuild_cdsl base = self._base_block() base["geometry"]["sketches"].append({ "id": "cut", "workplane": _workplane(), "profile": {"type": "circle", "radius_mm": 1}, }) with tempfile.TemporaryDirectory() as directory: root = Path(directory) baseline = rebuild_cdsl(base, root / "baseline.step") body = next(item for item in baseline["topology_records"] if item["kind"] == "body") cdsl = deepcopy(base) cdsl["features"].append({ "id": "cut_to_body", "atomic_id": "extrude_cut_blind", "depends_on": ["base_add"], "params": {"distance_mm": 0, "end_condition": { "type": "up_to_body", "reference": { "kind": "body", "owner_feature_id": "base_add", "geometry": body["geometry"], }, }}, "sketch_id": "cut", }) result = rebuild_cdsl(cdsl, root / "up-to-body.step") self.assertAlmostEqual(result["volume_mm3"], 1000 - 10 * 3.141592653589793, places=5) def test_selector_dependent_extent_without_reference_is_preflight_blocked(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["features"][0]["params"]["end_condition"] = {"type": "up_to_surface"} analysis = analyze_cdsl(cdsl) result = analysis.feature_results[0] self.assertIn("missing_extent_reference", [item.code for item in result.blockers]) def test_hole_wizard_unsupported_extent_is_preflight_blocked(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["features"].append({ "id": "hole", "atomic_id": "hole_wizard", "depends_on": ["base_add"], "params": { "hole_type": "plain", "diameter_mm": 2, "depth_mm": 2, "positions": [{"mm": [0, 0, 0]}], "host_face": {"kind": "face"}, "end_condition": {"type": "up_to_surface", "solidworks_code": 0}, }, }) analysis = analyze_cdsl(cdsl) result = next(item for item in analysis.feature_results if item.feature_id == "hole") self.assertIn("unsupported_hole_extent", [item.code for item in result.blockers]) def test_legacy_hole_atomics_have_the_same_host_and_shape_preflight_contract(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = self._base_block() cdsl["features"].append({ "id": "hole", "atomic_id": "hole_counterbore", "depends_on": ["base_add"], "sketch_id": "base", "params": { "diameter_mm": 2, "depth_mm": 3, "positions": [{"mm": [0, 0, 0]}], "counterbore_diameter_mm": 2, "counterbore_depth_mm": 1, }, }) analysis = analyze_cdsl(cdsl) result = next(item for item in analysis.feature_results if item.feature_id == "hole") codes = {item.code for item in result.blockers} self.assertIn("missing_host_face", codes) self.assertIn("invalid_hole_spec", codes) def test_body_mutation_without_a_preceding_solid_is_preflight_blocked(self) -> None: from cdsl_engine.runtime import analyze_cdsl cdsl = { "schema": "cad.cdsl.llm.v1", "schema_version": "1.1.0", "kind": "part", "part_id": "no-body", "meta": {"unit": "mm"}, "geometry": {"sketches": []}, "features": [{ "id": "hole", "atomic_id": "hole_wizard", "depends_on": [], "params": { "hole_type": "plain", "diameter_mm": 2, "depth_mm": 3, "end_condition": {"type": "blind", "solidworks_code": 0}, "positions": [{"mm": [0, 0, 0]}], "host_face": {"frame": _workplane()}, }, }], } result = analyze_cdsl(cdsl).feature_results[0] self.assertIn("missing_active_body", [item.code for item in result.blockers]) if __name__ == "__main__": unittest.main()