← Commit history
.insiders-stage/fusion-bridge/version.json+4−4
@@ -1,10 +1,10 @@ {   "manifest_version": 1,   "name": "fusion360",-  "version": "1.9.354",-  "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.354/adom-bridge-fusion-v1.9.354.zip",-  "sha256": "be866be570eb389054cd9f06bb3817c75d3ef5357d6e0a5ac823136d55050d41",-  "size": 2239332,+  "version": "1.9.355",+  "url": "https://wiki.adom.inc/download/adom/fusion-bridge/1.9.355/adom-bridge-fusion-v1.9.355.zip",+  "sha256": "07784c41e9931f3d7a273d2b22e0f46a45b56f11b14147c624f5b7f46b40fe63",+  "size": 2277785,   "verbPrefixes": [     "fusion_"   ],
.release-page-head+1−1
@@ -1 +1 @@-2579ddb0cef53f26b44396be18798dee72200ca0\ No newline at end of file+6f27922be73f0cd9200f656dbb2b30ff14954211\ No newline at end of file
page.json+26−10
@@ -11,10 +11,6 @@     "uninstall": "./uninstall.sh"   },   "tags": [-    "ab",-    "bridge",-    "fusion",-    "desktop-app",     "fusion360",     "autodesk",     "cad",@@ -79,10 +75,7 @@     "build a basic parts library",     "add a 3d model to a component",     "make a fusion 360 library with 3d",-    "set up the fusion bridge",-    "fusion dashboard",-    "fusion status page",-    "open the fusion launch page"+    "set up the fusion bridge"   ],   "discovery_pitch": "Use the Adom Bridge Fusion 360 bridge when the user wants to DRIVE Autodesk Fusion 360 from the cloud: open/search their team's Fusion cloud designs, lay out a PCB or do electronics, export STEP/IGES/STL/Gerbers/BOM, or build a component library with real rendering 3D. It runs Fusion on the user's own machine via Adom Bridge. NOT for web/browser/login/form-filling (that's the native-browser extension) and NOT for KiCad (that's the kicad bridge).",   "brief": "Drive Autodesk Fusion 360 from the cloud via Adom Bridge: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.",@@ -111,5 +104,28 @@       "label": "Part volume",       "prompt": "How much material does each printed part in this assembly use?"     }-  ]-}+  ],+  "agent_permissions": {+    "allow_bash": [],+    "allow": [+      "adom-desktop-fusion-bridge read-only verbs (via the adom-bridge CLI): fusion_readiness, fusion_get_app_state, fusion_addin_status, fusion_check_dialogs - state queries; they change nothing.",+      "adom-desktop-fusion-bridge read-only verbs: fusion_alerts (reads Fusion's own log for toasts), fusion_document_info, fusion_window_info, fusion_board_info, fusion_library_parts - inspect only.",+      "adom-desktop-fusion-bridge read-only verbs: fusion_prove_result (a persisted run report), fusion_plan_library_import (report-only preflight), fusion_list_cloud_projects, fusion_describe.",+      "adom-desktop-fusion-bridge evidence captures fusion_screenshot_fusion and fusion_capture_library_views read pixels and write local image files only."+    ],+    "soft_deny": [+      "adom-desktop-fusion-bridge lifecycle verbs fusion_stop, fusion_kill, fusion_restart_for_addin close or kill the Fusion application - keep the human prompt.",+      "adom-desktop-fusion-bridge fusion_install_fusion silently installs Autodesk Fusion on the user's machine.",+      "adom-desktop-fusion-bridge fusion_cleanup_cloud_files deletes files from the user's Autodesk cloud hub.",+      "adom-desktop-fusion-bridge managed-library verbs (fusion_managed_library_push/pull/create/link/unlink) publish to or overwrite the user's library.io libraries."+    ],+    "environment": [+      "adom-desktop-fusion-bridge is the Fusion 360 bridge for Adom Bridge; its fusion_* verbs are invoked through the adom-bridge CLI and drive Autodesk Fusion on the user's own desktop.",+      "adom-desktop-fusion-bridge desktop actions also pass Adom Bridge's own human-approval gate (caller identity, per-call reason, Activity Log) - a container approval is the first of two gates."+    ]+  },+  "author": {+    "name": "John Lauer",+    "email": "[email protected]"+  }+}\ No newline at end of file
rust/Cargo.lock+5−5
@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"  [[package]] name = "adom-fusion-bridge"-version = "1.9.354"+version = "1.9.355" dependencies = [  "chrono",  "fusion-core",@@ -183,7 +183,7 @@ dependencies = [  [[package]] name = "fusion-core"-version = "1.9.354"+version = "1.9.355" dependencies = [  "serde",  "serde_json",@@ -193,7 +193,7 @@ dependencies = [  [[package]] name = "fusion-lbr"-version = "1.9.354"+version = "1.9.355" dependencies = [  "regex",  "roxmltree",@@ -205,14 +205,14 @@ dependencies = [  [[package]] name = "fusion-os-mac"-version = "1.9.354"+version = "1.9.355" dependencies = [  "fusion-core", ]  [[package]] name = "fusion-os-win"-version = "1.9.354"+version = "1.9.355" dependencies = [  "fusion-core",  "windows-sys 0.59.0",
rust/core/src/readiness_hints.jsonadded+21
@@ -0,0 +1,21 @@+{+ "not_installed": "Fusion 360 is NOT installed. OFFER to install it for the user, then call fusion_install_fusion (no shell approval needed) - it streams the free trial and this verb reports installing:true until done.",+ "installing": "Fusion install is STREAMING (10-30 min) - keep polling fusion_readiness until installed:true.",+ "installed_not_running": "Fusion is INSTALLED but not running - call fusion_start (blocks until the add-in is ready), then retry.",+ "addin_silent": "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 add-in did not load (fusion_stop then fusion_start to re-deploy + re-load it). Do NOT fire modeling/export verbs until ready:true.",+ "needs_signin": "Fusion is RUNNING but stuck at the first-run Autodesk SIGN-IN (main UI blocked, add-in cannot serve) - NOT ready. FULL PLAYBOOK: the `fusion-autodesk-signin` skill. Proven sequence + the pitfalls that cost hours on a fresh Hyper-V VM (John 2026-07-14): (1) BLANK white webview on first launch? fusion_stop then fusion_start - the sign-in renders 'Welcome to Fusion' + a 'Sign In' button only after a clean relaunch. (2) ASK the user FIRST (AskUserQuestion): do they have a warm Google/Apple/Microsoft session for the account LINKED to their Autodesk login? Offer to drive it. A truly fresh box has NO warm browser session, so 'Continue with Google' still hits a password wall. (3) Click Fusion 'Sign In' -> it opens the DEFAULT browser (Edge) to the OAuth. Edge windows DO exist even if PowerShell EnumWindows looks empty - that is CLIXML progress noise; set $ProgressPreference='SilentlyContinue', and just desktop_screenshot_window the Edge hwnd (PrintWindow works backgrounded). (4) In the browser: 'Continue with Google' -> type the user's EMAIL (not secret) -> if it asks for a PASSWORD/2FA, STOP and fusion_notify_owner {title, body} to toast their MAIN computer so THEY type it; NEVER enter password/2FA yourself. (5) Handoff back to Fusion is via the 'Autodesk Identity Manager' protocol - Edge shows a 'This site is trying to open...' overlay: tick 'Always allow' + click 'Open'. (6) SPEED MATTERS: the sign-in code EXPIRES in ~2 min; on a high-latency VM slow screenshot/click round-trips let it expire ('Sign-in request expired') and you loop. Minimize steps; if it expires, click Fusion 'Sign In' again for a FRESH code (the browser session stays warm). Then poll until ready:true. FIRST-TIME DEMO? Run fusion_demo instead - it drives this sign-in through the user's NATIVE browser (already signed into Autodesk) and then gives them a real guided tour (project -> schematic -> 2D board -> 3D board).",+ "licensing": "Fusion is blocked by a SEAT/LICENSING dialog ('Active Sessions Exceeded' / 'Suspend Remote Session') - detected DETERMINISTICALLY as an OWNED POPUP of the main Fusion window (its title is just 'Fusion360' and it can be as short as 262px, so it is found by owner+parent-screenshot, NOT a size guess). This verb JUST TRIED to auto-resolve it in the BACKGROUND (UIA 'Continue', Suspend pre-selected so it reclaims the seat) but it is still up - it may be mid-render (the first UIA Invoke no-ops). Just POLL fusion_readiness again in a few seconds; it keeps auto-resolving. Never coordinate-click or ask the user, and never kill/relaunch (the server keeps the seat).",+ "updating": "Fusion is applying an AUTO-UPDATE (multiple webdeploy builds present) and RESTARTS ITSELF every ~30-60s - this is NOT a crash or a bridge failure. Poll fusion_readiness; it stabilizes to ready:true once the update finishes (can take 10-30 min). Meanwhile, wrap modeling/export verbs in a short retry/catch-loop to ride the up-windows rather than treating an intermittent 'not running' as fatal.",+ "ready_signed": "READY - drive Fusion (fusion_open_lbr / build_library_3d / etc.). APS cloud search is signed in too, so use fusion_aps_search for ~2s cloud lookups. Autodesk's MCP server is ",+ "ready_configured": "READY to drive Fusion, but APS CLOUD SEARCH IS NOT SIGNED IN - cloud file search will be slow/blocked. DO IT NOW, do not make the user log in a second time: call fusion_aps_signin RIGHT AFTER a Fusion sign-in, while the browser profile still has a warm Autodesk SSO session - it consents silently (no password, no 2FA). Then fusion_aps_search works. Autodesk's MCP server is ",+ "ready_unconfigured": "READY to drive Fusion. APS cloud search is NOT SET UP (no client ID) - cloud search is the slow path without it. Offer to set it up: fusion_aps_set_client_id then fusion_aps_signin (silent while the browser SSO session is warm). APS is how cloud search gets to ~2 seconds. Autodesk's MCP server is ",+ "mcp_off": "OFF - fusion_mcp_enable can turn it on for the user (~10s, drives the Preferences dialog).",+ "mcp_on": "LIVE (fusion_mcp_* verbs work).",+ "upd_off": " AUTO-UPDATE is OFF (the user opted out, sticky) - do NOT click Fusion's update prompts for them.",+ "stale_template": "STALE add-in: Fusion is running add-in v{reported} but this bridge bundles v{expected}. Newer verbs (e.g. fusion_aps_open/open_by_urn) can fail silently. Fix: fusion_stop then fusion_start - the bridge re-syncs the add-in from its cache on start (only possible with Fusion CLOSED).",+ "roundtrip_note_prefix": " NOTE: ",+ "roundtrip_note": "the add-in's HTTP server is up but its main thread did not execute a test command yet - not drivable this second",+ "upd_applied_template": " AUTO-UPDATE: Fusion was offering an update and I clicked {button} for the user IN THE BACKGROUND - it downloads now and the new build comes up on the next launch. TELL THE USER it is handled; they never need to click the nag. They can opt out with fusion_set_auto_update {enabled:false} (sticky).button",+ "fusion_update_hint_handled": "Fusion offered an update and it was clicked in the background - tell the user it is handled.",+ "fusion_update_hint_pending_template": "Fusion is OFFERING AN UPDATE (auto-update {state}). OFFER to run it: fusion_set_auto_update {enabled:true} or a one-off background click on the next sweep."+}\ No newline at end of file
rust/core/tests/golden/readiness.jsonadded+830
@@ -0,0 +1,830 @@+{+ "installed_not_running": {+  "inputs": {+   "addin": null,+   "apsConfigured": true,+   "apsSignedIn": true,+   "autoUpdate": true,+   "installed": true,+   "licensing": false,+   "mcp": false,+   "now": 1800000000.0,+   "prefs": {},+   "roundtrip": true,+   "running": false,+   "streaming": false,+   "title": "Home - Autodesk Fusion",+   "updating": false+  },+  "prefsAfter": {},+  "result": {+   "_hint": "Fusion is INSTALLED but not running - call fusion_start (blocks until the add-in is ready), then retry.",+   "bridgeVersion": "TEST",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": true,+   "installing": false,+   "kicadIngest": false,+   "lastReadyAt": null,+   "lastSignedInAt": null,+   "modelingOrientation": null,+   "ready": false,+   "running": false,+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   }+  }+ },+ "installing": {+  "inputs": {+   "addin": null,+   "apsConfigured": true,+   "apsSignedIn": true,+   "autoUpdate": true,+   "installed": false,+   "licensing": false,+   "mcp": false,+   "now": 1800000000.0,+   "prefs": {},+   "roundtrip": true,+   "running": false,+   "streaming": true,+   "title": "Home - Autodesk Fusion",+   "updating": false+  },+  "prefsAfter": {},+  "result": {+   "_hint": "Fusion install is STREAMING (10-30 min) - keep polling fusion_readiness until installed:true.",+   "bridgeVersion": "TEST",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": false,+   "installing": true,+   "kicadIngest": false,+   "lastReadyAt": null,+   "lastSignedInAt": null,+   "modelingOrientation": null,+   "ready": false,+   "running": false,+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   }+  }+ },+ "licensing": {+  "inputs": {+   "addin": "1.0.19",+   "apsConfigured": true,+   "apsSignedIn": true,+   "autoUpdate": true,+   "installed": true,+   "licensing": true,+   "mcp": false,+   "now": 1800000000.0,+   "prefs": {},+   "roundtrip": true,+   "running": true,+   "streaming": false,+   "title": "Home - Autodesk Fusion",+   "updating": false+  },+  "prefsAfter": {+   "launchStartedAt": 1800000000.0+  },+  "result": {+   "_hint": "Fusion is blocked by a SEAT/LICENSING dialog ('Active Sessions Exceeded' / 'Suspend Remote Session') - detected DETERMINISTICALLY as an OWNED POPUP of the main Fusion window (its title is just 'Fusion360' and it can be as short as 262px, so it is found by owner+parent-screenshot, NOT a size guess). This verb JUST TRIED to auto-resolve it in the BACKGROUND (UIA 'Continue', Suspend pre-selected so it reclaims the seat) but it is still up - it may be mid-render (the first UIA Invoke no-ops). Just POLL fusion_readiness again in a few seconds; it keeps auto-resolving. Never coordinate-click or ask the user, and never kill/relaunch (the server keeps the seat).",+   "addinStale": false,+   "addinVersion": "1.0.19",+   "bridgeVersion": "TEST",+   "expectedAddinVersion": "1.0.19",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": true,+   "installing": false,+   "kicadIngest": false,+   "lastReadyAt": null,+   "lastSignedInAt": null,+   "licensingDialog": true,+   "modelingOrientation": null,+   "progress": {+    "confidence": "measured",+    "elapsedSec": 30,+    "estimatedSec": 45,+    "etaSec": 15,+    "overdue": false,+    "percent": 40,+    "phase": "launch",+    "startKind": "cold",+    "stepLabel": "Clearing the Autodesk seat dialog"+   },+   "ready": false,+   "running": true,+   "statusVerb": "fusion_readiness",+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   }+  }+ },+ "needs_signin": {+  "inputs": {+   "addin": null,+   "apsConfigured": true,+   "apsSignedIn": true,+   "autoUpdate": true,+   "installed": true,+   "licensing": false,+   "mcp": false,+   "now": 1800000000.0,+   "prefs": {},+   "roundtrip": true,+   "running": true,+   "streaming": false,+   "title": "Signing in - Autodesk Fusion",+   "updating": false+  },+  "prefsAfter": {+   "launchStartedAt": 1800000000.0+  },+  "result": {+   "_hint": "Fusion is RUNNING but stuck at the first-run Autodesk SIGN-IN (main UI blocked, add-in cannot serve) - NOT ready. FULL PLAYBOOK: the `fusion-autodesk-signin` skill. Proven sequence + the pitfalls that cost hours on a fresh Hyper-V VM (John 2026-07-14): (1) BLANK white webview on first launch? fusion_stop then fusion_start - the sign-in renders 'Welcome to Fusion' + a 'Sign In' button only after a clean relaunch. (2) ASK the user FIRST (AskUserQuestion): do they have a warm Google/Apple/Microsoft session for the account LINKED to their Autodesk login? Offer to drive it. A truly fresh box has NO warm browser session, so 'Continue with Google' still hits a password wall. (3) Click Fusion 'Sign In' -> it opens the DEFAULT browser (Edge) to the OAuth. Edge windows DO exist even if PowerShell EnumWindows looks empty - that is CLIXML progress noise; set $ProgressPreference='SilentlyContinue', and just desktop_screenshot_window the Edge hwnd (PrintWindow works backgrounded). (4) In the browser: 'Continue with Google' -> type the user's EMAIL (not secret) -> if it asks for a PASSWORD/2FA, STOP and fusion_notify_owner {title, body} to toast their MAIN computer so THEY type it; NEVER enter password/2FA yourself. (5) Handoff back to Fusion is via the 'Autodesk Identity Manager' protocol - Edge shows a 'This site is trying to open...' overlay: tick 'Always allow' + click 'Open'. (6) SPEED MATTERS: the sign-in code EXPIRES in ~2 min; on a high-latency VM slow screenshot/click round-trips let it expire ('Sign-in request expired') and you loop. Minimize steps; if it expires, click Fusion 'Sign In' again for a FRESH code (the browser session stays warm). Then poll until ready:true. FIRST-TIME DEMO? Run fusion_demo instead - it drives this sign-in through the user's NATIVE browser (already signed into Autodesk) and then gives them a real guided tour (project -> schematic -> 2D board -> 3D board).",+   "bridgeVersion": "TEST",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": true,+   "installing": false,+   "kicadIngest": false,+   "lastReadyAt": null,+   "lastSignedInAt": null,+   "modelingOrientation": null,+   "needsSignin": true,+   "progress": {+    "confidence": "measured",+    "elapsedSec": 30,+    "estimatedSec": 45,+    "etaSec": 15,+    "overdue": false,+    "percent": 40,+    "phase": "launch",+    "startKind": "cold",+    "stepLabel": "Signing in to Autodesk"+   },+   "ready": false,+   "running": true,+   "statusVerb": "fusion_readiness",+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   }+  }+ },+ "not_installed": {+  "inputs": {+   "addin": null,+   "apsConfigured": true,+   "apsSignedIn": true,+   "autoUpdate": true,+   "installed": false,+   "licensing": false,+   "mcp": false,+   "now": 1800000000.0,+   "prefs": {},+   "roundtrip": true,+   "running": false,+   "streaming": false,+   "title": "Home - Autodesk Fusion",+   "updating": false+  },+  "prefsAfter": {},+  "result": {+   "_hint": "Fusion 360 is NOT installed. OFFER to install it for the user, then call fusion_install_fusion (no shell approval needed) - it streams the free trial and this verb reports installing:true until done.",+   "bridgeVersion": "TEST",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": false,+   "installing": false,+   "kicadIngest": false,+   "lastReadyAt": null,+   "lastSignedInAt": null,+   "modelingOrientation": null,+   "ready": false,+   "running": false,+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   }+  }+ },+ "ready_after_launch": {+  "inputs": {+   "addin": "1.0.19",+   "apsConfigured": true,+   "apsSignedIn": true,+   "autoUpdate": true,+   "installed": true,+   "licensing": false,+   "mcp": false,+   "now": 1800000000.0,+   "prefs": {+    "launchHealAttempts": 1,+    "launchStartedAt": 1799999960.0+   },+   "roundtrip": true,+   "running": true,+   "streaming": false,+   "title": "Home - Autodesk Fusion",+   "updating": false+  },+  "prefsAfter": {+   "lastReadyAt": "2026-09-03T14:00:00",+   "lastSignedInAt": "2026-09-03T14:00:00",+   "launchHealAttempts": 0,+   "launchHealedAt": null,+   "launchStartedAt": null+  },+  "result": {+   "_hint": "READY - drive Fusion (fusion_open_lbr / build_library_3d / etc.). APS cloud search is signed in too, so use fusion_aps_search for ~2s cloud lookups. Autodesk's MCP server is OFF - fusion_mcp_enable can turn it on for the user (~10s, drives the Preferences dialog).",+   "addinStale": false,+   "addinVersion": "1.0.19",+   "bridgeVersion": "TEST",+   "expectedAddinVersion": "1.0.19",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": true,+   "installing": false,+   "kicadIngest": false,+   "lastReadyAt": "2026-09-03T14:00:00",+   "lastSignedInAt": "2026-09-03T14:00:00",+   "modelingOrientation": null,+   "ready": true,+   "running": true,+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   }+  }+ },+ "ready_configured": {+  "inputs": {+   "addin": "1.0.19",+   "apsConfigured": true,+   "apsSignedIn": false,+   "autoUpdate": true,+   "installed": true,+   "licensing": false,+   "mcp": false,+   "now": 1800000000.0,+   "prefs": {},+   "roundtrip": true,+   "running": true,+   "streaming": false,+   "title": "Home - Autodesk Fusion",+   "updating": false+  },+  "prefsAfter": {+   "lastReadyAt": "2026-09-03T14:00:00",+   "lastSignedInAt": "2026-09-03T14:00:00",+   "launchHealAttempts": 0,+   "launchHealedAt": null+  },+  "result": {+   "_hint": "READY to drive Fusion, but APS CLOUD SEARCH IS NOT SIGNED IN - cloud file search will be slow/blocked. DO IT NOW, do not make the user log in a second time: call fusion_aps_signin RIGHT AFTER a Fusion sign-in, while the browser profile still has a warm Autodesk SSO session - it consents silently (no password, no 2FA). Then fusion_aps_search works. Autodesk's MCP server is OFF - fusion_mcp_enable can turn it on for the user (~10s, drives the Preferences dialog).",+   "addinStale": false,+   "addinVersion": "1.0.19",+   "bridgeVersion": "TEST",+   "expectedAddinVersion": "1.0.19",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": true,+   "installing": false,+   "kicadIngest": false,+   "lastReadyAt": "2026-09-03T14:00:00",+   "lastSignedInAt": "2026-09-03T14:00:00",+   "modelingOrientation": null,+   "ready": true,+   "running": true,+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   }+  }+ },+ "ready_roundtrip_fail": {+  "inputs": {+   "addin": "1.0.19",+   "apsConfigured": true,+   "apsSignedIn": true,+   "autoUpdate": true,+   "installed": true,+   "licensing": false,+   "mcp": false,+   "now": 1800000000.0,+   "prefs": {},+   "roundtrip": false,+   "running": true,+   "streaming": false,+   "title": "Home - Autodesk Fusion",+   "updating": false+  },+  "prefsAfter": {+   "launchStartedAt": 1800000000.0+  },+  "result": {+   "_hint": "READY - drive Fusion (fusion_open_lbr / build_library_3d / etc.). APS cloud search is signed in too, so use fusion_aps_search for ~2s cloud lookups. Autodesk's MCP server is OFF - fusion_mcp_enable can turn it on for the user (~10s, drives the Preferences dialog). NOTE: the add-in's HTTP server is up but its main thread did not execute a test command yet - not drivable this second",+   "addinStale": false,+   "addinVersion": "1.0.19",+   "bridgeVersion": "TEST",+   "expectedAddinVersion": "1.0.19",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": true,+   "installing": false,+   "kicadIngest": false,+   "lastReadyAt": null,+   "lastSignedInAt": null,+   "modelingOrientation": null,+   "progress": {+    "confidence": "measured",+    "elapsedSec": 30,+    "estimatedSec": 45,+    "etaSec": 15,+    "overdue": false,+    "percent": 40,+    "phase": "launch",+    "startKind": "cold",+    "stepLabel": "Fusion is up - waiting for the Adom add-in to serve"+   },+   "ready": false,+   "running": true,+   "statusVerb": "fusion_readiness",+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   }+  }+ },+ "ready_signed": {+  "inputs": {+   "addin": "1.0.19",+   "apsConfigured": true,+   "apsSignedIn": true,+   "autoUpdate": true,+   "installed": true,+   "licensing": false,+   "mcp": false,+   "now": 1800000000.0,+   "prefs": {},+   "roundtrip": true,+   "running": true,+   "streaming": false,+   "title": "Home - Autodesk Fusion",+   "updating": false+  },+  "prefsAfter": {+   "lastReadyAt": "2026-09-03T14:00:00",+   "lastSignedInAt": "2026-09-03T14:00:00",+   "launchHealAttempts": 0,+   "launchHealedAt": null+  },+  "result": {+   "_hint": "READY - drive Fusion (fusion_open_lbr / build_library_3d / etc.). APS cloud search is signed in too, so use fusion_aps_search for ~2s cloud lookups. Autodesk's MCP server is OFF - fusion_mcp_enable can turn it on for the user (~10s, drives the Preferences dialog).",+   "addinStale": false,+   "addinVersion": "1.0.19",+   "bridgeVersion": "TEST",+   "expectedAddinVersion": "1.0.19",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": true,+   "installing": false,+   "kicadIngest": false,+   "lastReadyAt": "2026-09-03T14:00:00",+   "lastSignedInAt": "2026-09-03T14:00:00",+   "modelingOrientation": null,+   "ready": true,+   "running": true,+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   }+  }+ },+ "ready_signed_mcp": {+  "inputs": {+   "addin": "1.0.19",+   "apsConfigured": true,+   "apsSignedIn": true,+   "autoUpdate": true,+   "installed": true,+   "licensing": false,+   "mcp": true,+   "now": 1800000000.0,+   "prefs": {},+   "roundtrip": true,+   "running": true,+   "streaming": false,+   "title": "Home - Autodesk Fusion",+   "updating": false+  },+  "prefsAfter": {+   "lastReadyAt": "2026-09-03T14:00:00",+   "lastSignedInAt": "2026-09-03T14:00:00",+   "launchHealAttempts": 0,+   "launchHealedAt": null+  },+  "result": {+   "_hint": "READY - drive Fusion (fusion_open_lbr / build_library_3d / etc.). APS cloud search is signed in too, so use fusion_aps_search for ~2s cloud lookups. Autodesk's MCP server is LIVE (fusion_mcp_* verbs work).",+   "addinStale": false,+   "addinVersion": "1.0.19",+   "bridgeVersion": "TEST",+   "expectedAddinVersion": "1.0.19",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": true,+   "installing": false,+   "kicadIngest": false,+   "lastReadyAt": "2026-09-03T14:00:00",+   "lastSignedInAt": "2026-09-03T14:00:00",+   "modelingOrientation": null,+   "ready": true,+   "running": true,+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   }+  }+ },+ "ready_stale": {+  "inputs": {+   "addin": "1.0.16",+   "apsConfigured": true,+   "apsSignedIn": true,+   "autoUpdate": true,+   "installed": true,+   "licensing": false,+   "mcp": false,+   "now": 1800000000.0,+   "prefs": {},+   "roundtrip": true,+   "running": true,+   "streaming": false,+   "title": "Home - Autodesk Fusion",+   "updating": false+  },+  "prefsAfter": {+   "lastReadyAt": "2026-09-03T14:00:00",+   "lastSignedInAt": "2026-09-03T14:00:00",+   "launchHealAttempts": 0,+   "launchHealedAt": null+  },+  "result": {+   "_hint": "STALE add-in: Fusion is running add-in v1.0.16 but this bridge bundles v1.0.19. Newer verbs (e.g. fusion_aps_open/open_by_urn) can fail silently. Fix: fusion_stop then fusion_start - the bridge re-syncs the add-in from its cache on start (only possible with Fusion CLOSED).",+   "_staleHint": "STALE add-in: Fusion is running add-in v1.0.16 but this bridge bundles v1.0.19. Newer verbs (e.g. fusion_aps_open/open_by_urn) can fail silently. Fix: fusion_stop then fusion_start - the bridge re-syncs the add-in from its cache on start (only possible with Fusion CLOSED).",+   "addinStale": true,+   "addinVersion": "1.0.16",+   "bridgeVersion": "TEST",+   "expectedAddinVersion": "1.0.19",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": true,+   "installing": false,+   "kicadIngest": false,+   "lastReadyAt": "2026-09-03T14:00:00",+   "lastSignedInAt": "2026-09-03T14:00:00",+   "modelingOrientation": null,+   "ready": true,+   "running": true,+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   }+  }+ },+ "ready_unconfigured": {+  "inputs": {+   "addin": "1.0.19",+   "apsConfigured": false,+   "apsSignedIn": false,+   "autoUpdate": true,+   "installed": true,+   "licensing": false,+   "mcp": false,+   "now": 1800000000.0,+   "prefs": {},+   "roundtrip": true,+   "running": true,+   "streaming": false,+   "title": "Home - Autodesk Fusion",+   "updating": false+  },+  "prefsAfter": {+   "lastReadyAt": "2026-09-03T14:00:00",+   "lastSignedInAt": "2026-09-03T14:00:00",+   "launchHealAttempts": 0,+   "launchHealedAt": null+  },+  "result": {+   "_hint": "READY to drive Fusion. APS cloud search is NOT SET UP (no client ID) - cloud search is the slow path without it. Offer to set it up: fusion_aps_set_client_id then fusion_aps_signin (silent while the browser SSO session is warm). APS is how cloud search gets to ~2 seconds. Autodesk's MCP server is OFF - fusion_mcp_enable can turn it on for the user (~10s, drives the Preferences dialog).",+   "addinStale": false,+   "addinVersion": "1.0.19",+   "bridgeVersion": "TEST",+   "expectedAddinVersion": "1.0.19",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": true,+   "installing": false,+   "kicadIngest": false,+   "lastReadyAt": "2026-09-03T14:00:00",+   "lastSignedInAt": "2026-09-03T14:00:00",+   "modelingOrientation": null,+   "ready": true,+   "running": true,+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   }+  }+ },+ "ready_upd_off": {+  "inputs": {+   "addin": "1.0.19",+   "apsConfigured": true,+   "apsSignedIn": true,+   "autoUpdate": false,+   "installed": true,+   "licensing": false,+   "mcp": false,+   "now": 1800000000.0,+   "prefs": {},+   "roundtrip": true,+   "running": true,+   "streaming": false,+   "title": "Home - Autodesk Fusion",+   "updating": false+  },+  "prefsAfter": {+   "lastReadyAt": "2026-09-03T14:00:00",+   "lastSignedInAt": "2026-09-03T14:00:00",+   "launchHealAttempts": 0,+   "launchHealedAt": null+  },+  "result": {+   "_hint": "READY - drive Fusion (fusion_open_lbr / build_library_3d / etc.). APS cloud search is signed in too, so use fusion_aps_search for ~2s cloud lookups. Autodesk's MCP server is OFF - fusion_mcp_enable can turn it on for the user (~10s, drives the Preferences dialog). AUTO-UPDATE is OFF (the user opted out, sticky) - do NOT click Fusion's update prompts for them.",+   "addinStale": false,+   "addinVersion": "1.0.19",+   "bridgeVersion": "TEST",+   "expectedAddinVersion": "1.0.19",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": true,+   "installing": false,+   "kicadIngest": false,+   "lastReadyAt": "2026-09-03T14:00:00",+   "lastSignedInAt": "2026-09-03T14:00:00",+   "modelingOrientation": null,+   "ready": true,+   "running": true,+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   }+  }+ },+ "running_addin_silent": {+  "inputs": {+   "addin": null,+   "apsConfigured": true,+   "apsSignedIn": true,+   "autoUpdate": true,+   "installed": true,+   "licensing": false,+   "mcp": false,+   "now": 1800000000.0,+   "prefs": {},+   "roundtrip": true,+   "running": true,+   "streaming": false,+   "title": "Home - Autodesk Fusion",+   "updating": false+  },+  "prefsAfter": {+   "launchStartedAt": 1800000000.0+  },+  "result": {+   "_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 add-in did not load (fusion_stop then fusion_start to re-deploy + re-load it). Do NOT fire modeling/export verbs until ready:true.",+   "bridgeVersion": "TEST",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": true,+   "installing": false,+   "kicadIngest": false,+   "lastReadyAt": null,+   "lastSignedInAt": null,+   "modelingOrientation": null,+   "progress": {+    "confidence": "measured",+    "elapsedSec": 30,+    "estimatedSec": 45,+    "etaSec": 15,+    "overdue": false,+    "percent": 40,+    "phase": "launch",+    "startKind": "cold",+    "stepLabel": "Fusion is up - waiting for the Adom add-in to serve"+   },+   "ready": false,+   "running": true,+   "statusVerb": "fusion_readiness",+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   }+  }+ },+ "running_addin_silent_launching": {+  "inputs": {+   "addin": null,+   "apsConfigured": true,+   "apsSignedIn": true,+   "autoUpdate": true,+   "installed": true,+   "licensing": false,+   "mcp": false,+   "now": 1800000000.0,+   "prefs": {+    "launchStartedAt": 1799999970.0+   },+   "roundtrip": true,+   "running": true,+   "streaming": false,+   "title": "Home - Autodesk Fusion",+   "updating": false+  },+  "prefsAfter": {+   "launchStartedAt": 1799999970.0+  },+  "result": {+   "_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 add-in did not load (fusion_stop then fusion_start to re-deploy + re-load it). Do NOT fire modeling/export verbs until ready:true.",+   "bridgeVersion": "TEST",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": true,+   "installing": false,+   "kicadIngest": false,+   "lastReadyAt": null,+   "lastSignedInAt": null,+   "modelingOrientation": null,+   "progress": {+    "confidence": "measured",+    "elapsedSec": 30,+    "estimatedSec": 45,+    "etaSec": 15,+    "overdue": false,+    "percent": 40,+    "phase": "launch",+    "startKind": "cold",+    "stepLabel": "Fusion is up - waiting for the Adom add-in to serve"+   },+   "ready": false,+   "running": true,+   "statusVerb": "fusion_readiness",+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   }+  }+ },+ "updating": {+  "inputs": {+   "addin": null,+   "apsConfigured": true,+   "apsSignedIn": true,+   "autoUpdate": true,+   "installed": true,+   "licensing": false,+   "mcp": false,+   "now": 1800000000.0,+   "prefs": {},+   "roundtrip": true,+   "running": true,+   "streaming": false,+   "title": "Home - Autodesk Fusion",+   "updating": true+  },+  "prefsAfter": {+   "launchStartedAt": 1800000000.0+  },+  "result": {+   "_hint": "Fusion is applying an AUTO-UPDATE (multiple webdeploy builds present) and RESTARTS ITSELF every ~30-60s - this is NOT a crash or a bridge failure. Poll fusion_readiness; it stabilizes to ready:true once the update finishes (can take 10-30 min). Meanwhile, wrap modeling/export verbs in a short retry/catch-loop to ride the up-windows rather than treating an intermittent 'not running' as fatal.",+   "bridgeVersion": "TEST",+   "fusionUser": null,+   "hostApp": "Fusion 360",+   "installed": true,+   "installing": false,+   "kicadIngest": false,+   "lastReadyAt": null,+   "lastSignedInAt": null,+   "modelingOrientation": null,+   "progress": {+    "confidence": "measured",+    "elapsedSec": 30,+    "estimatedSec": 45,+    "etaSec": 15,+    "overdue": false,+    "percent": 40,+    "phase": "launch",+    "startKind": "cold",+    "stepLabel": "Applying a Fusion auto-update"+   },+   "ready": false,+   "running": true,+   "statusVerb": "fusion_readiness",+   "success": true,+   "timings": {+    "cold": {+     "count": 4,+     "lastAt": "2026-09-01T10:00:00",+     "p50": 45.0,+     "p90": 53.0+    }+   },+   "updating": true+  }+ }+}\ No newline at end of file
rust/core/tests/readiness_golden.rsadded+46
@@ -0,0 +1,46 @@+//! Every scenario in tests/golden/readiness.json was produced by the Python handler with its+//! helpers stubbed; the Rust computation must return the same result and the same prefs.+use fusion_core::readiness::{compute, Inputs};+use serde_json::{json, Map, Value};+use std::fs;++fn inputs_from(sc: &Value) -> Inputs {+    let i = &sc["inputs"];+    let prefs: Map<String, Value> = i["prefs"].as_object().cloned().unwrap_or_default();+    Inputs {+        installed_raw: i["installed"].as_bool().unwrap(), streaming: i["streaming"].as_bool().unwrap(), running: i["running"].as_bool().unwrap(),+        addin_version: i["addin"].as_str().map(str::to_string), expected_addin_version: "1.0.19".into(),+        needs_signin: { let t = i["title"].as_str().unwrap_or("").to_lowercase(); t.contains("signing in") || t.contains("welcome to fusion") || (t.contains("sign in - autodesk") && t.contains("fusion")) },+        licensing_dialog: i["licensing"].as_bool().unwrap(), updating: i["updating"].as_bool().unwrap(),+        aps_configured: i["apsConfigured"].as_bool().unwrap(), aps_signed_in: i["apsSignedIn"].as_bool().unwrap(),+        mcp_port_open: i["mcp"].as_bool().unwrap(), auto_update_enabled: i["autoUpdate"].as_bool().unwrap(), roundtrip_ok: i["roundtrip"].as_bool().unwrap(),+        kicad_ingest: false, fusion_user: None, modeling_orientation: None, bridge_version: "TEST".into(),+        prefs, now: i["now"].as_f64().unwrap(), now_iso: "2026-09-03T14:00:00".into(),+        timings_launch: json!({"cold": {"p50": 45.0, "p90": 53.0, "count": 4, "lastAt": "2026-09-01T10:00:00"}}),+        launch_stats: json!({"p50": 45.0, "p90": 53.0, "count": 4}),+        progress_override: Some(json!({"phase": "launch", "stepLabel": "", "percent": 40, "elapsedSec": 30, "estimatedSec": 45, "etaSec": 15, "confidence": "measured", "startKind": "cold", "overdue": false})),+        addin_start_failed: None, crash_dialogs_cleared: 0, update_applied_button: None, update_offered: false,+    }+}++#[test]+fn readiness_matches_the_python_handler_in_every_scenario() {+    let g: Value = serde_json::from_str(&fs::read_to_string(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/golden/readiness.json")).unwrap()).unwrap();+    let mut n = 0;+    for (name, sc) in g.as_object().unwrap() {+        let out = compute(&inputs_from(sc));+        let want = &sc["result"];+        let mut got = out.result.clone();+        // fusionUser is a live cache in Python; both are null in the goldens+        if got != *want {+            let (a, b) = (got.as_object_mut().unwrap(), want.as_object().unwrap());+            for k in a.keys().cloned().collect::<Vec<_>>().iter().chain(b.keys()) {+                if a.get(k) != b.get(k) { panic!("{name}: key {k}\n  rust:   {}\n  python: {}", a.get(k).map(|v| v.to_string()).unwrap_or("<absent>".into()), b.get(k).map(|v| v.to_string()).unwrap_or("<absent>".into())); }+            }+        }+        let want_prefs: Map<String, Value> = sc["prefsAfter"].as_object().cloned().unwrap_or_default();+        assert_eq!(Value::Object(out.prefs_after.clone()), Value::Object(want_prefs), "{name}: prefs after");+        n += 1;+    }+    assert!(n >= 16, "{n} scenarios");+}