CadQuery manufacturability — DFM pass
Purpose
Make the part manufacturable and sourceable: correct fastener diameters, counterbores, process constraints, vendor BOM text, identification engraving. Run after detailing so counterbores are not destroyed by later fillets.
What this pass is NOT
- No structural ribs/gussets (detailing)
- No cosmetic fillets unless process-required (e.g. cast fillet note in comment when radius already exists in detailing)
- No changing functional mate diameters without updating
common.py and the partner part
Authoritative rules
cad-generation.txt — Mechanical Interfaces, Standard Vendor Parts.
Inputs
- Detailed part file from
cadquery-part-detailing
- Mating list: joint kind per partner
- Process: machined | sheet_metal | print | cast
- Which side of each bolted joint this part is (clamped → clearance; receiver → tap pilot)
Fastener responsibility split (critical)
For each bolted mate, exactly one part gets clearance + counterbore; the partner gets tap pilot — positions from common.py:
# common.py
M3_MOUNT_PTS = [(30, 15), (-30, 15), (30, -15), (-30, -15)]
# parts/bracket.py (clamped) — DFM pass
from common import M3_MOUNT_PTS, M3_CLEAR_DIA, M3_CBORE_DIA, M3_CBORE_DEPTH, PLATE_T
clear = cq.Workplane("XY").pushPoints(M3_MOUNT_PTS).circle(M3_CLEAR_DIA / 2).extrude(PLATE_T + 1)
cbore = cq.Workplane("XY").pushPoints(M3_MOUNT_PTS).circle(M3_CBORE_DIA / 2).extrude(-M3_CBORE_DEPTH)
bracket = bracket.cut(clear).cut(cbore)
# parts/base.py (receiver) — DFM pass
from common import M3_MOUNT_PTS, M3_TAP_DIA, TAP_DEPTH
pilot = cq.Workplane("XY").pushPoints(M3_MOUNT_PTS).circle(M3_TAP_DIA / 2).extrude(-TAP_DEPTH)
base = base.cut(pilot)
Never put both clearance and tap on the same part unless it is a standalone test piece.
Fastener table (mm)
| Thread |
Clearance Ø |
Tap-drill Ø |
Counterbore Ø × depth |
| M3 |
3.4 |
2.5 |
6.0 × 3.0 |
| M4 |
4.5 |
3.3 |
8.0 × 4.0 |
| M5 |
5.5 |
4.2 |
10.0 × 5.0 |
| M6 |
6.4 |
5.0 |
11.0 × 6.0 |
| M8 |
8.4 |
6.8 |
14.0 × 8.0 |
| M10 |
10.5 |
8.5 |
17.5 × 10.0 |
Define M3_CLEAR_DIA, M3_TAP_DIA, etc. in common.py when first used.
DFM checklist
Apply every item that fits:
All processes
- Mounting holes on anything that bolts to another surface — even if mate part is out of scope
- Vendor procurement comment (4 lines: category, key dims, standard, sample SKU) above interface geometry
- Part identification on a non-cosmetic face:
.faces("<Z").workplane().text("P/N …", 4, -0.3, combine="cut")
Press-fit / welded / snap
- Press-fit: document interference in comment; model zero gap
- Welded: flush faces +
# WELD — fillet weld along ... comment
- Snap-fit: explicit undercut dimension in comment + modeled beam/ledge
Machined
- Min wall 1.5 mm (Al), 1.0 mm (steel)
- No sharp internal pocket corners without noting min cutter radius (detailing should have fillet)
Sheet metal
- Uniform thickness; bend radius ≥ t; hole-to-edge ≥ 2t
3D print
- Min wall ≥ 1.2 mm; no modeled threads — clearance + comment for insert
Cast / molded
- 1–3° draft on vertical walls (model or comment)
- Uniform wall ±25%
Build order
- Re-open
parts/<name>.py inside build_*() after detailing block.
- Add
# ---- DFM ---- section.
- Each cut/union: single-extrusion tool, one at a time.
- Vendor comments immediately above related geometry.
- Return part; keep
result = build_<name>().
Workflow
- Edit part file.
- Smoke exec + orchestrator
cadquery-render.
- Orchestrator
cadquery-interference-check — counterbores often clash with neighbors.
Output
Updated parts/<function_name>.py. Do not modify assembly.py — orchestrator swaps the built part.
1---2name: cadquery-manufacturability3description: Apply a Design-for-Manufacture (DFM) pass to a CadQuery part by inspecting it against its presumed manufacturing process (machining, sheet-metal, 3D print, casting/molding) and adding the missing manufacturability features — minimum wall thickness, draft, tap-drill holes, counterbore depths, mounting-hole patterns, vendor-part procurement-grade comments, engraved part numbers, weld-bead notes, snap-fit undercuts. Use after `cadquery-part-detailing` finishes the geometric finish, when the user says "make this manufacturable", "add DFM features", "this should be machinable", or before final STEP export.4---56# CadQuery manufacturability — DFM pass78## Purpose910Make the part **manufacturable and sourceable**: correct fastener diameters, counterbores, process constraints, vendor BOM text, identification engraving. Run **after** detailing so counterbores are not destroyed by later fillets.1112## What this pass is NOT1314- No structural ribs/gussets (detailing)15- No cosmetic fillets unless **process-required** (e.g. cast fillet note in comment when radius already exists in detailing)16- No changing functional mate diameters without updating `common.py` and the partner part1718## Authoritative rules1920`cad-generation.txt` — Mechanical Interfaces, Standard Vendor Parts.2122## Inputs2324- Detailed part file from `cadquery-part-detailing`25- Mating list: joint kind per partner26- Process: machined | sheet_metal | print | cast27- Which side of each bolted joint this part is (**clamped** → clearance; **receiver** → tap pilot)2829## Fastener responsibility split (critical)3031For each bolted mate, **exactly one part** gets clearance + counterbore; the **partner** gets tap pilot — positions from `common.py`:3233```python34# common.py35M3_MOUNT_PTS = [(30, 15), (-30, 15), (30, -15), (-30, -15)]3637# parts/bracket.py (clamped) — DFM pass38from common import M3_MOUNT_PTS, M3_CLEAR_DIA, M3_CBORE_DIA, M3_CBORE_DEPTH, PLATE_T39clear = cq.Workplane("XY").pushPoints(M3_MOUNT_PTS).circle(M3_CLEAR_DIA / 2).extrude(PLATE_T + 1)40cbore = cq.Workplane("XY").pushPoints(M3_MOUNT_PTS).circle(M3_CBORE_DIA / 2).extrude(-M3_CBORE_DEPTH)41bracket = bracket.cut(clear).cut(cbore)4243# parts/base.py (receiver) — DFM pass44from common import M3_MOUNT_PTS, M3_TAP_DIA, TAP_DEPTH45pilot = cq.Workplane("XY").pushPoints(M3_MOUNT_PTS).circle(M3_TAP_DIA / 2).extrude(-TAP_DEPTH)46base = base.cut(pilot)47```4849Never put both clearance and tap on the same part unless it is a standalone test piece.5051### Fastener table (mm)5253| Thread | Clearance Ø | Tap-drill Ø | Counterbore Ø × depth |54|--------|-------------|-------------|------------------------|55| M3 | 3.4 | 2.5 | 6.0 × 3.0 |56| M4 | 4.5 | 3.3 | 8.0 × 4.0 |57| M5 | 5.5 | 4.2 | 10.0 × 5.0 |58| M6 | 6.4 | 5.0 | 11.0 × 6.0 |59| M8 | 8.4 | 6.8 | 14.0 × 8.0 |60| M10 | 10.5 | 8.5 | 17.5 × 10.0 |6162Define `M3_CLEAR_DIA`, `M3_TAP_DIA`, etc. in `common.py` when first used.6364## DFM checklist6566Apply every item that fits:6768### All processes6970- **Mounting holes** on anything that bolts to another surface — even if mate part is out of scope71- **Vendor procurement comment** (4 lines: category, key dims, standard, sample SKU) above interface geometry72- **Part identification** on a non-cosmetic face: `.faces("<Z").workplane().text("P/N …", 4, -0.3, combine="cut")`7374### Press-fit / welded / snap7576- Press-fit: document interference in comment; model zero gap77- Welded: flush faces + `# WELD — fillet weld along ...` comment78- Snap-fit: explicit undercut dimension in comment + modeled beam/ledge7980### Machined8182- Min wall 1.5 mm (Al), 1.0 mm (steel)83- No sharp internal pocket corners without noting min cutter radius (detailing should have fillet)8485### Sheet metal8687- Uniform thickness; bend radius ≥ t; hole-to-edge ≥ 2t8889### 3D print9091- Min wall ≥ 1.2 mm; no modeled threads — clearance + comment for insert9293### Cast / molded9495- 1–3° draft on vertical walls (model or comment)96- Uniform wall ±25%9798## Build order991001. Re-open `parts/<name>.py` inside `build_*()` after detailing block.1012. Add `# ---- DFM ----` section.1023. Each cut/union: **single-extrusion tool**, one at a time.1034. Vendor comments immediately above related geometry.1045. Return part; keep `result = build_<name>()`.105106## Workflow1071081. Edit part file.1092. Smoke exec + orchestrator **`cadquery-render`**.1103. Orchestrator **`cadquery-interference-check`** — counterbores often clash with neighbors.111112## Output113114Updated `parts/<function_name>.py`. Do not modify `assembly.py` — orchestrator swaps the built part.