Closed feature request

Composite verb: fusion_install_library_bundle (Cloud Library zip -> installed library, one call)

Colby Knox · 2mo ago ·closed by John Lauer

Ask from the adom-wiki CLI thread (adom-wiki-cli#42, Colby's routing call): the CLI wants library install --format fusion to mirror --format kicad, and the KiCad lane is only clean because kicad-bridge owns the WHOLE recipe as one composite verb (kicad_install_library_bundle: staged zip in, unpack, install, register, capture proof, restart contract out). Fusion's lane today is multi-verb orchestration living in the wiki's webcontrol.js (fusion_create_library, fusion_plan_library_import, fusion_build_library_3d with the runId poll, fusion_prove_result, addin_restart_required handling); duplicating that recipe in the CLI would rot the moment the bridge evolves.

Proposal: one verb, fusion_install_library_bundle {lbrPath (staged on the machine), libraryName?, capture?} -> {installed, partsBound, addinRestartRequired?, proof?}, owning the internal orchestration exactly as the KiCad twin does. The CLI then ships the thin composer (export, stage via send_files, one call) and both formats behave identically for container agents.

Separate bug found while checking the surface: fusion_describe crashes bridge-side with 'Internal error: not enough values to unpack (expected 8, got 7)' (Colby-ROG2, 2026-08-28), so the verb catalog is currently unreadable by callers. Worth a look independent of the composite.

6 Replies

John Lauer · 2mo ago

Yes to the composite, with the contract mirrored to the KiCad twin. Two notes, and the describe crash is fixed already.

Agreed on the shape and the reasoning. The KiCad lane is clean because kicad_install_library_bundle owns the whole recipe; the Fusion lane is multi-verb orchestration living in webcontrol.js, and a CLI that duplicated it would rot the first time the bridge evolved. One owner for the recipe is right, and the bridge is that owner.

Proposed contract, mirroring the KiCad twin field for field where the concepts line up:

fusion_install_library_bundle {
  lbrPath        staged .lbr on the machine (or bundlePath: a staged zip holding
                 the .lbr + one STEP per part, the Cloud Library shape)
  libraryName?   defaults to the file stem; same name rule as KiCad
  capture?       true -> four evidence frames per part, streamed
}
-> { installed, partsTotal, partsBound, partsProven,
     fusionRunning, addinRestartRequired?, userMessage?,
     partMetadata,                 <- the MPN/distributor gap, bubbled up
     runId, statusVerb }           <- so the CLI can poll, like create_library

It runs the existing chain (plan -> open_lbr -> build_library_3d -> prove), so nothing new is orchestrated - it is the one verb the wiki page has been assembling by hand, with addin_restart_required handled inside rather than by the caller.

Two things worth settling before I cut it:

  1. Sync or async? The KiCad twin is synchronous inside web-control's 135s envelope. A Fusion import with STEP binding is 3-15 minutes and cannot be. So the composite must return {runId, statusVerb} immediately and the CLI polls fusion_prove_result, which already carries measured progress and the streamed frames. Unless you want the CLI to block, that is the only honest shape; say if --wait is wanted and I will add a blocking mode with a hard ceiling.
  2. Partial failure. Continue past a bad part and report per-part results (my default, and what build_library_3d does today), or stop at the first? For an Export button I lean continue, with partsBound < partsTotal as the signal. Tell me if the wiki treats a partial library as unshippable.

fusion_describe crash: fixed in 1.9.238. fusion_close_all_documents was missing its returns dict (7 fields, not 8), so the entire catalog was unreadable by every caller. Found and fixed while working #69; both boxes are on it.

I will cut the composite once you confirm the two points; it is mostly wiring, since every stage already exists.

Colby Knox · 2mo ago

Both points confirmed, cut it:

  1. Async, exactly as you shaped it: return {runId, statusVerb} immediately, poll fusion_prove_result. Skip the --wait blocking mode; every caller of this verb (webcontrol today, the CLI next) already has a poll loop, and one contract shape beats two. The 135s web-control envelope makes blocking a lie anyway.

  2. Continue past a bad part, per-part results, partsBound < partsTotal as the signal. Same contract we settled on #65 and the wiki export report's standing rule: named skips, never silent, partial ships. A partial library is shippable; the receipt names what did not bind.

One adoption note so the recipe truly has one owner: when the composite lands, the wiki's webcontrol.js will migrate its Fusion flow onto it (open_lbr + build_library_3d + the runId poll collapse into your verb; the addin_restart_required ask UI stays on our side, driven by the userMessage/documentsKnown payload the verb bubbles up). So design the return as the one contract both the CLI and the Export button read, which is exactly where you already landed.

John Lauer · 2mo ago

Cut and shipped: fusion_install_library_bundle, bridge 1.9.246 (published; AdomLapper is on it, CRR will pick it up on its next bridge start).

Contract, exactly as settled:

  • in: {zipPath | lbrPath (staged on the box), libraryName?, capture? (default true)}
  • out, immediately: {installed, libraryPath, partsTotal, partsToBind, parts[], unbound[{package, why}], modelsUnused[], partMetadata, fusion{status, message}, plan[], estimatedSec, runId, statusVerb: "fusion_prove_result"}
  • Async only, no --wait. Poll fusion_prove_result {runId}: measured percent, stageLabel, evidence[] (four kind:"proof" frames per part, tagged part), provedParts[], and the addin_restart_required payload bubbles up unchanged, so your ask UI stays exactly where it is.
  • Partial ships: a deviceset with no STEP is a named skip in unbound[], the rest bind and prove; partsBound < partsTotal (and provedParts[]) is the signal.

What it does inside: zip-slip-guarded unpack (256 MB zip / 512 MB unpacked caps), finds the shallowest .lbr and every .step/.stp (beside it or under 3dmodels/), places them under ~/Documents/Adom Libraries/<libraryName>/ (stable, not a temp dir), pairs each deviceset to a model by name (deviceset, package, normalised, prefix; a one-deviceset one-model bundle pairs regardless of names), runs the same offline preflight and part-number lint every import runs, then starts the existing create_library chain. Same code path, same frames, same banner, same restart contract. capture:false places the files and returns.

First live run (AdomLapper, bundle = ray/dac80504 part.lbr + part.step, 39 KB zip): returned in one call with installed:true, partsToBind 1/1, fusion.status: ready, partMetadata "1 of 1 component has no manufacturer part number" (reported, not blocking), runId 066889906f4e. I'll post the terminal verdict here when the chain finishes.

Describe entry is in (fusion_describe lists it, 60s timeout, statusVerb fusion_prove_result), and the web-control + multipart skills document the lane. When webcontrol.js migrates onto it, the open_lbr + build_library_3d + runId poll collapse into this one call as you described.

John Lauer · 2mo ago

Proven live, bridge 1.9.248 on AdomLapper, run 9bf511091a96: fusion_install_library_bundle {zipPath, libraryName} returned in one call (installed:true, partsToBind 1/1, fusion.status: ready, partMetadata lint, runId), and fusion_prove_result reached stage: done, verdict: proven, partsProven 1/1, four kind:"proof" frames (symbol, footprint, package3d, ganged) tagged part: DAC80504, library doc and three scratch docs closed by the chain.

Two real bugs the first two runs flushed out, both fixed and in 1.9.248:

  1. Paths with spaces failed inside Fusion (3 : Filename not found). The bridge-side _orchestrate_open_lbr sent Document.newDesignFromLocal <path> raw and unquoted, bypassing the add-in's #196 quote/8.3 fallback. Measured inside Fusion's Python: the quoted form fails too; the Windows 8.3 short path with forward slashes opens first try. Every bridge-side text command that embeds a path now goes through _fusion_text_path(). Any user whose profile folder has a space would have hit this on every chain import.
  2. name means "prove only this deviceset" in fusion_create_library. The composite was passing the library name, so prove hunted for a symbol called ray-dac80504 and scored DAC80504's real frames not_proven. It no longer passes name; every deviceset is proved.

Install folder is ~/Documents/AdomLibraries/<libraryName>/ (space-free on purpose). The receipt shape is unchanged from my previous comment. Ready for webcontrol.js to migrate onto.

John Lauer · 2mo ago

Closing as shipped and proven end to end. fusion_install_library_bundle has been the production lane all weekend: the wiki page's Create Fusion Library button and the library Export flow both drive it, and it carried the LAN7800 six-variant library (one deviceset, six packages, each bound to its own STEP, pin 1 verified on every bind) plus the five-part Demo Board cloud-library export. A narrated demo video of the whole lane is now on this page's README (Demo video section): component page, cloud library, export with both human approvals on camera, bind, proof, finished library. Receipt shape unchanged from the last comment. One follow-up lives wiki-side, not here: the page should surface the waiting-for-your-approval state instead of going quiet when ab has no web-control grant yet.

John Lauer closed this · 2mo ago
Colby Knox · 2mo ago

The wiki-side follow-up from your closing comment is on prod: a page whose first web-control command sits unanswered for 3 seconds now shows an Approve-in-Adom-Bridge callout (top center, tells the user where the prompt is and that nothing runs without their OK), clears the moment any answer arrives, and never reappears once a command has succeeded in the session. Headless lifecycle test covers shown-at-3s, hidden-on-answer, and silent-after-grant.

Log in to reply.