--- version: "1.2.0" evaluation: programmatic agent: claude-code model: claude-sonnet-4-6 model_provider: anthropic snapshot: text-to-cad # Eat our own dog food: declare the headline deliverables so Mise surfaces them. primary_outputs: - model.step - snapshot.png origin: url: "https://github.com/earthtojake/text-to-cad/blob/main/skills/cad/SKILL.md" user_supplied_url: "https://github.com/earthtojake/text-to-cad" is_directory_mirror: false source_host: "github.com" source_title: "CAD generation, inspection, and validation" imported_at: "2026-06-10T14:33:57Z" imported_by: "skill-to-runbook-converter@1.1.0" attribution: collection_or_org: "earthtojake" source_collection: "text-to-cad" skill_name: "cad" confidence: "high" secrets: {} --- # Text-to-CAD — Agent Runbook ## Objective Turn a natural-language part description into a **validated, STEP-first parametric CAD model** using the [`earthtojake/text-to-cad`](https://github.com/earthtojake/text-to-cad) `cad` skill (build123d + OpenCascade). Author a parametric build123d generator, export a STEP file (plus STL/GLB mesh sidecars), inspect the geometry for the facts the prompt calls out, render a static verification snapshot, and write everything to `{{results_dir}}`. STEP is the primary artifact; STL/3MF/GLB are secondary exports that branch from it. The runtime (`snapshot: text-to-cad`) bakes the skill at `/opt/text-to-cad` with `build123d`, `cadquery-ocp`, and the skill's `cadpy` runtime already installed — no network install needed. --- ## REQUIRED OUTPUT FILES (MANDATORY) **You MUST write all of the following to `{{results_dir}}`. The task is NOT complete until every file exists and is non-empty. No exceptions.** | File | Description | |------|-------------| | `{{results_dir}}/model.py` | The parametric build123d generator (defines `gen_step()`), the source of truth for the geometry | | `{{results_dir}}/model.step` | The primary CAD artifact — a valid STEP solid/assembly exported from `model.py` | | `{{results_dir}}/model.stl` | STL mesh sidecar exported alongside the STEP | | `{{results_dir}}/snapshot.png` | A static verification render of the STEP/mesh (see Step 5) | | `{{results_dir}}/inspect_report.json` | Geometry facts: bounding box, volume, solid count, and the spec dimensions you verified | | `{{results_dir}}/summary.md` | Executive summary: the brief, parameters chosen, validation results, assumptions/caveats | | `{{results_dir}}/validation_report.json` | Structured validation with `stages`, `results`, and `overall_passed` | If you finish your analysis but have not written every file, go back and write them before stopping. --- ## Parameters | Parameter | Template Variable | Default | Description | |-----------|------------------|---------|-------------| | Results directory | `{{results_dir}}` | `/app/results` (Jetty) / `./results` (local) | Output directory for all results | | Prompt | `{{prompt}}` | *(required)* | Natural-language description of the part to model (dimensions, features, intent) | ### Inputs ```yaml # REPLACE-AT-RUNTIME — orchestrator fills {{prompt}}; {{results_dir}} is auto-substituted by Jetty. results_dir: /app/results prompt: ``` If `{{prompt}}` is empty, fail fast in Step 1 with `validation_report.json` stage `setup` set to `passed=false` and a message naming the missing input. --- ## Dependencies | Dependency | Type | Required | Description | |------------|------|----------|-------------| | `text-to-cad` snapshot | Runtime | Yes | Daytona snapshot with `/opt/text-to-cad`, `build123d`, `cadquery-ocp`, `cadpy` baked in | | `build123d` / `cadquery-ocp` | Python | Yes (baked) | Parametric CAD kernel (OpenCascade bindings) | | `cadpy` | Python | Yes (baked) | The skill's STEP/GLB artifact runtime (`scripts/step` imports it) | | `trimesh`, `matplotlib` | Python | Yes (baked) | Headless fallback render for `snapshot.png` | No secrets are required — the geometry pipeline is fully local. --- ## Step 1: Environment Setup ```bash set -e mkdir -p {{results_dir}} SKILL=/opt/text-to-cad/skills/cad # baked skill dir (scripts/step|inspect|snapshot) WORK={{results_dir}}/work mkdir -p "$WORK" # Verify the CAD runtime is present (baked into the text-to-cad snapshot). python -c "import build123d, cadpy; from build123d import Box; print('build123d', build123d.__version__)" \ || { echo 'ERROR: CAD runtime missing — is snapshot=text-to-cad?'; exit 1; } test -f "$SKILL/scripts/step/__main__.py" || { echo "ERROR: cad skill not at $SKILL"; exit 1; } # Fail fast on an empty prompt. PROMPT='{{prompt}}' [ -n "$PROMPT" ] || { echo 'ERROR: prompt is empty'; exit 1; } ``` --- ## Step 2: Write a CAD Brief From `{{prompt}}`, extract into a short brief (record it in `summary.md`): dimensions + units (default **mm**), coordinate convention (base plane **XY**, up **+Z**), feature intent (holes, fillets, pockets, bosses, …), the output geometry kind (single solid vs assembly), and any spec dimensions you will verify in Step 4. Apply the skill's defaults when unspecified: closed positive-volume solids; M3/M4/M5 clearance holes = 3.4/4.5/5.5 mm; cosmetic fillet 1–3 mm; plastic enclosure wall 2–3 mm. State every assumption explicitly. --- ## Step 3: Author the build123d Generator + Export STEP Write `{{results_dir}}/work/model.py` — a parametric build123d script that defines **`gen_step()`** returning the part (a build123d `Part`/`Compound`/ `BuildPart` result). Use named parameters at the top, verbose labels, and closed solids. Then export with the skill's `scripts/step` launcher: ```bash cd "$WORK" # Generated Python target -> writes model.step next to it, plus mesh sidecars. python "$SKILL/scripts/step" model.py -o model.step --stl model.stl --glb model.glb --verbose test -s model.step || { echo 'ERROR: STEP not generated'; exit 1; } ``` `scripts/step` infers `--kind` from `gen_step()`. Keep `model.py` and `model.step` in the same directory with the same basename. If generation fails, fix the **smallest** responsible section of `model.py` and rerun (see Step 6). --- ## Step 4: Inspect & Validate Geometry Run the skill's deterministic inspection, then verify the spec dimensions the brief called out. Persist a machine-readable summary to `inspect_report.json`. ```bash cd "$WORK" python "$SKILL/scripts/inspect" refs model.step --facts --planes --positioning \ | tee inspect_refs.txt ``` Then compute and record canonical facts directly from the STEP (robust even if the CLI output format shifts), writing `inspect_report.json`. Always record spec dimension checks under a `spec_checks` key using the canonical format `{"ok": bool, "expected_mm": float, "actual_mm": float}` — consistent format across runs is required for the evaluation harness to parse them. ```bash python - <<'PY' import json, pathlib from build123d import import_step shape = import_step("model.step") bb = shape.bounding_box() solids = shape.solids() rep = { "bounding_box_mm": { "min": [round(bb.min.X,3), round(bb.min.Y,3), round(bb.min.Z,3)], "max": [round(bb.max.X,3), round(bb.max.Y,3), round(bb.max.Z,3)], "size": [round(bb.size.X,3), round(bb.size.Y,3), round(bb.size.Z,3)], }, "solid_count": len(solids), "total_volume_mm3": round(sum(s.volume for s in solids), 3), "is_closed_positive_volume": all(s.volume > 0 for s in solids) and len(solids) >= 1, # Add one entry per named dimension in the prompt brief. # Use EXACTLY this layout for every check — the evaluation harness parses it: # spec_checks: { "