master
AdityaAngajala Merge PR #262: Carry fabrication sources through the molecule pipeline 82dd16c 1d ago

The molecule pipeline, step by step

How a board leaves your EDA tool, becomes a molecule on the wiki, and lands in a Hydrogen account wireable in the 3D editor. Every tool in the chain, in the order it runs, with the command each step actually issues and the gate that has to pass before the next step starts.

This page is the process; the README is the one-sentence front door.

The chain at a glance

 YOUR LAPTOP                       YOUR CONTAINER                                  ADOM CLOUD
 ───────────                       ──────────────                                  ──────────
 KiCad 10   ──kicad bridge──┐
 Fusion 360 ──fusion bridge─┼─▶ EDA export skill ─▶ step2glb (CLI) ──▶ OCCT service ─▶ anchored GLB
 Altium     ──altium bridge─┘        │                 ▲                  (adom-step2glb)   + footprint/symbol JSON
                                     │                 │                                          │
                                     ▼                 │                                          ▼
                              molecule-publish ────────┘   package.json + gates ──▶ adom-wiki ──▶ wiki page
                                     │                                                  (pkg publish + repo push)
                                     ▼                                                        │
                              adom-nucleus --render-board (APM render/ archive)               │
                                                                                              ▼
                              adom-molecule import <owner>/<slug> ─────────▶ Hydrogen /api/molecules/import
                                                                                   │
                                                                                   ▼
                                                                    3D Editor V2: load, place, wire

Every arrow is a real package. Versions this page was verified against (2026-09-03):

Layer Package Version Runs where
Router (this page) adom/molecule-pipeline 1.1.2 container skill
Laptop bridge adom/adom-bridge (adom-bridge CLI, formerly adom-desktop) 2.1.15 laptop app, driven from the container
KiCad bridge adom/kicad-bridge 1.0.111 inside Adom Bridge
Fusion bridge adom/fusion-bridge 1.9.352 inside Adom Bridge
EDA export skills adom/kicad-export-molecule · adom/fusion-export-molecule · adom/altium-export-molecule 1.1.2 · 1.2.2 · 2.3.1 container skills
Convert adom/adom-step2glb (step2glb CLI) + shared OCCT service 1.2.2 CLI / service 1.3.0 container CLI, cloud service
Package + publish adom/molecule-publish 2.10.0 container skill (+ scripts)
Wiki CLI adom/adom-wiki-cli (adom-wiki) 1.7.26 container
Render archive adom/adom-project-manager (adom-nucleus, alias adom-project-manager) 0.14.0 container
Import adom/adom-molecule (adom-molecule CLI, ships adom-molecule-import skill) 1.1.10 container
Destination Hydrogen (hydrogen.adom.inc) 1.0.185 cloud

Install the container side in one go:

adom-wiki pkg install adom/molecule-pipeline          # router; pulls molecule-publish + adom-molecule
adom-wiki pkg install adom/adom-step2glb              # step2glb CLI
adom-wiki pkg install adom/kicad-export-molecule      # or fusion-export-molecule / altium-export-molecule
adom-wiki pkg install adom/adom-project-manager       # adom-nucleus --render-board

Stage 0 — Before you export: the board must be a molecule

A molecule is a board that carries the Adom furniture: MP1–MP4 marker footprints (the anchoring references) and the corner machine pins / contacts from the Adom library. OCCT anchors on the markers; without at least three of them the conversion still runs but the GLB imports unanchored and unwireable.

  • KiCad: kicad-to-molecule adds wings + machine pins to a plain board; footprints come from adom/kicad-library-manager.
  • Altium: barrett-land/adom-altium-library-importer puts the machine-pin libraries inside Altium.
  • Fusion/EAGLE: use the Adom library parts (MACH_PIN_LRG_SHORT, etc.); the marker names carry the pin size, which the converter reads (pin_size_source: markers).

Gate: the 3D models for the pins, contacts and markers resolve in your EDA's 3D viewer on the machine that will export. A STEP only contains what resolved at export time; a model referenced by a path that only exists on someone else's machine is silently dropped, and anchoring fails downstream.

Stage 1 — Export on the laptop (EDA export skill + Adom Bridge)

The STEP is always exported on the design machine, driven from the container through Adom Bridge. Everything below is what the export skill does for you when you say "export molecule ABC".

