# Changelog

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.58 - 2026-07-08

- **Long cloud ops now SURVIVE AD's 60s relay cap (John: "get all 4 pins perfect" + "update your code
  with your learnings").** `fusion_build_library_3d` cloud Package3D + Hub upload takes MINUTES, but the
  relay hard-caps requests at ~60s and `timeoutSeconds` is ignored - so the synchronous build got its
  thread killed at 60s, wrote no bound .lbr, and lost every wip_urn (a demo board's 4th pin came back a
  flat pad). It now runs DETACHED (`async` default true) + is POLLABLE: returns immediately, keeps
  building in a daemon thread, writes the bound .lbr when done; caller polls it for one `<package3d
  wip_urn=urn:...>` per part. `async:false` for fast machines.
- **Background, no-dialog EAGLE board import documented + steered to.** For a `.brd` with placed
  `<elements>`, `fusion_open_board` (newDesignFromLocal) instantiates them with ZERO modal dialogs / ZERO
  foreground - vs `fusion_new_electronics_from_eagle`'s ImportSCHAndBRDCmd, which pops two Open dialogs
  that STEAL focus. The import verb no longer dead-ends when the AD direct API is absent; it points to
  `fusion_open_board`. NEW: embed `<packages3d>` + a per-`<element>` `package3d_urn` in the `.brd` to get
  REAL 3D component bodies on the board (Fusion resolves the Hub urns) - documented in the verb docstring.
- **Fixed the HWND-truncation window-finder bug (cost hours).** `handlers/fusion_ui.py` declared NO
  ctypes argtypes, so 64-bit HWND handles truncated to 32-bit `c_int` - `_find_fusion_hwnd`'s
  GetWindowTextW/IsWindowVisible ran on garbage handles and matched nothing, so `get_fusion_window_info`
  returned `mainHwnd:None` even though Fusion's window plainly existed, blinding all background driving.
  Declared HWND argtypes/restypes for the window funcs so the full handle marshals.

## 1.6.57 - 2026-07-08

