"""Phase J3 — assembly through the full pipeline (SPEC_ASSEMBLY.md §8). Offline. A canned build_spec whose generator uses `asm` flows through kernel -> repair -> metrics -> assembler into a valid composite contract whose parts are actually joined (≥2 components). """ import importlib.util import json import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from geometry import assembler, metrics, pipeline # noqa: E402 _spec = importlib.util.spec_from_file_location("schema_mod", ROOT / "tests" / "test_schema.py") _schema = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(_schema) validate_shape = _schema.validate_shape GEN = ('def build(p):\n' ' cyl = asm.cylinder(r=1.0, h=2.0, name="cylinder")\n' ' cone = asm.cone(r=1.0, h=1.5, name="cone")\n' ' asm.attach(cone, "base", cyl, "top")\n' ' return asm.combine([cyl, cone])\n') SPEC = { "type": "build_spec", "shape_id": "cone_on_cylinder", "shape_label": "Cone on Cylinder", "regularity": "composite", "generator": {"lang": "python", "entry": "build", "code": GEN}, "components": [{"id": "cylinder", "label": "Cylinder", "param_dependencies": []}, {"id": "cone", "label": "Cone", "param_dependencies": []}], "params": [], "constraints": [], "construction_steps": [], } def _geom(c): return c["geometry"]["vertices"], [f["indices"] for f in c["geometry"]["faces"]] def test_assembler_build_produces_valid_joined_contract(): contract, _ = assembler.build(SPEC) assert validate_shape(contract) == [] assert contract["complexity"]["component_count"] == 2 # cylinder + cone v, f = _geom(contract) assert metrics.passes(v, f, "composite")[0] def test_pipeline_accepts_asm_spec(): result, meta = pipeline.build(lambda p, q, f=None: SPEC, "cone on a cylinder") assert meta["status"] == "ok" assert result.get("type") != "guided_response" assert validate_shape(result) == [] comps = {c["id"] for c in result["components"]} assert {"cylinder", "cone"} <= comps # both parts kept, joined