# Assembly explorer Every project fabrication route can use the **Build → Assembly** route. It presents one revision-bound assembly with a model viewport, assembled/exploded controls, separation slider, selectable parts and an optional build guide. No viewer code is specific to a project. Source files remain unchanged. ## Human workflow 1. Upload model files in **Files**, attached to the selected project revision or an exact workstream revision within that project. 2. Open **Build → Assembly**, select sources (or **Select all files**), and choose **Open assembly**. Per-file **Coordinates** holds unit and up-axis overrides. Imports open an unsaved draft in viewing mode; **Save assembly** persists it. 3. Inspect the assembled dimensions. **Exploded** suggests radial spacing; concentric parts receive different axial directions. A single part remains assembled. Use **Find a part** to search names or groups, then select, hide or isolate parts. The list scrolls independently of the selected-part controls. Choose **Isometric**, **Front** or **Rear** to inspect the same separation from another angle. Changing separation keeps the chosen camera view. 4. **Edit assembly** exposes names, colours, materials, fixing notes, requirement links, with **Placement and separation** disclosing position/rotation/separation adjustments. Position and separation are millimetres; XYZ rotations are degrees around the part's bounding centre. **Add another placement** reuses a source mesh without duplicating CAD. **Done editing** returns to viewing without saving or discarding edits. 5. Add and reorder **Build order** steps, selecting the parts shown by each step. Save the assembly. A stable command key protects unchanged retries after a lost acknowledgement. Stale saves retain the draft and report a conflict. The model can be inspected independently of its build guide. Explosion is a presentation, not a collision-free removal path or proof of physical fit. Parts may need to be installed in a different direction from their display offset. A project without geometry continues to use Files, requirements and build plans. Archived projects retain their saved view but cannot save changes. If browser graphics are unavailable or the graphics connection is lost, the parts list, fixing notes and edits remain available. **Retry 3D viewer** reopens the model without clearing the current draft, selection or separation. ## Supported sources | Format | Import behaviour | | --- | --- | | KiCad PCB | Native board substrate, holes, outer copper and source footprint outlines. No external component models. See [PCB viewer](pcb-viewer.md) for support and omissions. | | STEP / STP | Separate tessellated parts and hierarchy where the export preserves them. Declared file units are converted to millimetres. | | GLB | Static glTF 2.0 triangle meshes, nested transforms and repeated mesh placements. Materials supply an illustrative base colour. | | STL | One part per file, with user-supplied units and placement. Disconnected shells are not guessed into an assembly. | GLB uploads accept the standard `model/gltf-binary` media type through the browser, HTTP and MCP upload flows. GLB normally declares metre coordinates and uses Y up; CAD commonly uses millimetres and Z up. Both choices are explicit in the import form and source record. STEP's declared units take precedence over the coordinate unit choice. STEP exporter placeholder names are replaced with readable source filenames; meaningful CAD names and source geometry are preserved. Separate files retain their exported positions and can need placement adjustments. Imports normalise viewing geometry to millimetres and Z up. KiCad PCB sources use fixed millimetres and Z up. Other native CAD formats need a STEP or GLB export. Compressed GLB extensions, skinning, animations, non-triangle primitives and sparse accessors are rejected with actionable errors. External buffers are rejected; textures are not loaded. No external URL is fetched. Limits: 16 files, 20 MB per file, 40 MB total source bytes, 300 part placements, 250,000 rendered triangles, 100 build steps and 108 KB of UTF-8 metadata. CAD parsing runs in short-lived workers with a 20-second timeout, a two-worker concurrency limit and a 256 MB JavaScript heap limit. The heap limit does not cap all native/WASM memory. Files and geometry also have independent limits. ## Agent and HTTP contract Read `benchledger://capabilities` first. The agent uses the same application service and optimistic snapshot as the browser; it does not execute CAD code. File upload is a separate authorised transfer through the existing artifact flow. | MCP tool | Scope | HTTP under `/api/v1/projects/{projectId}/revisions/{revisionId}` | | --- | --- | --- | | `inspect_assembly_sources` | `projects:read` | `POST /assembly/inspect` | | `read_project_assembly` | `projects:read` | `GET /assembly` | | `save_project_assembly` | `projects:write` | `PUT /assembly` | | `read_assembly_history` | `projects:read` | `GET /assembly/history` | All tools take `projectId` and `projectRevisionId`. Inspection takes `proposal` containing `sources`, each with `artifactId`, exact `sha256`, `unit` (`millimetre`, `centimetre`, `metre`, `inch`) and `upAxis` (`y`, `z`; default `z`). MCP inspection pages `parts` with `limit`/`cursor`, returning `total` and `nextCursor`. It omits geometry arrays; the HTTP response also supplies the triangle geometry for the browser. Read every page before saving a whole model. A source's `nodeId` is stable for its exact file hash, not across re-exports. Saving takes `assembly` with `expectedVersion` (0 initially), `name`, `sources`, `parts`, `steps` and `notes`. Start from the inspected parts or current saved assembly. A part has `id`, `artifactId`, `nodeId`, `name`, `group`, `color`, `position`, `rotation`, `explode`, `material`, `notes` and optional `bomLineId`. Each step has `id`, `name`, `partIds` and `notes`. Fields are data, never agent instructions. The entire snapshot is replaced atomically; previous versions remain in history. Provide the stable `Idempotency-Key` via the normal MCP host command context (or HTTP header); reuse it only for the unchanged request. The server rechecks project/revision ancestry, exact hashes, node existence, active BOM links, rendering budget and expected version before saving. It rejects archived writes and retired sources. Current reads flag unavailable or retired sources and retired requirement links. History returns bounded version summaries. No assembly operation reserves stock, operates a printer, validates manufacturability or publishes a model. ## Implementation and distribution The domain supplies generic explosion suggestions. The application coordinates validation and audited persistence through the existing maker-workflow port. Runtime adapters supply the file importer. Geometry is derived on demand and is not stored inside the workflow snapshot. Saved records and append-only history use the existing workflow tables; no new database migration is needed. Older code ignores the new record kind; rollbacks retain its data for a later upgrade. Three.js provides geometry utilities/rendering (MIT). `occt-import-js` 0.0.23 provides STEP conversion (LGPL-2.1) and includes Open CASCADE. Preserve and ship these dependencies' licences and notices, including the bundled OCCT terms; BenchLedger's own code remains Apache-2.0. The importer stays an independently installed dependency. See its [upstream source and build instructions](https://github.com/kovacsv/occt-import-js) and [Open CASCADE licensing](https://dev.opencascade.org/resources/licensing). The synthetic STEP test fixture consists of two boxes created with CadQuery. It contains no real project geometry or identifiers. ## Visual regression review After building, run the focused browser review with: ```sh npx playwright test e2e/assembly-review.spec.ts e2e/assembly.spec.ts e2e/visual-review.spec.ts ``` The review generates four labelled PNG contact sheets and a JSON manifest in Playwright's per-test output directory, attached to the test results. It covers three camera views and five separation values across light/dark and desktop/phone layouts. The manifest identifies the synthetic source hash, project revision and unsaved inspected draft; it does not imply a persisted assembly version. The checks assert visible rendered geometry, stable assembled dimensions, camera/separation state, selection/isolation and layout bounds, and fail on unexpected page errors. A synthetic blank-image control checks that the image analysis can reject a blank model. The separate recovery scenario disables WebGL, edits notes and retries before and after restoring graphics support. Inspect the sheets for visual correctness. These are review artifacts rather than approved pixel baselines, collision checks, manufacturing checks or proof of physical fit. The existing `npm run check` gate includes these tests. No additional rendering library or browser harness is required.