- **APS sign-in now REMEMBERS the browser+profile that authed to Autodesk and reuses it, instead of
  firing `webbrowser.open` at the OS default (John: "power users have tons of browsers with tons of
  profiles ... cache what browser you used when you successfully logged in ... don't be lazy").** The
  old code blindly opened the OS default browser, which on a multi-browser box is usually the wrong
  one (no warm Autodesk/SSO session / wrong profile) and forces a needless fresh login. Now
  `start_signin`: (1) launches a browser+profile passed in the call, else (2) the REMEMBERED one
  (`~/.adom/fusion-aps/signin-browser.json`) via a real `--profile-directory`/`--new-window` launch,
  else (3) opens NOTHING and returns `needsExtensionOpen:true` so the caller drives `authUrl` in the
  user's real browser via `nbrowser_open_window` (new window) — the OS default is only used when the
  caller explicitly passes `allowDefaultBrowser:true`. New verbs `fusion_aps_set_browser` /
  `fusion_aps_get_browser` / `fusion_aps_forget_browser` persist the choice across sessions.
- **Notify-before-foreground, baked into code (John: "if you have no other choice than to
  foreground, let me know with an ad notify ... do this via code").** SendInput key/click paths
  need Fusion in the FOREGROUND, which steals the user's focus mid-work. `_notify_before_foreground()`
  now fires an AD notify (reach_user, debounced to one per 45s burst) BEFORE `fusion_send_key` /
  `fusion_click_fusion` foreground Fusion, telling the user WHY, reminding that Adom ALWAYS drives in
  the background and only foregrounds as a last resort, and ribbing Fusion for not being AI-native
  enough to allow background driving (hoping Autodesk makes it fully AI-drivable soon). Prefer the
  background path first (e.g. `fusion_open_board`'s dialog-free `newDesignFromLocal`, `desktop_ui_*`,
  `WM_CLOSE`).

## 1.6.55 - 2026-07-07

- **Universal error-dialog guard, baked into every mutating/open verb (John: "you have to
  screenshot to get the parent/child array to constantly look for errors ... bake this into every
  command").** A malformed `.lbr` makes Fusion pop an owned "Error" dialog ("<file>.lbr has errors
  and cannot be opened") while `Document.newDesignFromLocal` still returns ok (it opened an EMPTY
  design behind the error), so `open_lbr` reported success on a library that never loaded. New
  `operation_error` dialog classification (title "Error" / "cannot be opened" / "has errors") + a
  shared `_apply_failure_dialogs()` now runs after every open/mutating verb: it screenshots the
  owned popup (hwnd-targeted, background), flips the result to `success:false` +
  `errorCode:fusion_operation_error` with the exact resolution, attaches the shot to
  `data.errorDialog`, and dismisses the pure-ack popup so it can't wedge the next command. A failed
  op can no longer be returned as success.

## 1.6.54 - 2026-07-07

- **Fixed 3D auto-orient laying TALL through-hole parts on their side** (John caught it: a machine
  pin's 3D came in horizontal). `make_3d_package` / `attach_3d_package` / `build_library_3d` always
  rotated the SMALLEST bbox dim to Z (correct for a flat SMD chip, wrong for a tall pin whose long
  axis is already Z). Now a tall-part guard keeps a part vertical when its largest dimension is
  already Z, and only flattens a clearly-thin part whose thin axis isn't Z yet. Verified: machine
  pins stay vertical, a flat 0603 stays flat, a chip imported on its edge still gets flattened.
- `attach_3d_package` and `build_library_3d` parts[] now honor an `orient` flag (default true) so a
  caller can skip auto-orient for an already-correctly-oriented STEP.

## 1.6.53 - 2026-07-07

- **Fixed slow-machine opens being mislabeled "add-in not running" (caught live on a GPU-less Azure
  VM).** `_orchestrate_open_lbr`'s `Document.newDesignFromLocal` call had a fixed 30s read timeout;
  on software-rendered machines the open takes 60s+, so the proxy timed out and the canned error
  claimed the ADD-IN was down while the document was actually opening fine -
  `fusion_build_library_3d` then aborted every part on a lie. Now: 120s timeout AND on any failure
  the open is VERIFIED BY STATE (poll `get_app_state` for the expected document) before failing -
  the doc actually being open outranks the synchronous return.
- **NEW verb `fusion_notify_owner` - deterministic last-resort human escalation (John's rule).**
  Toasts the user's MAIN computer by fanning out cross-AD to every relay peer, so a bridge on an
  unattended VM still reaches them. For true human walls only (password/2FA, UAC); hints tell the
  driving AI to exhaust programmatic options first and not to re-toast.
- **Hints now teach the parent/child screenshot discipline**: never poll blind, never fullscreen -
  `desktop_screenshot_window` returns a `screenshots[]` array where every OWNED POPUP rides back as
  its own child shot; launch/sign-in hints now say to READ those.

## 1.6.48-1.6.52 - 2026-07-06 (pkg hygiene, no runtime change)

- Declared the `adom/adom-desktop` pkg dependency (SDK two-artifact audit, #83).
- Trimmed the install tarball from ~1.18 MB to ~90 KB: dropped the 1 MB `hero.png` (a wiki
  billboard served from `/files`, not an install asset) and the publish-channel `PUBLISHING.md`.
- De-duplicated the library docs: `MAKING_LIBRARIES.md` / `LIBRARY_FINDINGS.md` had drifted root
  copies (broken relative image links) alongside the canonical `fusion-libraries` skill copies;
  removed the root copies. Moved `CREATING_BASIC_PARTS_LIBRARIES.md` into the `fusion-multipart-libraries`
  skill and fixed the cross-skill references.
- Split this changelog: recent versions ship in the tarball, older entries moved to
  `CHANGELOG_ARCHIVE.md` (kept on the wiki, not shipped).

## 1.6.47 - 2026-07-06

- **Tightened seat-dialog detection to DIALOG-SHAPED owned popups (fixes a false positive from
  1.6.46).** 1.6.46 matched any owned popup wider than 300px, but Fusion's own side panels (Data
  Panel / browser rail, ~300x785) are owned popups too - so the moment a document was open,
  `find_licensing_dialog()` flagged a drivable Fusion as "blocked by a seat dialog" and readiness
  reported ready:false forever (caught live on winvm: get_app_state succeeded while readiness lied).
  The seat modal is always WIDE (823x262 / 1235x527); side panels are narrow. Detection now requires
  width >= 480, so the panels are excluded and the real dialog still matches.

## 1.6.46 - 2026-07-06

- **Fixed the seat/licensing dialog going UNDETECTED (John caught it live on winvm).** The
  "Suspend Remote Session" seat-confirm variant is only **823x262**, but `find_licensing_dialog()`
  required height **320-820**, so it silently slipped through - readiness reported `licensingDialog`
  gone while the dialog sat there blocking the whole launch. Root cause: detecting a modal by a
  fragile SIZE window. Now detected DETERMINISTICALLY by the **owned-popup** signal: a seat/sign-in
  modal is always an OWNED POPUP of the main Fusion window (the splash/main app never are), found via
  `GetWindow(GW_OWNER)` in `family_windows()`. Size is only a fallback.
- **Resolution is now SCREENSHOT-VERIFIED, not heuristic-verified.** `_resolve_seat_dialog()` clicks
  'Continue' (background UIA) then confirms the dialog truly cleared by reading the parent window's
  `ownedPopupCount` via `desktop_screenshot_window` (parent/child screenshotting) - instead of trusting
  the same size filter that had false-'resolved' it. This is the check that would have caught the
  earlier "I solved it" that hadn't.
- **Closed the auto-resolve GAP.** The launch loop only auto-resolved the seat dialog during its ~50s
  window. But the dialog also appears LATER (minutes post-sign-in, when the license server notices too
  many sessions), long after that loop exited. `fusion_readiness` now SELF-HEALS: whenever it detects
  the dialog it auto-resolves it in the background and re-probes - so the seat is handled in CODE no
  matter when it shows up, and the AI never has to notice or act.
- ctypes `GetWindow` given explicit `restype/argtypes` (HWND) to avoid 64-bit handle truncation.

## 1.6.45 - 2026-07-06

- **Add-in changes now AUTO-DEPLOY - the bridge no longer only installs the add-in when it's MISSING.**
  Startup guarded `install_addin()` behind `if not addin_installed`, so a STALE add-in was never
  UPDATED: any add-in fix (e.g. the 1.6.44 get_parameters fix) failed to reach users who already had a
  copy - it silently kept running the old add-in. Now the bridge ALWAYS syncs on startup (idempotent -
  copies only changed files; safe while Fusion is up, lands on the next respawn with Fusion closed).
  This is a real distribution bug - without it, none of our add-in fixes shipped to existing installs.

## 1.6.44 - 2026-07-06 (add-in 1.0.4)

- **Fixed `fusion_get_parameters` crashing on non-numeric parameters** (found during the laptop verb
  battery). `Parameter.value` throws "Parameter is not numeric type" for string/text params, which
  blew up the entire enumeration. Every field is now read defensively so one odd parameter never kills
  the call. Add-in bumped to 1.0.4.

## 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
  port** (the bug that made 1.6.40 look solved when it wasn't - John caught it live). Two fixes: (1) the
  first UIA `Continue` Invoke on a mid-render dialog silently NO-OPs, so the launch loop now re-invokes
  every tick until `find_licensing_dialog` no longer sees it, instead of clicking once; (2) the loop
  and `fusion_readiness` no longer trust the add-in probe while the seat dialog is up - a STALE add-in
  server from a prior killed instance keeps port 8774 answering, which made the loop break early and
  readiness report `licensingDialog:false`/`ready:*` while the dialog sat right there on screen. Seat
  dialog now blocks `ready` whenever detected. Verified: full verb battery passes on the laptop
  (modeling, EPG+etch->STEP 234KB, export 234KB, dark mode) once the seat dialog is auto-cleared.

## 1.6.41 - 2026-07-06

- **Instant-minimize: disable the minimize ANIMATION during launch** so the focus-guard's minimize is
  a single-frame vanish, not the visible slide-down that read as a 'flash' (John). Combined with the
  1.6.40 seat auto-resolve (no more stuck sign-in retry loop = no repeated foregrounding) and the
  foreground-lock, a background launch should now be imperceptible. `SPI_SETANIMATION` off during the
  guard window, restored to the user's original value when it ends. (Chosen over virtual-desktop
  isolation, which proved un-shippable: Win11 virtual-desktop control is undocumented, per-build, and
  the driver won't even install cleanly - it would make the bridge brittle across Windows updates.)

## 1.6.40 - 2026-07-06

- **THE seat-dialog fix: `fusion_start` now auto-resolves "Active Sessions Exceeded" via UIA in the
  BACKGROUND** (John pushed me back to UIA - it was the answer). The launch handler is now a
  deterministic state loop: poll the add-in round-trip (ground truth for drivable) while, each tick,
  detecting the seat dialog (`find_licensing_dialog`, by process+size) and invoking its native
  'Continue' button with `desktop_ui_click` via the AD direct API - which UIA-Invokes it WITHOUT
  foregrounding (proven: "Clicked in the BACKGROUND - the window did NOT come to the foreground").
  Suspend is pre-selected, so Continue grabs the license. This un-sticks the launch, which is what
  caused the endless foreground-stealing: a blocked seat dialog made Fusion retry sign-in in a loop,
  foregrounding every retry. No more kill+relaunch (the licensing server keeps the seat anyway) and
  no notify/punt to the user.

## 1.6.39 - 2026-07-06

- **Dev skill: added THE CORE GOAL** (John) - the entire reason this bridge exists is for the AI to
  run EVERYTHING so the user never does anything manual. Never ask the user to click/close/sign-in;
  the only acceptable walls are UAC/elevation and a password/2FA we must not enter. Any flow that
  still needs the user is the next bug to fix in the bridge, not the user's job. Reinforces detecting
  + handling state deterministically in code, not via the AI following skills.

## 1.6.38 - 2026-07-06

- **Deterministic, process-based detection of the seat/licensing dialog** (John: track state in CODE,
  not via the AI following skills). The "Active Sessions Exceeded" dialog's window TITLE is literally
  just "Fusion360" (the text is CEF body content), so the title-based classifier never caught it - it
  silently blocked Fusion's main thread for a whole session. New `family_windows()` enumerates visible
  top-level windows by OWNING PROCESS (Fusion360 / FusionLauncher / AdskIdentityManager), and
  `find_licensing_dialog()` finds the licensing modal by process + dialog size. `fusion_start` now
  auto-reclaims on it deterministically, and `fusion_readiness` reports `licensingDialog:true` with a
  clear hint instead of a vague "add-in not responding" hang.

## Older versions

Entries for **1.6.37 and earlier** live in [`CHANGELOG_ARCHIVE.md`](https://wiki.adom.inc/adom/adom-desktop-fusion-bridge) (full history,
kept on the wiki page's Files tab, not shipped in the install tarball). The per-release changelog
for every version is also on the page's **Releases** tab.
