forgecad 0.9.14 → 0.9.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/LICENSE +6 -4
  2. package/README.md +8 -4
  3. package/dist/assets/{AdminPage-eWGs2K6H.js → AdminPage-CDyGUinA.js} +2 -2
  4. package/dist/assets/{BenchmarkPage-CTrLKfpo.js → BenchmarkPage-DfPMY_-d.js} +4 -15
  5. package/dist/assets/{BlogPage-5nPesyds.js → BlogPage-kF0fkdJT.js} +2 -2
  6. package/dist/assets/{DocsPage-C4Y3nbYc.js → DocsPage-B954L3YN.js} +9 -3
  7. package/dist/assets/EditorApp-Beb-IZ0y.js +14014 -0
  8. package/dist/assets/{EditorApp-BAnckbsk.css → EditorApp-CuDLxKqL.css} +698 -0
  9. package/dist/assets/{EmbedViewer-C8fB4n5U.js → EmbedViewer-C77B-TrF.js} +3 -3
  10. package/dist/assets/{LandingPageProofDriven-jSz0LaMM.js → LandingPageProofDriven-Cr6fXMDj.js} +35 -37
  11. package/dist/assets/LegalPage-BRlScr9A.css +91 -0
  12. package/dist/assets/LegalPage-Dzklqmmg.js +39 -0
  13. package/dist/assets/{PricingPage-BMedqFef.css → PricingPage-BPF6HKyO.css} +25 -0
  14. package/dist/assets/{PricingPage-B83B90zh.js → PricingPage-zWXkvlwl.js} +19 -19
  15. package/dist/assets/{SettingsPage-DY889pcu.js → SettingsPage-Bz0of4KQ.js} +2 -2
  16. package/dist/assets/app-CE3sYcV7.css +3890 -0
  17. package/dist/assets/{app-bEww1ic4.js → app-D3kDkggg.js} +2293 -946
  18. package/dist/assets/cli/{render-Cho2uKG_.js → render-DSY3mMQa.js} +337 -7
  19. package/dist/assets/{constructionHistoryWorker-HYwzJY4m.js → constructionHistoryWorker-gpDo-uH2.js} +927 -243
  20. package/dist/assets/{evalWorker-CjQwJSE-.js → evalWorker-CU0Ke6DP.js} +7800 -4164
  21. package/dist/assets/{forgecad_geometry-CH2nvuLA.js → forgecad_geometry-Dgceylq9.js} +43 -1
  22. package/dist/assets/forgecad_geometry_bg-dD4RNQF1.wasm +0 -0
  23. package/dist/assets/{inspectWorker-DeRnMVv1.js → inspectWorker-COyp8XXA.js} +927 -243
  24. package/dist/assets/{javascript-70-4uGcz.js → javascript-1kQXfVaz.js} +1 -1
  25. package/dist/assets/landing-proof-driven-DiGqdtWa.js +18 -0
  26. package/dist/assets/{landing-proof-driven-oFYW6mjz.css → landing-proof-driven-ORyigZ6p.css} +13 -7
  27. package/dist/assets/legalContent-ZfFGMmi4.js +251 -0
  28. package/dist/assets/{manifold-CG9Fokx-.js → manifold-BRI5prcH.js} +1 -1
  29. package/dist/assets/{manifold-uRzgk5O8.js → manifold-C-3h2M7p.js} +2 -2
  30. package/dist/assets/{manifold-rmfAcdwF.js → manifold-DNkrUWpA.js} +1 -1
  31. package/dist/assets/{reportWorker-4cW_ZpoS.js → reportWorker-CdBz5bNg.js} +7538 -10857
  32. package/dist/assets/{scalar-sampling-budget-CfDiFvh7.js → scalar-sampling-budget-wJF98aY9.js} +6935 -4331
  33. package/dist/assets/{scanProxyWorker-Bs2TDgLw.js → scanProxyWorker-B-9VbLIs.js} +32 -1
  34. package/dist/assets/{solver-DuJAO8S6.js → solver-BZ9LPTHs.js} +1 -1
  35. package/dist/assets/solver_bg-DAHZJ_rw.wasm +0 -0
  36. package/dist/assets/{targets-D6PWsv6X.js → targets-B9sGB5nB.js} +1 -1
  37. package/dist/assets/{vendor-react-Da3A2QmU.js → vendor-react-6j1Kke-Y.js} +6 -5
  38. package/dist/cli/render.html +1 -1
  39. package/dist/docs/index.html +2 -2
  40. package/dist/docs-raw/AI/ai-native-cad.md +50 -0
  41. package/dist/docs-raw/AI/usage.md +3 -12
  42. package/dist/docs-raw/CLI.md +30 -10
  43. package/dist/docs-raw/component-model.md +27 -11
  44. package/dist/docs-raw/generated/assembly.md +301 -212
  45. package/dist/docs-raw/generated/concepts.md +235 -237
  46. package/dist/docs-raw/generated/core.md +283 -6
  47. package/dist/docs-raw/generated/curves.md +274 -361
  48. package/dist/docs-raw/generated/lib.md +7 -1
  49. package/dist/docs-raw/generated/output.md +19 -4
  50. package/dist/docs-raw/generated/runtime-names.md +41 -0
  51. package/dist/docs-raw/generated/sdf.md +31 -0
  52. package/dist/docs-raw/generated/sheet-metal.md +9 -0
  53. package/dist/docs-raw/generated/sketch.md +44 -1
  54. package/dist/docs-raw/generated/viewport.md +11 -3
  55. package/dist/docs-raw/guides/coordinate-system.md +20 -16
  56. package/dist/docs-raw/guides/geometry-conventions.md +2 -2
  57. package/dist/docs-raw/guides/inspection-bundles.md +2 -1
  58. package/dist/docs-raw/guides/joint-design.md +24 -0
  59. package/dist/docs-raw/guides/positioning.md +13 -3
  60. package/dist/docs-raw/legal/privacy.md +63 -0
  61. package/dist/docs-raw/legal/software-license.md +55 -0
  62. package/dist/docs-raw/legal/terms.md +87 -0
  63. package/dist/docs-raw/skills/forgecad-3d-reconstruction.md +1 -1
  64. package/dist/docs-raw/skills/forgecad-blockout-model.md +1 -1
  65. package/dist/docs-raw/skills/forgecad-component-model.md +11 -2
  66. package/dist/docs-raw/skills/forgecad-high-level-spec.md +1 -1
  67. package/dist/docs-raw/skills/forgecad-image-replicator.md +8 -8
  68. package/dist/docs-raw/skills/forgecad-lld.md +1 -1
  69. package/dist/docs-raw/skills/forgecad-make-a-model.md +1 -1
  70. package/dist/docs-raw/skills/forgecad-model-grader.md +2 -2
  71. package/dist/docs-raw/skills/forgecad-prepare-prompt.md +2 -2
  72. package/dist/docs-raw/skills/forgecad-project.md +1 -1
  73. package/dist/docs-raw/skills/forgecad-reconstruction-benchmark.md +1 -1
  74. package/dist/docs-raw/skills/forgecad-render-inspect.md +4 -2
  75. package/dist/docs-raw/skills/forgecad-visual-spec.md +1 -1
  76. package/dist/docs-raw/skills/forgecad.md +4 -3
  77. package/dist/index.html +40 -12
  78. package/dist/llms.txt +8 -0
  79. package/dist/site.webmanifest +1 -1
  80. package/dist/sitemap.xml +49 -13
  81. package/dist-cli/{check-compiler-U5SOPN7X.js → check-compiler-SDX5QIXI.js} +1 -2
  82. package/dist-cli/{check-query-propagation-XOKNSSYU.js → check-query-propagation-EAYEFT77.js} +1 -2
  83. package/dist-cli/{chunk-EXWGNL6K.js → chunk-N4O47JLF.js} +12540 -9046
  84. package/dist-cli/forgecad.js +1786 -679
  85. package/dist-cli/{forgecad_geometry-GYVNKPIE.js → forgecad_geometry-QOQIIP53.js} +42 -1
  86. package/dist-cli/forgecad_geometry_bg.wasm +0 -0
  87. package/dist-cli/{solver-46FFSK2U.js → solver-OK4HECRH.js} +0 -1
  88. package/dist-cli/solver_bg.wasm +0 -0
  89. package/dist-skill/CONTEXT.md +1117 -721
  90. package/dist-skill/SKILL.md +3 -2
  91. package/dist-skill/docs/API/core/concepts.md +64 -1
  92. package/dist-skill/docs/CLI.md +30 -10
  93. package/dist-skill/docs/generated/assembly.md +277 -229
  94. package/dist-skill/docs/generated/core.md +283 -6
  95. package/dist-skill/docs/generated/curves.md +272 -362
  96. package/dist-skill/docs/generated/lib.md +7 -1
  97. package/dist-skill/docs/generated/output.md +19 -4
  98. package/dist-skill/docs/generated/runtime-names.md +41 -0
  99. package/dist-skill/docs/generated/sdf.md +31 -0
  100. package/dist-skill/docs/generated/sheet-metal.md +9 -0
  101. package/dist-skill/docs/generated/sketch.md +44 -2
  102. package/dist-skill/docs/generated/viewport.md +2 -87
  103. package/dist-skill/docs/guides/coordinate-system.md +20 -16
  104. package/dist-skill/docs/guides/geometry-conventions.md +2 -2
  105. package/dist-skill/docs/guides/inspection-bundles.md +2 -1
  106. package/dist-skill/docs/guides/joint-design.md +24 -0
  107. package/dist-skill/docs/guides/positioning.md +13 -3
  108. package/dist-skill/library/forgecad-component-model/SKILL.md +10 -1
  109. package/dist-skill/library/forgecad-image-replicator/SKILL.md +6 -6
  110. package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.py +166 -0
  111. package/dist-skill/library/forgecad-model-grader/SKILL.md +1 -1
  112. package/dist-skill/library/forgecad-prepare-prompt/SKILL.md +1 -1
  113. package/dist-skill/library/forgecad-render-inspect/SKILL.md +3 -1
  114. package/examples/api/assembly-kinematics-foundation.forge.js +65 -0
  115. package/examples/api/assembly-kinematics-four-bar.forge.js +115 -0
  116. package/examples/api/assembly-kinematics-limb.forge.js +116 -0
  117. package/examples/api/connector-frame-rig-chain.forge.js +102 -0
  118. package/examples/api/exact-sheet-shell-assembly.forge.js +0 -2
  119. package/examples/api/exact-surface-studio.forge.js +6 -8
  120. package/examples/api/helix-basics.forge.js +6 -6
  121. package/examples/api/lean-foundations/README.md +12 -0
  122. package/examples/api/lean-foundations/curve-blend-exact.forge.js +22 -0
  123. package/examples/api/lean-foundations/curve-fit-interpolation.forge.js +18 -0
  124. package/examples/api/lean-foundations/curve-helix-canonicalization.forge.js +27 -0
  125. package/examples/api/lean-foundations/curve-route-canonicalization.forge.js +16 -0
  126. package/examples/api/lean-foundations/curve-trim-reverse.forge.js +24 -0
  127. package/examples/api/lean-foundations/exact-curve-arc.forge.js +36 -0
  128. package/examples/api/mixed-edge-finishes-proof.forge.js +8 -11
  129. package/examples/api/route3d-elbow.forge.js +68 -0
  130. package/examples/api/transition-curves.forge.js +44 -15
  131. package/examples/api/y-blend-corner-showcase.forge.js +0 -2
  132. package/examples/generative/coral-vase.forge.js +1 -1
  133. package/examples/nurbs-tube.forge.js +1 -1
  134. package/package.json +14 -13
  135. package/dist/assets/EditorApp-lXv53A1m.js +0 -13610
  136. package/dist/assets/app-CsHnaBWt.css +0 -1789
  137. package/dist/assets/forgecad_geometry_bg-C5_E9Oa9.wasm +0 -0
  138. package/dist/assets/solver_bg-CWvv4lnN.wasm +0 -0
  139. package/dist/docs-raw/API/README.md +0 -16
  140. package/dist/docs-raw/API/core/concepts.md +0 -118
  141. package/dist/docs-raw/INDEX.md +0 -138
  142. package/dist/docs-raw/RELEASING.md +0 -87
  143. package/dist/docs-raw/agent-native-api.md +0 -27
  144. package/dist/docs-raw/beta-deployment.md +0 -304
  145. package/dist/docs-raw/beta-operations.md +0 -325
  146. package/dist/docs-raw/blueprint-first.md +0 -145
  147. package/dist/docs-raw/cli-monetization.md +0 -112
  148. package/dist/docs-raw/coding-best-practices.md +0 -120
  149. package/dist/docs-raw/coding.md +0 -340
  150. package/dist/docs-raw/deployment.md +0 -374
  151. package/dist/docs-raw/guides/skill-maintenance.md +0 -161
  152. package/dist/docs-raw/guides/surface-members.md +0 -82
  153. package/dist/docs-raw/harbor-cli.md +0 -854
  154. package/dist/docs-raw/internals/backend-vocabulary.md +0 -35
  155. package/dist/docs-raw/internals/compiler.md +0 -307
  156. package/dist/docs-raw/internals/constraint-solver-quality.md +0 -161
  157. package/dist/docs-raw/internals/constraint-solver.md +0 -176
  158. package/dist/docs-raw/internals/shape-from-slices.md +0 -152
  159. package/dist/docs-raw/internals/sketch-2d-pipeline.md +0 -108
  160. package/dist/docs-raw/platform/admin.md +0 -45
  161. package/dist/docs-raw/platform/architecture.md +0 -82
  162. package/dist/docs-raw/platform/auth.md +0 -139
  163. package/dist/docs-raw/platform/email.md +0 -67
  164. package/dist/docs-raw/platform/google-oauth-setup.md +0 -88
  165. package/dist/docs-raw/platform/observability.md +0 -197
  166. package/dist/docs-raw/platform/projects.md +0 -111
  167. package/dist/docs-raw/platform/sharing.md +0 -90
  168. package/dist/docs-raw/product/README.md +0 -39
  169. package/dist/docs-raw/product/api-as-product-language.md +0 -13
  170. package/dist/docs-raw/product/business-model.md +0 -15
  171. package/dist/docs-raw/product/competitive-positioning.md +0 -17
  172. package/dist/docs-raw/product/creative-manufacturing.md +0 -15
  173. package/dist/docs-raw/product/founder-story.md +0 -11
  174. package/dist/docs-raw/product/manufacturing-workflows.md +0 -15
  175. package/dist/docs-raw/product/onboarding-first-experience.md +0 -256
  176. package/dist/docs-raw/product/product-loop.md +0 -17
  177. package/dist/docs-raw/product/strategic-decisions.md +0 -22
  178. package/dist/docs-raw/product/user-outreach-email-templates.md +0 -161
  179. package/dist/docs-raw/product/user-segments.md +0 -15
  180. package/dist/docs-raw/product/vision.md +0 -26
  181. package/dist/docs-raw/rl-environments.md +0 -350
  182. package/dist/docs-raw/runbook.md +0 -611
  183. package/dist-cli/check-compiler-U5SOPN7X.js.map +0 -1
  184. package/dist-cli/check-query-propagation-XOKNSSYU.js.map +0 -1
  185. package/dist-cli/chunk-EXWGNL6K.js.map +0 -1
  186. package/dist-cli/forgecad.js.map +0 -1
  187. package/dist-cli/forgecad_geometry-GYVNKPIE.js.map +0 -1
  188. package/dist-cli/solver-46FFSK2U.js.map +0 -1
  189. package/dist-skill/SKILL-dev.md +0 -145
  190. package/dist-skill/docs-dev/API/core/concepts.md +0 -118
  191. package/dist-skill/docs-dev/CLI.md +0 -677
  192. package/dist-skill/docs-dev/agent-native-api.md +0 -27
  193. package/dist-skill/docs-dev/blueprint-first.md +0 -145
  194. package/dist-skill/docs-dev/coding-best-practices.md +0 -120
  195. package/dist-skill/docs-dev/coding.md +0 -340
  196. package/dist-skill/docs-dev/component-model.md +0 -164
  197. package/dist-skill/docs-dev/generated/assembly.md +0 -794
  198. package/dist-skill/docs-dev/generated/core.md +0 -2117
  199. package/dist-skill/docs-dev/generated/curves.md +0 -2583
  200. package/dist-skill/docs-dev/generated/lib.md +0 -169
  201. package/dist-skill/docs-dev/generated/output.md +0 -247
  202. package/dist-skill/docs-dev/generated/sdf.md +0 -446
  203. package/dist-skill/docs-dev/generated/sheet-metal.md +0 -504
  204. package/dist-skill/docs-dev/generated/sketch.md +0 -1811
  205. package/dist-skill/docs-dev/generated/viewport.md +0 -585
  206. package/dist-skill/docs-dev/generated/wood.md +0 -108
  207. package/dist-skill/docs-dev/guides/coordinate-system.md +0 -46
  208. package/dist-skill/docs-dev/guides/geometry-conventions.md +0 -52
  209. package/dist-skill/docs-dev/guides/inspection-bundles.md +0 -485
  210. package/dist-skill/docs-dev/guides/joint-design.md +0 -78
  211. package/dist-skill/docs-dev/guides/modeling-recipes.md +0 -78
  212. package/dist-skill/docs-dev/guides/positioning.md +0 -161
  213. package/dist-skill/docs-dev/guides/skill-maintenance.md +0 -161
  214. package/dist-skill/docs-dev/internals/backend-vocabulary.md +0 -35
  215. package/dist-skill/docs-dev/internals/compiler.md +0 -307
  216. package/dist-skill/docs-dev/internals/constraint-solver-quality.md +0 -161
  217. package/dist-skill/docs-dev/internals/constraint-solver.md +0 -176
  218. package/dist-skill/docs-dev/internals/sketch-2d-pipeline.md +0 -108
  219. package/dist-skill/library/forgecad-image-replicator/scripts/compare_images.mjs +0 -289
