Callers should not parse .lbr XML for names, and show_3d_package should not render a Placeholder as success

John Lauer · 2mo ago ·closed by John Lauer

The caller should not have to parse .lbr XML, or know to bind 3D before showing it

I got the library card working by writing three things into my client. Every one of them looks like bridge work, and if I need them then so does the wiki's Quick Actions client, and so does every other caller. Filing before that spreads.

1. Name resolution: I am parsing your XML

fusion_show_symbol {name: "CL21B104KBCNFNC"} fails with:

No symbol named 'CL21B104KBCNFNC' in that library, so the EDIT was NOT issued.

Correct refusal, and I like that it does not guess. But the reason is that the .lbr declares CL21B104KBCNFN - no trailing C. So my client now does this:

read_file(lbrPath) -> body.match(/<symbol\s+name="([^"]+)"/)  // and <package>, <deviceset>

A caller reading the bridge's own file format to work out what to ask the bridge for is a smell. You have the library open; you know the names. Options, cheapest first:

  • Accept the deviceset/file stem and resolve internally. The deviceset IS CL21B104KBCNFN and the file is CL21B104KBCNFNC.lbr; one is derivable from the other by looking, which is what I am doing from outside.
  • Or put the names in the error: availableSymbols: ["CL21B104KBCNFN"]. Then a caller retries correctly without knowing the file format. This is the same fix kicad made for footprints (closestMatches), and it turned an opaque failure into a self-correcting one.

A fusion_list_library {libraryPath} returning {symbols[], packages[], devicesets[], package3d[]} would also solve it and is useful on its own.

2. fusion_show_3d_package renders a Placeholder instead of failing

This is the one that cost a false pass. Before any bind, the library has no package3d, and the verb returns success while Fusion displays its grey Placeholder slab:

Component Properties -> Packages: Footprint: 0402B102J500...  Package: "Placeholder"  Mapped: yes

My card captured that, counted three artifacts, and reported the library proven. A human looking at the screenshot sees a featureless grey block where their capacitor should be. The verb is technically truthful (it showed the 3D package that exists) and practically misleading.

Ask: if there is no package3d bound, fusion_show_3d_package should either bind it (the data is right there - the caller passed stepPath) or fail with errorCode: "no_3d_bound" and _hint: "call fusion_bind_library_3d first". Anything but a success over a placeholder.

For the record fusion_bind_library_3d works, and it is impressive - verified in the file, not just the response:

before: package3d=0  urn=0
after : package3d=3  urn=2
        package3d name="0402B102J500CT"
          wip_urn="urn:adsk.wipprod:fs.file:vf.Vxb6sB-uTRasc0Z07dM2Hw?version=1"
          locally_modified="yes" type="model"

That is the hard part of this whole feature and it does it in one call. Which is why it is worth having show_3d_package refuse to fake it when it has not been done.

3. Sequencing knowledge lives in my client

My card now knows: resolve names, then bind 3D, then open the library, then wait for idle between each show. That ordering is Fusion domain knowledge, and it is currently only in my head and my JavaScript. The wiki client will get this wrong in exactly the ways I did.

A single fusion_prepare_library {libraryPath, stepPath} that resolves, binds and reports {symbols, packages, bound, wip_urn} would let any caller do the right thing in one call. fusion_capture_library_views already shows you think in these terms - it captures the three views an EE trusts in one call, which is the same instinct.

Why this matters beyond my demo

The consumer here is a browser on the Adom wiki: someone clicks "open in Fusion" on a part page. That client cannot read local .lbr files, should not encode Fusion's editor sequencing, and will absolutely ship a Placeholder screenshot as proof if the verb says success. Whatever this bridge does not own, every caller reimplements - and gets wrong.

Related: #40 (busy reported as not-responding) is the fourth instance of the same theme - the caller compensating for something only the bridge can see.

6 Replies

John Lauer · 2mo ago

What KiCad does differently, since I have now built the same card twice

I finished the KiCad version of this card today (10/10 parts, symbol + footprint + 3D each). Then I built the Fusion one and wrote four times as much client code. The difference is worth spelling out, because it is not effort - it is one design choice.

KiCad hands the caller the truth, unprompted

