← Commit history
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.192\ No newline at end of file+0.9.193\ No newline at end of file
adom-bridge-kicad-manifest.json+5−5
@@ -1,15 +1,15 @@ {   "manifest_version": 1,   "name": "kicad",-  "version": "0.9.192",-  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.192/adom-bridge-kicad-v0.9.192.zip",-  "sha256": "13bb28e8564364c640416ee08911286a6423213cead03066b378a186394108e5",-  "size": 460359,+  "version": "0.9.193",+  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.193/adom-bridge-kicad-v0.9.193.zip",+  "sha256": "d22381f4e66f2699baa2d1d0d19102ed64e4fc2b7099859bb29403e8b7685775",+  "size": 462071,   "verbPrefixes": [     "kicad_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-08-19T21:40:00Z",+  "released_at": "2026-08-19T21:55:00Z",   "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png",   "languages": [     "Python",
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.192",+  "version": "0.9.193",   "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/install_symbol.py+26
@@ -124,6 +124,23 @@ def handle_install_symbol(kicad_info: dict, args: dict) -> dict:     # hands the caller a success it did not ask for. Say so out loud instead.     requested_lib = (args.get("libraryName") or "").strip()     ignored_lib = requested_lib and requested_lib != ADOM_LIB_NAME++    # A Symbol Editor indexes its libraries once, when it opens. If one was+    # already open when we added these symbols, it cannot see them, and the next+    # kicad_show_symbol will fail in a way that looks like a search bug rather+    # than a stale index (0.9.192, found while verifying wiki issue #37). Say it+    # here, at the moment the staleness is created, not three calls later.+    stale_editor = ""+    if not editor_opened:+        try:+            from handlers.open_symbol_editor import _find_symbol_editor+            if _find_symbol_editor():+                stale_editor = (+                    "a Symbol Editor was already open when these symbols were added, so it "+                    "cannot see them until it reopens"+                )+        except Exception:+            pass     if editor_opened:         output_parts.append(f"Symbol Editor: {editor_output}")     elif open_editor:@@ -134,6 +151,8 @@ def handle_install_symbol(kicad_info: dict, args: dict) -> dict:             f"NOTE: libraryName '{requested_lib}' was ignored - every Adom symbol "             f"installs into the single global '{ADOM_LIB_NAME}' library"         )+    if stale_editor:+        output_parts.append(f"NOTE: {stale_editor}")      out = {         "success": True,@@ -143,6 +162,13 @@ def handle_install_symbol(kicad_info: dict, args: dict) -> dict:         "libraryName": ADOM_LIB_NAME,         "editorOpened": editor_opened,     }+    if stale_editor:+        out["staleEditorOpen"] = True+        out["_staleHint"] = (+            "The open Symbol Editor's library index predates this install. To show one of "+            "these symbols, call kicad_show_symbol with rescan:true (it closes and reopens "+            "the editor so KiCad re-indexes), or close the editor first."+        )     if ignored_lib:         out["ignoredArgs"] = {"libraryName": requested_lib}         out["_hint"] = (
handlers/open_symbol_editor.py+84−4
@@ -37,6 +37,44 @@ KICAD_SYMBOL_EDITOR_CMD_ID = 20011  # Project Manager > Tools > Symbol Editor SW_RESTORE = 9 GW_OWNER = 4 ++def _symbol_on_disk_but_not_in_editor(kicad_info: dict, name: str) -> bool:+    """True when `name` IS in the Adom library file but a Symbol Editor that+    opened earlier cannot see it.++    A Symbol Editor builds its library tree once, at open. Symbols added to+    Adom.kicad_sym after that are invisible to it until it reopens, which looks+    exactly like a failed background search and is the reason 0.9.192 stopped+    reporting it as one. Only the disk half is checked here: the caller reaches+    this after both background attempts already failed to find it in the tree."""+    if not name:+        return False+    try:+        import adom_library+        paths = adom_library._get_paths(kicad_info)+        if not paths:+            return False+        _table, lib_path = paths+        return name.lower() in {s.lower() for s in adom_library.list_symbols(lib_path)}+    except Exception:+        return False+++def _close_symbol_editor(hwnd: int) -> bool:+    """Ask an open Symbol Editor to close, so the next open re-indexes the+    libraries. WM_CLOSE, not a kill: KiCad gets to run its own shutdown (and to+    put up a save prompt, which the caller will see in the next screenshot)."""+    WM_CLOSE = 0x0010+    try:+        user32.PostMessageW(hwnd, WM_CLOSE, 0, 0)+    except Exception:+        return False+    for _ in range(24):  # up to ~12s for KiCad to tear the frame down+        time.sleep(0.5)+        if not user32.IsWindow(hwnd):+            return True+    return False+ if IS_WINDOWS:     user32 = ctypes.windll.user32     kernel32 = ctypes.windll.kernel32@@ -391,8 +429,10 @@ def handle_open_symbol_editor(kicad_info: dict, args: dict) -> dict:     symbol_name = args.get("symbolName", "")     library_name = args.get("libraryName", "") -    # Check if the symbol is already loaded in the Symbol Editor-    if symbol_name:+    # Check if the symbol is already loaded in the Symbol Editor.+    # rescan:true skips this: the caller is asking for a re-index precisely+    # because they do not trust what this editor currently has.+    if symbol_name and not args.get("rescan"):         sym_hwnd = _find_symbol_editor()         if sym_hwnd:             windows = _find_windows_by_title("Symbol Editor")@@ -408,6 +448,20 @@ def handle_open_symbol_editor(kicad_info: dict, args: dict) -> dict:      sym_hwnd = _find_symbol_editor()     pathways_tried: list[str] = []+    rescanned = False++    # 0.9.192: rescan:true is the remedy for errorCode library_cache_stale. An+    # editor only indexes its libraries at open, so the ONLY way to make it see a+    # newly installed symbol is to close it and let the flow below open a fresh+    # one. Opt-in, because it closes a window the user may be working in and+    # costs the cold library-index time (#35).+    if sym_hwnd and args.get("rescan"):+        if _close_symbol_editor(sym_hwnd):+            rescanned = True+            sym_hwnd = 0+            pathways_tried.append("rescan-closed-stale-editor")+        else:+            pathways_tried.append("rescan-close-failed")      if not sym_hwnd:         # Phase 3 Tier 1: try the reverse bridge first. Two sub-paths@@ -636,6 +690,7 @@ def handle_open_symbol_editor(kicad_info: dict, args: dict) -> dict:             "success": True,             "output": f"Symbol '{search_text}' loaded (background win32 path): {_get_symbol_editor_title()}",             "via": "win32-background",+            "rescanned": rescanned,         }      # BACKGROUND-FIRST CONTRACT (John, 2026-08-14): the win32 tier above is the@@ -657,17 +712,42 @@ def handle_open_symbol_editor(kicad_info: dict, args: dict) -> dict:                       f"{_get_symbol_editor_title()}",             "via": "win32-background",             "attempts": 2,+            "rescanned": rescanned,         }      if not args.get("allowFocusSteal"):+        # 0.9.192: before blaming the search tier, ask whether the symbol is even+        # in the editor's world. A Symbol Editor indexes its libraries ONCE when+        # it opens; a symbol added to Adom.kicad_sym afterwards is on disk but+        # not in that editor's tree, so no amount of filtering will ever find it.+        # Reproduced live: RP2040 (present when the editor opened) loads first+        # try; COL0-TEST, installed minutes later, fails forever. Reporting that+        # as "didn't load in the background" sends the caller to retry a call+        # that cannot succeed.+        stale = _symbol_on_disk_but_not_in_editor(kicad_info, symbol_name or search_text)+        if stale:+            return {+                "success": False, "editorOpened": True, "symbolLoaded": False,+                "attempts": 2,+                "errorCode": "library_cache_stale",+                "error": f"'{search_text}' exists in the Adom library on disk but the open "+                         f"Symbol Editor cannot see it: that editor indexed its libraries when "+                         f"it opened, before this symbol was added "+                         f"(title: {_get_symbol_editor_title() or 'unknown'})",+                "_hint": ("This is not a search-timing failure and retrying will never fix it. "+                          "The Symbol Editor has to re-index, which only happens when it opens. "+                          "Close the Symbol Editor and call again, or pass rescan:true to have "+                          "the bridge close and reopen it for you (that costs the cold "+                          "library-index time, roughly a minute - wiki issue #35)."),+            }         return {             "success": False, "editorOpened": True, "symbolLoaded": False,             "attempts": 2,             "error": f"Symbol Editor open but '{search_text}' didn't load in the background "                      f"after two attempts (title: {_get_symbol_editor_title() or 'unknown'})",             "_hint": ("Two background attempts did not confirm, so a third is unlikely to "-                      "help - check that the symbol really exists in this library "-                      "(kicad_list_symbols). The keystroke fallback "+                      "help - check that the symbol really exists in this library. "+                      "The keystroke fallback "                       "is disabled by default because it needs to foreground the window (rude while "                       "you're typing) — pass allowFocusSteal:true ONLY if you accept a focus grab, "                       "or foreground:true to bring the editor to you and load it yourself."),
handlers/show.py+6−2
@@ -69,8 +69,12 @@ def handle_show_symbol(kicad_info: dict, args: dict) -> dict:     """Show a schematic SYMBOL in the Symbol Editor. Args: symbolName (+ libraryName),     or libraryName alone to browse a library."""     a = args or {}-    already = _open_editor_showing("symbol editor", a.get("symbolName") or "",-                                   a.get("libraryName") or a.get("library") or "")+    # rescan:true means "I do not trust this editor's library index" (the remedy+    # for errorCode library_cache_stale), so the warm-reuse shortcut must not+    # swallow it - reuse is exactly what the caller is asking us to stop doing.+    already = None if a.get("rescan") else _open_editor_showing(+        "symbol editor", a.get("symbolName") or "",+        a.get("libraryName") or a.get("library") or "")     if already:         return {"success": True, "surface": "symbol", "reused": True,                 "window": already.get("title"),