← Commit history
BRIDGE_VERSION+1−1
@@ -1 +1 @@-1.13.58\ No newline at end of file+1.13.59\ No newline at end of file
CHANGELOG.md+4−3
@@ -3,10 +3,11 @@ 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.58 (2026-11-02)+## 1.13.59 (2026-11-02) -`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.+`fusion_edit_tracks` moves a track's end only along its own line. At an angle Fusion's MOVE bends the track into two segments, whatever+WIRE_BEND says: that is undone and refused (`end_move_bends`), pointing at ripup and route_net. Deleting and redrawing is no way round it:+DELETE of a net's last wire removes the whole signal. `fusion_sch_wire` and the wire `fusion_sch_swap_part` adds draw straight.  ## 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.58",+  "version": "1.13.59",   "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.",
contracts/describe.json+1−1
@@ -2009,7 +2009,7 @@         "verified": "bool"       },       "statusVerb": "fusion_routing_state",-      "summary": "Change given tracks by the id routing_state returns: width, layer, ends (the standard's edit_tracks). Read back: only those tracks may differ, or every change is undone. Change a track's layer and its ends in two calls.",+      "summary": "Change given tracks by the id routing_state returns: width, layer, ends (the standard's edit_tracks). Read back: only those tracks may differ, or every change is undone. An end moves only along the track's own line: at an angle Fusion bends the track into two segments, so that is undone and refused (end_move_bends). Change a track's layer and its ends in two calls.",       "timeoutSeconds": 120     },     {
dashboard/Cargo.lock+1−1
@@ -4,7 +4,7 @@ version = 4  [[package]] name = "fusion-dashboard"-version = "1.13.58"+version = "1.13.59" dependencies = [  "serde_json", ]
dashboard/Cargo.toml+1−1
@@ -1,6 +1,6 @@ [package] name = "fusion-dashboard"-version = "1.13.58"+version = "1.13.59" edition = "2021"  [dependencies]
describe.py+1−1
@@ -681,7 +681,7 @@ _T = [     ("fusion_place_parts", "Footprints from a .lbr onto a board with no schematic (the standard's place_parts), with position, rotation and side, read back. A board linked to a schematic is refused: Fusion takes its parts from the sheet (fusion_sch_place_part, then fusion_move_parts).",      {"parts":"[{library, footprint, ref?, value?, x, y, rotation?, side?}] mm (required)","dryRun":"bool","expectDocument":"the board","expectedRevision":"optional"},      {"results":"[ok|err:...]","placed":"[{ref, x, y, rotation, side}]","revision":"str"},120,"fusion_routing_state",False,{"parts":[{"library":"C:/lib/parts.lbr","footprint":"R0603","ref":"R1","x":10,"y":10}],"dryRun":True}),-    ("fusion_edit_tracks", "Change given tracks by the id routing_state returns: width, layer, ends (the standard's edit_tracks). Read back: only those tracks may differ, or every change is undone. Change a track's layer and its ends in two calls.",+    ("fusion_edit_tracks", "Change given tracks by the id routing_state returns: width, layer, ends (the standard's edit_tracks). Read back: only those tracks may differ, or every change is undone. An end moves only along the track's own line: at an angle Fusion bends the track into two segments, so that is undone and refused (end_move_bends). Change a track's layer and its ends in two calls.",      {"tracks":"[{id, width?, layer?, start?, end?}] mm (required)","dryRun":"bool","expectDocument":"the board","expectedRevision":"optional"},      {"results":"[ok|err:...]","changes":"[{id, before, after}]","verified":"bool","revision":"str"},120,"fusion_routing_state",False,{}),     ("fusion_model_check", "Per part, is there a 3D model and does its cloud file open for this account (the standard's model_check), for a board or a .lbr footprint. Fusion can't say whether one holds geometry without opening it, so hasGeometry is null. Read only.",
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.58"+DASH_VERSION="1.13.59" 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.58",+  "version": "1.13.59",   "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.58",+  "version": "1.13.59",   "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.58"+version = "1.13.59" dependencies = [  "adom-eda-checks",  "base64",@@ -299,7 +299,7 @@ dependencies = [  [[package]] name = "fusion-core"-version = "1.13.58"+version = "1.13.59" dependencies = [  "serde",  "serde_json",@@ -310,7 +310,7 @@ dependencies = [  [[package]] name = "fusion-lbr"-version = "1.13.58"+version = "1.13.59" dependencies = [  "regex",  "roxmltree",@@ -322,14 +322,14 @@ dependencies = [  [[package]] name = "fusion-os-mac"-version = "1.13.58"+version = "1.13.59" dependencies = [  "fusion-core", ]  [[package]] name = "fusion-os-win"-version = "1.13.58"+version = "1.13.59" 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.58"+version = "1.13.59" edition = "2021" license = "MIT" 
rust/bridge/src/routing_script.py+6−2
@@ -1200,8 +1200,10 @@ 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])]-                # 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]))+                # MOVE keeps one segment only along the track's own line: at an angle Fusion bends it into two,+                # whatever WIRE_BEND says. That is refused and undone below. Deleting and redrawing is no way+                # round it: DELETE of a net's last wire removes the whole signal, pad connections too (live, 2706).+                step.append('MOVE (%.6fmm %.6fmm) (%.6fmm %.6fmm);' % (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'])@@ -1235,6 +1237,8 @@ def edit_tracks(app, root, board, meta, args):     ok = ok and all(abs(width[_wire_key(p_['want'])] - p_['want']['width']) < 1e-6 for p_ in plan if _wire_key(p_['want']) in width)     if not ok:         _undo_until(app, args, meta['revision'], sum(1 for c in cmds if not c.startswith('DISPLAY')) + 2)+        if any(ed.get('start') is not None or ed.get('end') is not None for ed in edits):+            fail('end_move_bends', 'Fusion moves a track end only along the track\'s own line: at an angle it bends the track into two segments, so every change was undone. To re-aim a track, take it up with fusion_ripup_nets and route it again with fusion_route_net.')         fail('readback_mismatch', 'The copper after the edit was not exactly the asked tracks; every change was undone')     app.executeTextCommand('Electron.run RATSNEST;')     return {'success': True, 'mutated': True, **meta2, 'results': results, 'verified': True,
tests/sch_drill.py+7−2
@@ -207,8 +207,13 @@ 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, 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'))+step('edit_tracks end, along its line', 'fusion_edit_tracks', {'expectDocument': 'Untitled', 'tracks': [{'id': tid, 'end': [x0, y0 + 6] if pad else [0, 0]}]},+     lambda r: expect(r.get('verified') and any(abs(t['end']['y'] - y0 - 6) < 1e-3 or abs(t['start']['y'] - y0 - 6) < 1e-3 for t in call('fusion_routing_state', {'expectDocument': 'Untitled'}).get('tracks') or [] if t.get('net') == 'NB'), 'end did not move'))+now = next((t.get('id') for t in call('fusion_routing_state', {'expectDocument': 'Untitled'}).get('tracks') or [] if t.get('net') == 'NB'), None)+before_angle = sorted(track_widths())+r = call('fusion_edit_tracks', {'expectDocument': 'Untitled', 'tracks': [{'id': now, 'end': [x0 + 3, y0 + 9]}]})+results.append(('edit_tracks angled end refused, undone', expect(r.get('errorCode') == 'end_move_bends' and sorted(track_widths()) == before_angle, '%s, tracks %s' % (r.get('errorCode'), sorted(track_widths())))))+print('%-34s %s' % (results[-1][0], 'PASS' if results[-1][1] is None else 'FAIL  ' + str(results[-1][1]))) 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'))))) print('%-34s %s' % (results[-1][0], 'PASS' if results[-1][1] is None else 'FAIL  ' + str(results[-1][1])))