# 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](https://wiki.adom.inc/adom/adom-desktop-fusion-bridge/files/lib-06-adomlbr-viewer.png)

## 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`:
```python
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
  **"<file>.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. "<file>.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](https://wiki.adom.inc/adom/adom-desktop-fusion-bridge/files/lib-10-ads-chip-on-footprint.png)

![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](https://wiki.adom.inc/adom/adom-desktop-fusion-bridge/files/lib-09-3d-bound-moneyshot.png)

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:

```xml
<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.
