← Commit history

v0.9.81: kicad_uninstall testResidue mode — the verb suite cleans up ALL its residue after every run (phase 7)

John Lauer ·b9bbf40fd2 ·2mo ago ·parent cdac3a3
6 files changed +104−8
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.80+0.9.81
adom-bridge-kicad-manifest.json+5−5
@@ -1,15 +1,15 @@ {   "manifest_version": 1,   "name": "kicad",-  "version": "0.9.80",-  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.80/adom-bridge-kicad-v0.9.80.zip",-  "sha256": "50bfae0f62908302026ad365d44d5037c5ca84a0c20ec98a1837d3435753de60",-  "size": 375858,+  "version": "0.9.81",+  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.81/adom-bridge-kicad-v0.9.81.zip",+  "sha256": "7aa4415764ca5187da26322ee5563f2367e54a6e05b02121c84b41f1347b40a7",+  "size": 376754,   "verbPrefixes": [     "kicad_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-08-14T11:44:39Z",+  "released_at": "2026-08-14T12:59:59Z",   "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png",   "languages": [     "Python",
adom-bridge-kicad-v0.9.81.zipadded
⋯ 1 unchanged line ⋯
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.80",+  "version": "0.9.81",   "description": "Reverse bridge for KiCad \u2014 board/schematic introspection, lint via kicad-cli, plugin install, multi-instance probe, in-process DRC.",   "homepage": "https://wiki.adom.inc/adom/adom-bridge",   "author": "Adom Inc.",
handlers/uninstall.py+90−1
@@ -88,8 +88,20 @@ def _kicad_bin_dirs(kicad_info: dict) -> list[Path]:   def handle_uninstall(kicad_info: dict, args: dict) -> dict:-    """Remove every artifact this bridge created. Args: dryRun (default false)."""+    """Remove every artifact this bridge created.++    Args:+      dryRun (default false)      — preview only.+      testResidue (default false) — remove ONLY what the daily verb suite+        creates (kicad-bridge-test skill): the verbtest fixture dir, the demo+        project, the generated ADOM_VERBTEST symbol/footprint inside the Adom+        libraries, the AdomVerbTest lib-table registration, and stale discovery+        files. Leaves the plugin, the Adom libraries themselves, and every+        user artifact alone. This is how the suite cleans up after every run+        (John, 2026-08-14: "clean up after yourself").+    """     dry = bool(args.get("dryRun"))+    test_residue = bool(args.get("testResidue"))     removed: list[str] = []     failed: list[dict] = []     absent = 0@@ -118,6 +130,10 @@ def handle_uninstall(kicad_info: dict, args: dict) -> dict:         except Exception as e:             failed.append({"item": f"{label}: {p}", "error": str(e)}) +    if test_residue:+        return _remove_test_residue(kicad_info, dry, rm_file, rm_tree,+                                    removed, failed, lambda: absent)+     # 1) plugin payload (+ pycache), 2) Adom symbol lib file, 3) Adom.pretty     for ver_dir in _kicad_user_roots():         site = ver_dir / "3rdparty" / "Python311" / "site-packages"@@ -192,3 +208,76 @@ def handle_uninstall(kicad_info: dict, args: dict) -> dict:                   ".pyc open; re-run after KiCad exits if anything failed. Bridge removes its own "                   "bridges-cache during the cascade."),     }+++def _remove_test_residue(kicad_info, dry, rm_file, rm_tree, removed, failed, absent_fn):+    """The suite-residue subset (see handle_uninstall docstring)."""+    up = os.environ.get("USERPROFILE")+    if up:+        rm_tree(Path(up) / "Documents" / "adom-kicad-verbtest", "verbtest fixture dir")+        rm_tree(Path(up) / "Documents" / "adom-kicad-demo", "demo project")+    # ADOM_VERBTEST entries inside the Adom libraries (surgical, not the libs)+    for ver_dir in _kicad_user_roots():+        rm_file(ver_dir / "footprints" / "Adom.pretty" / "ADOM_VERBTEST.kicad_mod",+                "test footprint")+        sym = ver_dir / "symbols" / "Adom.kicad_sym"+        if sym.exists():+            try:+                import adom_library+                text = sym.read_text(encoding="utf-8")+                blocks = adom_library._extract_top_level_symbols(text)+                hit = [b for n, b in blocks if n == "ADOM_VERBTEST"]+                if hit:+                    if not dry:+                        for b in hit:+                            text = text.replace(b, "")+                        sym.write_text(text, encoding="utf-8")+                    removed.append(f"ADOM_VERBTEST symbol in {sym}")+            except Exception as e:+                failed.append({"item": f"ADOM_VERBTEST in {sym}", "error": str(e)})+    # AdomVerbTest lib-table registration (points at the deleted fixture dir)+    try:+        from lib_table import LibTable+        for cfg in _kicad_config_roots():+            for table_name in ("sym-lib-table", "fp-lib-table"):+                tp = cfg / table_name+                if not tp.exists():+                    continue+                try:+                    table = LibTable.parse_file(str(tp))+                    if table.has_library("AdomVerbTest"):+                        if not dry:+                            table.remove_library("AdomVerbTest")+                            table.write_file(str(tp))+                        removed.append(f"'AdomVerbTest' row in {tp}")+                except Exception as e:+                    failed.append({"item": f"'AdomVerbTest' row in {tp}", "error": str(e)})+    except Exception as e:+        failed.append({"item": "lib-table cleanup", "error": str(e)})+    # stale discovery files (dead pids)+    tmp = Path(tempfile.gettempdir())+    for f in tmp.glob("adom-kicad-bridge-*.json"):+        try:+            pid = int(f.stem.rsplit("-", 1)[-1])+        except ValueError:+            rm_file(f, "legacy discovery file")+            continue+        try:+            import ctypes+            h = ctypes.windll.kernel32.OpenProcess(0x1000, False, pid)+            if h:+                ctypes.windll.kernel32.CloseHandle(h)+            else:+                rm_file(f, "stale discovery file")+        except Exception:+            pass+    ok = len(failed) == 0+    return {+        "success": ok, "testResidue": True, "dryRun": dry,+        "removed": removed, "failed": failed,+        "alreadyClean": len(removed) == 0 and ok,+        "summary": f"{'would remove' if dry else 'removed'} {len(removed)} test-residue item(s), "+                   f"{len(failed)} failed",+        "_hint": ("Suite residue only — plugin, Adom libraries, KiCad and user files untouched. "+                  "Run after every verb-suite pass; idempotent."),+    }
skills/kicad-bridge-test/run_verb_tests.py+7
@@ -368,6 +368,13 @@ def main():         if phase == 3 and not a.skip_gui:             time.sleep(2) +    # phase 7: clean up EVERYTHING the suite created on the box (John 2026-08-14)+    if not only and not a.skip_gui:+        cr = ab(a.target, "kicad_uninstall", {"testResidue": True}, timeout=120)+        print(f"[7] cleanup: {cr.get('summary') or cr.get('error') or cr.get('status')}")+        results["_cleanup"] = {"result": "PASS" if ok(cr) else "FAIL",+                               "response_head": json.dumps(cr)[:400]}+     # skips recorded for non-phase entries     for verb, spec in M.items():         if spec.get("skip") and verb not in results: