← Commit history

suite: flap guard; skill: async/relay lessons

John Lauer ·25df48a475 ·1mo ago ·parent 393a16f
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