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.4.3, engine 0.1.2: a clearance rule as a value with its unit (Fusion's form), same-net rules left out (fusion-lane #12); regenerated with fusion360-1.12.6, which answers its five checks from the engine: Fusion 118 of 236
8 files changed
+71−25
README.md+4−2@@ -59,10 +59,10 @@ verb by verb: [docs/coverage.md](docs/coverage.md). |---|---|---|---|---| | **core** | 93 | 86 | 89 | 53 | | **native** | 46 | 38 | 39 | 20 |-| **checks** | 37 | 33 | 11 | 4 |+| **checks** | 37 | 33 | 11 | 9 | | **app** | 60 | 56 | 25 | 36 | | **platform** | 19 | 19 | 0 | 0 |-| standard (all but platform) | 236 | 213 | 164 | 113 |+| standard (all but platform) | 236 | 213 | 164 | 118 | <!-- coverage:end --> The **checks** tier is where the other bridges are furthest behind, and it is the cheapest to close: those verbs are@@ -155,6 +155,8 @@ Decisions so far (proposals from the KiCad and Fusion bridges, 2026-10-27; issue ## Status +Version 0.4.3: the engine (0.1.2) reads a clearance rule written as a `value` with its unit (`0.1mm`, Fusion's form) and+leaves same-net spacing rules out; Altium and Fusion now both answer their checks from the engine. Version 0.4.2: the engine (0.1.1) names a stack's copper layers by the stack entry's `layer`, or by place (`In<n>.Cu`), planes included, as 0.3.1 settled; from altium-lane, which runs the engine inside the Altium bridge now. Version 0.4.1: coverage counts only what works: a bridge marks a verb `partial` (with a note) or `unsupported` in its
docs/coverage.md+8−8@@ -1,6 +1,6 @@ # Coverage: every standard verb on each bridge -Altium bridge 0.2.76 (the reference), KiCad bridge 1.0.65, Fusion 360 bridge 1.12.4. 54 verbs came from other bridges' proposals (tools/proposed/); they are marked *proposed*.+Altium bridge 0.2.76 (the reference), KiCad bridge 1.0.65, Fusion 360 bridge 1.12.6. 54 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. @@ -8,10 +8,10 @@ Generated by `tools/generate.py` from `verbs/coverage.json`; do not edit by hand |---|---|---|---|---| | **core** | 93 | 86 | 89 | 53 | | **native** | 46 | 38 | 39 | 20 |-| **checks** | 37 | 33 | 11 | 4 |+| **checks** | 37 | 33 | 11 | 9 | | **app** | 60 | 56 | 25 | 36 | | **platform** | 19 | 19 | 0 | 0 |-| standard (all but platform) | 236 | 213 | 164 | 113 |+| standard (all but platform) | 236 | 213 | 164 | 118 | ## core @@ -182,15 +182,15 @@ Engineering computed from the board model, not by the tool: one shared engine, s | `build_schematic` | Draw a design's schematic from its netlist with the project library's symbols, inside a new project (.PrjPcb with the board and libraries), compile it, and... | `altium_build_schematic` | | `fusion_schematic_plan / fusion_schematic_run` (partial) | | `check_clearance` *proposed* | Measure the gaps between parts, courtyard to courtyard or pad copper to pad copper, for the listed parts or the whole board, and report every pair under a... | `altium_check_clearance` | `kicad_check_clearance` | | | `check_orientation` | Advisory: 2-pad parts whose rotation could be better. Each orthogonal rotation is scored in place by airline length (a routable net's MST with the pad... | `altium_check_orientation` | | |-| `copper_balance` | How much of each copper layer is copper (tracks, pads, vias and what Altium actually poured, inside the outline; a plane counts full), and how far apart... | `altium_copper_balance` | | |+| `copper_balance` | How much of each copper layer is copper (tracks, pads, vias and what Altium actually poured, inside the outline; a plane counts full), and how far apart... | `altium_copper_balance` | | `fusion_copper_balance` | | `cross_check` | Compare one schematic sheet's designators with a board's. Altium holds ONE current document, so the verb activates the board, reads it, then activates the... | `altium_cross_check` | `kicad_analyze_connections` (partial) | |-| `current_capacity` | Will this trace carry the current? For each power net and its current: the narrowest track on every layer it is routed on against the width IPC-2221 needs... | `altium_current_capacity` | | |+| `current_capacity` | Will this trace carry the current? For each power net and its current: the narrowest track on every layer it is routed on against the width IPC-2221 needs... | `altium_current_capacity` | | `fusion_current_capacity` | | `estimate_cost` *proposed* | The board's numbers a fab quote needs (size, layers, thickness, smallest track, gap and drill, holes, SMD and through-hole parts and joints, unique parts)... | | `kicad_estimate_cost` | |-| `fab_check` | Before ordering: the board's fab-relevant minimums measured from the live board (narrowest track, smallest via drill, diameter and annular ring, smallest... | `altium_fab_check` | `kicad_fab_check` | |+| `fab_check` | Before ordering: the board's fab-relevant minimums measured from the live board (narrowest track, smallest via drill, diameter and annular ring, smallest... | `altium_fab_check` | `kicad_fab_check` | `fusion_fab_check` | | `find_space` | Free spots on the board for a part near another (clear of every other part on that side, of that side's tracks and vias, and inside the outline), nearest... | `altium_find_space` | | | | `footprint_from_land_pattern` | Build a complete footprint from the land pattern a datasheet prints: pads numbered the standard way (chip, dual SOIC/TSSOP/DFN, SOT, quad QFP/QFN, BGA), an... | `altium_footprint_from_land_pattern` | | | | `height_check` | Will it fit the enclosure? Each part's height from its 3D bodies (top and bottom side), the parts over a height limit board-wide or under a lid feature in a... | `altium_height_check` | | |-| `impedance` | Controlled impedance from the board's own stack-up: the track width each copper layer needs for a single-ended target (50 ohm default) and the width and gap... | `altium_impedance` | | |+| `impedance` | Controlled impedance from the board's own stack-up: the track width each copper layer needs for a single-ended target (50 ohm default) and the width and gap... | `altium_impedance` | | `fusion_impedance` | | `import_part` | One call imports a whole part: footprint spec + STEP + symbol modes + models + params + save. | `altium_import_part` | | | | `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` | | | | `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` | | |@@ -201,7 +201,7 @@ Engineering computed from the board model, not by the tool: one shared engine, s | `plan_fanout` *proposed* | Plan the fanout of a BGA or QFN/QFP: the pad pitch, dogbone or via-in-pad per pad, the escape layer, surface stubs for the outer ring and a via grid for an... | | `kicad_plan_fanout` | | | `plan_route` *proposed* | Plan a path for one net between two pads without changing the board: a grid search around the existing copper that keeps the clearance rules, on the allowed... | | | `fusion_plan_route` (partial) | | `read_sheet_file` | Read a schematic sheet straight from its .SchDoc file, without Altium (it need not be installed or running): every part with its designator, library... | `altium_read_sheet_file` | | `fusion_read_sheet_file` |-| `return_path` | Where signal tracks lose their reference: for every signal net, the length of track on each layer that runs over no pour or plane copper on either... | `altium_return_path` | | |+| `return_path` | Where signal tracks lose their reference: for every signal net, the length of track on each layer that runs over no pour or plane copper on either... | `altium_return_path` | | `fusion_return_path` | | `review_board` | Design-review checks (14) over one board read; schematic checks with includeSchematic. | `altium_review_board` | `kicad_lint_board` (partial) | | | `route_pair` | Route a differential pair as a pair: one centreline from the source pads' midpoint through waypoints to the destination pads' midpoint, each net offset to... | `altium_route_pair` | `kicad_route_pair` | | | `schematic_review` | Review the schematic before Update PCB: every placed pin (read from the sheet files, with its electrical type) against Altium's compiled netlist. Errors:... | `altium_schematic_review` | `kicad_sch_check / kicad_design_review / kicad_lint_schematic` (partial) | |
engine/Cargo.lock+1−1@@ -4,7 +4,7 @@ version = 4 [[package]] name = "adom-eda-checks"-version = "0.1.1"+version = "0.1.2" dependencies = [ "serde_json", ]
engine/Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "adom-eda-checks"-version = "0.1.1"+version = "0.1.2" edition = "2021" description = "The EDA Bridge Standard's checks, computed from the board model JSON: no bridge calls, no tool." license = "MIT"
engine/src/fab.rs+32−3@@ -118,14 +118,32 @@ pub fn compare(meas: &Map<String, Value>, limits: &Map<String, Value>, clearance out } +/// A length written with its unit ("0.1mm", "4 mil", "0.004in", "100um") in mm; None without a known unit.+fn length_mm(text: &str) -> Option<f64> {+ let t = text.trim();+ let num: String = t.chars().take_while(|c| c.is_ascii_digit() || *c == '.').collect();+ let v: f64 = num.parse().ok()?;+ match t[num.len()..].trim().to_ascii_lowercase().as_str() {+ "mm" => Some(v),+ "mil" | "mils" | "th" => Some(v * 0.0254),+ "in" | "inch" => Some(v * 25.4),+ "um" | "\u{b5}m" => Some(v / 1000.0),+ _ => None,+ }+}+ /// The smallest clearance among the board's enabled clearance rules, in mm. A rule gives it as a number-/// (`clearanceMm`, `gapMm` or `minMm`) or in a descriptor the tool wrote ("Gap=10mil", "Gap=0.2mm": Altium's form).+/// (`clearanceMm`, `gapMm` or `minMm`), as a `value` with its unit ("0.1mm": Fusion's form), or in a descriptor the tool+/// wrote ("Gap=10mil", "Gap=0.2mm": Altium's form). Same-net spacing rules are not clearance between nets: left out. pub fn min_clearance(rules: &[Value]) -> Option<f64> {- rules.iter().filter(|r| r["kind"].as_i64() == Some(0) || r["kind"].as_str().map(|k| k.eq_ignore_ascii_case("clearance")).unwrap_or(false)- || r["kindName"].as_str() == Some("Clearance") || r["name"].as_str().map(|n| n.to_lowercase().contains("clearance")).unwrap_or(false))+ let text = |r: &Value, k: &str| r[k].as_str().map(|s| s.to_lowercase()).unwrap_or_default();+ rules.iter().filter(|r| r["kind"].as_i64() == Some(0) || text(r, "kind").contains("clearance")+ || r["kindName"].as_str() == Some("Clearance") || text(r, "name").contains("clearance"))+ .filter(|r| !["kind", "name", "kindName"].iter().any(|k| { let t = text(r, k); t.contains("same net") || t.contains("samenet") || t.contains("same-net") })) .filter(|r| r["enabled"].as_bool() != Some(false)) .filter_map(|r| { if let Some(v) = ["clearanceMm", "gapMm", "minMm"].iter().find_map(|k| r[*k].as_f64()) { return Some(v); }+ if let Some(v) = r["value"].as_str().and_then(length_mm) { return Some(v); } let d = r["descriptor"].as_str()?; let i = d.find("Gap=")? + 4; let num: String = d[i..].chars().take_while(|c| c.is_ascii_digit() || *c == '.').collect();@@ -291,4 +309,15 @@ mod tests { fn clearance_from_a_standard_rule() { assert_eq!(min_clearance(&[json!({"kind": "clearance", "clearanceMm": 0.127}), json!({"kind": "clearance", "gapMm": 0.2})]), Some(0.127)); }++ #[test]+ fn clearance_from_a_value_with_its_unit_and_never_same_net() {+ // Fusion writes rules as a kind name and a value with its unit+ let rules = vec![json!({"kind": "Copper Clearance", "value": "0.15mm"}), json!({"kind": "Copper Clearance", "value": "5mil"}),+ json!({"kind": "Same Net Clearance", "value": "0.05mm"}), json!({"kind": "Copper Clearance", "value": "0.2"})];+ assert_eq!(min_clearance(&rules), Some(0.127));+ assert_eq!(length_mm("0.004in"), Some(0.1016));+ assert_eq!(length_mm("100um"), Some(0.1));+ assert_eq!(length_mm("0.2"), None, "a bare number has no unit: never guessed");+ } }
package.json+1−1@@ -1,6 +1,6 @@ { "slug": "eda-bridge-standard",- "version": "0.4.2",+ "version": "0.4.3", "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+22−7@@ -1,5 +1,5 @@ {- "standard": "0.4.2",+ "standard": "0.4.3", "reference": { "bridge": "altium", "version": "0.2.76"@@ -7,7 +7,7 @@ "proposed": 54, "compared": { "kicad": "1.0.65",- "fusion360": "1.12.4"+ "fusion360": "1.12.6" }, "tiers": { "core": "Read and edit the design. Every bridge provides these: they are how an AI sees and changes a board.",@@ -2248,7 +2248,10 @@ "match": "exact" }, "kicad": null,- "fusion360": null+ "fusion360": {+ "verb": "fusion_copper_balance",+ "match": "exact"+ } }, { "name": "cross_check",@@ -2273,7 +2276,10 @@ "match": "exact" }, "kicad": null,- "fusion360": null+ "fusion360": {+ "verb": "fusion_current_capacity",+ "match": "exact"+ } }, { "name": "estimate_cost",@@ -2298,7 +2304,10 @@ "verb": "kicad_fab_check", "match": "exact" },- "fusion360": null+ "fusion360": {+ "verb": "fusion_fab_check",+ "match": "exact"+ } }, { "name": "find_space",@@ -2342,7 +2351,10 @@ "match": "exact" }, "kicad": null,- "fusion360": null+ "fusion360": {+ "verb": "fusion_impedance",+ "match": "exact"+ } }, { "name": "import_part",@@ -2470,7 +2482,10 @@ "match": "exact" }, "kicad": null,- "fusion360": null+ "fusion360": {+ "verb": "fusion_return_path",+ "match": "exact"+ } }, { "name": "review_board",
verbs/standard.json+2−2@@ -1,5 +1,5 @@ {- "standard": "0.4.2",+ "standard": "0.4.3", "reference": { "bridge": "altium", "version": "0.2.76"@@ -7,7 +7,7 @@ "proposed": 54, "compared": { "kicad": "1.0.65",- "fusion360": "1.12.4"+ "fusion360": "1.12.6" }, "tiers": { "core": "Read and edit the design. Every bridge provides these: they are how an AI sees and changes a board.",