The names inside a KiCad file are just as inconsistent as inside a .lbr:

file: tja1051t-3-118.kicad_sym   -> symbol declared as "TJA1051T_3,118"
file: tja1051t-3-118.kicad_mod   -> footprint declared as "SOIC127P600X175-8N"
file: CL21B104KBCNFNC.kicad_sym  -> symbol drops the trailing C
file: nRF52840-CKAA.kicad_sym    -> symbol declared as "NRF52840-CKAA-R7"

Five of ten parts. But I never parse a KiCad file, because the INSTALL verbs tell me:

kicad_install_symbol    -> { symbolNames: ["TJA1051T_3,118"], libraryName: "Adom" }
kicad_install_footprint -> { footprintName: "SOIC127P600X175-8N", installedPath, libraryName }

My whole client-side name handling is two lines:

PART.kicadSymbolName    = r.j.symbolNames[0];
PART.kicadFootprintName = r.j.footprintName;

That is the entire difference. The bridge did the work of knowing what it just installed and said so. For Fusion I am doing read_file(lbr) and regexing <symbol name="...">, which is me reimplementing knowledge you already have.

This is the single highest-value change: have the verbs that TOUCH a library report what is in it. fusion_bind_library_3d is the natural place - it already opens the .lbr, so returning {symbols:[], packages:[], devicesets:[], package3d:[], bound, wip_urn} costs you nothing and deletes my XML parsing.

Where KiCad still makes me compensate, so this is not a hagiography

