← Commit history

0.2.55 source (not yet released, needs signing): the Adom edge rule is scoped OnCopper (#26)

Colby Knox ·3e76806542 ·1d ago ·parent c2e4ce1
8 files changed +20−8
CHANGELOG.md+4
@@ -1,5 +1,9 @@ # Changelog +## 0.2.55 (2026-10-20)+- **The edge rule no longer flags silkscreen** (#26). `set_design_rules` takes `edgeClearance` from the fab pack, where it means copper to the board edge (KiCad's `edge_clearance`). When the board had no outline-clearance rule, it created "Adom edge" scoped to everything, so Altium's DRC flagged silkscreen near the edge too, such as AI Flow's contact frames 0.12 mm in. The new rule is scoped `OnCopper`, and an existing "Adom edge" still scoped `All` is re-scoped the next time the rules are set. Silkscreen keeps Altium's own silk rules.+- Verified live on Altium Designer 26.11.1, on two copies of the same saved board with the gap widened to 0.6 mm so silkscreen near the edge is in reach: scope `All` gave 28 edge violations, `OnCopper` gave 5 (the copper). Every other rule's count was identical (137 silk-to-mask, 48 silk-to-silk, 8 mask sliver).+ ## 0.2.54 (2026-10-20) - **An Altium board's STEP anchors as a molecule.** Altium's STEP export names each component's PRODUCT after its designator but leaves every assembly occurrence unnamed, where KiCad names the occurrence (`MP1`). `step2glb convert --molecule` finds the four machine pins by occurrence name, so an Altium molecule board converted `molecule_anchored: false` (0 MP markers) and its GLB was unanchored. `altium_export_step` now names each board component's occurrence after its designator (both lanes; `occurrences.named` in the reply, `nameOccurrences: false` to keep Altium's file as written). Nested model occurrences and occurrences Altium already named are left alone. - `altium_routing_state` answers `errorCode: "no_current_board"` and the agent's message when another document (a schematic) is in front, instead of an empty error.
bridge-rs/src/main.rs+1−1
@@ -31,7 +31,7 @@ use tiny_http::{Header, Method, Response, Server}; /// Attached to every failed reply (see the /command handler). const REPORT_ISSUES: &str = "If this looks like an altium-bridge BUG (not a refusal you expected), report it: check for a duplicate with `adom-wiki issue list adom/altium-bridge`, then `adom-wiki issue create adom/altium-bridge --title \"<verb>: <one-line symptom>\" --category bug-report --body \"<what you ran, what you expected, what happened, the VERBATIM response JSON (and errorShot if present), bridgeVersion, the Altium version from altium_status, the OS>\"`. A relay, routing, toast or lifecycle problem belongs on adom/adom-bridge instead. Reports from real machines are how this bridge gets correct: three different Altium builds behaved three different ways in one weekend."; -pub const BRIDGE_VERSION: &str = "0.2.54";+pub const BRIDGE_VERSION: &str = "0.2.55";  fn json_response(status: u16, body: &serde_json::Value) -> Response<std::io::Cursor<Vec<u8>>> {     let data = serde_json::to_vec(body).unwrap_or_else(|_| b"{}".to_vec());
bridge-rs/src/verbs.rs+2−2
@@ -452,10 +452,10 @@ fn describe() -> Value {             json!({"vertices": "int", "revision": "string", "outlineEdges": "[{kind: line|arc, ...}]"}), 120,             "Rounding corners: pass the same rectangle with cornerRadius (1 mm is a common default).", &["build_board", "routing_state", "repour"], &["Needs the board current."], json!({"outline": {"rect": [0, 0, 28, 20], "cornerRadius": 1}})),         verb_doc("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 (mm).",-            json!({"clearance": "mm", "trackWidth": "{min, preferred?, max?} mm", "via": "{diameter:{min, preferred?, max?}, drill:{min, preferred?, max?}} mm", "hole": "{min, max?} mm", "annularRing": "mm", "holeToHole": "mm", "edgeClearance": "mm", "polygonConnect": "the board's catch-all polygon connect style: direct|relief|none, or {style, reliefWidth: mm, reliefEntries: 2|4} (per-pour rules from apply_pours keep theirs)"}),+            json!({"clearance": "mm", "trackWidth": "{min, preferred?, max?} mm", "via": "{diameter:{min, preferred?, max?}, drill:{min, preferred?, max?}} mm", "hole": "{min, max?} mm", "annularRing": "mm", "holeToHole": "mm", "edgeClearance": "mm - copper to the board edge (the rule it creates is scoped OnCopper; silkscreen keeps its own rules)", "polygonConnect": "the board's catch-all polygon connect style: direct|relief|none, or {style, reliefWidth: mm, reliefEntries: 2|4} (per-pour rules from apply_pours keep theirs)"}),             json!({"landed": "[{kind, rulesWritten}]", "failed": "[{kind, error}]", "rules": "the board's rules after"}), 300,             "AI Flow's `rules use` calls this with the fab pack's numbers (adom/pcb-design-rules). Each kind is written separately, so one this Altium build lacks fails alone.",-            &["read_rules", "rules_by_kind", "build_board"], &["Needs the board current.", "Every existing rule of a kind gets the values; rule scopes are kept."], json!({"clearance": 0.15, "trackWidth": {"min": 0.15}, "via": {"diameter": {"min": 0.45}, "drill": {"min": 0.2}}, "hole": {"min": 0.2}, "edgeClearance": 0.3})),+            &["read_rules", "rules_by_kind", "build_board"], &["Needs the board current.", "Every existing rule of a kind gets the values; rule scopes are kept, except an Adom edge rule an earlier bridge scoped to All, which becomes OnCopper (#26)."], json!({"clearance": 0.15, "trackWidth": {"min": 0.15}, "via": {"diameter": {"min": 0.45}, "drill": {"min": 0.2}}, "hole": {"min": 0.2}, "edgeClearance": 0.3})),         verb_doc("add_rule", "Add one SCOPED design rule (a net's clearance or width, a net class's, any Altium query) that set_design_rules' board-wide values cannot express.",             json!({"kind": "clearance|width", "name": "string - the rule is named 'Adom <name>'; the same name replaces it", "scope1": "Altium query, e.g. InNet('SW') or InNetClass('Power')", "scope2": "query (default All)", "gap": "mm (clearance)", "min": "mm (width)", "preferred": "mm (width, optional)", "max": "mm (width, optional)", "comment": "optional", "remove": "bool - delete the Adom rule of this kind and name instead"}),             json!({"name": "string", "priority": "int (1 = first of its kind)"}), 60,
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "altium-bridge",-  "version": "0.2.54",+  "version": "0.2.55",   "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": [
page.json+1−1
@@ -4,7 +4,7 @@   "type": "app",   "title": "Altium - the Altium Bridge",   "brief": "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.",-  "version": "0.2.54",+  "version": "0.2.55",   "hero": {     "type": "image",     "path": "screenshots/hero.png"
src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.2.54+0.2.55
src/agent/AdomAgent.pas.tmpl+9−1
@@ -6223,7 +6223,13 @@ Begin             Else If Kind = 'hole' Then Begin Rule.MinLimit := A; Rule.MaxLimit := C; End             Else If Kind = 'ring' Then Rule.Minimum := A             Else If Kind = 'holetohole' Then Rule.Gap := A-            Else If Kind = 'edge' Then Rule.Gap := A+            Else If Kind = 'edge' Then+            Begin+                Rule.Gap := A;+                { the fab's edge clearance is copper to edge (KiCad's edge_clearance): an Adom edge rule an+                  earlier bridge scoped to All also caught silkscreen (adom/altium-bridge#26) }+                If (Rule.Name = 'Adom edge') And (Rule.Scope1Expression = 'All') Then Rule.Scope1Expression := 'OnCopper';+            End             Else If Kind = 'polyconnect' Then             Begin                 { A: 1 direct, 2 none, else relief with B spoke width (raw) and C spokes }@@ -6249,6 +6255,8 @@ Begin         Rule := PCBServer.PCBRuleFactory(Want);         Rule.Name := 'Adom ' + Kind;         Rule.Scope1Expression := 'All';+        { edge: copper to the board edge, as the fab pack means it; silkscreen keeps its own rules }+        If Kind = 'edge' Then Rule.Scope1Expression := 'OnCopper';         Rule.Scope2Expression := 'All';         If Kind = 'ring' Then Rule.Minimum := A         Else If (Kind = 'edge') Or (Kind = 'clearance') Or (Kind = 'holetohole') Then Rule.Gap := A
src/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "altium",   "displayName": "Altium Designer",-  "version": "0.2.54",+  "version": "0.2.55",   "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",