# Assemblies

An assembly is a document of its own that puts parts of the project together. Components are instances of parts (or of other assemblies) at their latest revision; one is grounded, and joints between two components place the rest. Positions come from the solver and the parts, never from you: you say which faces meet and how they may move, and Moket works out where everything goes.

On an assembly use `inspect_assembly`, `query_geometry` with a component, `measure`, `check_interference` and `check_motion`; the part tools answer only on parts. Read [Working with plans](working.md) first, and [Mechanisms](mechanisms.md) when something must move in a loop, by a gear or along a study.

## Components

- `component.insert` inserts components of a part or an assembly of the project, a standard part (`find_standard_part`) or a library version (`find_library_part`): `{"type":"component.insert","part":<id>,"count":2,"ids":["$a","$b"]}`. `"name"` is for one component (count 1); name several with `component.update` after inserting them.
- The first component of an empty assembly is grounded unless you say otherwise; ground the base part first. Others are free until a joint places them; give a free one a `placement` (mm and a unit quaternion `[x,y,z,w]`) only when it has no joint.
- `component.update` changes a component's name, placement, grounded, suppressed, configuration, appearance, parameters (below) and, for a subassembly, flexible and positions. `component.replacePart` moves components to another part and keeps their joints by face names. `component.delete` deletes one with its joints.

## Joints

`joint.create` connects two origins: `{"type":"joint.create","id":"$j","joint":{"type":"revolute","a":<origin>,"b":<origin>}}`.

