← Commit history

v1.6.43: dev skill + hints updated to UIA seat-dialog handling; narrate launch state

John Lauer ·3163f6430b ·3mo ago ·parent 2969fd3
6 files changed +52−23
BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.6.42\ No newline at end of file+1.6.43\ No newline at end of file
CHANGELOG.md+11
@@ -3,6 +3,17 @@ All notable changes to the Adom Desktop Fusion 360 bridge, newest first. Canonical: https://wiki.adom.inc/adom/adom-desktop-fusion-bridge · GitHub backup: `adom-inc/adom-desktop-fusion-bridge` +## 1.6.43 - 2026-07-06++- **Docs + hints caught up to the real (UIA) seat-dialog handling.** The `fusion-bridge-dev` skill now+  records the hard facts: it's a QT dialog (not CEF), titled just "Fusion360", its "Continue" button is+  UIA-invokable in the BACKGROUND, the radios are NOT in the a11y tree, kill+relaunch never frees a+  server-held seat (dead code), retry-until-verified-gone, and the seat check outranks a stale add-in+  port. Response hints now NARRATE what the bridge is doing + what happens next: `fusion_start` says it+  launched in the background (don't foreground), whether it auto-resolved a seat dialog, and to poll+  `fusion_readiness`; the readiness `licensingDialog` hint describes the UIA auto-resolve (not the old+  "free it from peer machines" text).+ ## 1.6.42 - 2026-07-06  - **Seat-resolve now RETRIES until the dialog is verified gone, and takes priority over a stale add-in
adom-bridge-fusion-manifest.json+5−5
@@ -1,13 +1,13 @@ {   "manifest_version": 1,   "name": "fusion360",-  "version": "1.6.42",-  "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.6.42/adom-bridge-fusion-v1.6.42.zip",-  "sha256": "19b1f290128591fed964570ab8f57be5eefcd2b04287e274672d4ad08a6d5104",-  "size": 340876,+  "version": "1.6.43",+  "url": "https://wiki.adom.inc/download/adom/adom-desktop-fusion-bridge/1.6.43/adom-bridge-fusion-v1.6.43.zip",+  "sha256": "0920a84b437d82db0ec91496439369ccad20e745e2af32692d35f0e7322221a7",+  "size": 341813,   "verbPrefixes": [     "fusion_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-07-06T13:47:22Z"+  "released_at": "2026-07-06T13:52:37Z" }
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "fusion360",   "displayName": "Autodesk Fusion 360",-  "version": "1.6.42",+  "version": "1.6.43",   "description": "Drive Autodesk Fusion 360 from the cloud: launch Fusion, electronics board layout, design rules, exports (STEP/IGES/STL/3MF/USDZ/OBJ/DXF/DWG/Gerbers/BOM/CPL), fast APS server-indexed cloud search plus browse/recent/file-info/versions, cloud file download/upload and folder creation, and in-app parametric modeling (fusion_run_modeling_script). Never-charge: APS calls are capped to the free tier.",   "homepage": "https://wiki.adom.inc/adom/adom-desktop-fusion-bridge",   "author": "Adom Inc.",
server.py+15−9
@@ -247,12 +247,17 @@ def _handle_launch(fusion_info: dict, args: dict) -> dict:             "recoveryFilesRelocated": reloc["moved"],             "resolvedPath": fusion_info.get("exe_path", ""),             "seatDialogResolved": _seat_clicks > 0,+            "backgroundLaunch": True,             "_hint": (-                ("Auto-resolved a seat/licensing dialog in the background (UIA, no foreground). "-                 if _seat_clicks else "")-                + "Fusion is LAUNCHING but the add-in isn't up yet - a FIRST launch / a fresh "-                "sign-in can take 2-4 min. This is NOT a failure and NOT a timeout to retry: do NOT "-                "re-call fusion_start. POLL fusion_readiness until ready:true, then drive."+                "Fusion was launched IN THE BACKGROUND (minimized, foreground-lock on) so it does not "+                "steal the user's focus - do NOT foreground it. "+                + ("A seat 'Active Sessions Exceeded' dialog appeared and was AUTO-RESOLVED in the "+                   "background via UIA (no foreground). " if _seat_clicks else "")+                + "The add-in isn't up yet - a first launch / fresh sign-in can take 2-4 min. This is "+                "NOT a failure and NOT a retry-able timeout: do NOT re-call fusion_start. WHAT HAPPENS "+                "NEXT: POLL fusion_readiness every ~10s; it returns needsSignin/licensingDialog/updating "+                "if something is mid-flight (the bridge handles the seat dialog itself), and ready:true "+                "when it is drivable - then run your fusion_* verbs."             ),         } @@ -895,10 +900,11 @@ def _handle_fusion_readiness(fusion_info: dict, args: dict) -> dict:     elif ready:         hint = "READY - drive Fusion (fusion_open_lbr / build_library_3d / etc.)."     elif installed and running and licensing:-        hint = ("Fusion is blocked by a SEAT/LICENSING dialog (e.g. 'Active Sessions Exceeded') - "-                "detected by owning process + dialog size, NOT by title. Call fusion_start: it "-                "AUTO-RECLAIMS the seat (frees it from peer machines, then relaunches clean). No user "-                "action needed - this is not a crash.")+        hint = ("Fusion is blocked by a SEAT/LICENSING dialog ('Active Sessions Exceeded') - detected "+                "by owning process + dialog size (its title is just 'Fusion360'). fusion_start "+                "AUTO-RESOLVES it in the BACKGROUND by UIA-invoking its 'Continue' button (no "+                "foreground, no user action) and retries until it clears. If you see this persist, just "+                "call fusion_start again - it is not a crash, and never coordinate-click or ask the user.")     elif installed and running:         hint = ("Fusion is RUNNING but its add-in is NOT responding yet - NOT ready to drive. Either it "                 "is still finishing launch / signing in (poll fusion_readiness a few more times), or the "
skills/fusion-bridge-dev/SKILL.md+19−7
@@ -62,13 +62,25 @@ opening path you add MUST follow it:   process; if Fusion is ALREADY running, `fusion_start` returns early and the guard never fires, so an   already-foreground Fusion stays put. When you need a clean background state, `fusion_kill` first,   confirm the process is gone, THEN launch.-- **Seat conflict ("Active Sessions Exceeded") = AUTO-RECLAIM, never ask.** John (2026-07-06): "just-  grab the license back… the user can always grab it back on the other machine, so there's no harm."-  The dialog is a CEF modal that ignores background `SendInput`, so do NOT try to click it. Instead-  `_handle_launch` frees the seat at the SOURCE — `_reclaim_seat_from_peers()` stops Fusion on peer ADs-  via the cross-AD direct API (fully background, reliable) — then kills the local instance stuck on the-  dialog and relaunches clean (guarded by a one-shot `_seatReclaimAttempted` arg). Do not reintroduce a-  notify+ask path for the seat dialog.+- **Seat conflict ("Active Sessions Exceeded") = AUTO-RESOLVE via UIA, in the background, never ask+  (SOLVED live 2026-07-06 after a very long day).** The definitive facts, hard-won:+  - It is a **Qt** dialog (`automationId` = `QTApplication.QTSessionBlockedStartup...`), NOT CEF.+  - Its window **TITLE is literally "Fusion360"** — the "Active Sessions Exceeded" text is body+    content — so detect it by **owning process + dialog size** (`find_licensing_dialog()`), never title.+  - Its native **"Continue"** button IS exposed to **UIA** (`desktop_ui_click {hwnd, name:"Continue"}`),+    and UIA **Invoke runs in the BACKGROUND — no foreground, no focus steal, no cursor move**. Suspend+    is the pre-selected radio, so Continue grabs the license. (The radios themselves are web content+    NOT in the a11y tree — do NOT depend on selecting them.)+  - **DO NOT** kill+relaunch to "free the seat at the source": the Autodesk licensing SERVER keeps the+    seat even when the other machine is OFF, so that never clears it. `_reclaim_seat_from_peers()` is+    dead code, kept only as a comment-marker — do not resurrect it.+  - **DO NOT** coordinate-click it (`desktop_click`/SendInput): that needs foreground (disturbs the+    user) AND the focus-guard fights you by minimizing the dialog.+  - **RETRY + VERIFY.** The FIRST UIA Invoke on a mid-render dialog silently NO-OPs. So the launch loop+    re-invokes "Continue" EVERY tick until `find_licensing_dialog()` no longer sees it — never click+    once and trust it. And a STALE add-in port (port 8774 from a just-killed instance) answers while+    the CURRENT Fusion is blocked behind the dialog, so the seat check must take PRIORITY over the+    add-in probe in both the launch loop and `fusion_readiness` (else you falsely read `ready`). - **The add-in still loads while minimized** — background operation (open/model/export) needs no   visible window. So minimized costs you nothing. - **Foreground is opt-in, only to SHOW the user something.** The ONLY time you may foreground/restore