← Commit history
BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.13.57\ No newline at end of file+1.13.58\ No newline at end of file
CHANGELOG.md+3−3
@@ -3,10 +3,10 @@ All notable changes to the Adom Bridge Fusion 360 bridge, newest first. Canonical: https://wiki.adom.inc/adom/fusion-bridge · GitHub backup: `adom-inc/fusion-bridge` -## 1.13.57 (2026-11-02)+## 1.13.58 (2026-11-02) -`fusion_edit_tracks` sets a fine grid before moving a track's end: without it MOVE snapped the end to the board's grid, the-readback found it elsewhere, and the edit was undone.+`fusion_edit_tracks` moves a track's end in a straight line: Fusion's default bend style made an angled move into two segments, so+the readback refused it and undid the edit. `fusion_sch_wire` and the wire `fusion_sch_swap_part` adds draw straight for the same reason.  ## 1.13.56 (2026-11-02) 
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "fusion360",   "displayName": "Autodesk Fusion 360",-  "version": "1.13.57",+  "version": "1.13.58",   "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/fusion-bridge",   "author": "Adom Inc.",
dashboard/Cargo.lock+1−1
@@ -4,7 +4,7 @@ version = 4  [[package]] name = "fusion-dashboard"-version = "1.13.57"+version = "1.13.58" dependencies = [  "serde_json", ]
dashboard/Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.13.57"+version = "1.13.58" edition = "2021"  [dependencies]
install.sh+1−1
@@ -21,7 +21,7 @@ echo "Installed fusion-bridge skills: $(ls "$PKG_DIR/skills" 2>/dev/null | tr '\ # binary (the three-artifact pattern: bridge zip -> ab, THIS pkg -> container skills + # this download, page media -> wiki). Per the wiki's PKG_VS_RELEASE policy, binaries are # platform-tagged RELEASE assets, never tarball content - the installer fetches them.-DASH_VERSION="1.13.57"+DASH_VERSION="1.13.58" DASH_URL="https://wiki.adom.inc/download/adom/fusion-bridge/${DASH_VERSION}/fusion-dashboard" DEST="" if [ -w /usr/local/bin ]; then DEST=/usr/local/bin
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "fusion-bridge",-  "version": "1.13.57",+  "version": "1.13.58",   "type": "app",   "description": "This package installs the bridge's SKILLS into your container so your AI knows how to drive it; Adom Bridge loads the bridge runtime itself from the release zip.",   "dependencies": {
page.json+2−2
@@ -1,6 +1,6 @@ {   "slug": "fusion-bridge",-  "version": "1.13.57",+  "version": "1.13.58",   "type": "app",   "description": "This package installs the bridge's SKILLS into your container so your AI knows how to drive it; Adom Bridge loads the bridge runtime itself from the release zip.",   "dependencies": {@@ -152,4 +152,4 @@   "confirmed_user_skills": [     "fusion-design-efficiency"   ]-}\ No newline at end of file+}
rust/Cargo.lock+5−5
@@ -19,7 +19,7 @@ dependencies = [  [[package]] name = "adom-fusion-bridge"-version = "1.13.57"+version = "1.13.58" dependencies = [  "adom-eda-checks",  "base64",@@ -299,7 +299,7 @@ dependencies = [  [[package]] name = "fusion-core"-version = "1.13.57"+version = "1.13.58" dependencies = [  "serde",  "serde_json",@@ -310,7 +310,7 @@ dependencies = [  [[package]] name = "fusion-lbr"-version = "1.13.57"+version = "1.13.58" dependencies = [  "regex",  "roxmltree",@@ -322,14 +322,14 @@ dependencies = [  [[package]] name = "fusion-os-mac"-version = "1.13.57"+version = "1.13.58" dependencies = [  "fusion-core", ]  [[package]] name = "fusion-os-win"-version = "1.13.57"+version = "1.13.58" dependencies = [  "fusion-core",  "windows-sys 0.59.0",
rust/Cargo.toml+1−1
@@ -4,7 +4,7 @@ members = ["core", "lbr", "os-win", "os-mac", "bridge"]  # One version for the whole bridge, in lockstep with BRIDGE_VERSION (tools/release.sh bumps both). [workspace.package]-version = "1.13.57"+version = "1.13.58" edition = "2021" license = "MIT" 
rust/bridge/src/routing_script.py+2−2
@@ -1200,7 +1200,8 @@ def edit_tracks(app, root, board, meta, args):         for key in ('start', 'end'):             if ed.get(key) is not None:                 to = [number(ed[key][0]), number(ed[key][1])]-                step.append('MOVE (%.6fmm %.6fmm) (%.6fmm %.6fmm);' % (x[key][0], x[key][1], to[0], to[1]))+                # WIRE_BEND 2 = straight: the default bend style makes an angled move into two segments+                step.append('SET WIRE_BEND 2; MOVE (%.6fmm %.6fmm) (%.6fmm %.6fmm); SET WIRE_BEND 0;' % (x[key][0], x[key][1], to[0], to[1]))                 want[key] = to         if ed.get('layer') is not None:             want['layer'] = _layer_number(board, ed['layer'])@@ -1218,7 +1219,6 @@ def edit_tracks(app, root, board, meta, args):         return {'success': False, 'mutated': False, **meta, 'errorCode': 'nothing_to_do', 'results': results, 'error': 'No track could be edited'}     visible = _visible_layers(root)     try:-        # without a fine grid MOVE snaps a track's end to the board's grid, and it reads back somewhere else         app.executeTextCommand('Electron.run GRID MM 0.001;')         for c in cmds:             app.executeTextCommand('Electron.run ' + c)
rust/bridge/src/sch_script.py+3−2
@@ -1182,7 +1182,7 @@ def sch_swap_part(app, args):     ends_after = ends(e.after, e.sheet2)     join = [p_ for p_ in lost if p_ in ends_after and ends_after[p_] != ends_before.get(p_)]     if join:-        e.again(['SET CONFIRM YES; NET %s %s %s; SET CONFIRM OFF;' % (quote(before[p_]), mm(ends_before[p_]), mm(ends_after[p_])) for p_ in join])+        e.again(['SET CONFIRM YES; SET WIRE_BEND 2; NET %s %s %s; SET WIRE_BEND 0; SET CONFIRM OFF;' % (quote(before[p_]), mm(ends_before[p_]), mm(ends_after[p_])) for p_ in join])         after = by_pad(e.after, e.sheet2)         lost = sorted(p_ for p_, n in before.items() if n and after.get(p_) != n)     # every pin that ended elsewhere and kept its net: joined by Fusion's REPLACE, or by the wire above@@ -1262,7 +1262,8 @@ def sch_wire(app, args):     if max(abs(v) for p_ in pts for v in p_) > 1950:         sfail('off_the_sheet', 'Keep points within 1950 mm: Fusion drops drawing past about 2 m')     name = args.get('net')-    e.cmds.append('SET CONFIRM YES; NET %s%s; SET CONFIRM OFF;' % (quote(str(name)) + ' ' if name else '', ' '.join(mm(p_) for p_ in pts)))+    # WIRE_BEND 2 = straight: the default bend style makes an angled segment into two+    e.cmds.append('SET CONFIRM YES; SET WIRE_BEND 2; NET %s%s; SET WIRE_BEND 0; SET CONFIRM OFF;' % (quote(str(name)) + ' ' if name else '', ' '.join(mm(p_) for p_ in pts)))     if dry(args):         return plan_reply(e)     e.run()
tests/run_verb_tests.py+4
@@ -292,6 +292,10 @@ t("fusion_project_info", "daily", dict(B), {"ok": True, "fields": ["files", "boa t("fusion_schematic_data", "daily", dict(B, designator="C11", params=True), {"ok": True, "fields": ["components", "count"]}) t("fusion_part_parameters", "daily", dict(B), {"ok": True, "fields": ["names"]}) t("fusion_cross_check", "daily", dict(B), {"ok": True, "fields": ["matched", "onSheetNotOnBoard", "onBoardNotOnSheet"]})+for v in ("fusion_sch_label", "fusion_sch_wire", "fusion_edit_tracks", "fusion_place_parts"):+    t(v, "manual", note="edits a design: run live by tests/sch_drill.py (place_parts and a track's end on a board with no schematic by hand)")+t("fusion_model_check", "daily", dict(B), {"ok": True, "fields": ["parts", "checked", "ok"]}, "does each part's cloud 3D model open, read only")+t("fusion_extensions", "daily", {}, {"ok": True, "fields": ["extensions", "count"]}, "the add-ins installed for this user") t("fusion_update_pcb", "daily", dict(B), {"ok": True, "fields": ["executed", "preview", "changeOrder"]}, "the standard's update_pcb: the preview, nothing to execute") t("fusion_preferences", "daily", {}, {"ok": True}, "the standard's preferences, read only here") t("fusion_select_items", "negative", {}, {"errorCode": "bad_args"}, "refs or clear is required")
tests/sch_drill.py+3−1
@@ -123,6 +123,8 @@ step('sch_delete_wire', 'fusion_sch_delete_wire', {'ids': [(wires or [{}])[-1].g # ── wires and labels by point ────────────────────────────────────────────────────────────────────── step('sch_wire', 'fusion_sch_wire', {'points': [[20, 200], [40, 200], [40, 220]], 'net': 'WIRED'},      lambda r: expect(any(w.get('net') == 'WIRED' for w in items('wires').get('wires') or []) and len(r.get('wires') or []) == 2, 'wire not on the sheet'))+step('sch_wire diagonal', 'fusion_sch_wire', {'points': [[60, 200], [75, 215]], 'net': 'DIAG'},+     lambda _r: expect(sum(w.get('net') == 'DIAG' for w in items('wires').get('wires') or []) == 1, 'a diagonal wire came back in pieces')) step('sch_label add', 'fusion_sch_label', {'text': 'WIRED', 'at': [30, 200]},      lambda _r: expect(any(l.get('text') == 'WIRED' for l in items('labels').get('labels') or []), 'label not on the sheet')) r = call('fusion_sch_label', {'text': 'OTHER', 'at': [30, 200]})@@ -205,7 +207,7 @@ def track_widths():  step('edit_tracks width', 'fusion_edit_tracks', {'expectDocument': 'Untitled', 'tracks': [{'id': tid, 'width': 0.4}]},      lambda r: expect(r.get('verified') and abs((track_widths().get(tid) or 0) - 0.4) < 1e-6, 'width now %s' % track_widths().get(tid)))-step('edit_tracks end', 'fusion_edit_tracks', {'expectDocument': 'Untitled', 'tracks': [{'id': tid, 'end': [x0 + 2, y0 + 4] if pad else [0, 0]}]},+step('edit_tracks end, at an angle', 'fusion_edit_tracks', {'expectDocument': 'Untitled', 'tracks': [{'id': tid, 'end': [x0 + 2, y0 + 4] if pad else [0, 0]}]},      lambda r: expect(r.get('verified') and any(abs(t['end']['x'] - x0 - 2) < 1e-3 for t in call('fusion_routing_state', {'expectDocument': 'Untitled'}).get('tracks') or [] if t.get('net') == 'NB'), 'end did not move')) r = call('fusion_edit_tracks', {'expectDocument': 'Untitled', 'tracks': [{'id': 'track:NOPE:1:0,0,1,1', 'width': 0.3}]}) results.append(('edit_tracks unknown id', expect(r.get('errorCode') == 'nothing_to_do', str(r.get('errorCode')))))