← Commit history

standard 0.25.0: reference Altium 0.2.84; update_footprints_from_library (core) and lint_library (checks) join; shared vocabularies in conventions

Colby Knox ·7797d05677 ·11d ago ·parent 202c4bd
6 files changed +172−12
README.md+4−3
@@ -60,12 +60,12 @@ verb by verb: [docs/coverage.md](docs/coverage.md). <!-- coverage:start (tools/generate.py writes this) --> | Tier | Verbs | Altium | KiCad | Fusion 360 | |---|---|---|---|---|-| **core** | 93 | 93 | 89 | 90 |+| **core** | 94 | 94 | 90 | 91 | | **native** | 47 | 47 | 44 | 42 |-| **checks** | 37 | 37 | 37 | 37 |+| **checks** | 38 | 38 | 38 | 38 | | **app** | 62 | 62 | 49 | 53 | | **platform** | 18 | 18 | 0 | 1 |-| standard (all but platform) | 239 | 239 | 219 | 222 |+| standard (all but platform) | 241 | 241 | 221 | 224 | <!-- coverage:end -->  The **checks** tier is where the other bridges are furthest behind, and it is the cheapest to close: those verbs are@@ -158,6 +158,7 @@ Decisions so far (proposals from the KiCad and Fusion bridges, 2026-10-27; issue  ## Status +Version 0.25.0: the reference is Altium bridge 0.2.84, which brings two verbs into the standard: `update_footprints_from_library` (core, #27) and `lint_library` (checks, #28). The standard is 241 verbs. Version 0.24.4: coverage reads Fusion 360 bridge 1.13.26 and kicad-lane's first #29 equivalents (KiCad 219); tiers for `update_footprints_from_library` (core, #27) and `lint_library` (checks, #28), which enter the standard with Altium's 0.2.84 reference. Version 0.24.3: coverage reads KiCad bridge 1.0.89 (214 of 239: animate_view). Version 0.24.2: engine 0.21.1: `duplicate_pad_number` counts touching or overlapping pads as one land (a split exposed pad is not a duplicate; KiCad #28). KiCad's `swap_footprint` is no longer counted as `update_footprints_from_library`, which does a different job (#27).
docs/conventions.md+40
@@ -112,3 +112,43 @@ The common ones. A bridge adds its own for its tool's states, in the same snake_ `<prefix>_describe` returns every verb with `summary`, `input`, `output`, `hint`, `pitfalls`, `related`, `example` and `timeoutSeconds`. `verbs/standard.json` is generated from the reference bridge's `describe` and the agreed proposals in `tools/proposed/`.++## Shared vocabularies++Some verbs answer with a fixed set of words, so an AI reads every bridge's answer the same way. A bridge can add a word by proposing it on the page's issues.++### `update_footprints_from_library` statuses (#27)++| status | meaning |+|---|---|+| `up_to_date` | the placed footprint matches the library's |+| `differs` | it doesn't; `diff` says how (`padsAdded`, `padsRemoved`, `padsChanged[{pad, changes}]`, `bodies3d?`) |+| `library_not_found` | the footprint's library or the footprint in it wasn't found; `why` says which |+| `unusable` | the library copy was found but can't be laid onto the board; `why` says why. Read it like `library_not_found` |++A plan (`apply` false, the default) changes nothing. Tracks don't follow pads that moved.++### `lint_library` rules (#28)++Severity belongs to the rule, not the bridge. A bridge emits only the rules it can evaluate, so a missing finding is not a pass. `ok` is true when there are no errors and nothing is `unread`; warnings don't fail it. The shared checks engine (`lint_library`) evaluates every rule but `tool_refuses_file` from the footprints and symbols a bridge reads.++| kind | rule | severity | what it means |+|---|---|---|---|+| footprint | `no_pads` | error | no pads at all |+| footprint | `zero_size_pad` | error | a pad with no copper and no hole |+| footprint | `unnumbered_pad` | warning | a pad that isn't mechanical has no number |+| footprint | `duplicate_pad_number` | warning | one number on lands apart from each other (pads that touch or overlap are one land) |+| footprint | `no_courtyard` | warning | no courtyard |+| footprint | `origin_off_part` | warning | the origin is outside the pads and courtyard, grown by 1 mm |+| footprint | `no_silkscreen` | info | no silkscreen |+| footprint | `no_3d_body` | info | no 3D body |+| footprint | `no_description` | info | no description |+| symbol | `no_pins` | error | no pins |+| symbol | `footprint_not_found` | error | the linked footprint isn't in the libraries checked |+| symbol | `pin_without_pad` | error | a pin number has no pad on the linked footprint |+| symbol | `unnumbered_pin` | warning | a pin has no number |+| symbol | `duplicate_pin_number` | warning | two pins share a number |+| symbol | `no_footprint` | warning | no footprint is linked |+| symbol | `pad_without_pin` | warning | a pad (not mechanical) on the linked footprint has no pin |+| symbol | `no_part_number` | info | no manufacturer part number |+| library | `tool_refuses_file` | error | the tool itself won't read the file; `detail` carries its message |
docs/coverage.md+6−4
@@ -1,17 +1,17 @@ # Coverage: every standard verb on each bridge -Altium bridge 0.2.83 (the reference), KiCad bridge 1.0.89, Fusion 360 bridge 1.13.26. 56 verbs came from other bridges' proposals (tools/proposed/); they are marked *proposed*.+Altium bridge 0.2.84 (the reference), KiCad bridge 1.0.89, Fusion 360 bridge 1.13.26. 56 verbs came from other bridges' proposals (tools/proposed/); they are marked *proposed*. `exact` is the same job under the same or an equivalent name; `partial` does part of it or does it differently (`verbs/coverage.json` names what in `note`); `unsupported` answers `not_supported` for the whole job and is not counted; blank is missing. Generated by `tools/generate.py` from `verbs/coverage.json`; do not edit by hand.  | Tier | Verbs | Altium | KiCad | Fusion 360 | |---|---|---|---|---|-| **core** | 93 | 93 | 89 | 90 |+| **core** | 94 | 94 | 90 | 91 | | **native** | 47 | 47 | 44 | 42 |-| **checks** | 37 | 37 | 37 | 37 |+| **checks** | 38 | 38 | 38 | 38 | | **app** | 62 | 62 | 49 | 53 | | **platform** | 18 | 18 | 0 | 1 |-| standard (all but platform) | 239 | 239 | 219 | 222 |+| standard (all but platform) | 241 | 241 | 221 | 224 |  ## core @@ -111,6 +111,7 @@ Read and edit the design. Every bridge provides these: they are how an AI sees a | `silk_state` | The silkscreen as it stands: every part's body box, its designator and comment (text, location, size, rotation, shown, Altium's measured box), and the free... | `altium_silk_state` | `kicad_silk_state` | `fusion_silk_state` | | `swap_footprint` | Replace the FIRST component on the current board with a footprint from a PcbLib at the same position, re-assigning nets by pad designator. Tracks are... | `altium_swap_footprint` |  | `fusion_swap_footprint` | | `symbol_prims` | Read a SchLib component's drawing: primitive counts by kind, body rectangle, polyline bounding box, display mode and the footprint models. Read-only, use it... | `altium_symbol_prims` | `kicad_symbol_prims` | `fusion_symbol_prims` |+| `update_footprints_from_library` | Compare each placed footprint with its library's current one and, with apply:true, replace the ones that differ, keeping where the part sits (position,... | `altium_update_footprints_from_library` | `kicad_update_footprints_from_library` | `fusion_update_footprints_from_library` (partial) | | `xcopy_footprint` | Copy a footprint ACROSS libraries: from fromLib into toLib (opened and made current), optionally under a new name. | `altium_xcopy_footprint` | `kicad_xcopy_footprint` | `fusion_xcopy_footprint` (partial) |  ## native@@ -195,6 +196,7 @@ Engineering computed from the board model, not by the tool: one shared engine, s | `import_part` | One call imports a whole part: footprint spec + STEP + symbol modes + models + params + save. | `altium_import_part` | `kicad_install_footprint / kicad_install_symbol` (partial) | `fusion_import_part` (partial) | | `jlcpcb_order` | A JLCPCB order in one call: the board checked against JLCPCB's fab limits, the Gerbers and drill zipped the way JLCPCB's order page takes them, the JLCPCB... | `altium_jlcpcb_order` | `kicad_jlcpcb_order` | `fusion_jlcpcb_order` | | `length_tune` | Lengthen a routed net to a target length, or to match another net (a pair's skew, a DDR byte lane): a serpentine in its longest straight track that has room... | `altium_length_tune` | `kicad_length_tune` | `fusion_length_tune` |+| `lint_library` | Check a .PcbLib or .SchLib before it ships: footprints without pads, courtyard, silkscreen or 3D body, with duplicate or missing pad numbers, an origin off... | `altium_lint_library` | `kicad_lint_library` | `fusion_lint_library` | | `net_lengths` | Routed length of every net (or the ones named) on the live board, by layer with its vias; the skew inside each differential pair; and the spread of... | `altium_net_lengths` | `kicad_net_lengths` | `fusion_net_lengths` | | `pin_pad_check` | Do the footprints fit their symbols? Part by part, the symbol's pin numbers (from the sheet files) against the footprint's pad names (from the board): pins... | `altium_pin_pad_check` | `kicad_pin_pad_check` | `fusion_pin_pad_check` | | `place_decoupling` *proposed* | Plan positions for decoupling capacitors beside an IC's power pins (each cap just outside the courtyard, its power pad facing the pin), answered as... | `altium_place_decoupling` | `kicad_place_decoupling` | `fusion_place_decoupling` |
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "eda-bridge-standard",-  "version": "0.24.4",+  "version": "0.25.0",   "type": "skill",   "title": "EDA Bridge Standard: one way for an AI to drive every PCB tool",   "description": "The standard every Adom EDA bridge follows so one AI can drive Altium, KiCad, Fusion 360 and any future PCB tool the same way: shared verb names in tiers, the board model JSON, reply and refusal conventions, and a coverage table of each bridge against the Altium reference.",
verbs/coverage.json+38−2
@@ -1,8 +1,8 @@ {- "standard": "0.24.4",+ "standard": "0.25.0",  "reference": {   "bridge": "altium",-  "version": "0.2.83"+  "version": "0.2.84"  },  "proposed": 56,  "compared": {@@ -1603,6 +1603,24 @@     "match": "exact"    }   },+  {+   "name": "update_footprints_from_library",+   "tier": "core",+   "source": "altium",+   "altium": {+    "verb": "altium_update_footprints_from_library",+    "match": "exact"+   },+   "kicad": {+    "verb": "kicad_update_footprints_from_library",+    "match": "exact"+   },+   "fusion360": {+    "verb": "fusion_update_footprints_from_library",+    "match": "partial",+    "note": "the plan only, against .lbr files named (a Fusion design's libraries are cloud items); apply is refused with the plan, since Fusion's UPDATE asks about each part in a dialog"+   }+  },   {    "name": "xcopy_footprint",    "tier": "core",@@ -2819,6 +2837,24 @@     "match": "exact"    }   },+  {+   "name": "lint_library",+   "tier": "checks",+   "source": "altium",+   "altium": {+    "verb": "altium_lint_library",+    "match": "exact"+   },+   "kicad": {+    "verb": "kicad_lint_library",+    "match": "exact"+   },+   "fusion360": {+    "verb": "fusion_lint_library",+    "match": "exact",+    "note": "a .lbr read offline (packages: pads, tKeepout courtyard, tPlace silk, 3D packages, description; components: pins, footprint, MPN) and judged by the shared engine"+   }+  },   {    "name": "net_lengths",    "tier": "checks",
verbs/standard.json+83−2
@@ -1,8 +1,8 @@ {- "standard": "0.24.4",+ "standard": "0.25.0",  "reference": {   "bridge": "altium",-  "version": "0.2.83"+  "version": "0.2.84"  },  "proposed": 56,  "compared": {@@ -3537,6 +3537,49 @@    "timeoutSeconds": 60,    "unitsNote": "The reference bridge answers this verb in mils (xMil). A bridge following the standard answers in mm under Mm names (xMm)."   },+  {+   "name": "update_footprints_from_library",+   "tier": "core",+   "source": "altium",+   "summary": "Compare each placed footprint with its library's current one and, with apply:true, replace the ones that differ, keeping where the part sits (position, rotation, side), its designator and comment, its pad nets by pad number and its schematic link. KiCad's verb of the same name.",+   "input": {+    "apply": "bool, default false: a plan; true replaces every differing footprint",+    "expectedRevision": "string: refuse with stale_board if the board changed since the plan",+    "filePath": "the .PcbDoc (required)",+    "libraries": "[.PcbLib]: several",+    "libraryPath": "a .PcbLib to take footprints from (default: the .PcbLibs the board's project lists; a part's own source library first)",+    "refs": "[\"U1\"] (default every footprint)"+   },+   "output": {+    "differs": "int",+    "footprints": "[{ref, footprint, library, status: up_to_date | differs | library_not_found, diff?: {padsAdded, padsRemoved, padsChanged: [{pad, changes}], bodies3d?}, why?}]",+    "mutated": "bool",+    "plan": "bool",+    "problems": "[read-back problems] (apply)",+    "replaced": "[{ref, replaced, netsKept, netsBefore}] (apply)",+    "revision": "string"+   },+   "hint": "Plan first: each differing part's diff names the pads added, removed or changed (moved, resized, reshaped, redrilled, surface or through-hole) and a 3D body count that differs. apply:true replaces every differing part and reads them back: each must sit where it did and match its library. Save with altium_save.",+   "pitfalls": [+    "A pad new in the library has no net, and a removed pad's net goes with it: read padsAdded and padsRemoved before apply.",+    "Tracks are not moved to follow pads that moved: run altium_routing_drc after."+   ],+   "related": [+    "edit_footprint",+    "footprint_from_land_pattern",+    "swap_footprint",+    "routing_drc",+    "save"+   ],+   "example": {+    "filePath": "C:/proj/Board.PcbDoc",+    "refs": [+     "U1"+    ]+   },+   "longRunning": false,+   "timeoutSeconds": 600+  },   {    "name": "xcopy_footprint",    "tier": "core",@@ -6360,6 +6403,44 @@    "longRunning": false,    "timeoutSeconds": 120   },+  {+   "name": "lint_library",+   "tier": "checks",+   "source": "altium",+   "summary": "Check a .PcbLib or .SchLib before it ships: footprints without pads, courtyard, silkscreen or 3D body, with duplicate or missing pad numbers, an origin off the part or no description; symbols without pins or a footprint, with duplicate pin numbers or no part number, and pins that no pad of the linked footprint answers. KiCad's verb of the same name (KiCad's checks that the file parses; an Altium library always does, so this one checks what is in it).",+   "input": {+    "footprintLibraries": "[.PcbLib] for a SchLib: where the linked footprints are (default the .PcbLibs in the SchLib's folder); footprintLibrary for one",+    "libraryPath": "the .PcbLib or .SchLib (required; filePath accepted too)",+    "names": "[name] (default every footprint or symbol)"+   },+   "output": {+    "checked": "int",+    "errors": "int",+    "findings": "[{name, kind: footprint|symbol, rule, severity: error|warning|info, detail}]",+    "footprintLibraries": "[path] (SchLib)",+    "infos": "int",+    "ok": "bool: no errors and every item read",+    "unread": "[{name, error}]",+    "warnings": "int"+   },+   "hint": "Rules: no_pads, zero_size_pad, unnumbered_pad, duplicate_pad_number, no_courtyard (Mechanical 15), no_silkscreen, no_3d_body, origin_off_part, no_description; no_pins, unnumbered_pin, duplicate_pin_number, no_part_number, no_footprint, footprint_not_found, pin_without_pad, pad_without_pin. Read-only.",+   "pitfalls": [+    "Each item is one read (about half a second): a library of hundreds takes minutes; names picks a few.",+    "duplicate_pad_number is right for one pin on several pads (a thermal pad): it is a warning, not an error."+   ],+   "related": [+    "read_footprint",+    "read_symbol",+    "edit_footprint",+    "pin_pad_check",+    "list_library"+   ],+   "example": {+    "libraryPath": "C:/lib/Parts.SchLib"+   },+   "longRunning": false,+   "timeoutSeconds": 600+  },   {    "name": "net_lengths",    "tier": "checks",