cadquery-modeling · diff
git:20260805.fb6c4b3 to git:20260806.5752d1d
5 added, 2 removed. Audit A to A.
---
name: cadquery-modeling
description: "Create, edit, or debug parametric 3D CAD parts in CadQuery (Python) that export to STEP for SolidWorks. Use when the request involves CadQuery, .step/.stl export, or modelling a physical part — enclosure, bracket, PCB, motor mount, cam, gear. Also use when a STEP will not open in CAD, or geometry looks correct in STL but is invalid as a solid. Enforces verification before export and documented OCC pitfalls."
---
# CadQuery Modeling for Manufacturable CAD
This skill turns natural-language part requests into clean, verified, parametric
CadQuery code whose STEP output reliably opens in SolidWorks. It encodes hard-won
rules from real multi-part product design.
## Core philosophy
1. **Parametric first** — every dimension is a named variable at the top of the file.
2. **Verify before trusting** — a script that printed `[OK]` is NOT proof of success.
Always check geometry validity and solid count before declaring done.
3. **Avoid OCC fragility** — prefer simple primitives + boolean operations over
fragile chained features. See `references/occ_pitfalls.md`.
4. **One part, one file** — each part is its own `.py` producing its own STEP/STL.
Assemblies are done in SolidWorks, not baked into a single STEP.
## Before creating anything: confirm where
Files land in whatever directory Claude Code was started in — that is the single
thing that determines whether the user finds their STEP later. Before creating a
project or the first part, **state the absolute path you are about to write to**,
and stop to confirm if it looks wrong:
- a cloud-synced path (`OneDrive`, `Dropbox`, `iCloud` — note that on Windows the
Desktop and Documents folders usually ARE OneDrive)
- `Downloads`, a home directory root, or a system folder
- the `claude-cad` toolkit repo itself
- a path containing spaces, parentheses, or non-ASCII characters
If any of those apply, say so and suggest a clean location such as
`C:\cad_project` before writing files. Getting this wrong is silent: everything
succeeds, and the user simply can't find the output.
## The standard workflow
When the user asks for a part, follow this loop:
1. **Match the questions to how specified the request is.** Never invent a
critical dimension — ask instead. Default to millimeters.
- **Underspecified** (e.g. "a bracket to hold a 60 mm pipe"): don't guess, and
don't refuse. Ask one focused batch of questions covering only what you truly
can't proceed without — typically overall size, thickness/wall, the
coordinate system/orientation, and any dimension that must match another part
or a real component. Keep it short; propose sensible defaults the user can
accept ("I'll assume 4 mm thick unless you say otherwise").
- **Fully specified**: don't interrogate. Build it, and list any assumption you
had to make in the final report.
- If the user asks you to **write a spec first**, produce it as text (coordinate
system, overall dimensions, every feature with size + position, interfaces)
and get approval before writing code.
2. **Write the part file** following the structure in
`references/part_file_structure.md`.
3. **Run it** and read the full terminal output, not just the last line.
4. **Verify** geometry validity, solid count, volume, and bounding box (see below).
5. **Report** the verification numbers to the user and note any assumptions made.
Read `references/occ_pitfalls.md` before writing any code that uses shell, fillet
after shell, countersinks, or lofts — the operations with a history of silent
corruption. Each pitfall now carries a **Verified behaviour** block saying what
the current kernel actually does; the one reliably reproduced silent failure is
shelling a solid whose removed face was filleted (Pitfall 4b).
## Project layout
Always use this structure so STEP files collect in one place for SolidWorks:
```
project/
├── parts/ # one .py per part
│ ├── housing.py
│ └── pcb.py
├── output/ # STEP + STL land here (git-ignored)
└── docs/
└── assembly_guide.md
```
Resolve the output path **relative to the script file**, not the working
directory. A bare `"../output/..."` breaks the moment the script is run from the
project root instead of from `parts/` — the export silently lands one folder too
high. Always use this pattern:
```python
from pathlib import Path
OUT = Path(__file__).resolve().parent.parent / "output"
OUT.mkdir(parents=True, exist_ok=True)
cq.exporters.export(part, str(OUT / "housing.step"))
cq.exporters.export(part, str(OUT / "housing.stl"))
print(f"[OK] exported to {OUT}")
```
This works whether the user runs `py -3.11 parts/housing.py` from the project
root or `py -3.11 housing.py` from inside `parts/`. Printing the resolved path
also makes it obvious where the files went.
## Mandatory verification block
Every part file MUST verify before export — either with this inline block, or
(preferred) with the `check()` helper from `scripts/diagnose.py` shown after it.
One of the two is required; a bare `[OK]` print is never sufficient. STL is
- forgiving and will hide geometry errors that make a STEP unopenable in SolidWorks.
+ forgiving and hides geometry errors that survive into the STEP — and CAD import
+ is no safety net either: SolidWorks has opened a provably invalid STEP without
+ warning (`references/occ_pitfalls.md`, "Do not count on CAD import to reject
+ bad geometry").
```python
# ===== Verification (do not skip) =====
from OCP.BRepCheck import BRepCheck_Analyzer
solid = part.val()
shape = solid.wrapped
# 1. Geometry validity — catches silent OCC corruption
analyzer = BRepCheck_Analyzer(shape)
is_valid = analyzer.IsValid()
print(f"[verify] IsValid = {is_valid}")
if not is_valid:
- raise RuntimeError("Invalid geometry: STEP will not open in SolidWorks.")
+ raise RuntimeError("Invalid geometry — part is unusable; CAD may still open it without warning.")
# 2. Single-body check — catches stray cut/union debris
n_solids = len(solid.Solids())
print(f"[verify] solid count = {n_solids}")
if n_solids != 1:
raise RuntimeError(f"Expected 1 solid, found {n_solids}.")
# 3. Volume — ASSERT it, don't just print it. Validity and solid count both
# pass on a part whose hollow/cut silently did nothing; volume is the only tell.
vol = solid.Volume()
print(f"[verify] volume = {vol:.2f} mm^3")
assert expected_lo <= vol <= expected_hi, (
f"Volume {vol:.1f} outside expected range — a feature may have done nothing.")
print(f"[verify] bbox = {solid.BoundingBox()}")
```
### Prefer the diagnostic helper
The block above is the minimum. For real work, use `scripts/diagnose.py`, which
does the same checks but also *classifies* what's wrong and *safely auto-repairs*
the unambiguous cases (e.g. discarding tiny offcut debris from a multibody result):
**Setup (once per project):** copy `scripts/diagnose.py` from this skill into the
project's `parts/` folder, next to the part files. Python puts a script's own
directory on the import path, so `from diagnose import check` then resolves when
running `py -3.11 parts/<name>.py`. Without this copy the import fails with
`ModuleNotFoundError: No module named 'diagnose'`. (`project_init` also keeps a
canonical copy in `claude-cad/` alongside `preflight.py`; the `parts/` copy is the
one imports resolve against — keep them in sync, and `preflight.py` warns if they
drift.)
**Prefer `export_verified()` — it does all three steps and cannot export a bad
part**, because verification raises before the write happens:
```python
from pathlib import Path
from diagnose import export_verified # diagnose.py must be in parts/
OUT = Path(__file__).resolve().parent.parent / "output"
export_verified(part, OUT, "bottom_housing",
expect_volume=32000, # +/-2% by default, or pass (lo, hi)
expect_bbox=(94, 65, 27.2),
inside=[(0, 0, 1)], # floor is solid here
outside=[(47, -12, 10)], # this port opening is void here
)
```
This verifies, exports STEP + STL, and writes `bottom_housing.verified.txt` beside
them — a durable record of what was checked, since terminal output vanishes when
the window closes. Use the lower-level `check()` / `assert_points()` directly only
for intermediate steps mid-build.
**Always pass `expect_volume`.** Validity and solid-count both PASS on a part
whose `.shell()` silently did nothing — the volume is the only signal that the
cavity never got made (`references/occ_pitfalls.md`, Pitfall 4b). Compute a rough
expected volume before you build, and check against it.
**Use `assert_points` for anything with a position that matters** — ports, holes,
bosses, openings on specific walls. Geometry can be valid, single-bodied, and the
right volume while a feature sits on the *wrong face*; no topological check sees
that. Sample the middle of each opening (expect outside) and the wall beside it
(expect inside).
When a part fails, **don't guess** — follow the symptom→diagnosis→fix decision tree
in `references/diagnosis.md`. Auto-repair only touches safe, unambiguous cases;
invalid topology is reported (not patched) so a real design error is never hidden.
## Verification, in SolidWorks
After import, the user should confirm in SolidWorks:
- **Mass Properties** — center of mass on expected symmetry axes (X/Y ≈ 0 for a
symmetric part) is the fastest correctness check.
- **Principal moments of inertia** — the relative sizes confirm which axis is
long/short, catching swapped dimensions.
- The part opens as a **solid body**, not a surface.
See `references/solidworks_checks.md` for the exact menu paths and what each
number tells you.
## Reference files
Load these as needed:
| File | When to read |
|------|--------------|
| `references/occ_pitfalls.md` | **Before** any shell, fillet, countersink, or loft. This is the most important reference. |
| `references/diagnosis.md` | When a part fails verification — the symptom→diagnosis→fix decision tree, and how to auto-repair. |
| `references/part_file_structure.md` | When writing a new part file from scratch. |
| `references/verification.md` | When a STEP won't open, or geometry looks wrong. |
| `references/solidworks_checks.md` | When validating a part inside SolidWorks. |
| `references/environment.md` | When `cadquery` or `OCP` import fails, or on first setup. |
## Scripts
| Script | Purpose |
|--------|---------|
| `scripts/diagnose.py` | **The intelligence layer.** Diagnoses a part (classifies validity / multibody / degenerate issues) and safely auto-repairs what it can. Use `from diagnose import check` inside a build, or run it on a `.step` from the CLI. |
| `scripts/preflight.py` | Project-level: runs and verifies every `parts/*.py`, then writes a committable `VERIFICATION.md` summary at the project root. Copy it into the project's `claude-cad/` folder and run `py -3.11 claude-cad/preflight.py`. |
| `scripts/verify_step.py` | Lightweight standalone check of any STEP/STL: validity, solid count, volume, bbox. |
## Hard rules (never violate)
- Never invent a critical/interface dimension. Ask instead.
- Never declare success on `[OK]` alone — only after the verification block passes.
- Never use `cskHole` on a shelled (thin-wall) face — build a cutter solid
instead. (Portability rule: the historical failure does not reproduce on the
current kernel — see Pitfall 1's Verified behaviour — but the cutter pattern
is explicit, uniform across hole types, and version-proof, at zero cost.)
- Never bake a multi-part assembly into a single STEP unless the user explicitly
wants a static snapshot; do assemblies in SolidWorks.
- Always default to millimeters and state the unit.
- Always print the absolute output directory after exporting, so the user never
has to guess where the file went.
- Never treat `IsValid` + single-solid as sufficient. Always compare volume against
a hand estimate — that is the only check that catches a hollow or cut that
silently did nothing.
- For any feature whose *position* matters, verify it with point classification
(`assert_points`), not just topology.