app
Altium - the Altium Bridge
Public Made by Adomby adom
Adom Bridge for Altium Designer: a resident DelphiScript agent gives the AI eval, the 1,100-command process launcher, all 8 EDA surfaces with screenshots, compile/ERC, DRC, OutJob outputs, native SchLib/PcbLib authoring with STEP 3D bodies, and dialog handling by PID.
← Commit history
v0.1.25: check-id filtering fixed, read_board exposed
7 files changed
+49−13
CHANGELOG.md+4@@ -1,5 +1,9 @@ # Changelog +## 0.1.25 — 2026-09-01+- **`checks:[...]` now filters on the ID you actually see.** The selector was derived from the Python function name (`_chk_components_outside_board`) while findings report `components_outside_board_outline`, so filtering by the visible name silently ran ZERO checks and returned a clean review. Each check now declares its own ID, the reply lists `availableChecks`, and an unrecognised name comes back as `unknownChecks` instead of being ignored.+- **New verb `altium_read_board`** — the raw read (components, nets, layers, copper, rules, occupancy, outline) in one round trip. It existed only in the Rust bridge, which meant nobody could see what a check saw; a review's silence had to be taken on trust. Now it can be inspected directly.+ ## 0.1.24 — 2026-09-01 - **`read_board` gains an `outline` section** — the board extent, read through the SAME guarded pattern the library reader already uses, because a library's internal board has a nil `BoardOutline` and touching `BoundingRectangle` on it halts the engine (measured 2026-08-30). It reports which source it used: `boardOutline` (the real outline's bounding box) or `boardBounds` (everything on the board, including strays outside it). - **Two review checks unlocked by it**, both named for what they actually measure:
adom-bridge-altium-manifest.json+4−4@@ -1,9 +1,9 @@ { "name": "altium",- "version": "0.1.24",- "url": "https://wiki.adom.inc/download/adom/altium-bridge/0.1.24/adom-bridge-altium-v0.1.24.zip",- "sha256": "db394b52c27eb6c501c25dc2779b05133ec1d70ec587baf3be41b3a9ddf10870",- "size": 124096,+ "version": "0.1.25",+ "url": "https://wiki.adom.inc/download/adom/altium-bridge/0.1.25/adom-bridge-altium-v0.1.25.zip",+ "sha256": "480d7fea5b48f0b5423096278512b2a4f597f04674806a095de31e6a464904b1",+ "size": 124534, "verbPrefixes": [ "altium_" ],
adom-bridge-altium-v0.1.25.zipadded⋯ 1 unchanged line ⋯
bridge.json+2−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "altium", "displayName": "Altium Designer",- "version": "0.1.24",+ "version": "0.1.25", "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",@@ -91,6 +91,7 @@ "altium_place_on_pcb", "altium_place_on_sch", "altium_progress",+ "altium_read_board", "altium_read_rules", "altium_read_schematic", "altium_read_symbol",
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "altium-bridge",- "version": "0.1.24",+ "version": "0.1.25", "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.24+0.1.25
src/server.py+37−6@@ -1630,9 +1630,19 @@ def _chk_components_near_board_edge(board, sheet, margin_mil=20): return out -BOARD_CHECKS = [_chk_placeholder_values, _chk_single_pin_nets, _chk_unused_mechanical_layers,- _chk_off_grid_components, _chk_duplicate_designators,- _chk_components_outside_board, _chk_components_near_board_edge]+# (id, fn). The ID is what appears in a finding's `check` field, so `checks:["..."]` filters on the+# name the caller actually sees. Deriving it from the Python function name instead was a trap: the+# function is `_chk_components_outside_board`, the finding says `components_outside_board_outline`,+# and filtering by the visible name silently ran NOTHING (2026-09-01).+BOARD_CHECKS = [+ ("placeholder_values", _chk_placeholder_values),+ ("single_pin_nets", _chk_single_pin_nets),+ ("unused_mechanical_layers", _chk_unused_mechanical_layers),+ ("off_grid_components", _chk_off_grid_components),+ ("duplicate_designators", _chk_duplicate_designators),+ ("components_outside_board_outline", _chk_components_outside_board),+ ("components_near_board_edge", _chk_components_near_board_edge),+] # Named so a clean result is not mistaken for a complete one. REVIEW_NOT_IMPLEMENTED = {@@ -1670,8 +1680,8 @@ def h_review_board(args): wanted = args.get("checks") findings = [] ran = []- for fn in BOARD_CHECKS:- name = fn.__name__.replace("_chk_", "")+ unknown = sorted(set(wanted or []) - {n for n, _ in BOARD_CHECKS})+ for name, fn in BOARD_CHECKS: if wanted and name not in wanted: continue ran.append(name)@@ -1685,8 +1695,10 @@ def h_review_board(args): 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 "@@ -1694,6 +1706,24 @@ def h_review_board(args): "Add includeSchematic:true to also read the current sheet.")} ++def h_read_board(args):+ """The raw board read: components, nets, layers, copper, rules, occupancy, outline — ONE round+ trip. This is the primitive every board view is derived from; exposing it means a caller can see+ exactly what a check saw, instead of inferring it from the check's silence."""+ g = _need_licensed()+ if g:+ return g+ r = agent.call("read_board", {"sections": args.get("sections") or "components,nets,layers,copper,rules,occupancy,outline",+ "limit": args.get("limit", 2000)},+ timeout=float(args.get("timeout", 90)))+ d = r.get("data") or {}+ if not d.get("ok"):+ return r+ return {"success": True, "board": d.get("board"), "sections": d.get("sections"),+ "data": d, "roundtripMs": r.get("roundtripMs")}++ # ---- licence lifecycle: status, ensure-licensed, trial setup (John 2026-08-31) ------------ def h_license_status(args):@@ -2261,6 +2291,7 @@ _VERB_CATALOG = { "close": "WM_CLOSE the main window; reports a save prompt instead of answering it; force:true taskkills.", "activate_document": "Make an ALREADY-OPEN document current by invoking its document-bar tab through UIA — no cursor, no foreground.", "layers_in_use": "How many layers the board ACTUALLY has: copper layers carrying primitives, densest first (a floor, not the stack).",+ "read_board": "The raw board read (components, nets, layers, copper, rules, occupancy, outline) in ONE round trip.", "review_board": "Design review over one board read: placeholder values, single-pin nets, unused mechanical layers, off-grid parts, duplicate designators.", "read_schematic": "Components + pins of the CURRENT schematic sheet (one round trip).", "cross_check": "Schematic sheet vs board: what is drawn but not placed, and placed but not drawn.",@@ -2328,7 +2359,7 @@ COMMAND_HANDLERS = { "window_info": h_window_info, "state": h_state, "check_dialogs": h_check_dialogs, "dismiss_dialogs": h_dismiss_dialogs, "launch": h_launch, "close": h_close, "activate_document": h_activate_document, "layers_in_use": h_layers_in_use,- "instances": h_instances, "review_board": h_review_board, "read_schematic": h_read_schematic, "cross_check": h_cross_check,+ "instances": h_instances, "review_board": h_review_board, "read_board": h_read_board, "read_schematic": h_read_schematic, "cross_check": h_cross_check, "agent_start": h_agent_start, "agent_stop": h_agent_stop, "agent_status": h_agent_status, "agent_recover": h_agent_recover, "eval": h_eval, "run_command": h_run_command, "run_script": h_run_script,