Getting component libraries into Fusion - findings log

Hard-won findings from cracking "attach the real 3D chip to a Fusion library package." Captured as we go so the final MAKING_LIBRARIES.md + fusion-libraries skill are complete and correct. Newest insight at the bottom of each section.

1. Use chip-fetcher's curated LIBRARY, not a raw /tmp download

chip-fetcher/library/<MPN>/ is curated and carries variant-specific files. For Fusion, the right inputs are the .fusion variant (generated by Adom's adom-sfconvert), paired with the curated named 3D:

File Use
<MPN>.fusion.kicad_sym symbol, Fusion-tuned -> feed to adom-lbr
<MPN>.fusion.kicad_mod footprint, Fusion-tuned -> feed to adom-lbr
<MPN>-named.step the curated 3D (part number etched on top; oriented). Pair this with the footprint
<MPN>.kicad_mod / .manufacturer.* / .altium.* raw / other-EDA variants, NOT for Fusion

MISTAKE made + fixed: I first built the .lbr from a raw /tmp/adom-chip-fetcher-*/EAGLE/*.lbr and the thin 245KB manufacturer.step. That gave a footprint/3D size mismatch. Switching to .fusion.kicad_mod + -named.step is the fix. (Convention confirmed in chip-fetcher/src/thumbnails.rs: it builds the lbr with --name <mpn>.fusion from the .fusion files.)

adom-lbr's viewer: symbol | the curated 4-sided LQFP-64 footprint | the embossed 3D | pin->pad map - inspect this before touching Fusion

2. adom-lbr is the right tool; pass it the .fusion files

adom-lbr generate --sym <MPN>.fusion.kicad_sym --fp <MPN>.fusion.kicad_mod -o <MPN>.lbr --name <MPN> then adom-lbr validate <MPN>.lbr. adom-lbr has no 3D/URN flag (it is symbol+footprint only); the 3D is attached separately in Fusion (sections 4-6). The VENDOR EAGLE .lbr must NOT be fed to Create3DPackage - it fails Fusion's XML parser ("File doesn't contain valid XML data"); an adom-lbr-generated .lbr parses fine.

3. Getting the .lbr / .step onto Windows

The pipeline runs in the cloud container; Fusion runs on Windows; there is NO container->Windows push verb. Serve the files (python3 -m http.server 8931) and pull them with shell_execute + Invoke-WebRequest -OutFile C:\Users\<user>\adom-lib\.... Build the shell JSON with json.dumps (Windows paths need \\).

4. Opening the footprint in the 3D viewer (Package3DEnvironment)

  • fusion_open_lbr the .lbr so the library document is ACTIVE (required).
  • fusion_execute_text_command {"command":"Electron.Create3DPackage C:/.../<MPN>.lbr"} opens the Package3DEnvironment workspace showing the footprint (green courtyard + pads + >NAME/>VALUE). ERROR if the library is not active: "Please run the command in library document."
  • Command defs in that env: Package3DCmd, Package3DCreateCmd, Package3DStop (Stop = FINISH).

5. Placing the real 3D chip via the API (works)

Inside the env, with fusion_run_modeling_script:

d = adsk.fusion.Design.cast(app.activeProduct); root = d.rootComponent
im = app.importManager
im.importToTarget(im.createSTEPImportOptions("C:/.../<MPN>-named.step"), root)   # the real body
oc = root.occurrences.item(root.occurrences.count-1)
# auto-orient: rotate the smallest bbox dimension to Z so it lies flat
# (named.step is usually already Z-thin; raw STEP often imports Y-up and needs +90 about X)

The chip imports at correct scale and sits on the footprint.

6. The library-context commands (the proper in-library path)

Enumerating command defs while the library is active surfaced the real in-library commands:

  • Electron::AddCustom3DModel - opens a design to add a custom 3D model to the selected package.
  • AttachToFootprintCmd - attaches the modeled body to the footprint (this is what maps it back + mints the eagle URN). It is INTERACTIVE (holds the main thread waiting for a footprint-align confirm), so it resists pure-API completion; recover with Escape if it wedges.
  • also: Electron::EditCustom3DModel, Electron::PackageFromWeb, Electron::RemoveCustom3DModel.

7. THE URN SCHEME (the crux)

A library .lbr references a 3D package by urn:adsk.eagle:package:NNNNN/V (managed-library scheme), NOT the urn:adsk.wipprod:... scheme that fusion_aps_upload / save_to_cloud produce. So you cannot just paste an uploaded-STEP URN into the .lbr. The eagle URN is minted only when Fusion's 3D Package Generator FINISHes mapped to the package. The .lbr markup (from the EAGLE spec + Autodesk forum): <packages3d><package3d urn="urn:adsk.eagle:package:N/V" type="model">...</package3d></packages3d> and the device carries override_package3d_urn / a <package3dinstance package3d_urn="...">.

8. Open item

The one step not yet automated end-to-end: minting + mapping the eagle URN (the FINISH inside the in-library generator). Current plan: open the footprint env with the chip placed (sections 4-5), let the human align + FINISH/save once, then export that .lbr, read the exact package3d markup + eagle URN, and from there automate the edit-and-reupload for every future chip.

9. Integration test (adom-lbr.fusion3d/1 sidecar) - results

Tested the adom-lbr-fixed .lbr + ads.fusion3d.json sidecar end to end:

  • Footprint writer fix: VERIFIED. The new adom-lbr generate emits a real 4-sided LQFP-64 (16 left / 16 right / 16 top / 16 bottom <smd>, X & Y ±5.74 mm), 64 <connect> intact. Opens in Fusion as a proper QFP, not a 2-column placeholder. (Format nit: the <smd> have no rot= - fine for square pads, but non-square pads on the top/bottom sides would need rot="R90".)
  • URN minting: the assumed scheme is WRONG. Running the 3D Package Generator (Create3DPackage -> import model -> Package3DStop/FINISH -> Save) mints urn:adsk.wipprod:dm.lineage:... (a regular user cloud design), NOT the urn:adsk.eagle:package:NNN/V the manifest expects. Fusion's generator saves a custom 3D model as an ordinary cloud file (wipprod), not a managed eagle package.
  • Binding the wipprod URN: FAILS. Writing <packages3d><package3d urn="urn:adsk.wipprod:dm.lineage:...">
    • a device <package3dinstance package3d_urn="..."> into the .lbr and reopening gives ".lbr has errors and cannot be opened. Close the empty design file..." So a wipprod URN is not valid in package3d; the .lbr genuinely needs an adsk.eagle:package URN.
  • THE REMAINING BLOCKER: how to mint an adsk.eagle:package URN for a custom model. The standalone generator path does not produce one. The in-library Electron::AddCustom3DModel + AttachToFootprintCmd path (section 6) is the likely minter, but it is interactive. This is the crux to crack next.

10. Capturing GUI error dialogs (Fusion) - ALWAYS check owned popups

Fusion error dialogs (e.g. ".lbr has errors and cannot be opened") are owned popups: they do NOT enumerate in desktop_list_windows and are invisible to a plain capture of the main window. A fusion_* verb can return success:true while an error dialog is actually up.

desktop_screenshot_window {hwnd} (AD v1.8.177+) captures them automatically and returns them at the TOP LEVEL: ownedPopupCount (always present; 0 = none) + _screenshots[] (each owned popup: title, kind:"owned_popup", localSafePath, rect, coordMap). The CLI auto-pulls each popup PNG.

RULE: after every Fusion open / execute_text_command / generator FINISH, grab desktop_screenshot_window on the Fusion main hwnd and check ownedPopupCount / read the screenshots[] array (each entry: title, localSafePath, coordMap, shotId; the CLI auto-pulls each PNG to /tmp/ad-shots/). Do NOT just read output.data.image (that is the main window only and misses owned dialogs). Treat ownedPopupCount > 0 as "a dialog is up - read each popup before proceeding." (Field note: _screenshots/_popups_note are doc-strings; the real array is screenshots.)

11. SUCCESS - the WORKING 3D-attach flow (supersedes the section 9 "bind fails" path)

The manual package3d-XML / pre-minted-URN approach (section 9) was a dead end. The generator FINISH, launched from the open library, DOES bind the 3D to the deviceset - no XML editing, no wipprod URN. Proven end-to-end on ADS8588SIPM (LQFP-64): the Content Manager tree shows the 3D nested under the package under the deviceset, the Packages panel's Package column flips from Placeholder to the part name, and the 3D preview becomes the real chip. This is the first time the full Fusion electronics 3D attach has been automated through the bridge.

The real ADS8588SIPM chip imported + auto-oriented onto the matched 4-sided footprint in the Package3D environment

Bound: the Content Manager shows the 3D under the package under the deviceset, and the Packages Package column reads the part name (not Placeholder) with the real 3D preview

The repeatable sequence (all via existing verbs):

  1. Build the .lbr from the chip-fetcher .fusion variant (adom-lbr generate); footprint must be a real 4-sided package (the writer fix). Stage .lbr + the model STEP to Windows.
  2. fusion_open_lbr the .lbr so the library is the ACTIVE document.
  3. fusion_execute_text_command Electron.Create3DPackage <C:/...lbr> -> opens Package3DEnvironment showing the footprint.
  4. fusion_run_modeling_script: importManager.importToTarget(createSTEPImportOptions(<C:/...step>), root), then auto-orient (rotate the smallest bbox dim to Z so it lies flat on the footprint).
  5. fusion_run_modeling_script: ui.commandDefinitions.itemById("Package3DStop").execute() (FINISH).
  6. A Save dialog appears (owned dialog) -> desktop_ui_click its QTApplication.QTFrameWindow.standardActions.SaveButton.
  7. Back on the library: the deviceset's package now carries the real 3D. The right-panel preview LAGS - re-grab the window (or click the package in the Content Manager) to see Package=part-name + the real 3D; the tree is the source of truth, the preview refresh is delayed.

PITFALL corrected: do NOT conclude "it didn't bind" from a stale gray preview right after FINISH - check the Content Manager tree / the Package column, and re-grab after a moment.

12. GROUND TRUTH - exactly how Fusion bakes the 3D URN into the .lbr

Exported the bound library to .flbr (fusion_export_source with a .flbr extension), which is a Fusion document ZIP; the EAGLE library XML lives in .../electron.BlobParts/ExtFile<uuid> (24 KB, <eagle version="9.7.0">). It DOES contain the 3D, and here is the exact markup Fusion writes for a CUSTOM (user-uploaded) 3D model:

<packages3d>
  <package3d name="ADS8588SIPM" urn="" wip_urn="urn:adsk.wipprod:fs.file:vf.<id>?version=1"
             locally_modified="yes" type="model">
    <description>ADS8588SIPM</description>
    <packageinstances><packageinstance name="ADS8588SIPM"/></packageinstances>
  </package3d>
</packages3d>
<!-- and inside the matching <device ...>: -->
<package3dinstances>
  <package3dinstance package3d_urn="urn:adsk.wipprod:fs.file:vf.<id>?version=1"/>
</package3dinstances>

THE DELTA (why the section-9 manual bind failed):

  • urn="" is EMPTY for a custom model (the urn slot is for MANAGED urn:adsk.eagle:package:N/V content only). The actual reference goes in wip_urn=.
  • The URN form is the versioned file URN urn:adsk.wipprod:fs.file:vf.<id>?version=1, NOT the dm.lineage form. (fusion_aps_upload/save_to_cloud return a dm.lineage lineage URN; the ?version=1 file URN is the one to use - get it from fusion_aps_file_info/the saved version.)
  • Add locally_modified="yes" and type="model".
  • The device's <package3dinstance package3d_urn="..."> uses the SAME fs.file:vf...?version=1 form.

So the manifest's adsk.eagle:package assumption is only for managed-library parts; for a self-uploaded model the binding is a wip_urn (fs.file:vf) with empty urn. With this exact shape, adom-lbr (or the bridge) CAN write the binding directly given the model's versioned file URN - no generator UI needed. (The generator FINISH path also works and is what produced this reference.)

13. Resolved sidecar contract (answers to adom-lbr v1.1.1)

Q: what does the sidecar ship for the FINISH path? Just model.path + package + placement. That is sufficient - the bridge does Create3DPackage -> import -> orient -> FINISH -> Save. placement stays useful as an orientation hint/override (the bridge's auto-orient is "rotate smallest bbox dim to Z"; a known pin-1 rotation/offset in placement lets adom-lbr override it when a part needs it).

Q: write the minted wip_urn back to the sidecar (status -> bound)? YES, record it - for idempotency (re-runs detect "already bound"), provenance (which model version), and to enable the offline bind-3d fast-path later. BUT the in-Fusion library is the runtime source of truth; the sidecar record is bookkeeping that adom-lbr owns + writes. The bridge's part is to REPORT the minted URN: after the Save, fusion_aps_file_info {query:"<package>"} returns the saved 3D-package design's tip fs.file:vf.<id>?version=N URN - adom-lbr writes that into the sidecar (urn, urn_status:"bound").

Q: dm.lineage vs fs.file in the .lbr? ONLY the versioned fs.file:vf.<id>?version=N goes in the .lbr - in both wip_urn= and the device package3d_urn=. NEVER dm.lineage in the .lbr (Fusion pins a specific version, consistent with locally_modified="yes"). dm.lineage is the stable cross-version ID; keep it (optionally) only as SIDECAR provenance for re-resolving the latest version, never in the binding.