[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71S0XRPYA245TMQSFZ0FQ9V":3,"skill-same:01M71S0XRPYA245TMQSFZ0FQ9V":46},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":20,"linkOnly":21,"markdownContent":22,"markdownBody":22,"frontMatter":23,"refName":24,"commitSha":25,"skillDirectory":26,"forksCount":27,"repositoryHtmlUrl":28,"repositoryHomepageUrl":6,"ownerHtmlUrl":29,"ownerBlogUrl":30,"ownerDescription":6,"updatedAt":31,"install":32,"resources":45},"01M71S0XRPYA245TMQSFZ0FQ9V","b123d-modeling",null,"pzfreo\u002Fbuild123d-mcp","pzfreo","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F1773202?v=4","01M71S0HXPCECNMYJD3RJ6QKE5","Paul Fremantle","01M71S0HYR5GR026NQN2V9P3NQ",147,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fpzfreo\u002Fbuild123d-mcp\u002Fblob\u002Fmain\u002Fsrc\u002Fbuild123d_mcp\u002Fskills\u002Fb123d-modeling\u002FSKILL.md",[],[18,19],"modeling","b123d modeling","2026-11-10 13:02:02",false,"# Build 3D Geometry with build123d\n\nUse this skill when asked to model, build, or modify a 3D part or assembly with\nbuild123d — from a text description, a technical drawing (image or PDF),\ndimensions in a spec, or an existing STEP\u002FSTL file.\n\n**Use the build123d-mcp MCP server as the execution and verification authority.**\nBuild incrementally with `execute()` by default and checkpoint the first valid\nmodel. If matching-view evidence then shows a body-family\u002Fglobal-form mismatch,\nkeep the replacement in one canonical `model.py` and run it with `execute_file()`;\nthen use `measure()` → `validate()` → `render_view()` before exporting it.\n`execute_file()` runs in a clean namespace and preserves the previous valid model\nif the rebuild fails, so a necessary global correction does not sacrifice the\nverified checkpoint. Do not globally rebuild a checkpoint merely because a\ncanonical source file is available.\n\n---\n\n## Step 0 — Start the session\n\n1. `reset()`, then `execute(\"from build123d import *\")`.\n2. Read the `build123d:\u002F\u002Fquickref` resource before writing build code — it has\n   accurate API syntax for the current build123d version.\n3. If the part uses fasteners, bearings, or threads, read\n   `build123d:\u002F\u002Fbd_warehouse` and probe catalogue sizes before scripting.\n\n## Step 1 — Extract the spec before modeling\n\nDo not model while reading the input. First convert it into a parameter block.\n\n**From a technical drawing** (image or PDF):\n\n- Identify the views — front \u002F plan (top) \u002F side — and the projection\n  convention; the title block states first- or third-angle, the SCALE\n  (e.g. 2:1), and the units\u002Ftolerance standard.\n- Printed dimension callouts are real part dimensions. Never replace one with a\n  pixel-derived value, and never multiply a printed dimension by drawing scale.\n- Hidden (dashed) lines are internal features — holes, pockets, bores.\n  Centrelines (long-short chain) mark hole centres and symmetry axes. Section\n  hatching shows solid material in a cut view.\n- Symbols: Ø diameter (not radius), R radius, M6\u002FM8… threads,\n  ⌴ counterbore, ⌵ countersink, typical hole note `4× Ø6.6 THRU`.\n- Cross-check every feature in at least two views before trusting it — a circle\n  in plan with no matching hidden lines in front is probably a boss, not a hole.\n\nAfter recording all printed dimensions, calibrated image evidence may resolve\n**undimensioned form**: curved silhouettes, taper transitions, blade\u002Fscoop profiles,\nand the placement of a feature whose size is printed but whose connecting contour\nis not. Calibrate each view from at least one printed span on the same projection,\nrecord the pixel-to-mm transform, and keep explicit dimensions authoritative.\nNever trace dimension\u002Fleader\u002Fhatch lines as part edges. Treat a pixel-derived\nprofile as an inference and verify it in another view or section. This is a form\nrecovery pass, not permission to override callouts.\n\nThen write the spec as named parameters in your first `execute()`:\n\n```python\n# All dims in mm, from drawing DWG-042 rev B\nLENGTH, WIDTH, HEIGHT = 80.0, 50.0, 12.0\nHOLE_DIA, HOLE_INSET = 6.6, 8.0      # 4x Ø6.6 THRU, 8 from each edge\nFILLET_R = 3.0                        # vertical corners only\n```\n\nIf the drawing leaves a critical dimension ambiguous — a value missing, or two\nviews disagreeing — ask the user which value to use. Do not guess silently.\n\n## Step 2 — Build incrementally\n\n- One feature (or one boolean) per `execute()` call. Small steps are easy to\n  debug; a 60-line block that fails tells you nothing about which line broke.\n- Register the part under a stable name as soon as it exists:\n  `show(part, \"part\")`. `show()` prints volume and face count immediately,\n  confirming the shape is non-empty.\n\n**Author for editability — a design to edit, not a shape to render.** A\nsyntactically valid script that hard-codes every number is a *shape*, not a\n*design*: no one can change the hole spacing without rebuilding from scratch.\nFollow the design-state conventions (Arko-T §4.3) so an edit is a one-number\nchange:\n\n- **Named parameter block at the top, with units** — `plate_thickness = 5.0  # mm`\n  — never inline magic constants.\n- **Consistent construction order** — base sketch\u002Fsolid\u002Fshell or cored body →\n  secondary features (holes, ribs, pockets) → finishing (fillets, chamfers).\n- **Canonical feature idioms** so a feature name maps to the obvious construction\n  pattern; reuse the same idiom for the same feature.\n- **Derive coordinates from parameters** (expressions \u002F references \u002F selectors),\n  not hand-computed magic positions — so moving one datum moves everything bound\n  to it. See `build123d:\u002F\u002Fquickref` Pattern 3 for a worked example.\n\nFinish by running `design_audit()` (Step 6) to prove the parameters are robust.\n\n## Step 2A — Choose the body family before detailing\n\nBefore adding holes, ribs, fillets or chamfers, decide the part's dominant body\nfamily and build that form first. Body-family correction means changing the\nprimary construction strategy — shell, revolve, sweep, cored housing, drafted\ncasting, open-spoke frame — not polishing a wrong mass with cosmetic blends.\nFillets, chamfers and small detail cuts do not count as dominant-form\ncorrection; use them only after the main volume, voids and silhouette match.\n\nUse these recipes as generic starting points. They choose the main construction\nstrategy; Step 5 and `build123d:\u002F\u002Fskill\u002Frepair` remain canonical for imported\nSTEP edits, topology repair and exact feature removal.\n\n- **Thin-walled open cover \u002F tray \u002F housing.** Build the outer envelope and inner\n  cavity as the first solids, then create the rim\u002Fflange, bosses, ribs and shallow\n  pockets. Do not approximate it as a solid block with recess lines drawn on top.\n  Common wrong-but-valid simplifications are filled cavities, missing open face,\n  bosses that become full-depth tubes, and ribs that close intended air space.\n  Check with `cross_sections()` through the wall, `measure()` for bbox\u002Fwall\n  sanity, `find_bosses()` for mounting posts, `find_holes()` for inserts\u002Fbores,\n  and clipped `render_view()` to prove the cavity is open. Keep booleans simple:\n  cut the cavity with one slightly-overlong tool, fuse bosses\u002Fribs with small\n  overlap, then cut final holes through the fused result.\n\n- **Cored annular flange \u002F circular housing.** Start from the central axis:\n  revolve or stack cylinders\u002Frings for the top flange, hub and main bore, then\n  remove the under-flange relief\u002Fcore before adding bolt-circle holes. Do not let\n  bolt holes imply full-depth tubes unless the drawing shows tubes; many are\n  local holes through a flange. Common failures are a solid disk where the section\n  shows an annular void, missing underside relief, and bolt-circle holes stopping\n  at the wrong depth. Check `find_hole_patterns()` for the bolt circle, `find_holes()`\n  for bore diameter\u002Fdepth, `cross_sections(axis=...)` across the axis for the\n  cored profile, and `measure()` for concentric bbox\u002Fvolume sanity. Prefer\n  coaxial revolve\u002Fring construction, then subtract axial bores and local bolt\n  holes from the fused flange\u002Fhub.\n\n- **Cast gearbox \u002F pump housing with bosses and flanges.** Build the cored main\n  housing and drafted shoulders first, then overlapping side bosses, feet, pads\n  and flanges. Do not model it as a rectangular box plus cosmetic rounded edges;\n  draft\u002Ftaper and offset bores are part of the dominant form. Common failures are\n  slab-sided bodies, bosses butt-joined to faces without buried overlap, side\n  bores that miss the housing core, and feet added after holes so cuts do not\n  pass through the final fused body. Check bbox and face inventory with\n  `measure()`, side\u002Fcore sections with `cross_sections()`, boss inventory with\n  `find_bosses()`, and hole axes\u002Fdepths with `find_holes()`. Fuse large lobes,\n  bosses and feet with slight interpenetration before cutting shared bores and\n  through-holes; reserve cosmetic fillets for the end.\n\n- **Open spoked \u002F ribbed \u002F windowed casting.** Preserve air gaps as primary\n  negative space. Start with hub, rim\u002Fframe and one rib\u002Fspoke, then pattern or\n  mirror the repeated material and cut windows all the way through. Do not extrude\n  the convex outer silhouette and engrave spoke lines; that gives valid CAD with\n  the wrong topology. Common failures are closed windows, ribs fused into a plate,\n  missing central opening and inconsistent repeated angles. Check with clipped\n  `render_view()`, `cross_sections()` across the open plane, and `measure()` for\n  volume that is plausible for an open frame. Use `find_hole_patterns()` only\n  when actual holes\u002Fbolt features define the rotational pattern; it does not\n  recognize arbitrary spokes. Use one fused material set, then one window-cut set;\n  avoid many tangent cutters that only graze the rim.\n\n- **Axisymmetric stepped part.** If the side view defines most of the shape, draw\n  the half-section profile and `revolve()` it before adding plan-view holes,\n  keyways, flats or slots. Do not stack boxes\u002Fcylinders from the plan view and\n  hope fillets make the side profile correct. Common failures are square\n  shoulders where the profile is tapered, missing grooves\u002Freliefs, and holes cut\n  before the revolved body changes diameter. Check with `cross_sections()` on the\n  axis, `measure()` for step diameters\u002Fheights, and `find_holes()` for axial or\n  radial bores. Keep the revolved base parametric, then apply secondary cuts and\n  final edge treatments.\n\n### Step 2B — Use canonical source only for evidenced global revisions\n\nFirst build and checkpoint a valid candidate incrementally. Compare matching views\nand identify the largest mismatch. If that evidence requires a different dominant\nbody family, silhouette, shell strategy, revolve profile, sheet-metal construction,\nor blade\u002Floft family, write the complete replacement in `model.py`, assign its final\nshape to `result`, and run `execute_file(path=\"model.py\", result_name=\"result\")`.\nThe returned SHA-256 identifies the exact source that created the active model and\nis retained in session\u002Fsnapshot provenance.\n\nKeep local feature, dimension, hole, boss, fillet, and chamfer adjustments in the\nincremental workflow. Do not rewrite the complete part when the existing body family\nand silhouette already match. After a global source rebuild, compare it against the\nsaved floor using bbox, feature counts, matching projections, and section evidence.\nExport the rebuild only when that evidence shows improvement; otherwise restore the\nfloor. If source execution fails or omits the required result, the prior active model\nremains available automatically.\n\n## Step 3 — Verify numerically, then visually\n\n`measure()` is the source of truth; renders confirm appearance, not geometry.\n\n**Compose in code, don't copy numbers.** The analysis functions are callable *inside*\n`execute()` and return real Python objects, so filter and compute in code instead of\nreading a number out of one tool result and re-typing it into the next call:\n`measure(part)[\"volume\"]`, `[h for h in find_holes(part) if h.location[0] \u003C 5]`,\n`clearance(a, b)[\"clearance\"]`, `align_check(a, b)[\"delta\"]`. These are\nin-`execute()` Python helpers for composing checks in code. For standalone MCP\ntool calls, use `compare(a=\"before\", b=\"after\", kind=\"shape\")`,\n`compare(a=\"part\", b=\"mate\", kind=\"fit\")`,\n`compare(a=\"a\", b=\"b\", kind=\"align\")`, or\n`compare(a=\"snapshot_name\", kind=\"snapshot\")` instead of separate comparison verbs. Also available:\n`cross_sections`, `find_bosses`, `find_countersinks`, `find_hole_patterns`. They take a\nshape (default: current shape); `measure`\u002F`clearance`\u002F`cross_sections` stay bounded on\nlarge shapes. The standalone MCP tools remain for one-shot queries.\n\n- **After every boolean (`-`, `+`, `&`) call `measure()`** and check\n  `topology.faces` changed. Unchanged face\u002Fedge counts mean the boolean\n  silently failed.\n- Check the bounding box against the drawing envelope, and the face-type\n  inventory against the features: each plain drilled hole contributes one\n  cylinder face whose diameter must match the callout (Ø6.6 hole → 6.6 mm\n  cylinder in the inventory). Identical faces are aggregated — `4× Ø6.6 THRU`\n  shows as ONE cylinder entry with `\"count\": 4`, so sum the counts, don't\n  count entries.\n- Render only after `measure()` agrees with the spec:\n  `render_view(save_to=\"\u002Ftmp\u002Fpart.png\")`, then show \u002Ftmp\u002Fpart.png to the user.\n  Use `clip_plane`\u002F`clip_at` to reveal internal features.\n- Assemblies: use the MCP `compare(a=\"a\", b=\"b\", kind=\"fit\")` tool for fit\n  (apart \u002F touching \u002F containing \u002F interpenetrating, with volumes), and\n  `compare(a=\"a\", b=\"b\", kind=\"align\", axis=\"Z\", mode=\"flush\")` for\n  flush\u002Fconcentric checks. Inside `execute()`, the lower-level Python helpers\n  `clearance(a, b)` and `align_check(a, b)` remain available for branching in\n  code. Connect parts with Joints (RigidJoint \u002F RevoluteJoint \u002F …) rather than\n  raw `.move()` — see `build123d:\u002F\u002Fquickref`.\n- Editing an imported reference: after changing a part loaded with\n  `import_cad_file()`, `compare(a=\"input\", b=\"edited\", kind=\"shape\")` localizes *where* the\n  geometry changed and reports the exact added\u002Fremoved volume and surface\n  displacement — confirm the changed region and magnitude match the request, and\n  that the rest stayed put. A tangential move (sliding a hole) shows no region;\n  cross-check `find_holes` and the bbox\u002Fcenter deltas for those.\n- Use `find_holes`' bore axis for holes on curved or BSpline faces. Face centers\n  and bounding-box centers can be off-axis; an apparent \"already at target\"\n  result is a prompt to re-measure against the axis.\n- Avoid large point grids with `is_inside()` on big solids. They are slow and can\n  hit the operation timeout; prefer `cross_sections()` or a targeted clipped\n  render for interiors.\n\n### Dominant-form correction after the first valid render\n\nA valid, watertight model can still be the wrong *kind* of body. Before final\nexport on a model built from a drawing, use the first valid render as a\ndiagnostic checkpoint:\n\n1. Render the primary orthographic\u002Fisometric views.\n   For an input drawing, render matching projections and compare them against the\n   calibrated view crops. An overlay or edge-difference image is preferred when\n   available; compare silhouette and major internal edges, not annotation ink.\n2. Classify the dominant body family, not just the holes and bosses:\n   axisymmetric shell \u002F lathe profile, curved impeller or spoked radial\n   pattern, bent sheet-metal body, cast web with blind pockets, thin-walled\n   cover\u002Fhousing, drafted casting.\n3. Name the single largest semantic mismatch: block\u002Fslab where the drawing\n   shows a casting or shell; through-open gaps where section hatching shows\n   blind pockets and a central web; merged repeated vanes\u002Flobes\u002Fspokes; wrong\n   axis\u002Fprofile interpretation in a revolved part; holes placed on the wrong\n   body surface.\n4. If the mismatch is body-class level, rebuild from the named dimension\n   table by editing the canonical source and calling `execute_file()` again. Do\n   not keep patching the wrong representation with cosmetic cuts, fillets, or\n   shallow relief. Structural validity is not drawing-fidelity evidence, so an\n   unresolved dominant mismatch remains actionable even after `validate()` passes.\n5. After any cosmetic radius pass, re-run `find_holes` \u002F `find_bosses` \u002F\n   relevant recognizers. Reject a fillet\u002Fchamfer that changes a through-bore\n   diameter\u002Fdepth, feature count, or mating interface.\n\nField-proven decision rules:\n\n- **Axisymmetric shell:** if a revolve is valid but the bbox or section profile\n  is wrong, switch to coaxial cylinders\u002Fcones\u002Ffrustums from the dimension table\n  instead of continuing with the wrong revolve-axis convention.\n- **Repeated radial features:** verify instance count after booleans using a\n  face inventory, recognizer, or render. If vanes\u002Fspokes\u002Flobes merge, rebuild\n  with explicit numeric rotations of the analytic feature geometry.\n- **Blind pocket vs through void:** if section views show material remaining,\n  build the full web\u002Fbody first and subtract shallow pockets from one or both\n  sides. Do not model through truss-like gaps where the section shows solid web.\n- **Sheet metal:** classify constant-thickness folded bodies early. Prefer\n  panels\u002Fflanges\u002Fbends over solid blocks; boss recognizers should be empty\n  unless the drawing actually has raised bosses.\n- **Interface preservation:** after adding fillets\u002Fchamfers, verify hole,\n  bore, boss, and fit features again. Cosmetic rounding is not acceptable if it\n  silently shortens a through-bore or changes a mating feature.\n\n## Step 4 — Experiments and recovery\n\n- `save_snapshot(\"before_fillet\")` before any operation you might want to undo;\n  `restore_snapshot()` brings the geometry back (Python variables are NOT\n  restored — re-run assignments).\n- For \"what if?\" questions, use the loop: snapshot → mutate → measure\u002Frender →\n  restore. It is cheaper and more accurate than rebuilding.\n- If an `execute()` times out, only that one step is dropped: the worker restarts\n  and the session is **rebuilt from your prior `execute()` history** — variables,\n  shapes and named objects come back (snapshots and geometry imported via other\n  tools do not). Just retry the step, smaller. Very long sessions may rebuild only\n  partially if replay runs out of budget; `script()` returns the executed history.\n- A successful `execute_file()` replaces stale construction history with the\n  canonical source as the crash-replay root. Its source hash appears in\n  `session_state()`. A failed source rebuild leaves both active geometry and its\n  prior provenance unchanged.\n\n## Step 5 — Heavy builds (threads, gears, many fillets)\n\nThe `execute()` timeout (default 120 s) hard-limits a single call. First, split the\nheavy step into smaller `execute()` calls (build up incrementally — a timed-out step\nis dropped, not the session) and\u002For raise the ceiling with `--exec-timeout N` or\n`BUILD123D_EXEC_TIMEOUT=N` (this also extends the import budget for heavy STEP files).\n\nFor additive edits, avoid exactly coincident faces: they often do not fuse into a\nclean solid. Interpenetrate slightly, bury the added feature into the base, or\nextend-and-trim with one planar cut. For imported solids, prefer targeted solid\nrepair over broad shape healing; global healing can reorient faces or collapse\nvolume. When a validity\u002Fexport failure has a recognizable topology pattern,\ncall `repair_advice(error_text=..., goal=..., context=...)` before writing the\nrepair code. It returns generic recipes and stop conditions for the agent to\nimplement explicitly in `execute()`, rather than silently mutating the B-rep.\nFor an edit to a boss with a central bore, call `find_bored_bosses()` first to\nenumerate plausible bore\u002Fcap candidates and detect split cap faces before\nchoosing a construction method.\n\n**Extending a boss, or relocating any planar\u002Fannular face along its own normal\n(raising a bore's counterbore opening, moving a shoulder) is a common instance\nof this trap, with a better fix than interpenetrating.** These features have a\nconstant cross-section at the join, so unioning a new cylinder, tube or ring\non top — butt-joined or buried with overlap — often leaves the old face stuck\nas a duplicate internal face the fuse won't dissolve (`validate()` FAILs with\n\"N mesh non-manifold edge(s) — faces meet >2-ways\", the two pieces stay\nseparate solids, or a `validate()` PASS is followed by an `export()` failure\nafter the STEP round-trip re-checks orientation on the old face's remnants).\nIf `find_bored_bosses()` reports a single complete cap face, extrude the\nfeature's *own* face instead of adding a separately-built\nprimitive — a face extruded from the part's own boundary shares the exact\nunderlying geometry, so a follow-up fuse dissolves it cleanly where a\ncoincidentally-matching new primitive would not, even when it's positioned to\nmatch exactly:\n\nIf the cap is split across multiple faces, do **not** extrude one face and then\ntry to sew the damaged result. Build an explicit full-profile extension\u002Fsleeve\nfrom the measured bore axis and opening plane, fuse with small overlap, and\nre-cut the central bore continuously through old plus new material. This is the\nsame generic pattern returned by `repair_advice()` as\n`split_bored_boss_extension`.\n\nIf the boss\u002Fannular edit still fails, pass the gate text and edit goal to\n`repair_advice()`. The `split_bored_boss_extension` and\n`export_roundtrip_sliver_cleanup` recipes capture the safer fallback: measure\nthe true feature axis, build an explicit full-profile extension, recut the bore\ncontinuously, then remove only near-zero old-plane slivers if the written STEP\nroundtrip exposes them.\n\n- **One-sided** (extend\u002Frelocate toward a target on one end only — the more\n  common case): `BRepFeat_MakePrism` turns the face into a prism *feature*\n  that extends the existing topology in place, so there is no second solid to\n  fuse at all. Take the direction from the seat's own normal — never hardcode\n  an axis, since a boss off the part's dominant axis (tilted, horizontal, on a\n  cast body) needs its own direction, and a wrong one silently collapses the\n  result to zero volume with `IsDone()` still True:\n\n  ```python\n  from OCP.BRepFeat import BRepFeat_MakePrism\n  from OCP.gp import gp_Dir\n  # seat = the planar face to move (an annulus if it rims a bore or coaxial hole)\n  n = seat.normal_at(seat.center())\n  mk = BRepFeat_MakePrism(part.wrapped, seat.wrapped, seat.wrapped, gp_Dir(n.X, n.Y, n.Z), 1, True)\n  mk.Perform(delta)  # delta = distance to move along the axis\n  if not mk.IsDone():\n      raise RuntimeError(\"BRepFeat_MakePrism failed — seat face may not belong to part's topology\")\n  extended = Solid(mk.Shape())\n  ```\n\n- **Symmetric** (both ends of a boss grow by the same total amount): no raw\n  OCCT needed — `extrude()` each end-cap face along its own normal by half the\n  total growth, then fuse both onto the same running result (fusing the\n  second extension onto the original `part` instead of the already-extended\n  result would silently discard the first end's growth):\n\n  ```python\n  half = delta \u002F 2   # delta = TOTAL growth; each end grows by half\n  top_ext = extrude(top_seat, amount=half)       # top_seat, bottom_seat = the boss's two end-cap faces\n  bottom_ext = extrude(bottom_seat, amount=half)\n  extended = part.fuse(top_ext).fuse(bottom_ext)\n  ```\n\nEither way, extruding the actual face preserves the exact cross-section — a\ncoaxial bore or hole extends along with the OD — so verify by re-measuring the\nmoved face's new position, not just that `validate()` passed; a `validate()`\nPASS on this construction can still fail the stricter `export()` round-trip\ncheck, which is the signal to switch from add-and-fuse to this technique if\nyou haven't already.\n\n**Removing a small feature (a fillet, chamfer, or sliver) is the subtractive\nmirror of the same trap.** A boolean cut broad enough to reach the feature\nalmost always removes more than the feature itself — the cutter's own profile\nrarely matches the feature's exact geometry, so it shaves material off\nadjacent faces too and can trigger unrelated topology merges elsewhere in the\npart. Defeature the exact face(s) instead — `BRepAlgoAPI_Defeaturing` removes\nonly the named face and extends its neighbours to close the gap, changing\nnothing beyond the feature's own volume:\n\n```python\nfrom OCP.BRepAlgoAPI import BRepAlgoAPI_Defeaturing\ndf = BRepAlgoAPI_Defeaturing()\ndf.SetShape(part.wrapped)\ndf.AddFaceToRemove(bad_face.wrapped)   # bad_face = the fillet\u002Fchamfer face itself\ndf.Build()\nsolids = Compound(df.Shape()).solids()   # result is usually a compound; works on OCP 7 and 8\nassert len(solids) == 1, \"defeaturing did not return one solid\"\nremoved = solids[0]\n```\n\nVerified on a boss with a root fillet: a broad annular cut removed **13×** the\nfillet's own volume (it also shaved the adjacent straight boss wall) and grew\nthe face count; defeaturing the exact torus face removed only the fillet's\nvolume and *reduced* the face count, since the neighbouring faces extend and\nmerge instead of adding new ones. If the volume delta from a cut is much\nlarger than the feature you meant to remove, that mismatch is the signal to\nswitch to this technique, not to trim the cutter's dimensions further.\n\nOnly if a single unavoidable operation (IsoThread, a multi-body fillet, a very\nhigh-face-count boolean) still can't fit, drop out of the session for that one op:\n\n1. Probe the API in-session with small `execute()` calls.\n2. Write the build as a script and run it with your shell tool.\n3. `import_cad_file(\"part.step\", \"part\")` to bring the result back in.\n4. Verify as usual: `measure(\"part\")`, `render_view(objects=\"part\")`.\n\n## Step 6 — Finish\n\n1. Final `measure()` against the spec: envelope, volume sanity, hole inventory.\n2. **`validate(\"part\")` before exporting.** A STEP\u002FSTL that is not a watertight,\n   manifold, single solid is rejected outright by strict CAD and mesh\n   consumers — no matter how close the geometry is.\n   A `FAIL` here almost always means the current shape is a leftover 2D sketch,\n   an open shell, an un-fused compound (`Part() + ...`), or a degenerate boolean\n   result; fix it and re-validate until it passes.\n   `validate()` runs the same exact mesh check as export — in-process for small\n   parts, out-of-process for large ones so it can't stall the session. A shape too\n   big to stitch even there comes back `mesh_check: \"skipped\"` with a \"mesh not\n   verified\" warning (not a silent pass) — treat that as a cue to test-export.\n   `export()` re-checks the written STEP and is the authoritative verdict, so an\n   occasional `validate()` PASS → `export()` warning is still expected on\n   coincident faces, near-tangent joins, or a huge imported B-rep; test-export to\n   a throwaway path before finalizing.\n3. `export(\"part.step\", \"step\", object_name=\"part\")` — STEP for CAD interchange,\n   STL for printing. If the project slices with\n   [estampo](https:\u002F\u002Fgithub.com\u002Festampo\u002Festampo) (`estampo.toml` present),\n   add\u002Fupdate the `[[parts]]` entry for the exported file and run `estampo run`\n   instead of stopping at export. To seed the entry's overrides, generate an\n   estampo.toml fragment from the printability report with your shell tool:\n\n   ```bash\n   python -c \"\n   import augura\n   from build123d import import_step\n   report = augura.analyze(import_step('part.step'))\n   print(augura.to_estampo_toml(report))\n   \"\n   ```\n\n   The fragment sets `enable_support`, `brim_type`, and advisory\n   `[slicer.overrides]` comments — review and merge it into the `[[parts]]`\n   entry rather than pasting blindly (see estampo's skill).\n4. Unless the user opts out, save a clean regeneration script to\n   `scripts\u002F\u003Cpart>.py`: the parameter block, the build steps, and the export\n   call. Follow the project's existing script layout, and pick a non-colliding\n   name if one exists. The part should live in version control as code, not\n   only as a STEP artifact. Keep dimensions in a named parameter block at the\n   top (`plate_thickness = 5.0  # mm`), not inline literals — then\n   `design_audit()` can surface those parameters and perturb each ±10% to flag\n   *brittle* ones (a nudge that fails the validity gate), so you ship an\n   editable design, not just a valid shape.\n5. If the part will be FDM printed, run `analyze_printability(\"part\")` and\n   report overhangs \u002F thin walls \u002F bed-fit findings.\n6. For an engineering drawing of the finished part, switch to the b123d-drawing\n   skill (or the `build123d:\u002F\u002Fskill\u002Fdrawing` resource).\n\n---\n\n## Pitfalls\n\n- `Box(...) + Cylinder(...)` returns a **ShapeList**, not a fused Part — it has\n  no `.volume` or `.faces()`. Fuse with `Part() + Box(...) + Cylinder(...)` or\n  `box.fuse(cyl)`.\n- Fillet failures usually mean the radius is too large for the local geometry\n  or the selected edges are non-manifold — reduce the radius or select fewer\n  edges.\n- Selector indices are not stable across rebuilds. Use\n  `resolve(\"part\", \".faces().sort_by(Axis.Z)[-1]\", label=\"top\")` to confirm a\n  selector grabs the entity you think it does.\n- For fillet\u002Fchamfer edge selection on turned parts, use the built-in\n  `find_edges(shape, geom=\"circle\", radius=4.25, at_z=10.2)` instead of\n  hand-rolled filtering — it prints the match count, radii, and Z levels so a\n  wrong selection is visible before the fillet runs.\n- Errors from `execute()` come back with a failure classification and fix hint —\n  read them before retrying; `last_error()` has the line number and excerpt.\n- `show()` stores by reference: mutating a shape after `show()` changes the\n  stored object too. Re-`show()` under a new name to keep a frozen copy.\n",{},"main","49023cd809092daa10dea0e9648a87006591d1f1","src\u002Fbuild123d_mcp\u002Fskills\u002Fb123d-modeling",20,"https:\u002F\u002Fgithub.com\u002Fpzfreo\u002Fbuild123d-mcp","https:\u002F\u002Fgithub.com\u002Fpzfreo","http:\u002F\u002Fpzf.fremantle.org","2026-11-12 02:40:24",{"source":7,"skillName":5,"commands":33,"prompt":43,"zipAvailable":44},[34,37,40],{"tool":35,"command":36},"npx","npx skills add pzfreo\u002Fbuild123d-mcp --skill b123d-modeling",{"tool":38,"command":39},"bunx","bunx skills add pzfreo\u002Fbuild123d-mcp --skill b123d-modeling",{"tool":41,"command":42},"pnpm","pnpm dlx skills add pzfreo\u002Fbuild123d-mcp --skill b123d-modeling","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fpzfreo\u002Fbuild123d-mcp\u002Fblob\u002Fmain\u002Fsrc\u002Fbuild123d_mcp\u002Fskills\u002Fb123d-modeling\u002FSKILL.md",true,[],[47,55,63,76],{"skillNo":48,"name":49,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":50,"categories":51,"keywords":52,"publishedAt":20},"01M71S113XAE1RPP5B4TZJJCGW","b123d-repair","https:\u002F\u002Fgithub.com\u002Fpzfreo\u002Fbuild123d-mcp\u002Fblob\u002Fmain\u002Fsrc\u002Fbuild123d_mcp\u002Fskills\u002Fb123d-repair\u002FSKILL.md",[],[53,54],"repair","b123d repair",{"skillNo":56,"name":57,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":58,"categories":59,"keywords":60,"publishedAt":20},"01M71S0T5QS1HV2ZCTWVZZ98KJ","b123d-edit","https:\u002F\u002Fgithub.com\u002Fpzfreo\u002Fbuild123d-mcp\u002Fblob\u002Fmain\u002Fsrc\u002Fbuild123d_mcp\u002Fskills\u002Fb123d-edit\u002FSKILL.md",[],[61,62],"edit","b123d edit",{"skillNo":64,"name":65,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":66,"categories":67,"keywords":73,"publishedAt":20},"01M71S0PAQB3ZRZWZBSDF4JXBW","b123d-drawing","https:\u002F\u002Fgithub.com\u002Fpzfreo\u002Fbuild123d-mcp\u002Fblob\u002Fmain\u002Fsrc\u002Fbuild123d_mcp\u002Fskills\u002Fb123d-drawing\u002FSKILL.md",[68],{"categoryNo":69,"categoryName":70,"taxonomyType":71,"taxonomyTypeDesc":72},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[74,75],"drawing","b123d drawing",{"skillNo":77,"name":65,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":78,"categories":79,"keywords":80,"publishedAt":20},"01M71S0MW5PWTPJ7MHYYVARWB0","https:\u002F\u002Fgithub.com\u002Fpzfreo\u002Fbuild123d-mcp\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fb123d-drawing\u002FSKILL.md",[],[74,75]]