skill
EDA Bridge Standard: one way for an AI to drive every PCB tool
Public Made by Adomby adom
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.
← Commit history
standard 0.3.1: an internal plane is In<n>.Cu by its place in the copper stack, kind plane (agreed with altium-lane on #8); the stack-up companion gains layer (the board model name); README: fixtures live on each bridge's page (Altium's on adom/altium-bridge fixtures/), the checks implementation's home is being decided; regenerated with altium 0.2.76 (223 verbs, all of batch 1): Altium 204, KiCad 143, Fusion 86 of 236
8 files changed
+40−28
README.md+9−10@@ -47,12 +47,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 | 77 | 72 | 29 |-| **native** | 46 | 35 | 35 | 18 |+| **core** | 93 | 80 | 72 | 29 |+| **native** | 46 | 36 | 35 | 18 | | **checks** | 37 | 32 | 11 | 4 | | **app** | 60 | 56 | 25 | 35 | | **platform** | 19 | 19 | 0 | 0 |-| standard (all but platform) | 236 | 200 | 143 | 86 |+| standard (all but platform) | 236 | 204 | 143 | 86 | <!-- coverage:end --> The **checks** tier is where the other bridges are furthest behind, and it is the cheapest to close: those verbs are@@ -142,7 +142,7 @@ Decisions so far (proposals from the KiCad and Fusion bridges, 2026-10-27; issue ## Status -Version 0.3.0: 33 more verbs from other bridges, all agreed by the Altium bridge's thread: 26 from the KiCad bridge (the board exports ODB++, IPC-2581, IPC-356, GenCAD and DXF, `render_board_3d`, `remove_parts`, `edit_parts`, `align_parts`, `copy_routing`, projects, library footprints, `estimate_cost`, `check_clearance`, `place_decoupling`, `plan_fanout`) and 7 from the Fusion bridge (`bodies`, `render_board_layers`, `plan_route`, `generate_body`, `library_views`, `close_all_documents`, `document_info`). A bridge's own equivalents now win over a name match, so a verb that shares a standard name but does another job is not credited for it. Version 0.2.1: this page has one keeper (standard-lane) and bridge threads push only their own files (*Keeping it in+Version 0.3.1: an internal plane is named `In<n>.Cu` by its place in the copper stack, with `kind: "plane"` ([board model](docs/board-model.md#settled-questions)); the stack-up companion gives each copper layer's board model name in `layer`. Version 0.3.0: 33 more verbs from other bridges, all agreed by the Altium bridge's thread: 26 from the KiCad bridge (the board exports ODB++, IPC-2581, IPC-356, GenCAD and DXF, `render_board_3d`, `remove_parts`, `edit_parts`, `align_parts`, `copy_routing`, projects, library footprints, `estimate_cost`, `check_clearance`, `place_decoupling`, `plan_fanout`) and 7 from the Fusion bridge (`bodies`, `render_board_layers`, `plan_route`, `generate_body`, `library_views`, `close_all_documents`, `document_info`). A bridge's own equivalents now win over a name match, so a verb that shares a standard name but does another job is not credited for it. Version 0.2.1: this page has one keeper (standard-lane) and bridge threads push only their own files (*Keeping it in sync*), one file per owner (`tools/equivalents/<bridge>.json`, `tools/proposed/<thread>.json`); the decisions on the KiCad and Fusion proposals are written down above; a lesson from the Fusion bridge on DRC severity is in [docs/conventions.md](docs/conventions.md). Version 0.2.0: verbs can come from any bridge. The first batch, 21 schematic verbs from the KiCad bridge (`sch_connect_pins`, `sch_bus`, `sch_items`, `sch_pins`, `sch_trace_point`, `render_schematic` ...), was agreed by the@@ -150,9 +150,8 @@ Altium bridge's thread; Altium now shows those as gaps. Earlier: version 0.1.2: settles the questions the KiCad and Fusion bridges raised as they adopted the board model (the y-down frame, pour islands, fractured fills, custom pads, reply units): the answers are in [docs/board-model.md](docs/board-model.md#settled-questions). -Next: the checks tier as a shared Rust crate and command-line tool every bridge can call, `adom-eda-checks`. It takes-the board model and its companions as plain JSON and calls no bridge, so a bridge can link it or run it. Its source-will live on this page under `engine/`, and a bridge vendors a copy at a tagged standard version (the wiki has no git-protocol, so Cargo cannot fetch it from here). It moves over from the Altium bridge's Rust a module at a time, Altium-swapping its own copies first. Then fixture boards (the same small board as `.PcbDoc`, `.kicad_pcb` and Fusion) with-the expected board model and check results, so a bridge can prove it conforms.+Next: the checks tier's implementation, so every bridge that serves the board model gets the same checks. Where it+lives is being decided (a shared crate, or Adom's analysis tools reading the board model); the verbs' names and reply+shapes stay here either way. Fixtures, a bridge's saved replies on a small board with the expected check results, live+on each bridge's own page, not here: Altium's are on [adom/altium-bridge](https://wiki.adom.inc/adom/altium-bridge)+under `fixtures/`.
docs/board-model.md+2−1@@ -61,7 +61,7 @@ layer names. | Model | Verb | What reads it | |---|---|---|-| Stack-up | `layer_stack` | impedance, current capacity, fab check (copper weight, thickness), copper balance, return paths (planes) |+| Stack-up | `layer_stack` | impedance, current capacity, fab check (copper weight, thickness), copper balance, return paths (planes). Each copper layer carries its board model name in `layer` (`F.Cu`, `In<n>.Cu`, `B.Cu`); an internal plane is `In<n>.Cu` by its place in the copper stack, with `kind: "plane"`, and counts as copper everywhere but its splits and antipads | | Poured copper | `pour_state` | copper balance, return paths and their fix plans. Island by island, outline and holes, filled by the even-odd rule: a fractured fill (KiCad: holes joined to the outline by zero-width cuts, `holes: []`, `fractured: true`) is the same copper and needs no separating | | 3D bodies | `bodies` (Altium: an agent call behind `height_check`) | height check, placement check. One box per body (Altium) or per part (Fusion); z from the surface the part sits on | | Sheet parts with pin types | `read_sheet_file` (Altium reads the .SchDoc file itself) | schematic review, pin-to-pad check. Pin types by name (`input`, `io`, `output`, `power`, `supply`, `not_connected`...); Altium's codes are accepted too |@@ -77,3 +77,4 @@ layer names. | Must fractured fills be split into outline and holes? | No: even-odd filling gives the same copper | [#1](https://wiki.adom.inc/adom/eda-bridge-standard/issues/1) | | Custom pads? | Bounding rectangle, plus `copperPoints` when known | [#2](https://wiki.adom.inc/adom/eda-bridge-standard/issues/2) | | Units in replies? | mm, and a unit-named field says mm (`xMm`); see conventions | [#2](https://wiki.adom.inc/adom/eda-bridge-standard/issues/2) |+| What is an internal plane called? | `In<n>.Cu` by its place in the copper stack, `kind: "plane"` in the stack-up; a tool's own id may ride along (`layerId`), never in its place | [#8](https://wiki.adom.inc/adom/eda-bridge-standard/issues/8) |
docs/coverage.md+7−7@@ -6,12 +6,12 @@ Generated by `tools/generate.py` from `verbs/coverage.json`; do not edit by hand | Tier | Verbs | Altium | KiCad | Fusion 360 | |---|---|---|---|---|-| **core** | 93 | 77 | 72 | 29 |-| **native** | 46 | 35 | 35 | 18 |+| **core** | 93 | 80 | 72 | 29 |+| **native** | 46 | 36 | 35 | 18 | | **checks** | 37 | 32 | 11 | 4 | | **app** | 60 | 56 | 25 | 35 | | **platform** | 19 | 19 | 0 | 0 |-| standard (all but platform) | 236 | 200 | 143 | 86 |+| standard (all but platform) | 236 | 204 | 143 | 86 | ## core @@ -86,14 +86,14 @@ Read and edit the design. Every bridge provides these: they are how an AI sees a | `sch_edit` | Change placed parts on a sheet: move (to x,y mil), moveBy (dx,dy), rotate (degrees), rename (designator), re-comment (comment), delete. One undo step. | `altium_sch_edit` | `kicad_sch_set_property / kicad_sch_move / kicad_sch_delete / kicad_sch_rotate` (partial) | | | `sch_edit_label` *proposed* | Edit net labels: rotate, move to a point or by a delta, rename, or move every label of a net at once. One undo step. | `altium_sch_edit_label` | `kicad_sch_edit_label` | | | `sch_fix_connectivity` *proposed* | Snap near-miss wire ends onto the pin, label, sheet pin or wire end they almost touch, within a tolerance. dryRun (the default) lists the moves;... | `altium_sch_fix_connectivity` | `kicad_sch_fix_connectivity` | |-| `sch_hierarchy` *proposed* | The sheet tree of a design, recursively: each sheet's name, file, id, instance path, page number, position, size and pins. renumber:true renumbers pages... | | `kicad_sch_hierarchy` | |+| `sch_hierarchy` *proposed* | The sheet tree of a design, recursively: each sheet's name, file, id, instance path, page number, position, size and pins. renumber:true renumbers pages... | `altium_sch_hierarchy` | `kicad_sch_hierarchy` | | | `sch_items` *proposed* | List a sheet's wiring items with ids and coordinates: wires, buses, bus entries, net labels (local, global, hierarchical), power ports, junctions,... | `altium_sch_items` | `kicad_sch_items` | | | `sch_junction` *proposed* | Add junction dots at points, or remove dots by id or point. One undo step. | `altium_sch_junction` | `kicad_sch_junction` | | | `sch_move_region` *proposed* | Move everything inside a box by (dx, dy): parts, wire ends inside the box (wires crossing its edge stretch), labels, junctions, no-connect markers, texts,... | `altium_sch_move_region` | `kicad_sch_move_region` | |-| `sch_page` *proposed* | Read or set a sheet's page: paper size, orientation and title block (title, date, revision, company, comment lines). | | `kicad_sch_page` | |+| `sch_page` *proposed* | Read or set a sheet's page: paper size, orientation and title block (title, date, revision, company, comment lines). | `altium_sch_page` | `kicad_sch_page` | | | `sch_pins` *proposed* | Every pin of the named parts on a sheet: its absolute position (rotation and mirror applied), number, name, electrical type, the side it leaves the body,... | `altium_read_sheet_file` (partial) | `kicad_sch_pins` | | | `sch_place_part` | Place a component from a .SchLib onto an EXISTING sheet at a position, with its library parameters (MPN, Manufacturer, distributor numbers) and its... | `altium_sch_place_part` | `kicad_sch_place_symbol` | |-| `sch_reset_fields` *proposed* | Put parts' designator and value text (or the named fields) back where the library symbol places them, through each part's rotation and mirror. One undo step. | | `kicad_sch_reset_fields` | |+| `sch_reset_fields` *proposed* | Put parts' designator and value text (or the named fields) back where the library symbol places them, through each part's rotation and mirror. One undo step. | `altium_sch_reset_fields` | `kicad_sch_reset_fields` | | | `sch_sheet_pins` *proposed* | A sheet symbol's pins: list, add, edit (rename, kind, move along an edge) or delete. import:true adds a pin for every hierarchical port inside the child... | `altium_sch_sheet_symbol` (partial) | `kicad_sch_sheet_pins` | | | `sch_sheet_symbol` | Make a design hierarchical: a sheet symbol on the parent sheet for a child sheet, its entries joined to the parent's nets, and matching ports on the child.... | `altium_sch_sheet_symbol` | `kicad_sch_sheet` (partial) | | | `sch_split_wire` *proposed* | Split the wire through a point into two wires there, with a junction dot at the cut, so a new branch can end on a wire end. One undo step. | `altium_sch_split_wire` | `kicad_sch_split_wire` | |@@ -158,7 +158,7 @@ Wraps the EDA tool's own feature (its DRC, ERC, Gerbers, pours, Update PCB). Eve | `routing_drc` | Design-rule violations on the current board WITH locations, plus the unrouted (ratsnest) count, as one clean/not-clean answer. | `altium_routing_drc` | `kicad_routing_validate` (partial) | `fusion_routing_drc` | | `run_drc` | Run the design rule check on a board and read the violation markers back. PCB:DesignRuleCheck opens a MODAL; the bridge clicks 'Run Design Rule Check'... | `altium_run_drc` | `kicad_run_drc` | `fusion_run_drc` | | `run_erc` | Electrical rules check. Same handler as altium_compile: in Altium, compiling the project IS the ERC. | `altium_run_erc` | `kicad_run_erc` | `fusion_run_erc` |-| `sch_update_from_library` *proposed* | Update the placed symbols on a sheet from their libraries (all, or the listed ones) and report which changed, which already matched and which no library has. | | `kicad_sch_update_from_library` | |+| `sch_update_from_library` *proposed* | Update the placed symbols on a sheet from their libraries (all, or the listed ones) and report which changed, which already matched and which no library has. | `altium_sch_update_from_library` | `kicad_sch_update_from_library` | | | `set_design_rules` | Write a fab's design rules into the current board: clearance, track width, routing via style, hole size, annular ring, hole-to-hole and board-edge clearance... | `altium_set_design_rules` | `kicad_set_design_rules` | `fusion_set_design_rules` | | `silkscreen_prep` | Clean the silkscreen off the copper with Altium's own Tools > Silkscreen Preparation: silk clipped clear of every mask opening (pad) by the board's silk... | `altium_silkscreen_prep` | | | | `symbol_mode` | Show one display mode (0 = Normal, 1.. = alternates) of the CURRENT SchLib component in the editor and capture a frame. Drives the editor's own mode toggle,... | `altium_symbol_mode` | | |
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "eda-bridge-standard",- "version": "0.3.0",+ "version": "0.3.1", "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.",
page.json+1−1@@ -4,7 +4,7 @@ "type": "skill", "title": "EDA Bridge Standard: one way for an AI to drive every PCB tool", "brief": "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.",- "version": "0.3.0",+ "version": "0.3.1", "hero": { "type": "billboard", "headline": "One standard for every EDA bridge",
schema/companions.schema.json+2−2@@ -12,9 +12,9 @@ "copperLayers": {"type": "integer"}, "totalUm": {"type": "number", "description": "board thickness, micrometres"}, "layers": {"type": "array", "description": "the copper layers top to bottom, each with the dielectric BELOW it", "items": {"type": "object", "required": ["name", "kind", "copperUm"],- "properties": {"name": {"type": "string"}, "kind": {"enum": ["signal", "plane"]}, "copperUm": {"type": "number"},+ "properties": {"name": {"type": "string", "description": "the tool's own name for the layer (Top Layer)"}, "layer": {"type": "string", "description": "the board model's name for this copper layer: F.Cu, In<n>.Cu by its place in the copper stack, B.Cu. An internal plane is named the same way (kind plane), never by a tool id"}, "kind": {"enum": ["signal", "plane"]}, "copperUm": {"type": "number"}, "dielectricUm": {"type": "number", "description": "to the next copper layer; absent on the last"}, "dk1000": {"type": "number", "description": "the dielectric constant x 1000"}, "material": {"type": "string"},- "mid": {"type": "integer", "description": "a signal layer's inner index (In<mid>.Cu)"}, "plane": {"type": "integer", "description": "an internal plane's index"}, "net": {"type": "string", "description": "a plane's net"}}}}+ "mid": {"type": "integer", "description": "a signal layer's inner index (In<mid>.Cu)"}, "plane": {"type": "integer", "description": "an internal plane's index in the tool; the board model names it by layer"}, "net": {"type": "string", "description": "a plane's net"}}}} } }, "pours": {
verbs/coverage.json+17−5@@ -1,5 +1,5 @@ {- "standard": "0.3.0",+ "standard": "0.3.1", "reference": { "bridge": "altium", "version": "0.2.76"@@ -956,7 +956,10 @@ "name": "sch_hierarchy", "tier": "core", "source": "proposed",- "altium": null,+ "altium": {+ "verb": "altium_sch_hierarchy",+ "match": "exact"+ }, "kicad": { "verb": "kicad_sch_hierarchy", "match": "exact"@@ -1009,7 +1012,10 @@ "name": "sch_page", "tier": "core", "source": "proposed",- "altium": null,+ "altium": {+ "verb": "altium_sch_page",+ "match": "exact"+ }, "kicad": { "verb": "kicad_sch_page", "match": "exact"@@ -1048,7 +1054,10 @@ "name": "sch_reset_fields", "tier": "core", "source": "proposed",- "altium": null,+ "altium": {+ "verb": "altium_sch_reset_fields",+ "match": "exact"+ }, "kicad": { "verb": "kicad_sch_reset_fields", "match": "exact"@@ -1842,7 +1851,10 @@ "name": "sch_update_from_library", "tier": "native", "source": "proposed",- "altium": null,+ "altium": {+ "verb": "altium_sch_update_from_library",+ "match": "exact"+ }, "kicad": { "verb": "kicad_sch_update_from_library", "match": "exact"
verbs/standard.json+1−1@@ -1,5 +1,5 @@ {- "standard": "0.3.0",+ "standard": "0.3.1", "reference": { "bridge": "altium", "version": "0.2.76"