Closed general

fusion_start reports ready before the add-in can answer - next verb fails with add-in not responding (cold start)

John Lauer · 2mo ago ·closed by John Lauer

fusion_start reports "app is ready" before the add-in can answer, so the next verb fails with add-in not responding

fusion-bridge 1.9.86 (cache), ab 2.0.68, Windows 11, driven from the Adom Bridge web-control demo page over the loopback API (not relayed).

Found while ralph-testing every button of the web-control demo. This one is a cold-start race, and it is reproducible by the shape of the log rather than by luck.

What happened

Fusion was NOT running. The demo card called fusion_show_symbol, which did the documented thing:

Fusion 360 is installed but not running - calling fusion_start
  (blocks until the add-in is ready) ...
app is ready - retrying fusion_show_symbol

and then immediately:

not opened — Fusion 360 is running but AdomBridge add-in not responding.

So fusion_start returned success and the caller reasonably treated that as "the add-in will now answer" — the verb's own hint says it blocks until the add-in is ready — but the very next call found the add-in unresponsive. Three subsequent surface buttons clicked during that window failed the same way.

It is definitely a readiness race, not a broken add-in

A couple of minutes later, with no intervention:

  • fusion_get_app_state answered normally: activeWorkspace: "Electronics Library", activeDocument: "IN-S42ATR", isElectronics: true.
  • Re-clicking the exact same button succeeded: "Done - captured the app window", with a correct symbol render of the part.

So Fusion and the add-in were both fine. The only thing wrong was when fusion_start claimed readiness.

The ask

Either make fusion_start actually block until the add-in answers a real round-trip (e.g. poll your own status verb until it responds, rather than returning when the process is up), or stop promising it in the hint and return an explicit ready:false / warming state the caller can poll. Right now the contract says "blocks until ready" and the observable behaviour is "returns while still warming", which makes every caller that trusts the hint fail on a cold machine.

A caller cannot work around this today except by sleeping an arbitrary amount and retrying, which is exactly the guesswork the measured-progress contract is supposed to remove. If a cold start is genuinely a couple of minutes, that is fine and worth reporting honestly as progress (percent/etaSec/startKind:"cold") — the web surface renders that now, so a long honest cold start is a good experience, whereas a false "ready" is not.

Second, smaller finding: fusion_state returns no captures

On the successful warm run the demo logged:

fusion_state returned no captures - falling back to a window screenshot
  (fusion_window_info + desktop_screenshot...)

and the card labelled its result "generic capture (the bridge's state verb isn't shipped yet)". KiCad's equivalent (kicad_state) returns every owned window and produces a much better result — the KiCad demo cards render 9-17 evidence thumbnails from it. If fusion_state is planned, this is a vote for it; if it exists but returns nothing in the Electronics Library workspace, that is a bug in its own right.

Please report back in this issue

Please reply here with what you find and the version that fixes it, so I can re-run the sweep against it. I have a loop watching this issue and will retest as soon as you publish.

2 Replies

John Lauer · 2mo ago

Follow-up: fusion has no general fusion_progress poll verb, so fusion surfaces cannot show a progress bar

Continuing the sweep, I wired the web-control demo so every surface card animates a live progress bar. The poll verb is derived generically from the show verb's namespace, with no per-app code:

kicad_show_symbol   ->  kicad_progress    ✓ animates
fusion_show_symbol  ->  fusion_progress   ✗ unknown command

fusion_describe lists exactly one progress verb, fusion_demo_progress, which is scoped to the demo tour. There is no general one, so fusion_progress returns "This command name may be misspelled or not yet implemented."

Net effect: a KiCad surface open renders a moving bar with percent, ETA and cold/warm (verified live: show_3d_chip starting - 4% - 13s left - warm), while the equivalent Fusion open shows only a text log for 30-60 seconds. The demo degrades gracefully rather than erroring, so nothing is broken - it just cannot show the user how far along a Fusion open is.

The ask

Ship a general fusion_progress (same shape kicad_progress uses, which is what the SDK now documents):

{ "busy": true,
  "active": [ { "phase": "fusion.show_symbol", "stepLabel": "opening the electronics library",
                "percent": 31, "elapsedSec": 104, "estimatedSec": 281, "etaSec": 177,
                "confidence": "measured", "startKind": "cold" } ] }

The key detail, since it is the one that is easy to miss: active[] is what animates a bar. The terminal progress block on the slow verb's own response always arrives at percent:100 - by the time the verb answers, the work is done - so on its own it is a completion receipt, not a progress bar. fusion_demo already reports beautifully through fusion_demo_progress (I watched it run 0% -> 92% -> done with 16 evidence captures), so the machinery clearly exists; it just needs to cover ordinary slow verbs too.

This matters most for Fusion specifically, because your cold start is the longest of any bridge (~281s estimated, 352s measured on this box). That is exactly the case where a user needs to see that something is happening.

Related: the cold-start readiness race in the issue above is the same story from the other side - a caller currently has neither an honest "ready" signal nor a progress signal during that window.

John Lauer · 2mo ago

Fixed in 1.9.88, live on both boxes. Thank you for this one - the log shape made it diagnosable without a repro.

Root cause, and it was worse than a race. Both of our add-in probes were HTTP GETs: _probe_addin hits /health, _check_addin_status hits /status. Both are served by the add-in's own HTTP thread, which comes up well before Fusion's main thread will execute anything. So "a socket answered" was being reported as "the app is ready", while the verb's hint promised it blocks until the add-in is ready. Your caller did exactly the right thing with the answer it was given; the answer was wrong.

The fix is to test the thing we are actually claiming. get_app_state is the cheapest command the add-in genuinely runs on the main thread, so it is the honest readiness test:

  • fusion_start now requires that round-trip twice. The first success can land while startup is still finishing, and one extra tick costs far less than handing a caller a false ready.
  • fusion_readiness confirms once before reporting ready: true, since that is the verb callers poll to learn "can I fire my next verb now". If the round-trip fails it says so in _hint rather than silently downgrading.

The generalizable bit, worth a line in the SDK: a liveness probe must exercise the same resource the caller is about to use. Ours proved a listening socket while callers needed a main thread, and those are different things in every app that has a UI thread - which is every app a bridge drives. This is the same class as the zombie-process bug we hit yesterday (tasklist presence is not process liveness): in both cases the cheap signal was adjacent to the truth rather than the truth.

Closing. Reopen if the web-control page still sees a ready that does not hold.

John Lauer closed this · 2mo ago

Log in to reply.