- Types: `fixed` (no motion), `revolute` (turns: `rotation`), `slider` (slides: `translation`), `cylindrical` (both), `ball` (`rotation`, `rotationX`, `rotationY` about one point), `planar` (`translationX`, `translationY`, `rotation`), `pin-slot` (`rotation` and a slide along X, `translationX`). Another axis key on a type is an error.
- An origin is a face or an edge of a component by its topological name, from `query_geometry` with that component: `{"component":<id>,"ref":{"kind":"face","body":<body id>,"name":<name>},"use":"center"}`, or the component's origin: `{"component":<id>,"ref":"origin"}`.
- `"use":"center"` on a planar face gives its centroid and outward normal (the centroid of the face as it is: holes in it can move it off the middle; use a component's origin or a circular edge for an exact point); `"use":"axis"` on a cylindrical, conical or toroidal face or a circular edge gives the axis, centered on the face; on a line edge, its midpoint and direction.
- The joint holds when side a's frame sits on side b's, Z axes opposed (faces meet face to face) unless `flip` makes them agree (flush, or a shaft standing on a face instead of sinking into it). `offset` moves along and `angle` turns about side b's Z. `offsetX` and `offsetY` move the joint's zero across Z, along side b's X and Y: positions and limits count from there, so a carriage's travel can start at a rail's end (`"offsetX":"-150 mm"` on a 300 mm rail whose origin is in its middle) without moving the rail's origin.
- A frame's X is the part's X laid flat on the plane across its Z (the part's Y when Z is along X), so a planar or pin-slot joint slides along it; a component's origin has the part's own axes.
- The component nearer the ground stays; the other moves.
- Axes take a `position` and `limits` (`{"rotation":{"position":"30 deg","limits":{"min":"-90 deg","max":"90 deg"}}}`). Positions, offsets and limits are expressions and may use the assembly's parameters (`parameter.create` on the assembly). An axis whose limits are equal is locked: held there, with no degree of freedom.
- A joint whose face is gone is dangling and moves nothing: reattach it with `joint.update` (new `a` or `b`), or delete it.

A shaft standing on a plate, turning about its axis:

```json plan:shaft-on-plate
{
  "summary": "Shaft standing on the plate's top face, free to turn",
  "steps": [
    {
      "label": "Components",
      "commands": [
        { "type": "document.rename", "name": "Shaft on plate" },
        {
          "type": "component.insert",
          "part": "<Mounting plate>",
          "ids": ["$plate"]
        },
        {
          "type": "component.insert",
          "part": "<Stepped shaft>",
          "ids": ["$shaft"]
        }
      ]
    },
    {
      "label": "The shaft's origin on the middle of the plate's top face",
      "commands": [
        {
          "type": "joint.create",
          "id": "$turn",
          "joint": {
            "type": "revolute",
            "a": { "component": "$shaft", "ref": "origin" },
            "b": {
              "component": "$plate",
              "ref": {
                "kind": "face",
                "body": "<Mounting plate.$plate>",
                "name": "f(<Mounting plate.$base>:start)"
              },
              "use": "center"
            },
            "flip": true
          }
        }
      ]
    }
  ],
  "checks": [
    { "label": "Nothing overlaps", "quantity": "interference", "max": 0 },
    { "label": "Only the shaft turns", "quantity": "dof", "min": 1, "max": 1 }
  ]
}
```

Values in angle brackets stand for ids you get from earlier answers: `<Mounting plate>` is that part's document id, `<Mounting plate.$base>` the id its plan gave `$base`. In your own plans, write the real ids, or the names `query_geometry` returns.

## Checking

- Read placements back with `inspect_assembly`: each component's position, how it is placed (ground, joint or stored placement), every joint's status and free axes, the degrees of freedom, mass and center of mass, and the parts list.
- Check overlaps with `check_interference` after placing components, and add plan checks `{"quantity":"interference","max":0}` and `{"quantity":"dof","min":1,"max":1}` (the motion you expect). Touching faces do not count, nor does the ring between a screw and its tapped hole when both carry a thread of the same size and pitch on one axis (standard screws do; give a stud of your own part a `core.thread`, or it counts). A press fit the user wants is marked with `interference.markIntended`.
- `check_interference` with `positions` checks a pose without changing the assembly: `{"positions":[{"joint":<id>,"axis":"translation","value":"120 mm"}]}`, for the ends of travel.

## Subassemblies

An assembly of the project can be a component (`component.insert` with its id). It is rigid by default and moves as one body; `"flexible": true` makes its joints part of this solve, and `positions` set its free axes from here. Joint origins and `query_geometry` reach a part inside it with `path` (the components from the subassembly down to the part, as `inspect_assembly` lists them in the subassembly's `inside`). An assembly cannot contain itself.

## Configurations and component parameters

- A part with configurations (its rows are under `part` on its first component in `inspect_assembly`) is inserted or switched per component with `"configuration":<row id>`; the parts list counts part and row, for example `Socket screw M3 x 10`.
- A component can set its part's parameters for itself: `component.update` with `"parameters":{"length":"bedLength - 160 mm"}`, by the part's parameter names, as expressions of the assembly (its parameters and `project.<name>`). Use it for a rail, a lead screw or a shaft that takes the length this assembly needs, instead of copying the part or adding a configuration row per length. `inspect_assembly` lists a part's parameters (`partParameters`) on its first component; the parts list counts other values as other items ("Shaft, length 110 mm"); `partsOnly` counts through subassemblies.

## Designing a part in context

A part that must fit the parts around it (a cover on a plate, a seat on a bearing, a bracket on a rail's holes) can be designed in the assembly: sketch on another component's face, project its edges, put a plane on it, or extrude `toFace` up to it. Use this for fits that follow other parts; use project parameters for sizes the user chooses.

- The part must be a component of the assembly first (insert it, `New Part` in the studio). Then, in a plan on the part, `context.create` names the assembly, the part's own component in it, and references to faces or edges of other components: a `source` with the component path in the assembly and the face or edge of that component's part, by the names `query_geometry` gives for that component.
- Leave out `copy` and `capturedAt`: Moket reads them from the assembly as it is now. The part keeps the copies and builds from them alone, in any assembly or drawing.
- Use a reference by its id: a sketch plane `{"kind":"context","ref":<id>}` (also as a sketch template's `plane`), a projection `{"context":<id>,"shape":...}` in `sketch.project` (its `ids` name the projection first, then its entities: for a circle `["$proj","$center","$circle"]`, so offset `$circle`), a `core.plane` base, or an extrude's `"extent":"toFace","toFace":{"kind":"context","ref":<id>}`. Only planar faces become planes; only straight and circular edges project.
- When the components it was designed against move or change, `inspect_assembly` shows the component's `context` as `stale` (`CONTEXT_STALE`); `{"type":"context.update","context":<id>}` in a plan on the part takes fresh copies, one new version. A source that is gone is `lost`: the part keeps building; reattach it with `context.update` and a new `source`, or change the features that use it.

Insert the part into the assembly, grounded where it goes:

```json plan:pad-insert
{
  "summary": "A cover pad in the assembly, at the plate's origin",
  "steps": [
    {
      "label": "Insert",
      "commands": [
        {
          "type": "component.insert",
          "part": "<Cover pad>",
          "ids": ["$pad"],
          "grounded": true
        }
      ]
    }
  ]
}
```

Then, on the part, a Ø12 pad standing on the plate's top face 25 mm from its middle:

```json plan:pad-in-context
{
  "summary": "Pad on the plate's top face, designed in the assembly",
  "steps": [
    {
      "label": "The plate's top face from the assembly",
      "commands": [
        { "type": "document.rename", "name": "Cover pad" },
        {
          "type": "context.create",
          "id": "$ctx",
          "assembly": "<Shaft on plate>",
          "component": "<Shaft on plate.$pad>",
          "refs": [
            {
              "id": "$top",
              "source": {
                "path": ["<Shaft on plate.$plate>"],
                "ref": {
                  "kind": "face",
                  "body": "<Mounting plate.$plate>",
                  "name": "f(<Mounting plate.$base>:end)"
                }
              }
            }
          ]
        }
      ]
    },
    {
      "label": "A pad on it",
      "commands": [
        {
          "type": "sketch.template",
          "id": "$circle",
          "name": "Pad",
          "template": {
            "kind": "circles",
            "diameter": "12 mm",
            "centers": [{ "x": "25 mm", "y": "0 mm" }],
            "plane": { "kind": "context", "ref": "$top" }
          }
        },
        {
          "type": "feature.create",
          "id": "$boss",
          "featureType": "core.extrude",
          "params": {
            "sketch": "$circle",
            "operation": "new",
            "body": "$body",
            "distance": "4 mm",
            "direction": "normal"
          }
        }
      ]
    }
  ]
}
```

## Screws, nuts, washers and bearings

These come from the built-in ISO and ANSI library, never modeled. `find_standard_part` (`{"query":"socket head M4"}`) gives a part's id, its rows (one configuration per size and length) and where its origin is. Insert with `component.insert` `{"part":<id>,"configuration":<row id>}`, and `"appearance"` for a finish.

- Screws stand on their axis with the origin under the head and +Z toward the head (countersunk and set screws: at the top face); washers and nuts have their origin on the bottom face with +Z through them.
- To fasten a hole: a joint from the screw's origin to the mouth edge of the hole the head sits on (`"use":"axis"`, the circular edge where the clearance hole of the top part meets its outer face, from `query_geometry`), flipped or not so the head stays outside (check with `inspect_assembly`); washers and nuts fixed to the screw's origin with an offset. Make it revolute with the spin locked by equal limits (`"rotation":{"position":"0 deg","limits":{"min":"0 deg","max":"0 deg"}}`), as the user's Fasten tool does: a fastened screw adds no degree of freedom, and an assembly bolted together has 0. Screws fastened before 2026-10-17 can still spin (one degree of freedom each) until their limits are set.
- Pick the size the hole is made for (a Ø4.5 clearance or a Ø3.3 tapped hole takes M4) and a length that engages about 1.5 diameters of thread without bottoming out. The user's Fasten tool does all this from a click on a hole.

```json
{
  "type": "joint.create",
  "id": "$screw1",
  "joint": {
    "type": "revolute",
    "a": { "component": "$screw", "ref": "origin" },
    "b": {
      "component": "<bracket component>",
      "ref": {
        "kind": "edge",
        "body": "<bracket body>",
        "name": "<the clearance hole's mouth edge from query_geometry>"
      },
      "use": "axis"
    },
    "rotation": {
      "position": "0 deg",
      "limits": { "min": "0 deg", "max": "0 deg" }
    }
  }
}
```

## Appearance and the parts list

- `component.update` with `"appearance":{"finish":"black-oxide"}` colours a whole component over its part, for example one of two equal brackets.
- `items.reorder` orders the parts list. Parts lists come top level, indented or parts only; drawings of the assembly show them (see [Drawings](drawings.md)).

## Assembly commands

`document.rename`, `parameter.create`, `component.insert`, `component.update`, `component.replacePart`, `component.delete`, `joint.create`, `joint.update`, `joint.delete`, `items.reorder`, `interference.markIntended`, `interference.unmark`, and for mechanisms `group.create`, `relation.create`, `study.create`, `exploded.create` and their updates. Read them with `describe_commands`.