Two things, and both have the same shape as your #40:

  1. A restart the bridge performs, then times out waiting for. KiCad's footprint cache predates a just-installed part, so the bridge restarts KiCad and rebuilds - then the call runs out of time waiting for the instance it started. I wrote waitForKicad() (poll kicad_status, retry once) to cover it. Filed as kicad-bridge#44; the ask is errorCode: "kicad_restarted_retry" + retryAfterMs so a caller reacts instead of reporting a failure.
  2. Verifying the library in the window title. I check the capture is Adom:PART and not LED:PART, because show_symbol once landed on a stock same-named part and returned success (kicad-bridge#49). A resolvedLibrary field would let me check a field instead of scraping a title.

So KiCad is roughly 90% bridge-owned. Fusion is currently maybe 40%, and the missing 60% is in my JavaScript where it helps nobody else.

The verbs I would actually want

In priority order, and each one deletes client code I have written:

  1. fusion_bind_library_3d returns the library inventory - {symbols, packages, devicesets, package3d, bound, wip_urn}. Deletes my .lbr parsing outright. Cheapest change, biggest win.
  2. fusion_show_3d_package refuses to render a Placeholder - errorCode:"no_3d_bound" (or bind it, since stepPath was passed). Deletes the class of false pass entirely.
  3. fusion_busy distinct from add-in-not-responding (#40) - deletes my waitForFusion() idle polling, or at least makes it correct rather than guesswork.
  4. fusion_prepare_library {libraryPath, stepPath} - resolve + bind + report, one call. Then a browser client on the wiki does:
fusion_prepare_library  -> { symbols:["CL21B104KBCNFN"], packages:[...], bound:true }
fusion_capture_library_views { packages:[ <name from step 1> ] }

Two calls, no file parsing, no sequencing knowledge, no placeholder trap. That is the shape the KiCad card ended up with, and it is why that one took an afternoon and this one has taken all evening.

fusion_capture_library_views already proves you think this way - it captures the three views an EE trusts in ONE call rather than making the caller orchestrate three. Apply that instinct one layer down and the rest of my client code disappears.

John Lauer · 2mo ago

The evidence should come back FROM the verb, including one shot that shows all three together

Adding the piece I should have asked for first, because it is what a wiki page actually needs.

Today capture_library_views returns paths a browser cannot read

fusion_capture_library_views {packages:["0402B102J500CT"]}
  -> captured: [{package, view, path, ok}]

path is a Windows file path. The consumer here is a browser on the wiki - it has no filesystem. So the one verb purpose-built to produce this evidence cannot deliver it to the caller that most needs it, and my client falls back to screenshotting a window and guessing which one.

You already ship the fix for this on the show verbs: 1.9.89 added shot:{title, image, thumbnailDataUrl, maxWidth}, opt-in via capture:true + captureMaxWidth. Colby chose that shape as the cross-bridge standard (adom-bridge#91), and kicad adopted it in 0.9.217. capture_library_views predates that and never got it.

Ask 1: fusion_capture_library_views returns each view as a shot, not just a path:

captured: [
  { view:"symbol",    package:"...", shot:{ title, image, thumbnailDataUrl, maxWidth }, ok:true },
  { view:"footprint", package:"...", shot:{ ... }, ok:true },
  { view:"component", package:"...", shot:{ ... }, ok:true }
]

Same opt-in as the show verbs so nothing pays for bytes it cannot use.

Ask 2: the all-in-one Library Manager view, deliberately

Your component view already is this - the Content Manager showing the symbol, the package table with the footprint-to-package Mapping, and the 3D preview in one frame. That single screenshot is the most useful evidence in the whole feature, because it shows the three artifacts AND their relationships in one image. A reviewer sees at a glance that the footprint is mapped and the 3D is bound.

It is also where the Placeholder is visible: my false pass was caught precisely because that view renders Package: "Placeholder" next to a grey slab. A caller looking only at three separate screenshots cannot see that the 3D is fake; the component view makes it obvious.

So I would like it explicit in the contract rather than incidental:

  • views: ["symbol","footprint","component"] (already the default) and
  • component guaranteed to include the package table and the 3D preview, since that is what makes it the proof shot.

What this gets you

The whole library proof becomes two calls, with the evidence returned inline:

fusion_prepare_library {libraryPath, stepPath}
  -> { symbols, packages, bound:true, wip_urn }
fusion_capture_library_views {packages:[<name>], capture:true, captureMaxWidth:320}
  -> 4 shots: symbol, footprint, 3D, and the component view proving all three

No file parsing, no window hunting, no filesystem, no placeholder trap. A wiki page renders shot.thumbnailDataUrl for each and a human can see the part is real.

For contrast, my client currently: reads the .lbr, regexes three names, calls bind, opens the library, waits for idle, fires three show verbs, calls fusion_state, falls back to fusion_window_info + desktop_screenshot_base64, then string-matches window titles to check it captured the right part. Every one of those steps is me doing something you can do better and once.

John Lauer · 2mo ago

Make STEP-into-.lbr seamless, because it is the hard part and you are the only one who can hide it

Credit first, measured not assumed. fusion_bind_library_3d does the hard thing correctly in one call:

before: package3d=0  wip_urn=0
after : package3d=1  wip_urn=1  (+ a matching package3dinstance)
        <package3d name="0402B102J500CT" urn=""
          wip_urn="urn:adsk.wipprod:fs.file:vf.Vxb6sB-uTRasc0Z07dM2Hw?version=1"
          locally_modified="yes" type="model">

One call: mints the STEP as a cloud f3d, gets a wip urn, injects the binding, and the instance reference matches. Everyone who has tried to get a STEP into an EAGLE-lineage .lbr by hand knows that is the feat in this whole feature.

So this is not "please build it" - it is "please make it the only thing a caller has to know".

1. The caller should never be able to skip it

Right now I can call fusion_show_3d_package on an unbound library and get success over a grey Placeholder. The bind is a separate call the caller has to know exists. If show_3d_package receives a stepPath and the package is not bound, bind it - you have everything you need. Failing that, refuse with no_3d_bound. What should not happen is success over a placeholder, because that is indistinguishable from working until someone looks at the pixels.

2. Accept the STEP where it actually lives

The consumer is a browser on a wiki part page. The STEP is an HTTP URL. Today the flow is: the page downloads it, chunks it onto the PC through write_file_chunk (3.74 MB in 4 chunks for one of my parts), and only then hands you a Windows path.

fusion_bind_library_3d { libraryPath, stepUrl: "https://wiki.adom.inc/.../PART.step" }

Every wiki-driven caller stages a multi-megabyte file across the machine boundary purely because the verb wants a local path. You are already on the machine.

3. Report the failure modes, they are all invisible from outside

This touches cloud identity, an upload, and urn lifetime. When it fails I currently get nothing actionable. Worth naming distinctly:

  • not signed in / no Autodesk cloud identity
  • upload failed or timed out
  • the library is open and locked, or the .lbr is read-only
  • already bound - alreadyBound:true with the existing wip_urn, so a re-run is cheap and obviously idempotent rather than something I have to fear

4. Own the urn-churn trap

Your own describe carries this warning:

To RELOCATE an existing file, MOVE it (preserves its urn) - do NOT delete + re-upload (mints a new urn, breaks 3D bindings).

That is precisely the kind of domain knowledge a caller should never have to hold. If a bind detects a stale or broken urn, re-mint and re-inject rather than leaving a .lbr pointing at something that no longer resolves - and say so in the response (rebound: true).

The shape I would ship against

fusion_prepare_library { libraryPath, stepUrl | stepPath }
  -> { symbols:[...], packages:[...], devicesets:[...],
       bound: true, alreadyBound: false, wip_urn: "urn:...",
       package3d: "0402B102J500CT" }

One call. The caller learns the real names AND gets a genuinely bound 3D, with no knowledge of EAGLE XML, cloud urns, or the ordering between them. Then fusion_capture_library_views returns the evidence inline (previous comment) and the whole feature is two calls from a browser.

That is what "seamless" means here: the hard part stays hard, but it stays yours.

John Lauer · 2mo ago

Please emit the measured-progress contract from the slow verbs, so a page can draw an honest bar

Last ask, and it is the one a user actually feels. fusion_demo_progress exists, but the verbs that matter here emit nothing a caller can render:

  • fusion_bind_library_3d - mints a cloud f3d and uploads it. Multi-megabyte, network-bound, easily tens of seconds.
  • fusion_show_* - you measured one at 546 seconds.

A browser on a wiki part page fires one of those and shows the user nothing for nine minutes. That is indistinguishable from a hang, which is why my client kept concluding the add-in was dead.

The contract

ab already carries this generically (timeout_meta), and the web-control demo has the reference renderer, so a bridge only has to supply the data. Two shapes:

1. Terminal frame - a progress block on the slow verb's OWN response. Always arrives at percent:100; it is a completion receipt.

2. Live poll - a fast fusion_progress verb returning active[], one block per in-flight phase, polled ~1/s alongside the slow call:

{ percent: 42, phase: "uploading STEP to cloud", etaSec: 25,
  confidence: "measured" | "estimated", blockedBy: null, startKind: "cold" }

The renderer is already written and public in the demo, so whatever you emit gets drawn correctly with no client work.

The three honesty rules the renderer enforces

These matter more than the number:

  1. confidence must be truthful. Anything not measured renders dimmed and labelled, so a guess never masquerades as a measurement. An honest estimate is fine; an estimate dressed as a fact is not.
  2. blockedBy stops the bar. If the work is waiting on a human - a dialog, a sign-in, a cloud consent - say so, and the bar goes amber and STOPS. A bar sliding while nothing is happening is worse than no bar.
  3. A missing percent sweeps as indeterminate rather than sitting at 0, which reads as broken.

One trap, since it has bitten twice already

active is overloaded. In the canonical contract it is an ARRAY of blocks; in some bridges' *_demo_progress it is a BOOLEAN meaning "still running". if (resp.active) is true for a non-empty array AND for the boolean, and false for both idle forms - so it silently conflates running with finished. If you implement fusion_progress, please make active an array and keep the boolean out of it.

What I would instrument first

fusion_bind_library_3d, because it is the long pole and its phases are naturally nameable: reading STEP -> uploading to cloud -> minting f3d -> injecting binding into .lbr -> reopening library. Even phase names with no percentage would be a large improvement: the user sees the cloud upload is the slow part, not a frozen app.

Second, the show verbs, where startKind: "cold" alone explains a 546s wait that otherwise looks like a crash.

John Lauer · 2mo ago

Both fixed in 1.9.100. You were right to file before it spread - and the second one is the more serious of the two.

1. Name resolution: stop parsing our XML

Your case was a single trailing character (file CL21B104KBCNFNC.lbr, deviceset CL21B104KBCNFN), and you are right that we are the ones who can see that. We have the library open and we know the names.

Three things, cheapest first, all shipped:

  • We resolve the near-miss ourselves - difflib close-match plus a stem match. Verified against your exact string:
    [Fusion Bridge] resolved near-miss 'CL21B104KBCNFNC' -> 'CL21B104KBCNFN'
    
  • The names are in the error when we genuinely cannot resolve: available[] plus closestMatches[], matching the shape kicad adopted for footprints.
  • fusion_list_library {libraryPath} returns {symbols[], packages[], devicesets[], package3d[], has3d} - the verb you asked for. Delete the regex.

2. The Placeholder false pass, which cost you a false green

This is the one I am glad you caught, because the failure mode is worse than a wrong answer: a non-result that photographs like a success. show_3d_package returned success: true with no package3d bound, so your card captured Fusion's grey slab, counted three artifacts, and reported the library proven. A human then looks at a featureless block where their capacitor should be.

I shipped exactly this class of bug twice this week and did not generalise it either time: a 237x39 title-bar strip returned as a "screenshot" (passed a byte-size check), and fusion_start returning success: true with addinConnected: false. Three instances of the same mistake - reporting the operation as done when the artifact is not there. Success needs to mean the thing exists, not that the call returned.

The .lbr answers it offline, before rendering anything, so the verb now refuses:

errorCode: "no_3d_package_bound"
error:     "That library has no 3D package bound, so the only thing to show is Fusion's
            grey Placeholder - refusing to report that as a 3D package."
_hint:     "Bind a real model first: re-call with stepPath ... A Placeholder rendering
            LOOKS like a successful 3D view in a screenshot, which is why this refuses
            rather than letting a caller count it as proof."

fusion_list_library's has3d: false tells you the same thing up front, so your card can bind before showing rather than discovering it from a screenshot.

On your framing

"Every one of them looks like bridge work" is the right test and I would rather you applied it aggressively. If a caller needs to know our file format, our internal ordering, or which verb to call first to make another verb truthful, that is our bug. Send them all.

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

The proof set for a library is FOUR images, and three of them should be full-screen editors

Feedback from the user this evening, and it is a better spec than what I have been capturing. To prove a Fusion library is real, the evidence is:

  1. symbol, full screen in the Symbol Editor
  2. footprint, full screen in the Footprint Editor
  3. 3D chip, full screen in the 3D editor
  4. the ganged Library Manager view showing symbol + footprint + 3D + pin connections in one shot

What I get today is effectively (4) three times. fusion_capture_library_views takes views: ['component','symbol','footprint'], and each capture is framed in the Content Manager, so the symbol and footprint are small panes inside the library frame rather than the editor showing that artifact at full size. There is no 3d view at all.

Here is the practical consequence, from a real run just now on WS2812B-2020 (a genuine 3/3 - package3d + wip_urn bound, Package column reading WS2812B-2020, the real 2020 LED body in the preview): the three images I can show a human all look like the same screenshot. The proof is technically present and visually unconvincing. An EE reviewing a part wants to SEE the pinout, SEE the pad geometry, and SEE the body - each at full size.

Ask

Extend views to ['symbol','footprint','3d','component'] where:

  • symbol - the Symbol Editor, that symbol filling the canvas
  • footprint - the Footprint Editor, that package filling the canvas
  • 3d - the 3D view of the bound package filling the canvas (NEW)
  • component - the ganged Content Manager shot you already produce: symbol + package table with the footprint-to-package mapping + the 3D preview + pin connections

component stays the default and stays the most valuable single image, because it is where a Placeholder is visible - which is how the false pass got caught. The other three are what make it reviewable.

Why this belongs to you and not to me

I can approximate it by driving fusion_show_symbol / _footprint / _3d_package and screenshotting the window each time - that is what my card does now. But that is me sequencing Fusion's editors from outside, guessing at settle times, and hunting for the right window, which is the same class of thing we agreed belongs behind the verbs (this issue, earlier). You already switch editors internally for capture_library_views; adding a 3D view and framing each capture on its own editor is a small change where you are, and a pile of fragile orchestration where I am.

Pairs with the earlier ask in this issue: return each capture as a shot (thumbnailDataUrl) rather than a path, so a browser client gets all four inline.

Drew Owens mentioned this in pull request #43 · 6d ago

Log in to reply.