app
Fusion - the Fusion 360 Bridge
Public Made by Adomby adom
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.
← Commit history
suite: flap guard; skill: async/relay lessons
2 files changed
+32−4
dev-skills/fusion-verb-tests/SKILL.md+14@@ -121,6 +121,20 @@ The AI running the session owns the rest, every time, unprompted: promoted into `tools/` instead of lingering in /tmp. - **Reports stay**: `tests/reports/` is the record, never part of cleanup. +## Long verbs vs the relay window (learned 2026-08-14, the hard way)++ab's relay caps a verb call at ~60s. A verb whose work can exceed that (cloud opens,+optimized-GLB export, the demo tour) MUST be async: fire, return an id instantly, poll a+status verb. Two live lessons:+- `fusion_export_optimized_glb` sync form died at the relay on a cold Fusion session, and+ the ORPHANED export kept grinding Fusion's main thread for 15+ minutes, wedging every+ later verb (each retry piled another export onto the queue). Recovery: fusion_stop /+ fusion_kill + fusion_start. NEVER probe a long sync verb "to see how long it takes" -+ use the async form (`async:true` -> exportId -> poll fetch).+- The runner aborts after 4 consecutive `target_not_found` results: that is the relay+ losing the box (ab auto-update, laptop roaming), not 70 verb failures. Re-run when+ `adom-bridge targets` shows the box stable; treat flap-aborted reports as void.+ ## Reading a failure - `wrong_document` where a PASS was expected: the guard fired - which document was
tests/run_verb_tests.py+18−4@@ -131,8 +131,6 @@ t("fusion_get_preferences", "daily", {}, {"ok": True, "fields": ["current"]}) t("fusion_set_preference", "daily", {"theme": "darkblue"}, {"ok": True}, "sets the value it already has") t("fusion_mcp_status", "daily", {}, {"ok": True, "fields": ["enabled"]}) t("fusion_mcp_tools", "weekly", {}, {"ok": True}, "only meaningful when MCP enabled")-t("fusion_mcp_call", "weekly", {"tool": "fusion_mcp_read", "arguments": {"queryType": "document"}}, {"ok": True},- "queryType must be a schema enum value (apiDocumentation|screenshot|document|projects|activeCommand) - 'summary' was a guess that failed validation") t("fusion_mcp_resources", "weekly", {}, {"ok": True}) t("fusion_mcp_enable", "manual", note="drives the prefs UI, takes foreground") t("fusion_prefs_open", "manual", note="opens a dialog on the user's screen")@@ -164,6 +162,8 @@ t("fusion_export_cpl", "weekly", {"outputPath": f"{SCRATCH}/cpl.csv"}, {"ok": Tr t("fusion_board_stackup", "weekly", {}, {"ok": True}, "exports the .brd - needs the board context") t("fusion_show_3d_board", "daily", {}, {"ok": True}) t("fusion_inspect_bodies", "daily", {}, {"ok": True, "fields": ["bodies", "units"]})+t("fusion_mcp_call", "weekly", {"tool": "fusion_mcp_read", "arguments": {"queryType": "document"}}, {"ok": True},+ "MCP reads the ACTIVE document - must run after the board opens; queryType is a schema enum (document|projects|...)") t("fusion_assembly_bom#realboard", "weekly", {}, {"pred": "ok_or_budget"}, "on the open BQ25792 3D PCB the budget guard must refuse FAST - the grind was issue #659") t("fusion_physical_properties", "daily", {}, {"ok": True})@@ -398,8 +398,13 @@ def main(): results.append({"verb": name, "status": "FAIL", "why": "in the test plan but MISSING from describe.py - undescribed verbs are invisible to callers"}) for name, spec in PLAN.items():- if spec["tier"] == "manual" or spec["tier"] not in tiers or (only and name not in only):- if spec["tier"] == "manual" and not only:+ if only:+ # An explicit --only list overrides the tier gates entirely - naming a+ # manual/weekly test IS the operator's consent to run it now.+ if name not in only:+ continue+ elif spec["tier"] == "manual" or spec["tier"] not in tiers:+ if spec["tier"] == "manual": results.append({"verb": name, "status": "MANUAL", "why": spec.get("note", "")}) continue run_args = spec["args"]@@ -422,6 +427,15 @@ def main(): else: d, dt = call(a.target, verb, run_args, timeout=vt) LAST_RESULTS[verb] = d+ if str(d.get("error") or "") == "target_not_found":+ flaps = getattr(sys.modules[__name__], "_FLAPS", 0) + 1+ setattr(sys.modules[__name__], "_FLAPS", flaps)+ if flaps >= 4:+ print("\nABORT: the relay lost target %s %d times in a row - the box is offline or its ab is mid-update. Re-run when `adom-bridge targets` shows it stable; these are not verb failures." % (a.target, flaps))+ results.append({"verb": name, "status": "FAIL", "why": "relay lost the target (run aborted; not a verb failure)"})+ break+ else:+ setattr(sys.modules[__name__], "_FLAPS", 0) errs = expect(d, spec["exp"]) if name.startswith("fusion_search_cloud_files") or name.startswith("fusion_walk_cloud_tree"): # must refuse: success:true here is the FAILURE