forge-parametric — Code CAD
The engineering-precision layer of Forge. Everything produced here is dimensionally exact,
reproducible from source, and verified via headless render-in-the-loop before being handed
downstream. No GUI. No hand-tweaked binaries.
Project memory: if FORGE.md exists at the project root, read it first — it carries
the chosen tool, coordinate system, scale unit, output paths, and poly budget for this project.
If ATELIER.md also exists, note the aesthetic world and signature moment; aesthetic intent
may constrain part geometry (organic vs. industrial, rounded vs. sharp edges).
Suite map:
- forge — router; call it when you are unsure which skill to use
- forge-brief — writes FORGE.md; run before this skill if FORGE.md is absent
- forge-standards — units, handedness, naming, poly budgets, validation thresholds (canonical home of 3D math)
- forge-parametric ← YOU ARE HERE — Code CAD, exact solids, STEP/STL/3MF export
- forge-model — polygonal/box modeling via bpy when mesh-not-CAD is right
- forge-topology — retopo, boolean cleanup, decimation, LOD
- forge-validate — manifold/watertight gate, printability, glTF-Validator
- forge-export — final format matrix (GLB/USD/FBX/web handoff)
- forge-optimize — Draco/Meshopt, KTX2, web budgets → atelier-webgl seam
- atelier-webgl — receives
.glb from forge-optimize for Three.js/R3F scenes
Run = call the Skill tool with the exact name above. Writing "now run forge-validate" in prose
runs nothing. Every cross-skill handoff is a Skill(...) call.
Decide first: which tool for which job?
Before writing a single line of geometry code, resolve the tool and confirm it is available.
Run the preflight check below, then use the decision matrix.
# Probe tool availability + project state (run this; do NOT inline-expand $(...) in the skill body):
python "$CLAUDE_CONFIG_DIR/skills/forge/scripts/probe.py" --json
probe.py reports openscad (via openscad.com) and python under tools[], plus any existing
.scad/.step/.stl sources. Reason over its output to confirm the tool you picked is present —
there is no --tools filter. build123d/CadQuery/FreeCAD are pip/installer packages (not on PATH),
so confirm those by their venv import or FreeCADCmd.exe --version, not by probe.py.
Decision matrix — pick one, commit:
| Need |
Tool |
Why |
| Mesh output (STL/3MF), FDM/resin print, algorithmic geometry, fast batch |
OpenSCAD + BOSL2 |
Natively headless, -D param injection, Manifold backend 8–30x CGAL, zero-install libs |
| Exact fillets on computed topology, STEP for CAM/CNC, assembly constraints, B-rep round-trip |
build123d (Python, OCP kernel) |
export_step() is lossless; fillet() acts on exact OCCT topology, not mesh |
| Vendor STEP import with colors/metadata, existing FCStd files, TechDraw, FEM, Assembly workbench |
FreeCAD (FreeCADCmd.exe) |
Only tool with full OCAF framework headlessly; do NOT mix with system Python |
| Quick STL → STEP conversion, mesh-to-solid sewing |
FreeCAD |
makeShapeFromMesh + sewShape + Import.export() |
Forge default: Start with OpenSCAD for any mesh-output task; escalate to build123d
when exact fillets on computed topology or STEP is required. FreeCAD only for vendor STEP
import, FCStd round-trips, or FEM.
Full CLI flags, API calls, and gotcha→fix tables are in references/ (read on demand):
references/openscad-cli.md — headless invocations, BOSL2, tolerances/threads
references/build123d-cadquery.md — Python B-rep API, export formats, validation
references/freecad-headless.md — FreeCADCmd, STEP I/O, PartDesign scripting
references/tolerances-fits.md — ISO 286-1 tables, FDM clearances, GD&T, design rules
The flow
Read FORGE.md → load target engine, coordinate system, output paths, poly budget.
If FORGE.md is absent, invoke Skill("forge-brief") to create it before continuing.
Decide-first gate → run preflight, pick tool (matrix above), confirm it is installed.
If neither OpenSCAD nor build123d is found, surface the gap and stop — do not hallucinate CLI.
Write the source file (.scad or .py) following these invariants:
- CONFIG block first (all user params with units in comments), then CALC, then GEOMETRY.
- No magic numbers in geometry code. Derive everything from the config block.
- For OpenSCAD: declare every
-D-overridable param with a default. Read references/openscad-cli.md §3.2–3.7.
- For build123d: parametric models are plain Python functions returning
Part. Apply fillets
and chamfers last, after all booleans. Read references/build123d-cadquery.md §3b–3e.
- For FreeCAD: run via
FreeCADCmd.exe; never import *Gui modules; always absolute paths;
call doc.recompute() after every structural change. Read references/freecad-headless.md §3.
Export geometry (first pass — STL/3MF for quick check or STEP for B-rep):
- OpenSCAD:
openscad.com -o out.stl --backend=Manifold model.scad (nightly) or omit --backend for stable.
- build123d:
export_step(part, "out.step") + export_stl(part, "out.stl", tolerance=1e-3, angular_tolerance=0.1).
- FreeCAD:
Import.export([body], "out.step") (preserves assembly structure and names).
Render verification PNG — headless render-in-the-loop:
- OpenSCAD (preferred for OpenSCAD models):
openscad.com -o verify.png --imgsize=1920,1080 --autocenter --viewall
--camera=0,0,0,55,0,25,200 --colorscheme=Cornfield model.scad
Add --render for final sign-off only (slower — uses Manifold/CGAL). Omit for quick geometry check.
- For build123d / CadQuery — native render via agentcad (the natural choice; gate on the
§9 asserts first):
agentcad run model.py --output iso --render iso,front,top,right, then
size-check + Read + diagnose per references/build123d-cadquery.md §8b.
- For build123d / FreeCAD models — or import STL into OpenSCAD for preview:
Write a temp
.scad with import("out.stl"); and render it as above. This avoids a Blender
dependency for quick checks. Reserve Skill("forge-render") for photorealistic verification.
- Confirm the PNG is non-trivial: file must exist and be > 10 KB. A < 10 KB PNG means blank render.
Read the PNG — call Read("verify.png") (or the resolved absolute path). Inspect visually:
- Is the geometry present and correctly shaped?
- Are fillets/chamfers visible where expected?
- Is scale plausible (bounding box consistent with spec)?
- If blank or wrong: diagnose against the visual-failure table for your tool
(
references/openscad-cli.md §10 for OpenSCAD, references/build123d-cadquery.md §8b for
build123d/CadQuery), fix the source, and re-render. Do not report success until the PNG passes.
Validate programmatically:
- OpenSCAD:
openscad.com -o out.stl --summary all --summary-file summary.json model.scad
→ parse summary.json for volume and bounding box; fail if volume is zero.
- build123d:
assert part.is_valid, assert part.volume > 0.
- FreeCAD: run
validate_shape() (see references/freecad-headless.md §6.1).
- For printability gate: invoke
Skill("forge-validate") — it runs the full manifold/watertight
check, printability analysis, and adversarial escalation.
Export final deliverable — format per FORGE.md target:
- FDM/resin print →
binstl or 3mf (3MF preferred: preserves units, color, metadata).
- CAM / CNC → STEP (B-rep only; STL is not accepted by most CAM tools).
- Web / Three.js → GLB from build123d (
export_gltf(part, "out.glb", binary=True)), then
invoke Skill("forge-export") → Skill("forge-optimize") → Skill("atelier-webgl").
- Batch design family → PowerShell loop over
-D flags (OpenSCAD) or Python function calls (build123d).
Handoff — record the verified output path in FORGE.md. If the downstream skill is
forge-render (photorealistic shot), forge-topology (cleanup), or forge-validate (gate),
invoke Skill("forge-render"), Skill("forge-topology"), or Skill("forge-validate")
as appropriate. Run = call the Skill tool. Nothing else.
Key invocation patterns (read-ready — no expansion needed)
OpenSCAD batch family (PowerShell):
openscad.com -o "bracket_w{w}.stl" -D "width={w}" --backend=Manifold model.scad
Loop $w in @(60, 80, 100) — each generates a separate STL without touching the .scad.
build123d venv setup (Python 3.12, Windows):
python -m venv .venv && .venv\Scripts\Activate.ps1
python -m pip install --upgrade pip && pip install build123d
FreeCADCmd invocation pattern:
FreeCADCmd.exe "C:\absolute\path\to\forge_part.py"
Script names must NOT shadow stdlib modules (test.py, math.py → rename to forge_test.py).
BOSL2 install (OpenSCAD user library path):
git clone https://github.com/BelfrySCAD/BOSL2.git "$env:USERPROFILE\Documents\OpenSCAD\libraries\BOSL2"
Operating principles
- Decide before executing. Run the preflight, pick the tool, read the decision matrix. Never
start writing geometry code before the tool choice is confirmed and verified as installed.
- Config → Calc → Geometry — always. All user dimensions live in the config block at the top.
No magic numbers in modules. Derived values are computed once in a calc block, consumed everywhere.
- Verify with eyes, not assumptions. Every geometry pass ends with a PNG render read by
Read.
Do not report the part correct until the image passes visual inspection. Blank PNG = broken render,
not success.
- Fillets and chamfers are last. In build123d and FreeCAD, apply fillets after all boolean ops.
In OpenSCAD, use BOSL2
cuboid(rounding=R) or edge_profile() — never minkowski(sphere) on
complex geometry (O(V²) cost; use Manifold backend if unavoidable).
- Run = call the Skill tool. When the flow says "invoke forge-validate", that is a
Skill()
call, not a narrated instruction. Writing "next, run forge-validate" in prose runs nothing.
1---2name: forge-parametric3description: Forge suite — Code CAD: produce engineering-grade solid geometry headlessly from code. Deliverables: STL, 3MF, STEP, GLB files with exact fillets/chamfers, ISO tolerances & fits, threads, draft angles, and ribs — verified by rendering a PNG and reading it back. Use whenever asked to: design a part, model a mechanical component, create a parametric CAD file, generate a bracket/enclosure/housing/fixture, export STEP or STL for 3D printing, design threads or screw fits, apply fillets or chamfers to a solid, specify ISO tolerances or engineering fits (H7/p6 etc.), batch-generate design variants with different dimensions, convert between CAD formats, or produce a solid model from a sketch/spec. Triggers on: "OpenSCAD", "CadQuery", "build123d", "FreeCAD", "STEP export", "STL export", "3MF", "fillet", "chamfer", "tolerance", "clearance fit", "press fit", "threaded rod", "nut", "bolt", "BOSL2", "parametric model", "print-ready part", "CAD script", "solid model". HEADLESS-ONLY: driven from code (.scad / .py), output v4---56# forge-parametric — Code CAD78The engineering-precision layer of Forge. Everything produced here is dimensionally exact,9reproducible from source, and verified via headless render-in-the-loop before being handed10downstream. No GUI. No hand-tweaked binaries.1112> **Project memory:** if **`FORGE.md`** exists at the project root, read it first — it carries13> the chosen tool, coordinate system, scale unit, output paths, and poly budget for this project.14> If **`ATELIER.md`** also exists, note the aesthetic world and signature moment; aesthetic intent15> may constrain part geometry (organic vs. industrial, rounded vs. sharp edges).1617---1819> **Suite map:**20> - **forge** — router; call it when you are unsure which skill to use21> - **forge-brief** — writes FORGE.md; run before this skill if FORGE.md is absent22> - **forge-standards** — units, handedness, naming, poly budgets, validation thresholds (canonical home of 3D math)23> - _forge-parametric_ ← **YOU ARE HERE** — Code CAD, exact solids, STEP/STL/3MF export24> - **forge-model** — polygonal/box modeling via bpy when mesh-not-CAD is right25> - **forge-topology** — retopo, boolean cleanup, decimation, LOD26> - **forge-validate** — manifold/watertight gate, printability, glTF-Validator27> - **forge-export** — final format matrix (GLB/USD/FBX/web handoff)28> - **forge-optimize** — Draco/Meshopt, KTX2, web budgets → **atelier-webgl** seam29> - **atelier-webgl** — receives `.glb` from forge-optimize for Three.js/R3F scenes30>31> Run = call the Skill tool with the exact name above. Writing "now run forge-validate" in prose32> runs nothing. Every cross-skill handoff is a `Skill(...)` call.3334---3536## Decide first: which tool for which job?3738Before writing a single line of geometry code, resolve the tool and confirm it is available.39Run the preflight check below, then use the decision matrix.4041```powershell42# Probe tool availability + project state (run this; do NOT inline-expand $(...) in the skill body):43python "$CLAUDE_CONFIG_DIR/skills/forge/scripts/probe.py" --json44```45`probe.py` reports `openscad` (via `openscad.com`) and `python` under `tools[]`, plus any existing46`.scad`/`.step`/`.stl` sources. Reason over its output to confirm the tool you picked is present —47there is no `--tools` filter. build123d/CadQuery/FreeCAD are pip/installer packages (not on PATH),48so confirm those by their venv import or `FreeCADCmd.exe --version`, not by `probe.py`.4950**Decision matrix — pick one, commit:**5152| Need | Tool | Why |53|---|---|---|54| Mesh output (STL/3MF), FDM/resin print, algorithmic geometry, fast batch | **OpenSCAD + BOSL2** | Natively headless, `-D` param injection, Manifold backend 8–30x CGAL, zero-install libs |55| Exact fillets on computed topology, STEP for CAM/CNC, assembly constraints, B-rep round-trip | **build123d** (Python, OCP kernel) | `export_step()` is lossless; `fillet()` acts on exact OCCT topology, not mesh |56| Vendor STEP import with colors/metadata, existing FCStd files, TechDraw, FEM, Assembly workbench | **FreeCAD** (`FreeCADCmd.exe`) | Only tool with full OCAF framework headlessly; do NOT mix with system Python |57| Quick STL → STEP conversion, mesh-to-solid sewing | **FreeCAD** | `makeShapeFromMesh` + `sewShape` + `Import.export()` |5859**Forge default:** Start with **OpenSCAD** for any mesh-output task; escalate to **build123d**60when exact fillets on computed topology or STEP is required. FreeCAD only for vendor STEP61import, FCStd round-trips, or FEM.6263Full CLI flags, API calls, and gotcha→fix tables are in **`references/`** (read on demand):64- `references/openscad-cli.md` — headless invocations, BOSL2, tolerances/threads65- `references/build123d-cadquery.md` — Python B-rep API, export formats, validation66- `references/freecad-headless.md` — FreeCADCmd, STEP I/O, PartDesign scripting67- `references/tolerances-fits.md` — ISO 286-1 tables, FDM clearances, GD&T, design rules6869---7071## The flow72731. **Read FORGE.md** → load target engine, coordinate system, output paths, poly budget.74 If FORGE.md is absent, invoke `Skill("forge-brief")` to create it before continuing.75762. **Decide-first gate** → run preflight, pick tool (matrix above), confirm it is installed.77 If neither OpenSCAD nor build123d is found, surface the gap and stop — do not hallucinate CLI.78793. **Write the source file** (`.scad` or `.py`) following these invariants:80 - CONFIG block first (all user params with units in comments), then CALC, then GEOMETRY.81 - No magic numbers in geometry code. Derive everything from the config block.82 - For OpenSCAD: declare every `-D`-overridable param with a default. Read `references/openscad-cli.md §3.2–3.7`.83 - For build123d: parametric models are plain Python functions returning `Part`. Apply fillets84 and chamfers **last**, after all booleans. Read `references/build123d-cadquery.md §3b–3e`.85 - For FreeCAD: run via `FreeCADCmd.exe`; never import `*Gui` modules; always absolute paths;86 call `doc.recompute()` after every structural change. Read `references/freecad-headless.md §3`.87884. **Export geometry** (first pass — STL/3MF for quick check or STEP for B-rep):89 - OpenSCAD: `openscad.com -o out.stl --backend=Manifold model.scad` (nightly) or omit `--backend` for stable.90 - build123d: `export_step(part, "out.step")` + `export_stl(part, "out.stl", tolerance=1e-3, angular_tolerance=0.1)`.91 - FreeCAD: `Import.export([body], "out.step")` (preserves assembly structure and names).92935. **Render verification PNG** — headless render-in-the-loop:94 - **OpenSCAD (preferred for OpenSCAD models):**95 ```96 openscad.com -o verify.png --imgsize=1920,1080 --autocenter --viewall97 --camera=0,0,0,55,0,25,200 --colorscheme=Cornfield model.scad98 ```99 Add `--render` for final sign-off only (slower — uses Manifold/CGAL). Omit for quick geometry check.100 - **For build123d / CadQuery — native render via agentcad** (the natural choice; gate on the101 §9 asserts first): `agentcad run model.py --output iso --render iso,front,top,right`, then102 size-check + `Read` + diagnose per `references/build123d-cadquery.md §8b`.103 - **For build123d / FreeCAD models — or import STL into OpenSCAD for preview:**104 Write a temp `.scad` with `import("out.stl");` and render it as above. This avoids a Blender105 dependency for quick checks. Reserve `Skill("forge-render")` for photorealistic verification.106 - **Confirm the PNG is non-trivial:** file must exist and be > 10 KB. A < 10 KB PNG means blank render.1071086. **Read the PNG** — call `Read("verify.png")` (or the resolved absolute path). Inspect visually:109 - Is the geometry present and correctly shaped?110 - Are fillets/chamfers visible where expected?111 - Is scale plausible (bounding box consistent with spec)?112 - If blank or wrong: diagnose against the visual-failure table for your tool113 (`references/openscad-cli.md §10` for OpenSCAD, `references/build123d-cadquery.md §8b` for114 build123d/CadQuery), fix the source, and re-render. Do not report success until the PNG passes.1151167. **Validate programmatically:**117 - OpenSCAD: `openscad.com -o out.stl --summary all --summary-file summary.json model.scad`118 → parse `summary.json` for volume and bounding box; fail if volume is zero.119 - build123d: `assert part.is_valid`, `assert part.volume > 0`.120 - FreeCAD: run `validate_shape()` (see `references/freecad-headless.md §6.1`).121 - For printability gate: invoke `Skill("forge-validate")` — it runs the full manifold/watertight122 check, printability analysis, and adversarial escalation.1231248. **Export final deliverable** — format per FORGE.md target:125 - FDM/resin print → `binstl` or `3mf` (3MF preferred: preserves units, color, metadata).126 - CAM / CNC → STEP (B-rep only; STL is not accepted by most CAM tools).127 - Web / Three.js → GLB from build123d (`export_gltf(part, "out.glb", binary=True)`), then128 invoke `Skill("forge-export")` → `Skill("forge-optimize")` → `Skill("atelier-webgl")`.129 - Batch design family → PowerShell loop over `-D` flags (OpenSCAD) or Python function calls (build123d).1301319. **Handoff** — record the verified output path in FORGE.md. If the downstream skill is132 forge-render (photorealistic shot), forge-topology (cleanup), or forge-validate (gate),133 invoke `Skill("forge-render")`, `Skill("forge-topology")`, or `Skill("forge-validate")`134 as appropriate. Run = call the Skill tool. Nothing else.135136---137138## Key invocation patterns (read-ready — no expansion needed)139140**OpenSCAD batch family (PowerShell):**141```142openscad.com -o "bracket_w{w}.stl" -D "width={w}" --backend=Manifold model.scad143```144Loop `$w in @(60, 80, 100)` — each generates a separate STL without touching the `.scad`.145146**build123d venv setup (Python 3.12, Windows):**147```148python -m venv .venv && .venv\Scripts\Activate.ps1149python -m pip install --upgrade pip && pip install build123d150```151152**FreeCADCmd invocation pattern:**153```154FreeCADCmd.exe "C:\absolute\path\to\forge_part.py"155```156Script names must NOT shadow stdlib modules (`test.py`, `math.py` → rename to `forge_test.py`).157158**BOSL2 install (OpenSCAD user library path):**159```160git clone https://github.com/BelfrySCAD/BOSL2.git "$env:USERPROFILE\Documents\OpenSCAD\libraries\BOSL2"161```162163---164165## Operating principles166167- **Decide before executing.** Run the preflight, pick the tool, read the decision matrix. Never168 start writing geometry code before the tool choice is confirmed and verified as installed.169- **Config → Calc → Geometry — always.** All user dimensions live in the config block at the top.170 No magic numbers in modules. Derived values are computed once in a calc block, consumed everywhere.171- **Verify with eyes, not assumptions.** Every geometry pass ends with a PNG render read by `Read`.172 Do not report the part correct until the image passes visual inspection. Blank PNG = broken render,173 not success.174- **Fillets and chamfers are last.** In build123d and FreeCAD, apply fillets after all boolean ops.175 In OpenSCAD, use BOSL2 `cuboid(rounding=R)` or `edge_profile()` — never `minkowski(sphere)` on176 complex geometry (O(V²) cost; use Manifold backend if unavoidable).177- **Run = call the Skill tool.** When the flow says "invoke forge-validate", that is a `Skill()`178 call, not a narrated instruction. Writing "next, run forge-validate" in prose runs nothing.