# fusionctl: rules for agents Read this before driving Fusion with `fusionctl`. Every rule below was checked on a live Fusion (build 2705.1.30, macOS, October 2026) unless marked otherwise. ## Start of a session 1. `fusionctl doctor`. If a check fails, fix it before modelling. 2. `fusionctl ping` and read `orientation`. On a **Z-up** install the print bed is the XY plane and +Z is up. On a **Y-up** install the floor is XZ. Do not assume either. 3. `fusionctl new` for a fresh parametric design in millimetres. ## Units - The API works in **centimetres** (and radians). `mm(5)` returns `0.5`. Writing millimetre numbers straight into `Point3D` makes the part 10 times too large and raises no error. - Prefer `VS("12 mm")` or `VS("width / 2")` for feature sizes: they stay parametric. - Create user parameters first (`design.userParameters.add(name, VS("40 mm"), "mm", "")`) and use their names in dimensions and features, so `fusionctl params --set` regenerates the model. ## Sketch planes (API-created sketches) | Plane | sketch +X | sketch +Y | extrude (+) | |---|---|---|---| | `xYConstructionPlane` | world +X | world +Y | +Z | | `xZConstructionPlane` | world +X | **world −Z** | +Y | | `yZConstructionPlane` | **world −Z** | world +Y | +X | On XZ, a point at world height `z` is `P(x, -z, 0)` in sketch coordinates. Sketches on body faces choose their own axes: check `sketch.xDirection` or use `sketch.modelToSketchSpace(...)`. ## Pitfalls seen in practice - Two sketch points that share an X cannot both get a horizontal dimension from the origin (`VCS_SKETCH_OVER_CONSTRAINTS`, with no visible constraint). Dimension the first point fully, give the second only its Y, then `geometricConstraints.addVerticalPoints(a, b)`. - A hole drills against its sketch normal. Holes sketched on the XY plane at the bed go down (away from the part) by default: use `ExtentDirections.NegativeExtentDirection` to drill up. - A bare sketch point does not move when parameters change. Dimension it to the origin with an expression if it must follow. - New occurrences are grounded to their parent: setting `occurrence.transform2` is ignored until `occurrence.isGroundToParent = False`. Then call `design.snapshots.add()` if `design.snapshots.hasPendingSnapshot`. - The 3MF export method is `createC3MFExportOptions` (`fusionctl export x.3mf` handles it). - Never open dialogs (`ui.messageBox`, selections) from a script: a modal dialog blocks the bridge and the CLI times out. Print instead. - Unsure about a class or method name? `fusionctl apidoc ExtrudeFeatures.addSimple` searches the API of the installed build. Guessing from older samples fails on renamed methods. ## Assemblies and joints - Make each part its own component: `occ = root.occurrences.addNewComponent(adsk.core.Matrix3D.create())`, then build inside `occ.component`. See `examples/watch/` for a full assembly (11 components, 8 rigid and 3 revolute joints). - Revolute joint about Z at a part's origin: `JointGeometry.createByPoint(comp.originConstructionPoint .createForAssemblyContext(occ))` for both parts, `root.joints.createInput(moving, fixed)`, `setAsRevoluteJointMotion(JointDirections.ZAxisJointDirection)`. Drive it with `joint.jointMotion.rotationValue = radians` and read the world bounding box back (`fusionctl bodies`). - `rotationValue` snaps to the angular display precision (12.34° reads back as 12.3°). - Any recompute, for example `fusionctl params --set`, puts driven joints back to 0°: set the pose again after parameter edits. Clockwise seen from +Z is a negative `rotationValue`. - A sketch and a feature with the same name make Fusion rename the feature (`Lug (1)`): keep references to feature objects instead of looking them up by name. - Fully constrain sketches that parameters drive. A half-dimensioned outline lets the solver drag free points when a parameter changes (a watch hand hub slid into its arbor at a smaller case size). - Check `fusionctl interference` and measure the gaps between moving parts at the pose you ship. - Hide joint and axis glyphs before screenshots: `root.isJointsFolderLightBulbOn = False` and `comp.isConstructionFolderLightBulbOn = False` per component. ## Work in small, checked steps - One feature (or a few related ones) per `run`, with `--atomic` so a failure removes the timeline items the script added instead of leaving a half-built part. `fusionctl undo N` deletes the last N timeline items and lists them; it does not revert parameter edits. - After each step: `fusionctl timeline` (every item `ok`), `fusionctl bodies` (body count, volume and bounding box change as expected). An operation that changed nothing failed silently. - A script that ran without an exception is not proof that the geometry is right. Measure the dimensions that matter against the spec, run `fusionctl interference` for assemblies, and look at `fusionctl screenshot shot.png --view iso` (plus `front` and `top`) before saying it is done. - Fix a failing native feature (fillet, pattern, shell) instead of replacing it with many manual cuts: the workaround leaves a timeline nobody can edit. - Export STEP as the source of truth, 3MF or STL for the slicer, and print a small test piece of any critical fit before the whole part. ## Output and exit codes - JSON when stdout is not a terminal, or with `--json`. Tables otherwise. - `0` success, `1` Fusion or the script failed (the script's own traceback is on stderr, plus what `--atomic` rolled back), `2` bad arguments.