← Commit history

v0.1.27: review switches documents, reports skipped checks

John Lauer ·31bfbc655f ·1mo ago ·parent 9965b32
7 files changed +88−35
CHANGELOG.md+4
@@ -1,5 +1,9 @@ # Changelog +## 0.1.27 — 2026-09-01+- **`review_board` switches documents and returns PARTIAL results.** Altium holds one current document, so a board read and a sheet read cannot both be "now" — with a schematic focused, the previous version failed the whole review because `read_board` had nothing current. It now takes optional `board` and `sheet` paths and activates each in turn (like `cross_check`), and when one document cannot be read it still runs the checks that CAN run.+- **`skipped` names every check that did not run and why** — "no board read", "no schematic read — pass includeSchematic:true". A review that silently drops half its checks reads exactly like a clean board, which is the failure mode worth engineering against.+ ## 0.1.26 — 2026-09-01 - **The Adom house rule is now a check.** `vendor_part_numbers` (warning) flags any purchased component missing Mouser / DigiKey / LCSC part numbers — Mouser named first because it is the preferred vendor, so if only one gets filled in, that is the one. Test points, fiducials and mounting holes are exempt: they are not purchased parts, and flagging them would train people to ignore the check. - `missing_datasheets` (info) and `schematic_placeholder_values` (error) — the latter catches default Comments at the SOURCE, where the BOM is actually generated from, rather than only on placed parts.
adom-bridge-altium-manifest.json+4−4
@@ -1,9 +1,9 @@ {   "name": "altium",-  "version": "0.1.26",-  "url": "https://wiki.adom.inc/download/adom/altium-bridge/0.1.26/adom-bridge-altium-v0.1.26.zip",-  "sha256": "4d39499ee8bbf1af001b29043920415852e95cc8d9820393595f49fed75558b7",-  "size": 125669,+  "version": "0.1.27",+  "url": "https://wiki.adom.inc/download/adom/altium-bridge/0.1.27/adom-bridge-altium-v0.1.27.zip",+  "sha256": "14e69c2e7d8543c9ae4a87ffc415d422ed124e4f32c8457cc558ece41ae89e93",+  "size": 126211,   "verbPrefixes": [     "altium_"   ],
adom-bridge-altium-v0.1.27.zipadded
⋯ 1 unchanged line ⋯
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "altium",   "displayName": "Altium Designer",-  "version": "0.1.26",+  "version": "0.1.27",   "description": "Drive Altium Designer from Adom: a resident DelphiScript agent (eval + the 1,100-command process launcher), the X2 -R command line, UIA over the VCL shell, dialogs by PID, screenshots of every Altium window, and Altium 365.",   "homepage": "https://wiki.adom.inc/adom/altium-bridge",   "docs": "https://wiki.adom.inc/adom/altium-bridge",
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "altium-bridge",-  "version": "0.1.26",+  "version": "0.1.27",   "type": "app",   "description": "Skills for your container so your AI knows how to drive the Altium Designer bridge (altium_* verbs). The bridge runtime itself is the release zip; Adom Bridge loads that.",   "tags": [
src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.1.26+0.1.27
src/server.py+77−28
@@ -1746,54 +1746,103 @@ REVIEW_NOT_IMPLEMENTED = {   def h_review_board(args):-    """Run the design-review checks over ONE board read (plus the current sheet, if asked).+    """Run the design-review checks. One agent round trip per document; all analysis in the bridge. -    One agent round trip, all the analysis in the bridge. Findings name the exact designator, net or-    layer and say what is wrong — never 'verify X'. `severity` is error | warning | info.+    Altium holds ONE current document, so a board read and a sheet read cannot both be "now" — this+    activates each in turn when given `board` / `sheet` paths, exactly like cross_check.++    Returns PARTIAL results rather than nothing: if the board cannot be read, the schematic checks+    still run and `skipped` says which checks had no data and why. A review that fails wholesale+    because one document was not current is a review nobody runs.     """     g = _need_licensed()     if g:         return g+    timeout = float(args.get("timeout", 90))+    board, sheet = {}, None+    board_err = sheet_err = None++    def _open(path):+        bad = _validate_openable(path)+        if bad:+            return bad+        o = agent.call("open", {"path": path}, timeout=timeout)+        d = o.get("data") or {}+        return None if d.get("ok", True) else {"error": d.get("error")}++    if args.get("board"):+        e = _open(args["board"])+        if e:+            return {"success": False, "stage": "board", **e}     r = agent.call("read_board", {"sections": "components,nets,layers,copper,occupancy,outline",-                                  "limit": args.get("limit", 2000)},-                   timeout=float(args.get("timeout", 90)))-    board = r.get("data") or {}-    if not board.get("ok"):-        return r-    sheet = None-    if args.get("includeSchematic"):-        sr = agent.call("read_schematic", {"sections": "components", "params": "1"},-                        timeout=float(args.get("timeout", 90)))-        sd = sr.get("data") or {}-        sheet = sd if sd.get("ok") else None+                                  "limit": args.get("limit", 2000)}, timeout=timeout)+    bd = r.get("data") or {}+    if bd.get("ok"):+        board = bd+    else:+        board_err = bd.get("error") or "board read failed"++    want_sheet = bool(args.get("includeSchematic") or args.get("sheet"))+    if want_sheet:+        if args.get("sheet"):+            e = _open(args["sheet"])+            if e:+                sheet_err = e.get("error")+        if not sheet_err:+            sr = agent.call("read_schematic", {"sections": "components", "params": "1"}, timeout=timeout)+            sd = sr.get("data") or {}+            if sd.get("ok"):+                sheet = sd+            else:+                sheet_err = sd.get("error") or "schematic read failed"++    if not board and not sheet:+        return {"success": False, "boardError": board_err, "schematicError": sheet_err,+                "_hint": "Nothing could be read. Pass board:<path> and/or sheet:<path> — Altium holds "+                         "only ONE document current, so the review activates each in turn."}+     wanted = args.get("checks")-    findings = []-    ran = []     unknown = sorted(set(wanted or []) - {n for n, _ in BOARD_CHECKS})+    findings, ran, skipped = [], [], {}     for name, fn in BOARD_CHECKS:         if wanted and name not in wanted:             continue+        needs_sheet = name in ("vendor_part_numbers", "missing_datasheets", "schematic_placeholder_values",+                               "duplicate_designators")+        if not board and not needs_sheet:+            skipped[name] = f"no board read ({board_err})"+            continue+        if needs_sheet and not sheet:+            skipped[name] = ("no schematic read" + (f" ({sheet_err})" if sheet_err else+                             " — pass includeSchematic:true or sheet:<path>"))+            continue         ran.append(name)         try:             findings.extend(fn(board, sheet))-        except Exception as e:  # a broken check must not lose the other checks' findings+        except Exception as e:             findings.append({"check": name, "severity": "info",                              "message": f"check {name} failed to run: {e}"})     order = {"error": 0, "warning": 1, "info": 2}-    findings.sort(key=lambda f: (order.get(f.get("severity"), 9), f.get("check"), str(f.get("designator") or f.get("net") or f.get("layer") or "")))+    findings.sort(key=lambda f: (order.get(f.get("severity"), 9), f.get("check"),+                                 str(f.get("designator") or f.get("net") or f.get("layer") or "")))     counts = {}     for f in findings:         counts[f["severity"]] = counts.get(f["severity"], 0) + 1-    out_unknown = {"unknownChecks": unknown} if unknown else {}-    return {"success": True, "board": board.get("board"),-            "findings": findings, "counts": counts, "checksRun": ran,-            "availableChecks": [n for n, _ in BOARD_CHECKS], **out_unknown,-            "notImplemented": REVIEW_NOT_IMPLEMENTED,-            "roundtripMs": r.get("roundtripMs"),-            "_hint": ("A clean result means the checks that RAN found nothing — see notImplemented for "-                      "what this review cannot see yet (board outline, ratsnest, silkscreen geometry). "-                      "Add includeSchematic:true to also read the current sheet.")}-+    out = {"success": True, "board": board.get("board"), "sheet": (sheet or {}).get("document"),+           "findings": findings, "counts": counts, "checksRun": ran,+           "availableChecks": [n for n, _ in BOARD_CHECKS],+           "notImplemented": REVIEW_NOT_IMPLEMENTED,+           "_hint": ("A clean result means the checks that RAN found nothing. `skipped` lists checks with "+                     "no data; `notImplemented` lists what this review cannot see yet.")}+    if skipped:+        out["skipped"] = skipped+    if unknown:+        out["unknownChecks"] = unknown+    if board_err:+        out["boardError"] = board_err+    if sheet_err:+        out["schematicError"] = sheet_err+    return out   def h_read_board(args):