app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Reference implementation of the KiCad bridge: multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.
← Commit history
Publish 1.0.140
5 files changed
+74−4
SKILL.md+3−1@@ -197,6 +197,8 @@ manual edits while committing. Connectivity state that changes mid-read is refus ### General routing workflow +A board is routed when `kicad_routing_validate` says 0 unconnected and 0 new errors; anything else is unfinished work, and the loop that finishes it (vias, rip-up, then back to placement and route again) is the `kicad-place-route-loop` skill in this package.+ Use this workflow for ordinary board work, repair, and review with any Adom AI caller. Recording and demonstration playback are optional, separate tasks; no demo script, narrator, Codex package, or particular model is required to use the@@ -279,7 +281,7 @@ For optional presentation and recordings, see [demo/routing/README.md](demo/rout ## Placement (from 1.0.3) -Three verbs let an AI place footprints on a real board the way the routing verbs let it route one: read the state, move parts live through the KiCad IPC API as native Undo steps, validate. Same requirements as live routing (KiCad 10.0.1+, IPC API server on, the board open in the PCB editor); the read-only calls and a `dryRun` move also answer from the file on disk when no editor has the board (`source: "file"`), so planning can start before KiCad is up.+Three verbs let an AI place footprints on a real board the way the routing verbs let it route one: read the state, move parts live through the KiCad IPC API as native Undo steps, validate. Place for routability (escape bands around fine-pitch parts, each passive at the pin it serves, the crystal group at the oscillator pins): the rules and the go-back-and-move loop are in the `kicad-place-route-loop` skill. Same requirements as live routing (KiCad 10.0.1+, IPC API server on, the board open in the PCB editor); the read-only calls and a `dryRun` move also answer from the file on disk when no editor has the board (`source: "file"`), so planning can start before KiCad is up. | Verb | What | Args | |---|---|---|
package.json+2−1@@ -1,6 +1,6 @@ { "slug": "kicad-bridge",- "version": "1.0.139",+ "version": "1.0.140", "type": "app", "description": "Skills for your container so your AI knows how to drive the KiCad bridge. The bridge runtime itself is the release zip; Adom Bridge loads that.", "tags": [@@ -29,6 +29,7 @@ "skills/kicad-interaction/SKILL.md", "skills/kicad-3d-models/SKILL.md", "skills/kicad-autorouting/SKILL.md",+ "skills/kicad-place-route-loop/SKILL.md", "skills/kicad-tour/SKILL.md", "skills/kicad-tour/tour_runner.py", "skills/kicad-web-control/SKILL.md",
page.json+2−1@@ -4,7 +4,7 @@ "slug": "kicad-bridge", "title": "KiCad - the KiCad Bridge", "brief": "Reference implementation of the KiCad bridge: multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.",- "version": "1.0.139",+ "version": "1.0.140", "tags": [ "kicad", "pcb",@@ -88,6 +88,7 @@ "skills/kicad-interaction/SKILL.md", "skills/kicad-3d-models/SKILL.md", "skills/kicad-autorouting/SKILL.md",+ "skills/kicad-place-route-loop/SKILL.md", "skills/kicad-tour/SKILL.md", "skills/kicad-tour/tour_runner.py", "skills/kicad-web-control/SKILL.md",
skills/kicad-autorouting/SKILL.md+5−1@@ -33,8 +33,12 @@ The worked examples, power pours and current-density review live in the adom/cod - `kicad_autoroute {"engine": "freerouting", "filePath": B, "passes": 20}`: writes a Specctra DSN from the board, runs Freerouting headless with a deadline, reads the SES back, and lands the copper: as native Undo steps through the IPC API when the PCB editor has the board open, or into the file with a `.adom-bak` when it does not. Then a DRC. Read `routed`, `drc` and `applied`; tell the user what to inspect. `dryRun: true` routes but lands nothing. - `kicad_freerouting {"action": "uninstall"}`: removes that folder and nothing else. The bridge keeps working; the engine is offered again next time and installs again on request. `kicad_uninstall` (the bridge's own removal) also removes it. +## 100% or it is not done++A routing result with unconnected items is a failed take, not a result. The rule, and the loop that gets there (route, then change vias and order, then rip up, then go back and move the parts that block the escapes, then route again, to the bitter end), is the kicad-place-route-loop skill in this package. Read it before you report any routing number.+ ## Honesty rules -- Never say a board is routed because a verb returned success; say what `drc` and `unconnected` say.+- Never say a board is routed because a verb returned success; say what `drc` and `unconnected` say. `unconnected` above zero means you are not finished: go back to the loop. - Freerouting's result is connectivity, not electrical sign-off. Current, impedance and thermal review remain the user's or the AI's job. - If Freerouting is not installed and the user did not ask for it, do not install it. Offer, with the size, and wait.
skills/kicad-place-route-loop/SKILL.mdadded+62@@ -0,0 +1,62 @@+---+name: kicad-place-route-loop+description: How an AI gets a KiCad board to 100 percent routed through the Adom KiCad Bridge, the way a human does it: place for routability, route, and when routing cannot close, go back and move the parts that block it, then route again, to the bitter end. A board with unconnected items is a failed take, never a result. Covers the placement rules that make routing possible (escape bands around fine-pitch ICs, passives at the pin they serve, crystal and its capacitors at the oscillator pins, test points and indicators out of the escape lanes), the routing order (planes and stubs first, shortest nets first, power wide), the levers to pull when connections fail (route the failed nets first, smaller vias where the rules allow, rip-up, then placement changes), the offline DRC gate before anything touches the live board, the live landing through kicad_move_footprint and kicad_route_net, and how to report the true wall clock from the human's prompt to the finished board. Trigger words: 100% routed, fully routed, unconnected items, unrouted nets, ratsnest left, routing failed, cannot route, go back to placement, move parts to route, place and route, placement for routing, escape routing, fine pitch escape, route to completion, finish the routing, place the board, route the board, kicad_move_footprint, kicad_route_net, placement loop, routing loop.+---++# Place, route, go back, route again: 100 percent or it is not done++The demo that produced this skill, with the numbers and videos: [the ESC G431 demo](https://wiki.adom.inc/adom/kicad-bridge/files/docs/esc-demo.md). The engines that route through the bridge: the kicad-autorouting skill. The placement verbs: the "Placement" section of the kicad-bridge skill.++## The rule++A human layout engineer does not stop at "7 unconnected, close enough". They change the plan, change the vias, rip up and reroute, and when routing still cannot close they go back to placement, move the part that is in the way, and route again. They do everything under the sun until the ratsnest is gone, because a board with one unconnected item does not work. The same is expected of you. Your report says "0 unconnected, 0 new DRC errors" or it says "not finished" and what you are doing next. Nothing in between is a result.++## Place for routability first++Placement decides whether routing can close. Rules that came out of the ESC G431 rounds, where the first placement looked fine and left seven connections that no router could finish:++1. **Fine-pitch ICs need an escape band.** A 0.5 mm pitch QFP or QFN cannot take a track between its pins (0.2 mm gap, 0.2 mm clearance). Every pin that leaves the IC escapes straight out on its own axis and drops a via a little further out. Keep a clear band of at least 2.5 mm beyond every pin row: no test points, no indicator LEDs, no series resistors that do not belong to that pin, no capacitors of other nets. Decoupling capacitors sit in the band only at the pin they decouple, with their far pad toward the outside so their plane via does not sit in a neighbour's escape lane.+2. **A passive goes at the pin it serves, not at the IC it shares the most nets with.** The planner's "attach to the biggest neighbour" heuristic put the /PB12 pull-up and the LED resistor on the wrong side of the MCU and the crystal capacitors on the opposite side from the crystal. Every one of the seven failures traced back to this. Look at which pin a two-pad part connects to and put it in front of that pin.+3. **Crystal, its two capacitors and the oscillator pins are one group.** The crystal within 3 mm of the OSC_IN and OSC_OUT pins, the two load capacitors between the crystal and the IC with their ground pads facing the same way, nothing else in that pocket. A crystal 11 mm away with its capacitors somewhere else costs three long nets through the densest part of the board.+4. **Test points and indicator LEDs go last and go where nothing else needs to be.** They connect to one net each and carry no timing; put them outside every escape band, at the board edge or in the open areas, and move them without hesitation when routing needs the space.+5. **The interface is fixed.** Machine pins, contacts, connectors set by a scaffold or a pattern do not move (the fixture locks them). Plan around them; the verb refuses to move a locked part.+6. **Check the ratsnest per net, not only the total.** A short total ratsnest with three nets that cross the IC body is worse than a longer total where every net has an open path. `kicad_placement_state` gives the nets and the refs; look at the long ones and the crossing ones before you commit.++## Route in the order that leaves room++1. **Planes and stubs first.** GND and power on their planes; every SMD pad on a plane net gets a short stub and a via before any signal is routed, so the signals route around fixed vias instead of the vias hunting for holes later.+2. **Shortest nets first, then the long ones.** Short nets have the least freedom; a long net can always go around.+3. **Power and phase nets wide, signals narrow.** Try the wide width first and fall back per connection.+4. **Every connection lands on the net's own copper**, not necessarily the pad it was aimed at, so branches join the trunk and junctions stay clean.+5. **One net per commit on the live board**, DRC-checked against a snapshot by `kicad_route_net`; a rejection lands nothing.++## When a connection fails, pull the levers in this order++Each lever is cheap until it is not; stop at the first that closes the board, and report which ones you used.++1. **Route the failed nets first in the next pass.** The router that produced 7 failures on the ESC got to 2 by giving the failed nets priority in pass two.+2. **Smaller vias where the rules allow.** KiCad's defaults allow a 0.5 mm via with a 0.3 mm hole; 0.6/0.3 is safe on every fab. Signal vias at 0.6/0.3 instead of 0.8/0.4 took the ESC's fine-pitch escapes from impossible to routable; keep 0.8/0.4 (or larger) on the power and phase nets that carry current. Read the board's setup block and netclasses; never go below the board's own minimums.+3. **Rip-up and retry.** Rip the nets that block the failed connection (most recently routed first), route the failed one, re-queue the ripped ones. Bound it per net and globally, keep the best intermediate state, and add a history cost so a contested corridor gets dearer every time it is fought over.+4. **Go back to placement.** When the same pins fail across passes and the "blocked by" list is the same neighbourhood, the placement is the problem. Read what blocks (the router names the nets; `kicad_routing_state` and the board file give the parts), then move the parts that do not need to be there: test points and LEDs out of the escape band, the crystal group to the oscillator pins, each pull-up or series resistor to its pin. Move them offline first: `tools/pack_moves.py` on the kicad-bridge page finds a legal spot near where each part should be using the real courtyard outlines from the file (pad boxes are not enough: test points and headers have courtyards much wider than their pads, and KiCad's DRC and the bridge both check courtyards), `tools/move_footprints.py` edits the board, then KiCad's DRC on the moved fixture must read exactly the inherited errors and nothing new; re-run the router and the offline gate, and only then land the moves on the live board with `kicad_move_footprint` (one batch, one undo step, exact courtyard check by the bridge) and route again. A placement move on a board that already has copper: rip the routes that touch the moved parts first (`kicad_remove_route`), or start the routing take over from the moved placement, which is what the demo does so the video shows one clean routing.+5. **Wider search, then a different engine for a second opinion.** Freerouting on the same board tells you whether a shape-based router finds a path where the grid router did not; its copper is not the answer (its DSN rules are still being fixed, issue #93 on the kicad-bridge page) but its ratsnest is a hint.++Never stop with a number above zero and call it done. If you run out of levers, say so, say what you tried, and hand the human the exact pins and parts that are in the way.++## The offline gate++Nothing lands on the live board until the plan passes KiCad's own DRC offline:++1. Write the plan into a copy of the board (`demo/routing/esc/write_copper.py` on the kicad-bridge page does this for the plan format the router emits, fills the planes, and runs `service-kicad pcb drc`).+2. Gate: zero new errors against the unrouted baseline (inherited library errors are counted separately and named), zero unconnected after the zones are filled.+3. Only then replay the plan on the live board (`route_live.py`), one `kicad_route_net` per net, `expectedRevision` from the previous reply, and finish with `kicad_routing_validate`, which must agree with the offline gate.++## Report the time the human waited++The headline number is the wall clock from the moment the human gave the prompt to the moment the finished board (or the issue that reports it) was handed back. Not the seconds the verbs took on the board, not the planning time. Record the prompt's timestamp in UTC as your first action, record the hand-back timestamp, and report both with the difference in minutes; the per-step times (planning, commits, on-the-board seconds) go in the table below it. A take that stops short of 100 percent has no finish time; when the loop above sends you back to placement, the clock keeps running.++## The ESC G431 case, in numbers++- First routing take, Fable's grid router, 0.8/0.4 vias: 105 commits, 519 s on the board, 0 new DRC errors, 7 unconnected around the MCU. Reported as a result; rejected.+- Smaller signal vias (0.6/0.3): 2 unconnected after pass one, the same neighbourhood: the crystal capacitors and the /PB12 pull-up on the wrong side of the MCU, LEDs and test points in the top and right escape bands.+- Placement adjustment: 13 parts moved offline with the packer and the mover, fixture DRC back to the 13 inherited errors, router 3 passes to 0 failed, offline gate 0 new errors and 0 unconnected. The crystal could not get a courtyard-legal pocket at the oscillator pins (the debug header owns that side and the fixed contacts stop it moving), so it stayed and its capacitors moved to it: the one debt the board still carries.+- Live: one move batch (0.15 s, verified) and 114 commits in 570 s, validate 0 unconnected, 0 new errors. Wall clock the human waited: 5 h 34 min across two takes, against 16 min for the placement round's other engine. The full story: [the ESC demo page](https://wiki.adom.inc/adom/kicad-bridge/files/docs/esc-demo.md), Round 2.