@@ -1,27 +0,0 @@
1
- ---
2
- skill-group: dev-conventions
3
- skill-order: 3
4
- ---
5
-
6
- # Agent-Native API Design
7
-
8
- ForgeCAD assumes that much of its model code will be written by AI coding agents. That is a strength of code-first CAD: the artifact is plain source, the API is documented, and the CLI gives the agent a feedback loop. A strong agent can read domain documentation and temporarily become useful in almost any specialty.
9
-
10
- But "make the agent more specialized through documentation" is not the core problem ForgeCAD should solve. If the agent needs pages of instructions to hand-derive standard mechanical behavior, manually tune magic numbers, compute trigonometry, or reconstruct common CAD operations from primitives, the missing capability belongs in the API. Documentation can teach the vocabulary, but it should not compensate for a weak vocabulary.
11
-
12
- The goal is an API specific and powerful enough to cover most serious modeling needs directly. Instead of asking agents to become gear experts, sheet-metal experts, fixture experts, or assembly-kinematics experts from scratch on every task, ForgeCAD should give them first-class concepts with names, contracts, diagnostics, and examples. The agent should read the docs to discover `lib.spurGear()`, `SheetMetal.flange()`, connector-based assemblies, inspection bundles, and exact export workflows, then compose those concepts with confidence.
13
-
14
- Docs are leverage, not product substitute. They should explain what exists, when to use it, what guarantees it provides, what limitations remain, and how to validate the result. They should not become long recipes for reimplementing missing algorithms in user code. When a doc repeatedly explains how to work around an API gap, that is a design signal: promote the intent into the API, add diagnostics, and keep the recipe as orientation rather than obligation.
15
-
16
- This is also why API specificity matters. Generic geometry escape hatches are valuable for unusual work, but the common path should preserve domain intent. A belt drive is not just a loop of tangent lines. A countersunk hole is not just three boolean cylinders. A multi-part assembly is not just a collection of translates. When ForgeCAD encodes those concepts directly, AI agents produce code that is shorter, more inspectable, easier to repair, and closer to what a mechanical designer meant.
17
-
18
- ## Design Gate
19
-
20
- When improving docs or adding API surface, ask:
21
-
22
- - Are we teaching the agent an existing ForgeCAD concept, or teaching it to reimplement a concept ForgeCAD should own?
23
- - Would three different agents likely write three different fragile versions of this from the same docs?
24
- - Does the current solution require hand-tuned constants, manual trigonometry, raw coordinate derivation, or silent fallback behavior?
25
- - Could a domain-specific function, builder, namespace, diagnostic, or verification primitive make the intended model obvious?
26
-
27
- If the answer points to missing vocabulary, prefer an API improvement over longer instructions. Let documentation make agents fluent in ForgeCAD; do not make documentation carry the weight of the CAD system.
@@ -1,145 +0,0 @@
1
- ---
2
- skill-group: dev-conventions
3
- skill-order: 3
4
- ---
5
-
6
- # Blueprint-First Philosophy
7
-
8
- ## Code Should Read Like a Mechanical Blueprint
9
-
10
- ForgeCAD is a **mechanical compiler**. The user inputs design intent — dimensions, constraints, relationships — and the engine handles the geometry. If a mechanical feature can be dimensioned on a 2D drawing without explicit X/Y coordinates, a user should be able to model it in ForgeCAD without calculating those coordinates.
11
-
12
- We are building a geometry engine, not a math test.
13
-
14
- ## The Trigonometry Tax
15
-
16
- Every time a user writes `Math.sin()` or `Math.cos()` to place a bolt hole, position a circular pattern, or compute a tangent point, our API has failed them. This is the **Trigonometry Tax** — the cost of translating declarative engineering intent into imperative Cartesian math.
17
-
18
- The tax is real and measurable. In our own example library, ~40 of 250+ files contain manual trig. The same patterns repeat: circular positioning, polygon vertex layout, rotation result computation, polar-to-cartesian conversion. Each one is a place where the API should have provided an abstraction but didn't.
19
-
20
- ## The Core Pillars
21
-
22
- ### Pillar I: Intent-Driven Constraints over Explicit Coordinates
23
-
24
- Traditional CAD-as-code requires calculating exact center points and intersections. ForgeCAD uses **constraint-driven pathing** — users define the knowns (radii, angles, distances), and the solver calculates the implicit intersections.
25
-
26
- ```javascript
27
- // The old way — calculating tangent arc intercepts by hand
28
- const cx = r1 * Math.cos(angle) + offset * Math.sin(angle);
29
- const cy = r1 * Math.sin(angle) - offset * Math.cos(angle);
30
-
31
- // The ForgeCAD way — declare intent, solver handles geometry
32
- sketch.route([
33
- { tangent: circleA },
34
- { fillet: 17 },
35
- { tangent: circleB },
36
- ]);
37
- ```
38
-
39
- ### Pillar II: Topological Selection
40
-
41
- Hardcoding vertex indices or exact 3D coordinates to apply fillets makes models brittle. If a base dimension changes, the coordinates change, and the build breaks. ForgeCAD borrows from the DOM: **select geometry via topological queries, not hardcoded indices.**
42
-
43
- ```javascript
44
- // Brittle — breaks if topology changes
45
- body.fillet(5, edgeIndex[14]);
46
-
47
- // ForgeCAD — semantic edge selection
48
- fillet(body, 5, { parallel: [0, 0, 1], convex: true });
49
- fillet(body, 3, selectEdges(body, { atZ: 0 }));
50
- ```
51
-
52
- ### Pillar III: Mechanical First-Class Citizens
53
-
54
- Primitives should not be limited to circles, rectangles, and polygons. Mechanical engineering relies on standard features that are tedious to build from scratch. If a machinist has a specific tool bit for it, the API should have a specific primitive for it.
55
-
56
- ```javascript
57
- // Holes with real mechanical features — not raw boolean subtraction
58
- shape.hole('top', { diameter: 11, depth: 20,
59
- counterbore: { diameter: 18, depth: 5 },
60
- thread: { designation: 'M10' },
61
- });
62
-
63
- // Standard mechanical profiles
64
- slot(30, 10);
65
- arcSlot({ pitchRadius: 50, sweep: 60, width: 12 });
66
- lib.spurGear({ module: 2, teeth: 24 });
67
- ```
68
-
69
- ### Pillar IV: Relative Workplanes
70
-
71
- Everything in mechanical design is relative. You don't drill a hole at `[15.5, 30.2, 100.0]` in global space. You drill a hole on the top face of the flange, centered on the lug. The API must support dynamic coordinate systems that attach to existing geometry.
72
-
73
- ```javascript
74
- // Global coordinates — fragile, unreadable
75
- const hole = circle2d(5).extrude(20).translate(15.5, 30.2, 100.0);
76
-
77
- // Relative to geometry — moves with the parent
78
- circle2d(5).onFace(flange, 'top', { u: 10, v: 0 }).extrude(-20);
79
- ```
80
-
81
- ### Pillar V: No Manual Math for Standard Layout
82
-
83
- If a user has to import `Math.sin` to place elements in a circle, compute `Math.sqrt(3)` for an equilateral triangle, or manually convert degrees to radians, our API has a gap. Standard layout operations are first-class:
84
-
85
- ```javascript
86
- // Circular layout — no trig
87
- const positions = circularLayout(12, radius);
88
-
89
- // Polygon vertex positions — no sqrt(3)
90
- const vertices = polygonVertices(3, radius);
91
-
92
- // Polar positioning — no sin/cos
93
- shape.translatePolar(radius, angleDeg);
94
- ```
95
-
96
- ## The Anti-Patterns
97
-
98
- ### 1. No sin/cos in user code for standard layout
99
- If a user imports `Math.sin` to place a bolt hole, we have a missing abstraction. We provide polar coordinates, patterns, and layout helpers natively.
100
-
101
- ### 2. No magic shrinkwraps without explicit control
102
- Connections between shapes must be explicit. "Connect Shape A to Shape B with a tangent arc of R15" — not a black-box convex hull that guesses intent.
103
-
104
- ### 3. No silent failures
105
- If a fillet radius is too large, or a tangent route can't be solved, the API throws a descriptive mechanical error — not a generic engine crash. (See also: CLAUDE.md "No Silent Fallbacks" rule.)
106
-
107
- ### 4. No coordinate math for relative positioning
108
- If a feature is defined relative to another feature (a hole on a face, a boss centered on a lug), the API must express that relationship directly — not force the user to compute the absolute position.
109
-
110
- ## Design Gate
111
-
112
- **Every new public API method must pass this test:**
113
-
114
- > Can the user accomplish this without `Math.sin`, `Math.cos`, `Math.atan2`, manual degree-to-radian conversion, or computing intermediate Cartesian coordinates from polar/angular intent?
115
-
116
- If the answer is no, the API needs a higher-level alternative. The raw math path can still exist for power users, but the common case must be trig-free.
117
-
118
- ## Ergonomics in JavaScript
119
-
120
- JavaScript lacks Python's `with` statement, so ForgeCAD relies on **method chaining** and **callback scopes** for clean, fluid modeling:
121
-
122
- ```javascript
123
- // Fluid chaining — sketch to solid to features
124
- const part = circle2d(50)
125
- .extrude(15)
126
- .fillet(5, { atZ: 0 });
127
-
128
- // Callback workplane — scoped 2D ops on a 3D face
129
- shape.onFace('top', (face) => {
130
- face.subtract(
131
- circularPattern(circle2d(5.5), 6, { radius: 32.5 })
132
- );
133
- });
134
- ```
135
-
136
- ## Architecture Constraints
137
-
138
- - **TypeScript native.** Autocomplete and compile-time checking for mechanical parameters are mandatory.
139
- - **B-Rep capable.** Topological naming and precise filleting require a real CAD kernel (Manifold for mesh, OCCT for B-Rep).
140
- - **Immutable geometry, mutable builders.** Underlying geometry is immutable for predictable undo/redo. Builder classes maintain state for ergonomic chaining.
141
- - **Degrees at the API boundary.** All user-facing angles are in degrees. Radians are an internal implementation detail.
142
-
143
- ## Summary
144
-
145
- We are not building a 3D drawing tool. We are building a **mechanical compiler**. The user inputs the blueprint's design intent, and our API handles the geometry.
@@ -1,120 +0,0 @@
1
- ---
2
- skill-group: dev-conventions
3
- skill-order: 1
4
- ---
5
-
6
- # Coding Best Practices
7
-
8
- ## Minimal Implementation
9
-
10
- Write only the code needed to solve the problem. No verbose implementations, no speculative features.
11
-
12
- ## TypeScript
13
-
14
- - Use explicit types for function parameters and return values
15
- - Avoid `any` — use `unknown` or proper types
16
- - Prefer interfaces for object shapes
17
-
18
- ## React Components
19
-
20
- - Functional components only
21
- - Inline styles for simplicity (no CSS files unless necessary)
22
- - Extract reusable logic to custom hooks or store actions
23
-
24
- ### Stable References in Render Bodies
25
-
26
- Never use `?? []`, `?? {}`, or inline fallback literals in a component render body if the value flows (directly or transitively) into a `useMemo`/`useCallback`/`useEffect` dependency array. Each render creates a new reference, which silently invalidates every downstream memo.
27
-
28
- ```tsx
29
- // BAD — new [] every render, breaks every useMemo that depends on `items`
30
- const items = config?.items ?? [];
31
-
32
- // GOOD — stable reference across renders
33
- const items = useMemo(() => config?.items ?? [], [config]);
34
- ```
35
-
36
- Use `useMemo` for any derived-with-fallback value that feeds into other hooks.
37
-
38
- ## State Management
39
-
40
- - All global state lives in `forgeStore.ts`
41
- - Use Zustand selectors to prevent unnecessary re-renders
42
- - Keep actions pure and synchronous where possible
43
-
44
- ## Performance
45
-
46
- ### Geometry Operations
47
-
48
- - Manifold operations are expensive — minimize boolean ops
49
- - Cache geometry results when parameters don't change
50
- - Use debouncing for real-time updates
51
-
52
- ### React Rendering
53
-
54
- - Use Zustand selectors to prevent unnecessary re-renders
55
- - Memoize expensive computations with `useMemo`
56
- - Keep component tree shallow
57
-
58
- ## Linting & Formatting
59
-
60
- [Biome](https://biomejs.dev/) handles both linting and formatting for all TS/JS code.
61
-
62
- ```bash
63
- npm run lint # check for lint issues (no changes)
64
- npm run lint:fix # auto-fix lint issues
65
- npm run format # auto-format all files
66
- ```
67
-
68
- Biome runs as part of `npm run check:suite`. Configuration lives in `biome.json` at the repo root.
69
-
70
- ## Self-Review Before Commit
71
-
72
- 1. Remove console.logs and debug code
73
- 2. Check for unused imports
74
- 3. Verify TypeScript has no errors
75
- 4. Test the change works as intended
76
- 5. Read the diff — does it make sense?
77
-
78
- ### What to Look For
79
-
80
- - Does this solve the problem with minimal code?
81
- - Are there edge cases not handled?
82
- - Is the code readable without comments?
83
- - Does it follow existing patterns?
84
-
85
- ## API Naming: Methods Read Like Sentences
86
-
87
- Public API method names must read like natural English. Prefer verbose, intuitive names over short, ambiguous ones. The method name alone should tell you exactly what it does — no options objects or magic strings needed to disambiguate.
88
-
89
- ```ts
90
- // BAD — technical jargon, not a sentence
91
- shape.rotateAxisAngle([0,1,0], 45)
92
-
93
- // GOOD — reads like a sentence
94
- shape.rotateX(45) // "rotate X 45 degrees"
95
- shape.rotateY(45) // "rotate Y 45 degrees"
96
- shape.rotateZ(45) // "rotate Z 45 degrees"
97
- shape.rotate([0, 1, 0], 45) // "rotate around axis"
98
- shape.rotateZ(45, { pivot: [10, 0, 0] }) // "rotate Z at pivot"
99
-
100
- shape.scale(2) // "scale by 2" (from center)
101
- shape.scaleAround(pivot, 2) // "scale around pivot"
102
-
103
- shape.mirror([1,0,0]) // "mirror along X" (through center)
104
- shape.mirrorThrough(point, [1,0,0]) // "mirror through point"
105
- ```
106
-
107
- Rules:
108
- - The **bare method** (`rotate`, `scale`, `mirror`) does the common-sense default (operates relative to the shape's own center)
109
- - **Longer names** make the reference point explicit — no ambiguity
110
- - **No string literals** as behavior selectors (`'center'`, `'origin'`)
111
- - **No options objects** to disambiguate what should be separate methods
112
-
113
- ## Runtime Global Namespace
114
-
115
- Do not add new lowercase injected globals to `.forge.js` scripts. Existing lowercase globals such as `box`, `union`, and `loft` are established core vocabulary; new domain helpers must live under a PascalCase namespace or class such as `Product.materials`, `Product.profiles`, or `Shape.fromSlices`.
116
-
117
- The repo enforces this in `npm run check:suite -- --profile smoke`. If a lowercase global truly must be added for compatibility, it needs an explicit API review and an allowlist update in `cli/check-runtime-globals.ts`.
118
-
119
- ## File length
120
- Keep files under 200 lines. If a file grows beyond that, consider splitting it into smaller, focused files.
@@ -1,340 +0,0 @@
1
- ---
2
- skill-group: dev-conventions
3
- skill-order: 2
4
- ---
5
-
6
- # ForgeCAD Coding Guidelines
7
-
8
- ## Development Workflow
9
-
10
- ### Building & Running
11
- ```bash
12
- npm install # Install dependencies
13
- npm link # Install the local forgecad binary
14
- forgecad studio examples # Start the browser studio (localhost:5173)
15
- npm run build # Production build
16
- npm run preview # Preview production build
17
- ```
18
-
19
- ### CLI Tools
20
- ```bash
21
- forgecad export svg examples/constraints/01-fully-constrained-rect.forge.js # Export sketch to SVG
22
- forgecad render 3d examples/products/cup.forge.js # Render to PNG (Puppeteer + Chrome)
23
- ```
24
-
25
- See [CLI.md](../CLI.md) for full CLI documentation.
26
-
27
- ### Project Structure
28
- ```
29
- src/
30
- ├── forge/ # Core geometry engine (shared by browser + CLI)
31
- │ ├── kernel.ts # Manifold WASM wrapper, Shape class, primitives
32
- │ ├── headless.ts # Single entry point for all contexts (browser + Node CLI)
33
- │ ├── index.ts # Browser entry point (re-exports from headless.ts)
34
- │ ├── runner.ts # Script sandbox — executes user scripts and resolves imported .svg assets
35
- │ ├── params.ts # Parameter system (param() → UI sliders)
36
- │ ├── library.ts # Part library (lib.boltHole, lib.pipe, etc.)
37
- │ ├── section.ts # Plane intersection / projection
38
- │ ├── meshToGeometry.ts # Manifold mesh → Three.js BufferGeometry
39
- │ ├── sceneBuilder.ts # Three.js scene setup (shared with CLI renderer)
40
- │ └── sketch/ # 2D sketch system
41
- │ ├── core.ts # Sketch class
42
- │ ├── primitives.ts # rect, circle2d, polygon, ngon, etc.
43
- │ ├── transforms.ts # translate, rotate, scale, mirror
44
- │ ├── booleans.ts # add, subtract, intersect, union2d
45
- │ ├── operations.ts # offset, simplify, warp
46
- │ ├── extrude.ts # extrude, revolve (2D → 3D)
47
- │ ├── path.ts # PathBuilder, stroke
48
- │ ├── anchor.ts # attachTo/matchTo positioning
49
- │ ├── constraints.ts # Constraint solver (18 types)
50
- │ ├── entities.ts # Point2D, Line2D, Circle2D, Rectangle2D, Constraint helpers
51
- │ ├── topology.ts # TrackedShape, face/edge naming
52
- │ ├── patterns.ts # linearPattern, circularPattern, mirrorCopy
53
- │ ├── fillets.ts # filletEdge, chamferEdge
54
- │ ├── arcBridge.ts # arcBridgeBetweenRects
55
- │ └── index.ts # Re-exports everything
56
- ├── components/ # React UI components
57
- │ ├── Viewport.tsx # 3D viewport (Three.js + R3F)
58
- │ ├── CodeEditor.tsx # Monaco editor with ForgeCAD types
59
- │ ├── FileExplorer.tsx # Project file tree
60
- │ ├── ViewPanel.tsx # Render mode, views, object settings
61
- │ ├── ParamPanel.tsx # Parameter sliders
62
- │ └── ExportPanel.tsx # STL export
63
- ├── store/
64
- │ └── forgeStore.ts # Zustand global state
65
- ├── App.tsx # Main application shell
66
- └── main.tsx # React entry point
67
-
68
- cli/
69
- ├── forgecad.ts # Top-level CLI entrypoint and command routing
70
- ├── forge-svg.ts # SVG export (uses real engine via headless.ts)
71
- ├── forge-render.mjs # PNG render launcher (Puppeteer)
72
- ├── render.ts # Headless render entry (loaded in browser by Puppeteer)
73
- └── render.html # HTML shell for headless render
74
-
75
- examples/ # Example scripts
76
- ├── *.forge.js # 3D part and 2D sketch examples
77
- └── constraints/ # Constrained sketch examples
78
- ```
79
-
80
- ## Coding Standards
81
-
82
- See [coding-best-practices.md](coding-best-practices.md) for TypeScript, React, state management, and performance best practices.
83
-
84
- ## Agent-Native API Design Standard (Required)
85
-
86
- ForgeCAD expects AI agents to author serious `.forge.js` models, but agent specialization through documentation is not a substitute for API design. Read [agent-native-api.md](agent-native-api.md) before treating a modeling difficulty as "just a docs problem."
87
-
88
- If a common modeling task requires agents to reimplement a known CAD concept from recipes, trigonometry, magic numbers, or raw boolean construction, prefer a domain-specific API improvement with clear docs and diagnostics. Documentation should make agents fluent in ForgeCAD's vocabulary; it should not carry missing vocabulary.
89
-
90
- ## Domain Localization Standard (Required)
91
-
92
- This standard is package-wide for any new user-facing concept or API family.
93
-
94
- ### Contract
95
- - Each concept family must have a clear primary home in both code and docs.
96
- - Extend the module that already owns the mental model instead of scattering helpers across unrelated files.
97
- - When a feature spans multiple layers, pick one domain owner and make the other layers mirror that owner.
98
- - Keep related runtime code, examples, checks, and docs close to the same domain name whenever practical.
99
-
100
- ### Examples
101
- - Assembly and kinematics live under `src/forge/assembly.ts` and `docs/permanent/API/assembly/assembly.md`.
102
- - Sketch constraints live under `src/forge/sketch/constraints.ts` and the sketch/entity API docs.
103
- - Transform/placement helpers should stay grouped with transform and positioning surfaces, not reappear as ad-hoc helpers in unrelated modules.
104
-
105
- ### Enforcement
106
- - Before adding a new API, state which domain owns it.
107
- - If a concept currently has no clean home, create one instead of spreading the first implementation across multiple files.
108
-
109
- ## Backend Compiler Standard (Required)
110
-
111
- This standard is package-wide for any geometry feature that affects runtime lowering, export-backend coverage, or backend capability routing.
112
-
113
- Read [compiler.md](../internals/compiler.md) before changing this area.
114
- For large multi-agent migrations or architecture programs, also read [PROGRAM-LEAD.md](../../processes/PROGRAM-LEAD.md).
115
-
116
- ### Contract
117
- - Forge semantic intent comes first. Backends are lowerers, not the authoring model.
118
- - Scene-level capability routing must stay centralized. Do not re-derive export/runtime policy ad hoc in unrelated modules.
119
- - Public feature APIs must not hide backend-specific behavior directly in their callsites. Backend code belongs in the lowerers.
120
- - New backend limitations must surface as diagnostics, not silent fallback or silent exactness loss.
121
-
122
- ### Enforcement
123
- - If a feature is compile-covered, update the canonical compile graph and the scene compiler.
124
- - If a feature is not yet dual-lowered, add explicit unsupported diagnostics for the missing backend rather than bypassing the compiler.
125
- - Any geometry feature change must update invariant coverage and the living backend-compiler tracker.
126
-
127
- ## Multi-Agent Program Standard (Required For Large Migrations)
128
-
129
- For work that spans multiple agent branches or staged dependency waves, the Program Lead role in [PROGRAM-LEAD.md](../../processes/PROGRAM-LEAD.md) is the default operating model.
130
-
131
- Use it when:
132
-
133
- - one missing foundation blocks several feature lanes
134
- - multiple agents need isolated tasks with dependency ordering
135
- - the repo needs a living task graph and capability tracker to stay truthful
136
-
137
- The key rule is simple:
138
-
139
- - solve the deepest shared prerequisite first
140
- - only then open the parallel wave that builds on top of it
141
-
142
- ## Frame Composition Standard (Required)
143
-
144
- This standard is package-wide for any code that composes transforms (`Transform`, joints, assemblies, kinematic helpers).
145
-
146
- ### Contract
147
- - `A.mul(B)` means **apply A, then B**
148
- - Use `composeChain(...)` for 3+ composed transforms instead of manual `.mul()` chains
149
- - In assembly/kinematics, always express composition in this canonical order:
150
- - `local -> childBase -> jointMotion -> jointFrame -> parentWorld`
151
-
152
- ### Why this is mandatory
153
- Transform order bugs can produce geometry that "looks valid" but is globally wrong (detached mechanism segments, drifting pivots, mirrored motion paths) and often pass casual visual checks.
154
-
155
- ### 5-Why (2026-02 Assembly disconnect incident)
156
- 1. Why were arm segments disconnected?
157
- Because child world transforms were composed in the wrong order in `assembly.solve()`.
158
- 2. Why was order wrong?
159
- Because `.mul()` chain semantics (apply self, then other) were interpreted inconsistently with matrix notation.
160
- 3. Why was that ambiguity possible?
161
- Because there was no single canonical frame equation documented and enforced in code review.
162
- 4. Why didn’t tests catch it immediately?
163
- Because there was no invariant test comparing assembly-solved frame origins against an analytic kinematic oracle.
164
- 5. Why no invariant test existed?
165
- Because we had feature-level example checks, but no package-wide transform convention gate.
166
-
167
- Root cause: **missing, enforced transform/frame composition contract across code + tests.**
168
-
169
- ### Enforcement
170
- - Any change touching transforms, joints, or assembly solving must run:
171
- - `npm run check:suite -- --profile smoke`
172
- - If the change affects user-facing geometry behavior, also run:
173
- - `forgecad run <affected-example>`
174
-
175
- ## Editor Declaration Parity Standard (Required)
176
-
177
- This standard is package-wide for any user-facing API exposed to scripts.
178
-
179
- ### Contract
180
- - Runtime API and editor declarations must ship together:
181
- - Runtime surface: `src/forge/*` exports + `src/forge/runner.ts` sandbox bindings
182
- - Editor surface: `src/components/CodeEditor.tsx` `FORGE_TYPES`
183
- - Docs surface: `docs/permanent/API/**/*.md`
184
- - If an important feature is missing from editor declarations, you must either:
185
- - implement declarations in the same change, or
186
- - create a tracked task in `tasks/` that explicitly names the missing surface and scope.
187
-
188
- ### Enforcement
189
- - Before merge, verify new/changed script APIs are present in all three surfaces above.
190
- - Do not ship runtime-only features without either declaration parity or a tracking task.
191
-
192
- ## Multi-File Native Standard (Required)
193
-
194
- ForgeCAD projects must scale cleanly across multiple files. Complex models should be **expressible as a composition of separate, independently-authored files** — not as a single growing script.
195
-
196
- ### Tenet
197
-
198
- Every user-facing import mechanism must make multi-file composition a first-class path, not an afterthought:
199
-
200
- - Any `.forge.js` file (returning a `Shape`, `ShapeGroup`, `Assembly`, or other type) can be imported with `require("./file.forge.js")`.
201
- - `require()` accepts an optional second argument for parameter overrides: `require("./part.forge.js", { Width: 100 })`.
202
- - Files can also use `exports.name = value` alongside or instead of `return` for named multi-export.
203
- - Plain JS helpers and constants belong in regular `.js` modules imported via `require()` or standard `import`.
204
-
205
- No user should have to choose between "clean file structure" and "full API access". If a new return type or authoring primitive appears, **its import path must ship at the same time or in the same milestone**.
206
-
207
- ### Consequences for API design
208
-
209
- - New authoring primitives (anything a user might `return` from a `.forge.js` file) are automatically importable via `require()`.
210
- - Imported objects must expose the same child-access and placement-reference ergonomics that inline objects do.
211
- - Parameter overrides must be supported via the second argument to `require()` so multi-instance use is natural.
212
- - If a sub-file feature cannot yet be fully composed (e.g. kinematic merge across file boundaries), document the limitation explicitly and create a tracked task — do not silently downgrade.
213
-
214
- ### Enforcement
215
-
216
- - When adding a new return-type contract, check: is there an `import*()` function for it?
217
- - When adding placement-reference or child-access APIs to an object type, check: do imported versions of that type also expose those APIs?
218
- - Verify with `forgecad run` that a multi-file example using the new feature works end-to-end before merge.
219
-
220
- ## Script API Contract Standard (Required)
221
-
222
- This standard is package-wide for any API exposed to user scripts.
223
-
224
- ### Contract
225
- - Collection-shaped script APIs must accept the intuitive collection forms the docs advertise:
226
- - variadic operands when the operation naturally works on many inputs
227
- - a single array of operands when that keeps call sites composable
228
- - Method syntax and function syntax must mirror each other for the same operation family.
229
- - User-facing APIs must not silently ignore extra arguments. Unsupported arity or operand types must throw a direct runtime error with the API name in the message.
230
- - If a future API needs configuration, do not smuggle it in as an ambiguous trailing object on an operand list. Use a distinct helper or a clearly named options-bearing API.
231
-
232
- ### Enforcement
233
- - Any change to user-facing script APIs must run:
234
- - `npm run check:suite`
235
- - If the change also affects transforms, dimensions, placement refs, or geometry semantics, run the relevant existing invariant checks too.
236
-
237
- ## Git Workflow
238
-
239
- ### Commit Every Major Change
240
- Each logical unit of work should be a separate commit:
241
-
242
- ```bash
243
- git add <files>
244
- git commit -m "Add file explorer panel"
245
- ```
246
-
247
- ### Commit Message Format
248
- ```
249
- <verb> <what>
250
-
251
- Examples:
252
- - Add file explorer panel
253
- - Fix measure mode toggle
254
- - Update parameter slider styling
255
- - Remove unused imports
256
- ```
257
-
258
- Use present tense verbs: Add, Fix, Update, Remove, Refactor
259
-
260
- ### What Counts as "Major"
261
- - New feature or component
262
- - Bug fix
263
- - Refactoring that changes structure
264
- - Performance improvement
265
- - Breaking API change
266
-
267
- ### What to Commit Together
268
- - Related files for a single feature
269
- - Tests with the code they test
270
- - Documentation with the feature it describes
271
-
272
- ### Example Workflow
273
- ```bash
274
- # Feature: Add file explorer
275
- git add src/components/FileExplorer.tsx
276
- git add src/store/forgeStore.ts
277
- git add src/App.tsx
278
- git commit -m "Add file explorer panel"
279
-
280
- # Next feature: Add keyboard shortcuts
281
- git add src/hooks/useKeyboard.ts
282
- git add src/App.tsx
283
- git commit -m "Add keyboard shortcuts for file operations"
284
- ```
285
-
286
- ## Testing
287
-
288
- ### Manual Testing Checklist
289
- Before committing UI changes:
290
- - [ ] Test in browser at localhost:5173
291
- - [ ] Check console for errors
292
- - [ ] Verify responsive behavior
293
- - [ ] Test with example scripts
294
-
295
- ### Integration Testing
296
- - Load example files and verify they render
297
- - Test parameter sliders update geometry
298
- - Verify STL export produces valid files
299
- - Check measure mode calculates correctly
300
-
301
- ## Common Patterns
302
-
303
- ### Adding a New Sketch Primitive
304
- 1. Add function to `src/forge/sketch/primitives.ts`
305
- 2. It's auto-exported via `sketch/index.ts` → `headless.ts` → `index.ts`
306
- 3. Add it to the sandbox in `src/forge/runner.ts` (both the `new Function()` args and the call)
307
- 4. Add TypeScript hints in `src/components/CodeEditor.tsx` (`FORGE_TYPES`)
308
- 5. Update `docs/permanent/API/sketch/primitives.md`
309
- 6. Commit: "Add [primitive] sketch primitive"
310
-
311
- ### Adding a New 3D Primitive
312
- 1. Add function to `src/forge/kernel.ts`
313
- 2. Export from `headless.ts`
314
- 3. Add to runner sandbox in `src/forge/runner.ts`
315
- 4. Add TypeScript hints in `src/components/CodeEditor.tsx`
316
- 5. Commit: "Add [primitive] 3D primitive"
317
-
318
- ### Adding a New CLI Command
319
- 1. Create `cli/your-command.ts`
320
- 2. Import from `../src/forge/headless`
321
- 3. Call `await init()` then use `runScript()`
322
- 4. Add script to `package.json`
323
- 5. Update `docs/permanent/CLI.md`
324
- 6. Commit: "Add [command] CLI command"
325
-
326
- ### Adding UI State
327
- 1. Add to `src/store/forgeStore.ts` interface
328
- 2. Add initial value and actions
329
- 3. Wire up to component
330
- 4. Commit: "Add [feature] UI state"
331
-
332
- ### Adding a Component
333
- 1. Create in `src/components/`
334
- 2. Import and use in `App.tsx` or parent
335
- 3. Commit: "Add [Component] component"
336
-
337
- ### Key Architecture Rule: Single Source of Truth
338
- All geometry logic lives in `src/forge/`. CLI tools import from `src/forge/headless.ts`.
339
- **Never** duplicate forge logic in CLI scripts. If you need something in CLI, make sure
340
- it's exported from `headless.ts` and import it.