KiCad (kicad-export-molecule)

  1. Preflight: adom-bridge ping; adom-bridge commands | grep kicad_; kicad-cli is KiCad 10 (C:\Program Files\KiCad\10.0\bin\kicad-cli.exe version).
  2. Export the STEP with the laptop CLI (models substituted from the laptop's libraries):
    kicad-cli pcb export step --subst-models --force -o ABC.step ABC.kicad_pcb
    
    Read the output: every Could not add 3D model for MPn line means a marker did not bake in. Fix the model path (KiCad resolves relative model paths against the board's directory, ${KICAD10_3DMODEL_DIR} against the install) and export again.
  3. Optional silk PNGs (top/bottom, white art on transparent).
  4. Pull the bundle to the container: adom-bridge pull_file {"filePaths":[...], "saveTo":"..."} — ABC.step, ABC.kicad_pcb, ABC.kicad_sch, ABC.kicad_pro, silks.

Never use KiCad's own GLB export for a molecule: it bypasses anchoring, gold pins, normalization and the footprint emit. STEP in, OCCT makes the GLB.

Fusion 360 Electronics (fusion-export-molecule)

  1. adom-bridge fusion_start if Fusion is closed (blocks until the add-in is ready; the first fusion_get_app_state after that can take a minute).
  2. Open the board file directly so the multi-design picker never appears:
    adom-bridge fusion_open_cloud_file {"projectName":"Main","folderPath":"COSMIIC","fileName":"PM COIL Molecule","fileExtension":"fbrd","recovery":"discard"}
    
    Find the design first with fusion_mcp_call {"tool":"fusion_mcp_read","arguments":{"queryType":"document","operation":"search","name":"..."}} (APS search needs a fresh sign-in; MCP search works with any open document).
  3. Export sources: fusion_export_eagle_source for the .brd, switch with fusion_electron_run {"command":"EDIT .s1"}, export the .sch, switch back with EDIT .brd.
  4. fusion_show_3d_board, then fusion_export_step (and fusion_export_f3d / fusion_export_usdz / silkscreen screenshots if you want the kitchen sink).
  5. pull_file the bundle: .brd, .sch, .step (+ extras).

Altium (altium-export-molecule)

Native STEP export with the machine-pin models baked in, board metadata derived for OCCT, then the same converter call as everyone else. The skill ends when the OCCT artifacts are on the container; it never packages or publishes.

Gate for all three: the bundle on the container has STEP + board + schematic + <slug>-fab.zip + export.json (a molecule without its schematic is not reproducible and molecule-publish refuses it).

For each EDA, collect fabrication layers on every export and verify them with molecule-publish's scripts/fab_bundle.py before handing off. Preserve native board sources and mark imported backfills honestly.

Stage 2 — Convert: STEP → anchored GLB + footprint/symbol (step2glb)

One command, run on the container, does the whole OCCT molecule pipeline server-side:

step2glb convert ABC.step --board ABC.kicad_pcb --pin large -o <slug>.glb     # .brd boards work too

The CLI uploads to the shared OCCT service (https://step2glb-gmdoncpxdwx0.adom.cloud, async POST /convert → GET /jobs/{id} → /jobs/{id}/result), and downloads <slug>.glb, <slug>_footprint.json and <slug>_symbol.json beside it. What the service does: MP-marker anchoring (Z-up, front-left machine pin at the origin, board-surface Z), canonical gold machine-pin material, optional silkscreen overlay, meter normalization, Draco compression, and footprint + symbol JSON with the per-pad wire-attach Z — the numbers Hydrogen's wire tool snaps to.

Gate on the printed stats before going any further:

Stat Must be Otherwise
molecule_anchored true markers missing from the STEP; go back to Stage 1
footprint_applied / footprint_pins true / 4 board parse failed; read the warnings
contact_z 2.8–8.4 mm wrong pin size or wrong marker models
pin_size_source markers param means the size fell back to --pin
warnings none every entry is actionable

The GLB carries asset.extras.adomMolecule — durable provenance you can check on any downloaded copy (do not look for a molecule_anchor node; the optimizer flattens it by design).

Stage 3 — Package (molecule-publish)

<slug>/
  package.json            type "component", component.mpn, hero block, dependencies {}
  <slug>.glb              the OCCT GLB, named EXACTLY <slug>.glb
  ABC.kicad_pcb | .brd    board source
  ABC.kicad_sch | .sch    schematic source
  ABC.step                STEP (reproducibility)
  <slug>-fab.zip          Gerbers and drill
  export.json            exporter versions and source/member hashes
  <slug>_footprint.json   OCCT artifact (wire-attach Z)
  <slug>_symbol.json      OCCT artifact
  README.md, screenshots/hero.png, [silk PNGs, .f3d, .usdz, bom, cpl]

Rules that bite:

  • Name the GLB <slug>.glb. The wiki auto-converts the STEP to a plain <slug>.glb at publish time and points model_3d_path at it; an anchored GLB with any other name is shadowed by the plain one and every import gets an unanchored model.
  • The component block is what creates the page's component-metadata row; without it model_3d_path is never set and import fails with "no published GLB".
  • Key the page by MPN, never by value. Do not ship a hand-made page.json.
  • Component dependencies are enforced before publish: python3 scripts/component_graph.py plan <board> --out component-plan.json then gate package.json --plan component-plan.json --expect-board <board> --resolve (declared coverage + every dependency resolves to a real page). Missing component pages: Stage 2.6 auto-creates them.
  • Stamp what you tested on: python3 scripts/tested_with.py stamp package.json --family <model>.
  • Hero camera: python3 scripts/hero_camera.py pose <slug>.glb --page page.json.

Stage 4 — Publish + verify (adom-wiki)

adom-wiki pkg publish [--org adom]                                   # registry version
adom-wiki repo push <owner>/<slug> --files <every file above> -m "<name> molecule"

Two layers, both required: pkg publish registers the version (and generates page.json), repo push puts the files on the page. Verify before calling it done:

  • metadata.model_3d_path on the page == <slug>.glb;
  • the page's GLB is byte-identical to your gated conversion (curl it with your token, cmp against the local file) — this is the anchoring proof;
  • the full set is present: GLB + STEP + schematic + board + footprint/symbol JSON.

Private pages are fine end to end; nothing in the chain needs the page public.

Stage 5 — Render archive (adom-nucleus --render-board)

Run after the publish, every time (it stamps the archive with the version it reads from the published package.json):

adom-nucleus --render-board <owner>/<slug>       # alias: adom-project-manager --render-board

It renders render/pcb.svg, render/sheet-*.svg, render/board.glb, content-hashes them and archives render/<version>/ + render/manifest.json only when the content changed. Check manifest.current == <version> and python3 scripts/render_parity.py render/board.glb <slug>.glb (same mesh count, else the render dropped geometry). EAGLE/Fusion sources report "nothing rendered" and skip gracefully today.

Stage 6 — Import into Hydrogen (adom-molecule)

adom-molecule import <owner>/<slug> --owner <your-user-or-org> [--name <name>]

What it does (1.1.10): reads the page owner-qualified with your container credential (private pages included), checks model_3d_path and that a footprint JSON or board file exists, downloads the GLB as a preflight, then POSTs {wikiRef:"<owner>/<slug>", owner, name, includeSource:true} to https://hydrogen.adom.inc/api/molecules/import. Hydrogen re-reads the page with the same credential, reuses the wiki GLB verbatim (no re-optimization, no silkscreen, no Blender), carries the sources, and attaches the footprint so the molecule lands with wire-attach points. Re-importing creates a new version of the same molecule.

Same thing from any script:

curl -X POST https://hydrogen.adom.inc/api/molecules/import \
  -H "X-Api-Key: $(cat /var/run/adom/api-key)" -H "Content-Type: application/json" \
  -d '{"wikiRef":"<owner>/<slug>","owner":"<you>","includeSource":true}'

Gate: the reply carries a version and no warnings; adom-molecule list shows the name under your owner; https://hydrogen.adom.inc/<owner>/molecules/<name> renders it.

Stage 7 — Use it in Hydrogen

Open a repo's editor (https://hydrogen.adom.inc/<owner>/<repo>/edit), add a 3D Editor V2 tab, then:

  1. Browse and Load Molecule → pick the owner → the molecule → Load → set the placement position → OK.
  2. More Objects tools → Add Wires (tick Draw Multiple Wires) → hover a machine pin until the "Machine Pin: FL/BL/FR/BR" tooltip shows → click → click the second molecule's pin. The console logs New contact-based wire created from <a> - Pin: X to <b> - Pin: Y.
  3. Save the layout.

The in-app Optimize / Regenerate 3D model button is retired (it pointed at a worker that no longer exists); optimization happens in Stage 2 and import reuses it.

Regression: the e2e harness

project/molecule-e2e/run-e2e.sh on gallia republishes two private test molecules — drew2/e2e-kicad-solonoid (KiCad) and drew2/e2e-fusion-pmcoil (Fusion/EAGLE) — through the current stack every time a molecule-family package changes (cron, every 30 minutes, check-and-run.sh). Stages it gates: EDA export on the laptop (kicad-cli 10 / Fusion bridge), OCCT conversion stats, provenance stamp, pkg publish + repo push, wiki GLB checksum, APM render archive + manifest, and the private import into Hydrogen through adom-molecule import. last-status.json is the verdict; e2e.log is the trail.

The one-sentence versions

  • "Move molecule ABC from KiCad on my computer up to the wiki." → Stages 1–5.
  • "Get molecule ABC into my Hydrogen account." → Stage 6.
  • "Publish my molecule ABC and import it into my Hydrogen account." → Stages 1–6, each gated before the next.
  • "Wire ABC to DEF in the 3D editor." → Stage 7.