Kicad autorouter
Status: reusable native AI controller, bounded PNS session and human handoff verified; autonomous PCB planning/completion incomplete (2026-10-19). The original Rust plan is deferred to Phase C; current priority is KiCad PNS plus planning. Repositories, fork builds, benchmark harness and replay/diagnostic tools exist. Decision: kicad-autorouter-from-scratch. Related: KiCad, rusefi-sti-2004, SOP-021, ~/kicad-lib/scripting/README.md (Known traps).
One KiCad router of our own (eeroute) that works like a native tool: push-and-shove bulk routing, differential pairs, length matching, fanout and our house post-processing (45° chamfers, widest-width fitting, teardrops, nudge), with every trap we have hit fixed at its source, running on Windows, macOS and Linux like KiCad itself, and optionally guided by an AI planner.
Handoff — 2026-10-20, Cow Robot test (read this first)¶
Goal: route the human-routed Cow Robot Board (stripped) with tools/eeroute_route.py, compare with the human, learn. Nothing is committed in ~/eeroute (branch native-usb-pair-skew) or ~/kicad-fork (eeroute/waypoint-diagnosis, uncommitted edits to qa/tools/pns/pns_route_nets.cpp); commit ONLY when the owner says; no GitLab push. Run from ~/eeroute: python3 tools/eeroute_route.py --board work/cow-robot/bare2/RobotCow.kicad_pcb --out work/cow-robot/balance9 --balance-layers route $(tr '\n' ' ' < /tmp/er/order4.txt) (58 nets: GND +3.3V +5V +12V VBUS /~{CSN} /IRQ /NRST /D+ /D- /_D+ /_D- then the rest; bare2 = tracks and teardrops stripped, stock format 20260206).
OWNER RULE (2026-10-20) ENFORCED: no corner or via turn with interior angle under 90 degrees (45 degree bends are fine; all nets) and no signal via within 1.0 mm of a pad of its own net (plane nets GND/+3.3V exempt from the via rule). Router: pns_route_nets --min-corner-deg 90 --min-via-pad-mm 1.0 (shapeDefects/snapNewCorners, guard in the plan loop and in speculativeConnection; failure_stage shape_guard). Joints with 3+ legs and shoved existing vias are checked too (existing copper = COPPER_SNAPSHOT, KIID -> start point; a via the shove moved closer than the limit counts). Front door passes both by default; --min-corner-deg 0 / --min-via-pad-mm 0 switch off. Checker: python3 -I tools/route_quality.py <pcb> (exit 2 on any defect). Memory rule 7 in schematic-design-preferences.md carries it.
FINAL balance10 (after the reviewer fixes: 3+ leg via joints checked, snapshot keeps both ends so shoved tracks count as changed): 198 -> 4 opens (+5V, /SCL, /CAN2_RX, /CAN2_TX 1 each), 50 min, 0 acute corners, 0 near-pad vias, DRC = human baseline. Known false-reject left on purpose: two tracks leaving one pad at 45 degrees count as an acute corner.
Earlier balance9 (bare2, defaults prepare + power-first, 58 nets, ~70 min): opens 198 -> 5 (/SCL, /AN0, /CAN2_RX, /CAN2_TX, +5V 1 each), route_quality 0 acute corners and 0 near-pad vias (balance7 had 20 and 11), independent DRC (tools/drc_downgraded.sh) = human baseline (92 lib_footprint, 2 courtyard, 1 clearance). Segment lengths: /D+ 18.89 vs /D- 18.98, /_D+ 8.17 vs /_D- 7.85 (USB chain skew now ~0.2-0.4 mm, was 5.7), CAN 9.69/9.14 (0.546, human 0.526; no tune loop yet). The guard makes runs slower (~70 vs 31 min) and leaves the U4 fan-out nets open; a relaxed last-resort retry was NOT added (owner decision). Earlier: shape1 (14 nets) all complete; balance8 left 2 near-pad vias because shoves moved vias (fixed by the snapshot check).
Front-door defaults added (uncommitted, tested): prepare_planes (plane_connect then kicad-cli pcb drc --refill-zones --save-board; only when a plane net has opens and the board is stock format; balance9 report prepare: +3.3V 34 -> 4 and GND 47 -> 6 opens), power-first ordering (POWER_NAME: plane nets and supply rails, then pairs, then singles), --no-prepare / --keep-order opt out; unblock protects plane nets and ranks obstacles from the fallback reports. Tests (no pytest, from ~/eeroute/tools, no -I): route 45, plane_connect 24, unblock 17, route_quality 4; router run-tool.sh --test-guards and --test-pair-guards pass with the new binary. Debug env vars PNS_ONLY_STRATEGY and PNS_VIA_DEBUG stay as undocumented diagnostics (off by default).
2026-10-20 fresh-context review of the fork diff (Sonnet, static): angle math, mm/nm units, snapshot keying, 3+ leg joints and guard coverage pass. Finding 1 CONFIRMED and fixed: the C++ via-near-own-pad rule had no plane-net exemption (the checker and the note had it, the router did not); shapeDefects now skips vias whose net owns a filled non-rule-area zone (same definition as route_quality.plane_nets). Rebuilt; --test-guards, --test-pair-guards, test_eeroute_route pass; NO new test covers the exemption and balance10 was not re-run. Finding 2 rejected: binary defaults are 0 by design, the front door passes 90 / 1.0. Known minor gaps left: snapNewCorners runs after the last check (sub-um), a track end landing mid-span on another track forms no joint and is not angle-checked, onPad is computed once per joint across layers.
Copper renders of balance10 (2026-10-20): kicad-autorouter-cow-robot-copper. Recipe: fork kicad-cli pcb downgrade --target 10.0 in the docker image (see tools/drc_downgraded.sh), installed kicad-cli pcb export svg --mode-single --layers "<L>,Edge.Cuts", then uv run --with cairosvg for PNG.
Handoff state (chat ending, 2026-10-20): uncommitted work in ~/eeroute and ~/kicad-fork (plane-net via exemption built and guard-tested, not yet covered by a new test or re-run on the board); no jobs running.
2026-10-25: local checkouts deleted (owner, disk space). ~/kicad-fork and ~/kicad-fork-pairdiag are gone, builds too. Everything is on the NAS repo: the uncommitted pns_route_nets.cpp edits are branch wip/waypoint-diagnosis-2026-10-20 (5dee2990). Restore: git clone /mnt/storage/EmbeddedEra/Github/kicad-fork.git ~/kicad-fork && git -C ~/kicad-fork switch eeroute, then rebuild. 2026-10-26: the 18 linear branches (master, eeroute/, wip/) were collapsed into one branch eeroute = 5dee2990; old tip hashes stay in its history. Private GitHub copy: EmbeddedEra/kicad-fork (remote github on the NAS repo).
Next actions: (0) add a --test-guards case for the plane-net via exemption, re-run the Cow Robot board and compare opens (4 expected, GND/+3.3V should not regress); (1) USB audit: J11 has two pads per member (A6/B6, A7/B7), pair_audit is ambiguous; define chain ends; add a length-tune loop for CAN (skew 0.546 vs 0.15 target). (2) The 5 open nets: U4 fan-out congestion; options are an owner-approved relaxed retry or a smarter fan-out order. (3) Reviewer items not fixed: plane_connect DRC robustness (#4/#5), split plane zones (#6), freerouting engine bypasses the shape rules (#8). (4) Fresh-context review of the final fork diff (the snapshot check was added after the last review). (5) Commit when the owner says. Rules: route from a refilled or stripped copy; teardrops are derived and stripped from router input; downgrade with the fork CLI before installed-kicad DRC; cancel docker jobs by their own container name only; never rebuild the binary during a run (bash work/pns-spike/build-replay.sh, ~2 min).
Current goal and handoff — 2026-10-19, native AI workflow¶
The owner reaffirmed full AI PCB planning, placement, routing, inspection and finishing through the KiCad fork, with native human takeover (especially pairs/shove) and visual quality. PCB work is the priority while schematic automation improves. Shared rules/examples/tests improve future sessions; this is not model-weight training. Fork development is intentional; old Rust/stock-API-only/upstream-first proposals are historical. No GitLab push or upstream submission is authorized.
- USB pair/skew checkpoint — 2026-10-19 (uncommitted, eeroute branch
native-usb-pair-skewfrom a313855): newtools/pair_audit.py(+test_pair_audit.py, 19 tests pass; read-only, per-member length, via-inclusive length, skew vs 0.15 mm, width/gap vs netclass, reference coverage,unknownwhere undeterminable, exit 2 when open/over) andnative-editor-smoke.py --usb-pair --pair-net P --pair-net N [--pair-script JSON](tools/pair_route_checks.py; steps are control-CLI calls plus a disposable-copyremove; saves only if a cli step changed copper; setKICAD_PNS_API_TRACE=1for fit/target/reached/delta and head traces). Source STI board/pro/dru hashes unchanged. Real findings (boardwork/route_H_native_usb, runswork/native-editor/runs/usb-pair-{a01,b01,c01,d01,f02,g01,h01}): audit verdict OPEN: D+ complete J2:B6→M1:N3 (40.12 mm xy, 44.66 mm with via, 3 vias, F>B>F>B); D− open at M1:N2 and at J2 pad A7 (no A7/B7 tie). Coupled pair U1:6/U1:4→M1:N3/N2 withbegin --differentialon a copy with D+'s U1→M1 section removed (11 items): paired via accepted only at (97.5,105.5); every waypoint along the old D+ corridor fits (fit=1) but the pair never reaches the pads (fit=0at (118.2,99.2),reached=0); single-ended D− plan U1:4→M1:N2 also stops at (117.13,97.73). Cause: VBAT B.Cu (0.2 mm) runs diagonally (116.1,99.25)→(117.5,97.8) across the N2 approach and around the slot D+ sits in; F.Cu is crossed by vertical CRUISE_/MIL traces. D− cannot reach M1:N2 without a human VBAT reroute; no rule was relaxed. Hand estimate (unverified): D+ is ~1.5-2 mm longer than D− before U1, so skew is probably over 0.15 mm regardless. Next:* (1) reroute/shove VBAT at M1 by hand or allow a bounded VBAT shove, then finish D− and the J2 A7/B7 tie; (2) runpair_auditon the saved result and stock 10.0.7 check; (3) automatictunestart item/region/cursor from the audit (long straight F.Cu D− run at y=110.73, x 84.2–92.6 is the obvious region); (4) generalizeplanto pairs; (5) advisory impedance without GPL code; (6) reconcile diff_pair_gap 0.15 (native) vs 0.25 (.kicad_pro excerpt). - Current physical-corridor checkpoint — 2026-10-19: eeroute
native-waypoint-corridorsa313855, librarydocs/native-waypoint-corridors215f8b5, both pushed to private GitHub. Fork/binary unchanged at e6a0e38f / c873c61a…7844b8 (trace build on NAS origin). The bounded A now checks both actual cardinal/45° edge decompositions and carries terminal heading, with a checked final approach and small corner cost. Reconstructs exact knees/via boundaries, compresses only same-direction runs; no native/identity/ownership/cancellation/stock gate changed. State/frontier/cache150,000 bounds,32stage cap, one shared refinement expansion allowance and deadline checks through inventory/geometry/search/output. Proof: 145Python tests (98tools+47bench), 43actual-editor checks (28fixture+3stock-saved readonly view+12real baseline/plan checks),12new controls with14old-code assertion failures. Reviewer late-success and preprocessing deadline findings plus missing saturation proof were fixed and deterministically tested; one running geometry primitive is not interruptible. Off-axis fixture(70,50)→(105,53.123456)mm uses one exact knee, passes native Preview/Cancel/Apply/connectivity/Undo/Redo and unrelated-copper preservation, and is explicitly isolated/saved before other fixture operations. Stock10.0.7 and complete fork audit pass:17→16opens, zero new findings/regressions, two unchanged bends, sidecars exact, per-net teardrops pass; primary inspected the saved native join. Real source remains115opens, no retained copper: C1 fallback28→11checkpoints rejects stage3 after exact arrival because shove creates a45° VVT_LH branch at(161.784,115.775)mm; M1:N22 fallback11→4 rejects stage3 with a one-point head; R43 fallback14→8 rejects stage1 after exact arrival because shoved copper forms a90° join at(176.6091,131.6195)mm. Every final native model equals its own baseline; all original source PCB/project/rule bytes remain exact. Final batch pinned current source hashes and repeated fixture, stock/full audit, saved reopen and all three real trials. Next: use native trace/world geometry to identify the moved branch/obstacle and generate bounded shove-feasible alternatives or permitted passive-placement changes; nominal heading legality alone does not certify PNS shove. Identity-bound cancellation acknowledgement remains separate; keep generic unknown-session errors fatal and original stock/native gates intact. Full-board/USB electrical/desktop gates remain open. Evidencebench/results/native-waypoint-corridors-2026-10-19.json; guidedocs/native-planning-tuning.mdsection Physical corridor geometry and heading; rawwork/waypoint-corridors/,work/native-editor/runs/waypoint-corridors-*. Four enforced read-only GPT-6.1 Sol medium* helper jobs actually ran (design/test design/review/follow-up); primary integrated, corrected and independently verified. Fresh Codex app-server37→38%weekly, shared attribution; no paid API/reset credits, CLI cost unavailable. No GitLab push and no task editor/build/helper jobs remain. - Current waypoint diagnosis checkpoint — 2026-10-19: eeroute
native-waypoint-diagnosis0291ed3 (diagnosis f5f8973), forkeeroute/waypoint-diagnosise6a0e38f, librarydocs/native-waypoint-diagnosisf73c0c3. Opt-inKICAD_PNS_API_TRACEnow records exact waypoint head chains, Move result and fixed/requested starts; no routing/guard/retry behavior changed. Binary c873c61a…7844b8. 39 actual-editor checks and 66 Python tests pass. Exact retained real corridors reproduce: C1→R42 stage13 produces a one-point head; R43→R42 stage7 stops at(178.424,127.431)mm instead of(178.325,125.530)mm; M1:N22→JP11 stage4 reaches/fixes the exact point but fails a90° join against the preceding fixed tail. Omitting only the preceding checkpoint rescues that M1 prefix; a separate follow-through fails at the next original checkpoint, so no full route. The same omission does not rescue either rail corridor. Uneven-axis index diagonals are chord-checked although PNS builds45° sections, and proposals do not carry terminal heading. Native stage rejection already discards its session; the planner's duplicate Cancel gets unknown-session and correctly stops under the current acknowledgement contract. All three staged corridors were already the last alternative, so these failures do not prove a remaining candidate was skipped. Every real final model matches its own editor baseline, all original PCB/project/rule bytes remain exact, no Apply/Save/source promotion; source remains115opens. Next: normalize bounded corridor geometry using both native45° decompositions, checked constituent segments and heading-aware checkpoint pruning; inspect actual rail obstacles/shove limits as needed. Separately add an identity-bound native acknowledgement of completed cancellation before relaxing retry. Do not suppress generic unknown-session errors, weaken native/stock gates or spend another unbounded beam. Evidencebench/results/native-waypoint-diagnosis-2026-10-19.json; guidedocs/native-waypoint-diagnosis.md; rawwork/waypoint-diagnosis/,work/native-editor/runs/waypoint-diagnosis-*. Two enforced read-only GPT-6.1 Sol medium subscription CLI helpers actually ran (source/cancellation audits); primary built/replayed/measured and integrated. Codex app-server observed36→37% weekly, shared attribution; fresh post-batch refresh, no paid API/reset credits, CLI cost unavailable. Branches backed up to private GitHub/NAS origin only; no GitLab push. Full-board/USB electrical and desktop-delivery gates remain open. No task editor/build/helper jobs remain running. - Current physical-arrival checkpoint — 2026-10-19: eeroute
native-copper-arrivals32897e0 and librarydocs/native-copper-arrivals9151cc4 are pushed to their private GitHub origins. Fork stays 2eaea9a4, binary 3a5e3ccc…237dba0. Copper targets now deduplicate exact point/layer slots (eligible flashed via before endpoint before interior) and skip provably impossible fixed-track arrivals using every same-net straight track on the arrival layer, including noncomponent/locked copper. Pad/arc bounds, degenerates, incomplete bounds and a200,000-item advisory budget defer uncertain decisions; native authority and all identity/ownership/cancellation/DRC gates remain intact. Proof: two old-code regression failures now pass; 133 Python tests, 70 actual-editor checks (45copper,23advanced,2stock-saved read-only view),256compass-ray subsets within one test, and three saved fixture audits pass:15→14opens each,0new stock/full findings/regressions, two bends, teardrops pass. Primary inspected the saved pad join. Three enforced read-only GPT-6.1 Sol medium helpers actually ran; independent implementation review found no actionable issues. Real-board result: four bounded trials retained no copper: R42:2→R43:2 B.Cu found no corridor; C1:1→R42:2 F.Cu, M1:N22→JP11:2 F.Cu and R43:2→R42:2 F.Cu stopped at a rejected waypoint with unconfirmed cancellation, without further alternatives. Every final model equals its baseline and all original source bytes match; source remains115opens. Evidencebench/results/native-copper-arrivals-2026-10-19.json; guidedocs/native-planning-tuning.md; rawwork/copper-arrival/andwork/native-editor/runs/copper-arrivals-*. Next: diagnose one retained real corridor stage by stage from native trace/head geometry, then improve bounded waypoint/approach generation; do not spend another beam on impossible branch sites or weaken cancellation/native/stock gates. Full board completion, USB pair/skew and desktop delivery remain open. No editor/build/helper jobs remain running. - Current native intersection-angle checkpoint — 2026-10-19: fork
eeroute/branch-angle-guard2eaea9a4 (NAS origin), eeroutenative-branch-angle-guard774e74b (implementation 651559a, specific-rejection correction 19486d7, private GitHub), librarydocs/native-branch-angle-guard6b25d54 (GitHub). Binary 3a5e3ccc…237dba0.validApiRouteBendsnow checks surviving same-net/same-layer segment intersections, including unsplit interiors and all arms; preview includes unfixed heads and final Apply checks the fixed delta. Only complete unchanged primitive pairs retain inherited bad angles. Same-net translated-pad exemptions remain; a same-layer via is not an exemption. Stock direction/interior-angle calculation, the existing native 0.01° tolerance and clamped normalized dots preserve integer-rounded meanders without NaN. Exact saved-board DRC limits remain unchanged. Proof: 409 actual-editor checks (403 across nine final suites, four exact-STI saved-corridor controls, two read-only saved views), 125 Python tests and 514 production-expression numerical controls pass. Original build and degree-filter-only build fail the bare-interior preview control; unclamped math fails its straight-continuation assertion. The exact earlier eight-stage M1:N32→P20:1 corridor now reaches its target but logs 45° at (113.8814,139.7)mm withfit=1/reached=1/delta=0: no Apply, exact original model/source preserved. Three fixture saves (endpoint, existing via, pad-covered interior) each close one open 15→14 and pass stock 10.0.7 plus complete-report gates: zero new findings/regressions, two baseline bends, teardrops pass. Primary inspected the stock-saved pad join in the native editor. Corrected two inherited false-positive negative tests: use bareplan --outartifacts rather than the stdout response envelope, and require the intended component/ownership rejection reason. Real board and limits: final-build IN_CAM B.Cu and 0.25mm-grid IN_VMAIN trials reject all bounded proposals without retained copper; three earlier pre-final trials are separately pinned. Original STI PCB/project/rules remain byte-exact at 115 opens/eight routing errors. Fresh original-rules stock/full audit still rejects the old 114-open candidate: two symmetric new 45° angle findings; stock cosmetic reports remain capped, raw deltas retained. Authoritative input/output project/rules match the original bytes; no rules relaxed or source promotion. Native tolerance is not exact arbitrary-rule parity; USB electrical gates, remaining routing, rotation/arcs/differential drag and desktop delivery remain incomplete. Pickup: evidenceeeroute/bench/results/native-branch-angle-2026-10-19.json; rawwork/branch-angle/andwork/native-editor/runs/branch-angle-*. Four enforced read-only GPT-6.1 Sol medium helpers actually ran; primary integrated, corrected and verified. Codex weekly 33→35%, fresh app-server readings, shared attribution; CLI cost unavailable, no paid API/reset credits. All three branches backed up; no task build/editor/reviewer jobs remain, no GitLab push. Next: make target-copper selection avoid geometrically impossible bare multi-arm joins and duplicate endpoint slots while preserving logical/component/ownership bindings, then obtain a legal retained real-board completion under the original rules. The guard repair supersedes the automatic-planner checkpoint's next action below. - Current automatic target-copper planner — 2026-10-19: eeroute
native-copper-plannerimplementation 996f090, librarydocs/native-copper-plannere7eb1d7; fork unchanged 05bf3ed8, binary ca37bd6f…9e453d. New opt-inplan --target-copperkeeps logical PAD endpoints immutable and chooses exact existing track/via points from the native target-pad component. Every alternative is checked before Begin for component/net/point/layer/ownership/snapshot/via budgets; protected ancestry and disconnected same-net copper reject. Matching samples are advisory, not atomic native revisions. Ambiguous cancellation stops retries, disabled intermediate layers and unknown nested instructions reject.31 new fixture and17 real STI editor checks,23 existing planning/tuning checks and125 Python tests pass; saved read-only views/primary visual inspection recorded in evidence. Fixture track/via routes pass stock and complete-report gates. Real M1:N32→P20:1 trial reaches an existing +3.3V track interior (113.8814,139.7)mm after two no-fit endpoints, two new through vias/eight stages; stock confirms115→114 opens (+3.3V4→3), no other-net regression, six bends and teardrops pass. Rejected for promotion: two symmetric stock/full-report records of one new45° branch angle. Source PCB/project/rules remain byte-exact at115 opens/eight baseline routing errors; authoritative audit sidecars match original bytes, no rules relaxed. Two other bounded STI trials stopped at unconfirmed cancellation, no retained route. Three enforced read-only GPT-6.1 Sol medium helpers actually ran; primary integrated/reviewed. Capacity codex-app-server31→33% weekly, shared attribution, no paid API/reset use. Evidence: eeroutebench/results/native-copper-planner-2026-10-19.json; rawwork/copper-planner/,work/native-editor/runs/copper-planner-*, canonical auditcopper-planner-sti-original-rules/stock-audit/. Next: fix nativevalidApiRouteBendsskipping three-arm single-ended joins, using this rejected saved route as a failing control and matching stocktrack_anglesemantics; then find a legal retained target-copper completion under original rules. USB electrical gates, remaining routing, rotation/arcs/differential drag and desktop delivery remain incomplete. No task build/editor/reviewer jobs remain at close; no GitLab push. - Current native reference-association checkpoint — 2026-10-19: supersedes placement acceptance below. Fork
eeroute/reference-association05bf3ed8 (NAS origin), eeroutenative-reference-association096062b (implementation b2680ec, private GitHub), librarydocs/reference-association9d0c5be (GitHub). Binary ca37bd6f…9e453d. Native drag reference finishing now requires a strict same-mounting-side owner-pad witness against rival body envelopes, using exact wide-integer distances; remote owner artwork cannot establish ownership. Carried/search/pre/post-FixRoute checks share the same predicate and existing4mm bound. 311 actual-editor checks (76association+42finishing+126edit+55STI+12read-only saved views),112 Python tests and ten exact distance controls pass. Old native build fails the clear-but-misassociated-label consumer; narrow-size distance control fails the wide-envelope assertion. Three enforced read-only GPT-6.1 Sol medium helpers actually ran; primary fixed the64-bit box-dimension issue they caught. The three R40 moves all retain115 opens, six house bends and teardrop gates, with zero new complete-report findings. R53 up/down now reject reference finishing; the earlier upward junction repair remains mechanically valid but finds no associated clear label within the bound. Independent saved-board IPC geometry confirms new R40 ownership and the old R53 label's nearer R52; primary inspected all closeups. Final DRC input/output sidecars match the original project/rules byte-for-byte (the fork had normalized its disposable project schema); inspected footprint blocks match the final stock boards. Stock cosmetic cap remains, so strict stock all-finding acceptance stays incomplete/false; no source-board promotion. Original STI PCB/pro/rules hashes exact, source still115opens/eight baseline routing errors. Evidence: eeroutebench/results/native-reference-association-2026-10-19.json; rawwork/reference-association/,work/native-editor/runs/reference-*; final auditreference-sti-original-rules/stock-audit/. Capacity codex-app-server weekly30→31%, shared attribution, CLI cost unavailable. Next: integrate copper targets into automatic planning on the115-open source copy, then remaining STI routing/USB electrical gates. Rotation/arcs/differential drag and desktop delivery remain open. No task build/editor/reviewer jobs remain; no GitLab push. - Current complete-report checkpoint — 2026-10-19: eeroute branch
native-complete-drc-audit8a0b7d7, library documentationdocs/native-complete-drc7448464. Fork/binary remain9da49606/fe6aef1d…fab08b1. Diagnosed the six changed M1 silk records: stock10.0.7 caps silk-overlap and padstack reports at199 each, ignores the fork reporting-limit environment, and selects different existing partners. Complete fork reports on the same stock-saved geometry contain502 silk overlaps and416 padstack findings. All four R40/R53 candidates have zero new complete-report findings,115 real opens, six house bends and passing teardrop gates. Original board/project/rules hashes unchanged; no candidate promoted. Stock all-finding acceptance stays incomplete/false, and empty selected-reference silk lists are marked unknown when capped. Optionalcheck-native-candidates.py --full-reportpreserves stock results separately, imports only the footprint table into isolated config, pins the native binary and verifies stock-bundle immutability; diagnostic errors are recorded without aborting stock checks, while bundle mutation remains fatal. 112 Python tests (47bench+65tools) pass, including collision addition beyond502 findings, equal-count replacement and diagnostic failure/mutation controls; final helper also verified against the real binary. Four-candidate full audit predates final error/mutation hardening; final controls and real-helper check cover those changes. Evidence:bench/results/native-complete-drc-2026-10-19.json; rawwork/native-editor/runs/native-complete-drc-sti-20261019/stock-audit/,work/m1-silk-review/. Three enforced read-only GPT-6.1 Sol medium workers actually ran; primary implemented/reproduced and resolved reviewer failure-handling finding. Next: enforce native reference-owner association (R53-up can place its clear label closer to R52), then copper-target planner integration and remaining STI routing/USB electrical gates. No new router/build/editor jobs were launched; reviewer/diagnostic processes completed. No GitLab push. - Current native reference/junction checkpoint — 2026-10-18: supersedes the finishing checkpoint below. Fork
eeroute/native-fields-junctions9da49606 (NAS origin; core 787ec7ce), eeroutenative-fields-junctions2f76e63 (private GitHub; implementation 36123c0), toolsdocs/native-fields-junctionsc1c3692 (GitHub). Native drag finishes only the existing reference position within4mm on0.5mm offsets, preserving UUID/text/style/unknown protobuf attributes; other fields translate exactly. Registered native preview overlay shows the planned label before Apply. Movement, field finishing and routing use one native commit/Undo; Cancel/Escape/stale/failed updates preserve the model. R53 upward1mm now reroots its short +5V three-arm junction into stationary R51 pad2, preserves the certified long tail UUID/attributes/far endpoint and all original pad connections. Exact removed-copper guards protect off-centre contacts; locked/generated stems, uncertified tails and bridge/stem contact with another moving pad reject. - Current proof, limits and pickup: 250 actual-editor checks (42 finishing,70 uncut-STI,126 edit regression,6 pending-preview,6 saved-result reopen/view) and61 Python tests pass on binary
fe6aef1d…fab08b1. Primary inspected pending R40/R53 labels and saved stock-refilled closeups. Stock10.0.7: all three R40 moves and R53 (207,60)→(207,59)mm have zero selected-reference silk findings,0new routing findings/other-net regressions,115real opens, six bends and passing per-net teardrop gates. Strict all-finding gate remains FALSE: six raw silk partner records on byte-identical M1;129 other footprint blocks, R51 and non+5V copper are exact. Fixture routing gates pass, strict gate retains Test-library warnings. R53 downward move has legal routing but no reference fit within the4mm bound and rejects. No suppression/rule relaxation or source promotion: original STI PCB/pro/rules remain hash-identical. Evidence/contract: eeroutebench/results/native-fields-junctions-2026-10-18.json,docs/native-copper-edit.md; rawwork/native-fields-junction-review/,work/native-editor/runs/native-fields-junction-*-verified/. Three enforced read-only GPT-6.1 Sol medium helpers actually ran; primary integrated and verified. Next: resolve the unchanged-M1 silk gate and label association, then copper-target planner integration; broader junctions, rotation, arcs, differential drag and desktop delivery remain open. No task build/editor/reviewer jobs remain. - Previous native finishing/connected placement — 2026-10-18: fork
eeroute/native-finishing-placementcd8189c3 (NAS origin), eeroutenative-finishing-placementf349f6b (private GitHub; implementation dcfaaee), toolsdocs/native-finishing-placement60bbc01 (GitHub). API component drag now uses bounded KiCad chamfers, preserves unchanged track UUIDs/attributes, protects off-centre copper contacts and width transitions, and checks translated pad exemptions plus new branch pairs with exact layer identity. Move/cleanup remains one native commit/Undo. R40's former four new angle records were existing remote-junction findings with a replaced rail UUID; teardrops did not suppress those angles. R53 changes a shared branch and now rejects before Apply. - Previous finishing proof and gate (superseded above): 206 actual-editor checks (22 finishing,54 uncut-STI,124 edit regressions,6 stock-result reopen/view checks) and61 Python checks pass on binary
339f5d72…507c5. Stock10.0.7: simple-corner fixture bends8→6 and angle rows16→12, no routing/open regression, teardrop gate passes. Three R40 moves left/down/right retain115 real opens, six bends,0new individual routing findings/regressions and passing teardrops; the118mm rail identity stays exact. Strict all-finding gate remains FALSE: genuine R40 reference collisions plus unchanged-M1 partner differences give7/9/7 added silk records;129 other footprint blocks are byte-identical. Fixture retains a moved missing-Test-library warning. All raw findings stay visible; original STI board/pro/rules remain unchanged and no candidate is promoted.tools/check-native-candidates.pyaudits saved copies and exits2 when none pass strict finishing. Evidence/guide: eeroutebench/results/native-finishing-placement-2026-10-18.json,docs/native-copper-edit.md; rawwork/native-finishing-review/,work/native-editor/runs/native-finishing-*. Three enforced read-only GPT-6.1 Sol medium reviewers actually ran; primary verified their fixes. Next: native reference-field finishing/placement and shared-junction topology repair, then copper-target planner integration. Rotation, arcs, differential drag and desktop delivery remain open. No build/editor/reviewer jobs left running at handoff. - Native copper targets and edit checkpoint — 2026-10-18:
begin --target-itemnow routes through native PNS to exact straight-track endpoints/interiors or an existing via centre.dragadds bounded native segment/via drag and translation of permitted unlocked simple SMD footprints, including existing connected fanout; previews/Cancel/Escape and one-step Undo/Redo preserve native objects and identities. Through-via drag, 1 mm placement grid and a10 mm per-axis movement cap; locked/generated copper and fixed module/connector/hole anchors remain protected. Component drag stretches tracks and rejects collisions; it does not shove obstacles. Custom-field history identity and long-fanout bounds were fixed at their native source; canonical lessons are in kicad-lib README Known traps. - Native edit proof and backed-up heads: 367 recorded native/editor checks across16suites,61Python checks, saved stock10.0.7 refill audits and primary closeup review pass their stated execution gates. Final edit fixture124, controlled-STI72 and unrouted-placement20; evidence pins every suite's binary, including earlier unchanged routing regressions. Fork
eeroute/copper-targets-native-edit4ae0dce8 is backed up to NAS origin; eeroutenative-copper-targets-edit7eddadf and kicad-libdocs/native-copper-editcdc3731 are pushed to private origins. Guide/evidence: eeroutedocs/native-copper-edit.md,bench/results/native-copper-edit-2026-10-18.json; rawwork/native-edit-review/,work/native-editor/runs/native-edit-fanout-*,native-unrouted-placement-final/andnative-edit-view-*. Four GPT-6.1 Sol medium workers actually ran (three enforced read-only reviews, one scoped test implementation); primary integrated and verified. No GitLab push or source-board modification. - Edit acceptance limits and next work: the source STI remains115 real opens. Two short traces are deliberately withheld on a disposable copy (117opens); each controlled track/via repair returns it to116, with0new routing findings/regressions, six bends and teardrop checks passing. Copper drags retain117opens with0routing findings; R39's separate unrouted1mm move retains115opens and every saved track/via exactly. Additional silk records concern the byte-identical M1 block; raw differences remain recorded and strict all-finding finishing remains FALSE. Connected R53/R40 moves pass mechanics/history but are rejected after refill for new track-angle findings. Interior splitting preserves original copper but the strict fixture finding gate rejects a reidentified dangling tail; fixtures also lack final teardrops. Nothing is promoted to the source board. Pre-finishing next work (now superseded above): connected placement/corner cleanup, then planner integration of copper targets; rotation, differential/corner/arc drag, broader footprint/rule geometry and desktop packaging remain open.
- Precise breakout and alternatives checkpoint — 2026-10-18: native pad polygons/straight-track capsules, local anchor coordinates, conservative fallback/through-via masks and unlocked-track shove proposals now drive up to4fresh speculative corridor sessions.
plan --alternative-corridorstries reversed pad breakouts and source-connected existing vias; explicit target-layer/source-component policy stays binding. Shared search deadlines/cell/stage caps, stale checks, preflight of every alternative and transport/identity/unconfirmed-cancel stop controls remain enforced. 136tool GUI regressions+7real routing checks+50Python tests PASS, plus saved stock-format native reopen and primary inspection of module closeup/route overview. Contract/evidence: eeroutedocs/native-planning-tuning.md,bench/results/native-module-breakout-2026-10-18.json; rawwork/native-editor/runs/breakout-native-06/,breakout-final-*,breakout-stock-view-01/; source review/buildwork/breakout-review/. - Real native success and fix — 2026-10-18: current H1a USB_D+ U1:6→M1:N3 is routed on separate board branch
layout-v2-native-usb-breakout268b447, stock KiCad10 format, refilled/saved and pushed to board origin. Forward heads stopped at VBAT x117.1649 before target x118.199995; the second alternative starts at M1 on B.Cu, legally shoves VBAT outwards, adds one0.6/0.3mm via outside modules and finishes U1 on F.Cu. Native components2→1, stock target opens1→0, real board opens116→115(raw185→184),0new findings/regressions, house bends6→6 and changed-net teardrop gate PASS (676total). Eight baseline errors remain; USB_D−/90Ω/return path/0.15mm pair skew and historical no-via ideal are not completed. All130footprint placements/locks and950pad geometries/nets/layers exact; only USB_D+/VBAT track/via geometry changes. Debugger control fixed native pair-gap inheritance casting a via/pad/arc to segment; final fixture covers recognized-pair-net via Begin/Cancel. Two enforced read-only GPT-6.1 Sol medium reviews actually ran; primary resolved four safety findings and verified results. Capacity fresh codex-app-server1792367879.5592408,21%weekly used; CLI cost unavailable, no paid API/reset credit. - Planning/pairs/tuning checkpoint — 2026-10-18: automatic bounded A single-trace corridor/via-site proposals (
plan/preview-plan), exact native waypoint stages, API-owned differential F/B/F through-via stages and native single/pair/skew meander tuning implemented. Final binary passes 22new+113regression actual-editor checks and40Python tests; exact Cancel/Escape and one-step Undo/Redo verified. Both pair member lengths, projected-interval hard gaps, full stroked tuning region, actual via-site/span rules and staged house bends checked. Stock10.0.7 independently confirms four target opens closed,6vias,0new routing findings/regressions, bends2→2; full finishing grade FALSE (teardrops)*. Independent segment measurements: single40.000008mm, pair44.999995mm, skew0.000008mm against0.05mm fixture tolerance. Contract/evidence: eeroutedocs/native-planning-tuning.md,bench/results/native-planning-tuning-2026-10-18.json; rawwork/native-editor/runs/advanced-routing-05/andplanning-final-*; primary inspected final fixture screenshot. - Historical coarse-grid limit — 2026-10-18 (resolved by the precise-breakout checkpoint above): current H1a USB_D+ U1:6→M1:N3 was disconnected:1/0.5mm grids found no proposal,0.25mm exhausted bounded search. No retained copper; original PCB/project/rules hashes unchanged. Earlier J2A6→U1:6 selection was already connected.
advanced-sti-plan-03/is the final-binary trial; neither success of safe failure checks nor fixture success means real routing improved. Conservative boxes and soft pour costs guide search; precise legal module breakout/native rule guidance, alternative corridors and topology remain next. Read-only GPT-6.1 Sol medium actually ran5source reviews; primary integrated and verified fixes. Capacity checkpoint fresh codex-app-server1792364876.1937654,18%weekly used; CLI cost unavailable, no paid API/reset credit. - Staged-via checkpoint — 2026-10-18: native
StageRouteVia/CLIstage-viaretains single-ended intermediate tracks/through vias inside PNS, changes to explicitly permitted routing layers, and commits only at final Apply.begin --allow-layer ... --target-layer ...; max8stages, exact position, immutable target layer, full-span native dimension/rule and actual-width checks, complete delta/owned-hole validation. Existing vias cannot move. Any rejected native owner stage discards everything; foreign owners cannot cancel. Saved sizes/modes restored. 31new GUI checks,82existing GUI regressions (32session/29anchor/21controller),23Python tests PASS: outer/inner changes, existing copper starts, blocked/forbidden/zero/overflow/pair rejection, dirty-state preservation, Escape/expiry, exact one-Undo/Redo for1/2via routes. Docs/evidence: eeroutedocs/native-staged-vias.md,bench/results/native-staged-vias-2026-10-18.json. - Staged audit and limits: stock10.0.7 independently confirms both fixture nets1→0opens,3new vias,0new routing findings/regressions and no bend increase; full finishing grade FALSE (teardrops). No real STI trial in this milestone; current H1a remains116opens at last measurement and both worktrees clean. Raw
~/eeroute/work/native-editor/runs/staged-vias-01/plusstaged-session-regression/,staged-anchor-regression/,staged-controller-regression/; review/buildwork/staged-via-review/. Primary inspected final/preview screenshots. Historical single-ended scope; differential through-via staging and actual-site checks are now covered by the planning/pairs/tuning checkpoint above. Blind/buried/micro vias and broader sizing remain open. - Native anchor checkpoint — 2026-10-18: extended fork BeginRoutePreview with exact straight-track endpoint/existing-via centre starts, same-net single-ended planar routes to pads; legacy differential pad starts preserved. CLI
begin --start-item UUID --start-end start|end(omit endpoint for via),inspect --tracksnow includes vias. Native guards reject conflicting/incomplete fields, interior/off-centre positions, wrong net/layer/type, arcs and direct/nested generated ownership. 29anchor GUI checks,32native session regressions,21controller regressions,18Python tests PASS. Native Escape/cancel and exact one-step Undo/Redo verified; existing via identity unchanged. Collinear track Apply merged/replaced its start UUID while preserving physical span/connectivity; refresh live item IDs after edits. Contract/evidence: eeroutedocs/native-route-anchors.md,bench/results/native-route-anchors-2026-10-18.json. - Anchor audit and real limit: stock10.0.7 confirms fixture ANCHOR_TRACK2→0 and ANCHOR_VIA1→0opens,0new routing findings/regressions; full finishing gate FALSE (teardrops). Historical STI after prior connector bridge: existing USB track08e442f3… start→U1:6 has can_apply FALSE;6real GUI checks PASS for inspection/preview/cancel/explicit Save, source bundle hashes unchanged. Stock USB_D+3→3opens,0new findings/regressions; no real improvement claimed. Raw
~/eeroute/work/native-editor/runs/anchors-final/,anchors-session-regression/,anchors-controller-regression/,anchors-sti-usb-01/; review/build~/eeroute/work/route-anchor-review/. Primary inspected fixture/real screenshots. - Native analysis checkpoint — 2026-10-18:
kicad-control.py analyzenow returns native pad-bearing components, exact endpoint membership, effective netclass guidance and optional selected-layer region inventory with native bounds, padstack presence, locks, copper text, zones/rule areas and conservative drill envelopes. Uses existing IPC; no fork/schema build required. 21controller GUI checks,13unit tests (8analysis+5controller),9real trial checks PASS. Split-zone fixture proves shared zone UUIDs do not merge disconnected islands; busy rejection, copper text layer filtering, missing-box identity and mid-read change rejection covered. Docs/evidence: eeroutedocs/native-analysis.md,bench/results/native-analysis-2026-10-18.json. - Analysis real proof and limits: historical STI USB copy reports5pad groups→4 after J2A6→B6; stock10.0.7 agrees USB_D+4→3opens and221→220board opens,0new routing findings/regressions. Full grade FALSE (remaining opens/teardrops). Original hashes unchanged; raw
~/eeroute/work/native-editor/runs/analysis-sti-usb-reviewed/, fixtureanalysis-fixture-reviewed/, review~/eeroute/work/analysis-review/. Region boxes are inspection candidates, not clearance/blockage proof; current loaded fills only; padless islands/house exclusions differ from DRC. Matching before/after geometry samples are advisory, not atomic revision/custom-rule identity. - Controller checkpoint — 2026-10-18: reusable Python client and JSON CLI now inspect live nets/pad UUIDs/track geometry, select exact logical documents and physical pads, resume private client/editor/schema-bound sessions across CLI calls, preview/apply/cancel natively and Save explicitly. 15controller GUI checks,32native session regression checks and5unit tests PASS. Lost/malformed mutation replies report outcome unknown, with no retry/automatic Undo. Docs: eeroute
docs/native-controller.md; evidencebench/results/native-controller-2026-10-18.json. - Real controller trial: historical STI USB benchmark copy, J2A6→B6 on USB_D+,7GUI checks PASS including exact one-step Undo/Redo and explicit Save. Stock10.0.7:221→220board opens, USB_D+4→3,2.8mm/5segments/0vias,0new routing findings and0other-net regressions. Full grade FALSE: remaining opens and missing new-copper teardrops. Original PCB/project/rules hashes unchanged. Raw
~/eeroute/work/native-editor/runs/controller-sti-usb-01/; fixture/regression siblingscontroller-final/,controller-session-regression/. Primary visually inspected screenshots; full-board real image is not a local geometry proof. - Live-session checkpoint — 2026-10-18: built and verified typed Begin/Update/Apply/Cancel through the existing editor PNS tool. 32 actual GUI checks PASS: single and differential apply/undo/redo, neighbour shove, preview/cancel copper preservation, owner/input/commit rejection, stale netclass invalidation, Escape/native pair-tool takeover, native 45-degree drag of AI-created copper and60s abandoned-session expiry. Existing7item/undo checks PASS on the same binary. Stock10.0.7 reopens the downgraded final fixture:0unconnected,0errors,4missing Test-library warnings. Primary inspected preview/saved screenshots. Proof: eeroute
bench/results/live-route-preview-2026-10-18.json; raw~/eeroute/work/native-editor/runs/route-preview-final/,item-regression-final/and~/eeroute/work/native-session-review/. Prototype scope: single-layer pad-to-pad, planar shove, native-recognized pair names; complementary target pad on the target footprint. Full manual route extension is not automated by this fixture; native drag/cancel is verified. - Priority clarification — 2026-10-18: owner offered Draw.io/Grafana/Craft as ideas to evaluate, not integration priorities. The visual prototype remains an optional experiment. The native session and reusable controller milestones are now verified; remaining real topology/ground/finishing failures take priority.
- Latest visual-planning checkpoint: owner asked to continue evaluating existing drawing tools. A real STI H1a view now uses native KiCad Fab/Courtyard/Edge.Cuts export,130UUID-bound footprint origins, explicit M1/USB review regions and3heuristic cut-pressure segments from the existing scorer. Private artifacts:
~/design/pcb-planning/sti-h1a-20261018/final-verified/(Mac NAS:/Volumes/EmbeddedEra/design/pcb-planning/sti-h1a-20261018/final-verified/). Openeditable.vectorcraftfor annotation,registered.svg/preview.pngfor review. No board edits; original PCB/project/rules hashes unchanged. - Visual verification: all130anchors within0.001mm (max0.00001058mm),3registration fiducials,10mmbar, source-bound native subtree/outline and native-only1304x1123raster comparison pass. Annotation metadata edit and reference locks survive native save/reopen. Six Python controls and5negative cases (physical units, moved anchor, moved native artwork despite intact anchors, stale identity, output alias) pass. Review caught/fixed parse-before-snapshot race and incomplete output guards. Raw unitless VectorCraft0.13.1 SVG needs its mm frame restored; trap in kicad-lib scripting README. Full proof: eeroute
bench/results/visual-planning-2026-10-18.json; reproduction/limits:docs/visual-planning.md. - Visual bridge still missing: checker is a generated-view round trip, not arbitrary edited-plan import or a copper/placement command. If annotations later prove useful, connect typed intentions and source revision to native preview/apply; this is optional and follows the native routing session milestone. Review labels must be explicit; a J2 reference does not infer USB. Pressure is a ratsnest/capacity heuristic, not actual routing congestion, clearance or return-path proof. Draw.io functional diagrams and Grafana progress panels were assessed as supporting roles; neither was configured. Existing native PNS session API remains the main product-control gate.
- Current heads, backed up: fork
~/kicad-fork,eeroute/module-breakouts5bbc7cbc (NAS origin); eeroute~/eeroute,native-module-breakouts2c6e77a (private GitHub); kicad-libdocs/native-module-breakoutsd98862e (GitHub); routed board worktree~/eeroute/work/route_H_native_usb,layout-v2-native-usb-breakout268b447 (board origin). No GitLab pushes or development/main/master merges. Native tool contracts/reproduction live in eeroutedocs/native-workflow.mdanddocs/native-planning-tuning.md. - Native GUI proof now real: reusable eeroute
tools/native-editor-smoke.pystarts the actual fork editor under isolated Xvfb, with a private IPC socket/profile and generated board. Seven checks PASS: open document, create two native tracks in one commit, one undo removes both, one redo restores both, drop a staged deletion, disk unchanged before explicit save, native save. Primary inspected screenshots. Rerun with Python-Oalso passes after review fixes (explicit checks and bounded cleanup). Stock10.0.7 reopen after downgrade:0opens, four fixture warnings (2missing Test library,2silk); not a board-quality pass. - GUI build trap resolved locally: focused PNS builds lacked
resources/images.tar.gz, causing a blocking modal. Buildbitmap_archive_buildwithpcbnew/pcbnew_kiface. Isolated10.99 test profile needs all three library tables and startup choices. Details have one home in kicad-lib scripting README; no real user profile was edited. - Router fix: skip already-plane-anchored ground fragments while seeking a partner. Old blocked control makes12proposals, new makes1seed then exact rollback with0retained vias. All11ground controls PASS; successful2-via bootstrap retained. New real seven-net KA1 replay still11→3opens (OUT_ETB+, OUT_ETB-, +12V_ETB), stock/native agree,0new findings/regressions,0sharp bends. Six provisional stitch proposals across rejected ETB trials; legal partner sites/short ground escapes still missing. This is not a zero-open gate pass. USB4/CAN6 remain the prior verified measurements, not rerun in this batch.
- Proof: eeroute
bench/results/native-workflow-2026-10-18.json; raw~/eeroute/work/pns-spike/native-workflow/and~/eeroute/work/native-editor/runs/final/. Full workflow contract/source entry points/reproduction live in eeroutedocs/native-workflow.md. README corrected from stale Rust-only description.54Python tests,11native ground controls and7GUI checks pass; remaining native controls retain their previous evidence, not a claim of a full rerun this batch. - What remains: (1) USB_D− module connection and qualified paired corridor/skew/return-path/impedance result, explicit CAN_H/L pair identity, arc/interior starts, broader padstack/reload and richer tuning; (2) generalized autonomous placement/branch/ground topology, stage2 ETB/ground and remaining real opens; (3) current/analog/noisy spacing, eligible-junction teardrop coverage, tidy routing and visual review; (4) stable bench integration and tested Mac/Windows delivery. Precise bounded alternatives and one real STI module connection are now verified; full nice-board completion remains open.
- Next implementation: use the accepted native USB_D+ breakout as the held-out positive control, then route USB_D− through a compatible corridor and apply native pair-length/skew tools with the actual stackup/return-path requirements. Preserve module/layer/width/via policy, exact cancellation and independently saved stock acceptance. Continue CAN trunk/branches, ground/ETB completion, broader held-out boards and finishing/delivery. Supporting visual integrations remain optional.
- Original board untouched; no routing/build/helper jobs left running. H1a
route_Hremains c0ed69a/116real opens and main layout-v2 stays unchanged; the separate routed branch is268b447/115real opens, all worktrees clean. Final_pcbnew.kifaceSHA2561810f6bf6c35697bc24c4eedced937e924750fbe77d0d9a656881821466c5786; source and routed-board branches are backed up.
Review and completion plan — 2026-10-18¶
Assessment: headless routing feasibility is proven; a trustworthy autonomous real-board release is not. Keep KiCad PNS as the detailed executor, build the missing planner around it, and leave the separate Rust routing engine deferred. KiCad already has interactive shove/pairs/length tuning; our driver exposes only part of that machinery. Packaging and another engine rewrite do not resolve the measured completion gap.
Professional checks: (1) add negative controls for data preservation and ordinary net names before extending search; (2) use actual source, serialized DRC, fixture outputs and raw matrix rows; (3) use the installed fork binary, native guard tests and independent Python/stock-KiCad checks; (4) success means autonomous completion under unchanged rules on held-out real boards, not a replay or a test harness exiting successfully.
| Capability | Verified position | Still needed |
|---|---|---|
| Native PNS execution | Shove, pair identity, paired vias, rollback, bounded rip-up implemented; fixtures demonstrate mechanisms | Integration and regression fixes; reliable cancellation and deadlines |
| Autonomous planning | Direct/offset/via heuristics; reference copper replay | Obstacle-aware corridors, branch topology, via sites, layer policy, congestion/order/retries; no routed reference in production |
| Real-board result | Fresh strict ground-repair replay 11→3; normal USB 8→4 and CAN 8→6, stock 10.0.7 agrees | Zero-open legal replay controls, then no-reference real-stage completion |
| Pair/length behavior | Coupled routing exists; real USB/CAN incomplete | Single-ended connector breakouts plus coupled trunks; CAN branches; automatic skew/length tuning and stackup-derived dimensions |
| House rules | Shared scripts and style measurements exist separately | One guarded finishing pipeline for widths, 45-degree bends, teardrops, plane stitching, sensitive-net spacing and module copper |
| Benchmark | Individual findings, house gates, saved exact net identities, dependency-aware cache and stock real reruns verified; historical rows unchanged | PNS adapter into main bench and held-out completion cases; teardrop eligibility coverage |
| Product integration | QA executable in fork; eeroute repo holds Python experiments/bench | Stable CLI/orchestrator, IPC/native UI, preview/apply/undo, progress/cancel, stale-board detection |
| Delivery | Linux container executable; selected downgrade/stock checks recorded | Portable clean-checkout builds, sidecars/resources, automated stock round-trip gate, tested Mac/Windows packages and full GUI fork build |
| Placement/swaps | Floorplan scorer and swap prototype measured | Placement acceptance by real routing; approved schematic/firmware remaps; no claim of complete automatic PCB design |
Priority findings (historical review before implementation; the five execution fixes are now implemented, see current handoff):
- P1: output path protection.
qa/tools/pns/pns_route_nets.cpp:2605opens report with truncation, without protecting input/output/reference/sidecars. On a disposable fixture,--reportequal to the input board overwrote it with JSON and returned success. Validate all output paths/aliases before writes; publish output atomically with matching project/rules. - P1: net identity parsing.
pns_route_nets.cpp:591parses the first bracketed portion of a display description rather than using UUID-resolved net identity. Same fixture completes as CAN_H but remains open as DATA[0] (cli_connectivity_guard). Add bus/escaped-name fixtures and use object net names in driver and independent graders. - P1: allowed-layer policy absent.
pns_route_nets.cpp:2033includes every enabled copper layer; --layer only changes preference. Saved normal s2 attempts include In1.Cu (24 trials) and one accepted In2.Cu trial, although the stage manifest permits only F/B and In1 is the ground plane. Add explicit allowed layers and per-net plane exclusions, enforced in planning and grading. - P1: known pair defects remain.
pcbnew/router/pns_diff_pair_placer.cpp:359takes the empty-best path after all walks fail;:1124can return stale success after gateway no-fit. Independently confirmed in source; not new bugs introduced by this review. Pairdiag branch fc1799fe is unmerged with replay head 7743b2c9. Integrate diagnostics, add failing fixtures, repair and rerun USB/CAN normal/replay. - P1 acceptance gap.
eeroute/bench/grade.py:63gates error-count deltas only and does not gate recorded bends/teardrops; common.py caches DRC against PCB alone. Two actual s5a KRT rows report pass with bends 1→2 and zero teardrops. Use shared mandatory routing/house checks; compare individual findings and hash PCB, project, rules and tool/version/options. Keep documented bounded benchmark allowances distinct from production completion. - P2 product gaps. Cooperative timers, synchronous external DRC per candidate, hardcoded build/work paths, no output-sidecar contract, no portable installation/UI, no integrated length/skew tuning. Profile before refactoring the DRC boundary; preserve independent serialized final validation. Nondeterminism is measured, but pointer-ordering as its cause remains unverified.
Completion sequence (each gate blocks promotion to the next claimed capability):
- Trustworthy execution and acceptance: fix priorities 1–5; all targeted negative controls fail on the old behavior and pass after repair; existing guard/rollback fixtures remain green. Integrate PNS into the same bench. No project/rule edits or plane-policy bypasses accepted.
- Prove the executor: legal ETB branch through pad/via, planned GND island healing, real USB breakout+coupled trunk and CAN trunk+branches, with zero opens and no regressions/new routing findings. Replay is an oracle only. Repeat fixed-work-budget controls to separate nondeterminism from time/load effects.
- Autonomous route planning: obstacle-aware corridor/layer/topology planning drives the same PNS sessions; complete s2 and progressively the remaining declared stages without reference copper. Test a second board family and held-out stages. Report incomplete connections honestly; bounded search failure is not proof of impossibility.
- Finish electrical and house requirements: stackup-derived USB geometry and stated skew tolerance (existing project target 0.15 mm), matched-length group, net-class current widths, module-via keepouts including rotations, analog spacing, 45-degree bends, teardrops and plane stitching. Validate final output in stock KiCad 10 after conversion. Require zero real opens and zero routing errors on supported completion cases; documentation must identify any separate baseline findings.
- Ship the requested workflow: stable batch CLI, native/IPC preview/apply/undo, progress and cancel with recoverable checkpoints; packages and clean-machine smoke tests on Linux/Mac/Windows, full GUI-fork builds, license/source distribution and stock-open round trips. Public GitLab pushes still require owner instruction.
Independent checks run this review: 21 style tests, 5 swap tests, 5 calibration tests PASS; native --test-guards and --reproduce-cleanup PASS. Three disposable native fixture runs reproduce the plain-vs-bus difference and report/input overwrite. Source board/rules untouched. Repro outputs: ~/eeroute/work/pns-spike/review-20261018/ (review-evidence.json and per-case files). No full routing matrix or real-board PNS route rerun. Two constrained read-only GPT-6.1 Sol workers ran (medium source audit; low delivery audit); primary independently confirmed source findings and ran the checks. CLI cost unavailable.
Owner direction (2026-10-16)¶
- No pushes to GitLab (the KiCad fork or upstream) until the owner says so (2026-10-16). Pushes to the private GitHub repo
EmbeddedEra/eerouteare fine. - "Combine these two [Freerouting and KiCadRoutingTools] into like a plugin for KiCad."
- "Every single feature that we have used, that we have encountered an issue, to be fixed … kind of native to KiCad. If I want to push and go around and all that, you should be able to do it."
- Consider an AI key if it helps.
- Own GitHub repo; fork and merge, or understand and reverse engineer; pick the language that runs on every machine like KiCad.
- "Build on top of it or just from scratch … even if it takes longer, if at the end it's more scalable or faster, better" — a real decision, not assumed away.
- Look at KiCad 11 (GitLab master) so the design does not go obsolete, and contribute upstream where it helps.
- Later the same day: build the whole thing from scratch, no patching the old tools; clone/branch KiCad and tweak it where that makes the tool better.
Before building (Deeper, not bigger)¶
- What a top professional would do that was not asked: measure before choosing. A bench of our own boards with fixed metrics decides build-on vs rewrite, not opinion. Push fixes upstream so we do not carry a fork forever.
- Rawest material: the two codebases (cloned and read, file:line below), KiCad master source, and a real trial on a copy of rusefi-sti-2004, graded by KiCad's DRC and our per-net guard.
- Methods and tools: KiCad IPC API (kicad-python / kipy), Freerouting's KiCad-JSON input and native GraalVM binaries, KiCadRoutingTools' pyo3 Rust core,
kicad-cliDRC,opens.py --compare. - What proves it right: the bench — per-net opens vs the previous commit, DRC 0 against the board's own rules (never relaxed), 45° check, teardrop count, run time — on rusefi-sti-2004 stage replays and Freerouting's 173 test boards.
What the research found (2026-10-16)¶
Freerouting (code read at master 5aa1b24)¶
- v2.5.0 released 2026-10-05; GPL-3.0-or-later; 144k lines of Java, core (board, autoroute, geometry, rules, drc) about 61k; Java 25; one maintainer (Andras Fuchs, 1,224 of ~1,320 commits in 6 months). Builds in a container in ~5 min (eclipse-temurin:25-jdk, needs
BUILD_REVISION). - Already ships a KiCad 10 IPC extension and native CLI binaries (Linux x64/arm64, macOS arm64/Intel, Windows). Tested 2026-10-16: the 2.5.0 Linux native CLI crashes on a real DSN (GraalVM
UnsupportedFeatureErrorforComponentOutline, writes an empty SES; upstream #957, closed, so the fix should be in the next release). The 2.5.0 jar on the same board ineclipse-temurin:25-jrerouted fine (764 wires). Until a fixed release is tested, the plugin ships the jar with a bundled Java runtime. - Reads a KiCad JSON board directly (
io/kicad/KiCadJsonReader) and writes KiCad JSON back: a way round the SES import that wipes other nets. Gap: it drops keepouts and rule areas (Package.Keepout[0], line 614). - Push-and-shove: yes. Differential pairs: none (#133, #358 closed unbuilt). Length rules: parsed but never used (#716 open).
- Core is cleanly separated (ArchUnit test forbids GUI imports) and published on Maven Central — embeddable.
KiCadRoutingTools (drandyhaas, original; atomic14 is a stale fork)¶
- MIT; v0.23.1; very active (1,505 commits in the last month, 2 main authors, AI-assisted). 367k lines of Python + only 5k lines of Rust (A*, pair search, Dubins); 374k lines of tests plus a corpus of real boards. Prebuilt Rust library for all four platforms.
- Reads/writes
.kicad_pcbwith its own parser; round trip on our 9.4 MB board changed only 2 trailing newlines. - Plugin is a SWIG ActionPlugin — dies in KiCad 11. atomic14's
ipc-migrationbranch is a model for the IPC port. - No push-and-shove (rip-up and reroute only), no blind/buried vias, only a subset of
.kicad_dru. - Its "Claude tab" runs the local
claudeCLI with Bash allowed and no sandbox; board data leaves the machine. Do not ship that pattern.
Trial on a copy of rusefi-sti-2004 (15 open signal nets, 4 layers): KiCad shows all 15 connected, unconnected 123 → 106, +65 vias, about 13 min. But: 70 DRC errors against the board's own rules, because by default it shrinks tracks and vias below our minimums and rewrites .kicad_pro. USB diff pair: failed (0/1, left D+/D− open). CAN_H/CAN_L not recognised as a pair (needs _P/_N or +/- names). Verdict: usable only with strict flags (--escalation off --no-fix-drc-settings) and behind our guard.
KiCad platform¶
- IPC API in 10: tracks, vias, zones, refill, stackup, design rules and custom rules, net classes. Needs a running GUI in 10; KiCad 11 adds headless mode and plotting. SWIG bindings are deprecated and planned out in 11.
- KiCad's own PNS (push-and-shove) router is not exposed to plugins (no API, no issue found — see the KiCad 11 section).
- PCM installs of IPC plugins had bugs on 9.x (kicad-python #81); re-test on 10.0.x.
Others checked¶
- Topola (Rust, MIT, topological router, NLnet-funded): experimental, no releases — the best base if we ever write our own core.
- OrthoRoute (GPU, huge regular backplanes only), tscircuit autorouter (TypeScript, no KiCad import): not fits.
- Quilter, DeepPCB: paid, cloud (design data leaves the machine) — rejected under free-beats-paid and our NDA work.
- AI: LLMs fail at routing geometry (OmniRouting, 1,681 designs); LLM plans + search engine does the geometry is the approach with results (Chalmers ECCE 2025, PCBWorld).
Every trap we hit → requirement¶
Owner: FR = Freerouting patch (upstream PR), KRT = KiCadRoutingTools patch, KC = KiCad upstream MR, P = our plugin. Test = the bench check that fails without the fix.
Since the from-scratch decision, every FR and KRT row is built into eeroute instead of patched; the column now says where the old engines failed. KC rows go to our KiCad build and upstream. The tests stay the same and become eeroute's acceptance tests.
| # | Trap (where found) | Fix | Owner | Test |
|---|---|---|---|---|
| 1 | SES import replaces all routing (README) | Now: lock other nets' copper before import (locked items survive). Plugin: KiCad-JSON path or 11's ImportSpecctraSession inside one IPC commit |
P, KC (keep_existing) |
Other nets' copper unchanged, per net |
| 2 | Judge per net, not by total (README) | Per-net guard built in; any regression rolls back | P | opens --compare |
| 3 | Custom .kicad_dru rules invisible to the router (README) |
Translate what maps (class-class clearances, widths) into Freerouting rules; DRC gate catches the rest | P, FR (L) | Rule probe: 5 mm clearance makes errors explode |
| 4 | .kicad_dru bare number disables file silently (rusefi log) |
Plugin validates rules and runs the explode probe before routing | P | Probe |
| 5 | Board-level rule areas / keepouts dropped (README) | Add keepouts to the JSON reader | FR (S–M) | Track inside a keepout = fail |
| 6 | Power/mixed layer type ignored; inner layers stripped (README) | .rules layer_rule … (active off) instead of stripping; mixed layers handled |
FR (S), P | Router never touches a plane layer |
| 7 | Near-zero segments at junctions (README) | Minimum-length filter in the writer | FR (S) | Count of segments < 1 µm = 0 |
| 8 | Stall on a board-size keepout (README) | Skip rip-up/door expansion for unrippable keepout rooms | FR (M) | Hellen keepout board routes within time |
| 9 | Subset routing fails where the group succeeds (README) | Keep every net in the board, route a subset by class exclusion | P (FR flags exist) | Same nets route alone and in group |
| 10 | Killed run writes nothing (README) | Freerouting's own job_timeout (writes output); per-pass checkpoint |
P, FR (S–M) | Kill at pass 3 still yields pass-2 result |
| 11 | No 45° chamfer, sharp bends (ROUTING-PLAN) | Chamfer post-pass (ours, ported) | P, later FR (M) | No bend < 135° |
| 12 | Widest width that fits; step down only at obstacles (README, SOP-021) | Port our widen/step-down; Freerouting neck_width_um is one fixed retry only |
P, FR (L) | Share of length at class width ≥ baseline |
| 13 | Move parts, not traces (owner rule, SOP-021 1.12) | Port kicad-nudge into the plugin |
P | Nudged parts keep courtyard clear |
| 14 | Teardrops are zones, must be stripped and rebuilt (README) | Strip before, refill after, through IPC | P | Teardrop count matches pads + vias |
| 15 | Inner rings: trimmed rings come back; router sees full rings (README) | Trim on every run; export hole-sized inner shapes | P | Inner ring count unchanged |
| 16 | Hellen module keepouts with one door per edge pin; module vias on extra layers (README) | Port keepout builder and module_vias |
P | No copper inside a module outline |
| 17 | Mixed inner layer: plane net tracks first (README) | Stage order in the plugin | P | Plane not fenced into islands |
| 18 | Analog spacing to switching nets (rusefi step 4) | Port spacing.py, extra-clearance option |
P | Spacing report ≤ baseline |
| 19 | Editing the script mid-run killed it (README) | Gone: the plugin is one process per run | P | — |
| 20 | Diff pair (USB 90 Ω, matched to 0.15 mm) — Freerouting has none; KRT failed on our USB | KRT pair router, fixed on our board; impedance width from the stackup | KRT, P | USB routed, skew ≤ 0.15 mm, DRC 0 |
| 21 | CAN not seen as a pair (KRT trial) | Pair naming patterns configurable | KRT (S) | CAN_H/L routed as a pair |
| 22 | KRT relaxes our rules and rewrites .kicad_pro by default (trial) |
Strict mode default in our use; upstream PR to make relaxing opt-in | KRT (S), P | .kicad_pro unchanged; DRC vs own rules |
| 23 | ~~KRT broke another net~~: false alarm from our opens.py, which ignored any open with one Hellen module pad (fixed 2026-10-16, kicad-lib test_opens.py) |
Guard counts passive-to-module opens | P | test_opens.py |
| 24 | Length matching not tested | Bench it on a DDR-style group before relying on it | KRT | Group within tolerance |
Historical decision (2026-10-16): build from scratch; Rust priority superseded¶
"I kind of want to build the whole thing from scratch, so we don't have to patch things … get them done the right way. And also, if needed, we can … branch out KiCad and make our own tweaks to it."
Why existing tools do not fit (Hard rule 1 asks for this in writing):
- Freerouting has no differential pairs and no length matching. Its length rules are read in but never used.
- KiCadRoutingTools has no push-and-shove. Measured on our board, it left 70 DRC errors against our own rules. The USB pair failed.
- Neither tool understands our custom rules, Hellen module keepouts or house rules, and gluing two engines together means handing nets between them and patching both forever.
- Freerouting is Java with one maintainer; its native binary crashed on our board. KiCadRoutingTools is 367k lines of Python, slow on a dense board (13 min), and dies in KiCad 11 (SWIG).
- KiCad's own interactive router has push-and-shove, pairs and tuning in one engine, but it is cursor-driven and cannot run as a batch. No autorouter does all of it with our rules.
- Topola (Rust, MIT) is too early to build on (experimental, no releases, DSN-based). Tried 2026-10-16: it builds in 2 min and routes its own test boards, but it panics loading our rusefi DSN (rstar R-tree unwrap on None, even with the wiring removed). It needs a hand-written pin list for multilayer and cannot scope to chosen nets. Not usable as a baseline; recorded as "could not run".
What the old tools become:
- The baselines on the bench: Freerouting 2.5 jar (inside kicad-autoroute), KiCadRoutingTools in strict mode and today's kicad-autoroute set the score to beat, stage by stage.
- Sources of ideas, not code. Freerouting and KiCad's router are GPL, and our research read their source, so the rule is:
- no GPL code is copied or translated;
- implementer briefs come from papers, specs and observed behaviour;
- the research clones stay out of implementer worktrees;
- review checks for structural copying.
This keeps eeroute MIT. KiCadRoutingTools is MIT, so its Rust pair search and Dubins code may be reused with attribution if it beats ours.
- From scratch means the routing engine. Everything around it uses existing crates, each checked for an MIT/Apache-compatible license before use:
- .kicad_pcb parser: kiutils-rs, else a small s-expression crate
- spatial index: rstar
- polygon booleans and offsets: i_overlay / geo, or a Clipper2 port
- KiCad IPC: kicad-ipc-rs or kicad-api-rs, else prost + nng
- dialog: egui
- threads: rayon
- kicad-autoroute stays in use until the new engine passes the bench, then retires.
Honest cost: this is the long road. - KiCadRoutingTools, for scale, took 10 months, about 5,600 commits and about $500 a month of AI to get where it is. - Estimate for us, AI-assisted, part-time: - 3–5 months to a batch router that matches Freerouting on our boards. - 6–12 months to beat both baselines, with pairs and length matching included.
These are estimates, to be corrected by the phase gates.
Historical Rust architecture (deferred)¶
KiCad (stock 10/11, or our KiCad build)
▲ IPC API: protobuf over nng, one commit per stage (Ctrl+Z undoes it)
▼
eeroute: one Rust program per platform (KiCad "exec" plugin + CLI for headless and CI)
├─ model board in integer nanometres (KiCad's own unit); reads .kicad_pcb directly or through IPC
├─ rules net classes, custom .kicad_dru rules (same expression language), stackup, impedance widths
├─ geom polygons, clearance hulls, spatial index
├─ route global planner (channels, layers, order; negotiated congestion)
│ detailed router: walk around + shove (push-and-shove), 45° native, class vias
│ diff pairs routed as a pair from the start; length and delay tuning; SMD/BGA/QFN fanout
│ rip-up and reroute that never breaks a routed net (the per-net guard lives inside)
├─ house widest width that fits, step down only at the obstacle; move parts not traces (nudge);
│ Hellen module keepouts with doors, module vias, inner rings, teardrops, analog spacing
├─ check per-net opens, rule probe, spacing; final word = KiCad's own DRC
└─ ai optional planner (JSON only)
eeroute route BOARD --stage 4.
- It is a library too. A C interface lets our KiCad build call the engine directly (see below).
- Rules come from KiCad, never relaxed. Our checker must agree with KiCad's DRC on every bench board before routing is trusted (phase 2 gate).
- No file format changes, ever. Boards stay plain KiCad files that open in stock KiCad.
Our KiCad build (fork)¶
- How:
- Fork KiCad (
embeddedera/kicadon GitLab) and make one branch per change off master. - Every change is also sent upstream as a merge request. The fork only carries a change until KiCad accepts it.
- eeroute must always work on stock KiCad through the public API; fork features only make it faster or more native.
- Changes, in order of value:
RunDrcAPI command: KiCad's own DRC called from the router during a run, instead of akicad-cliround trip. Medium; useful from phase 2.keep_existingon SES import,RegenerateGeneratorsfor tuning patterns. Small.- Live progress: the API cannot push events to plugins yet, so routing cannot be watched live. Medium.
- Our engine inside KiCad's interactive router: a tool in the router menu that uses eeroute's shove, pairs and tuning while you drag. Large. The one most likely to stay fork-only, because it changes KiCad's router UI.
- A fast bulk read of the board, if phase 2 measures the item-by-item API as too slow on a 9 MB board.
- A
RouteNetsAPI driving KiCad's own push-and-shove router headless (from the KiCad 11 study). It gives a native baseline to bench eeroute against, and a fallback for nets eeroute cannot finish. Large, and it needs maintainer agreement upstream. - What a fork costs:
- a rebase on KiCad master every month;
- our own builds and installers for Linux, macOS and Windows on every release, through GitHub Actions (free for public repos) and the docker build we already use;
- anyone on stock KiCad does not get fork-only features.
- The fork is GPL-3, so we publish its source; eeroute stays MIT (MIT code may be linked into GPL KiCad).
- Rule: start a fork change only when the stock API blocks a gate, or for item 1, which pays from phase 2.
AI key (optional, off by default)¶
- Use: plan net order and layer assignment, pull constraints (diff pair impedance, length groups, keep-aways) from the schematic and datasheets, triage DRC errors and suggest nudges. It writes a JSON plan; the engine does the geometry; the DRC and guard judge it.
- Never: let the model run tools or shell commands on the board (KRT's tab grants Bash); our planner returns data only, validated against a schema.
- Key: our Anthropic key (
~/.config/anthropic/env), within the $100/month cap; works without a key. - Confidentiality gate: off for customer and NDA boards (Friending: no AI until the NDA is executed); per-project switch, default off.
Re-prioritised (owner, 2026-10-16 pm): human-like routing first, through KiCad's own router¶
"I want the auto router to behave like a human ... do the diff pairs and all that. If that means modifying KiCad, we need to be able to do it."
The plan above put pairs in phase 5 and KiCad-router integration last. That order is withdrawn. KiCad's PNS router already does push-and-shove, diff pairs (PNS_MODE_ROUTE_DIFF_PAIR), length and skew tuning and 45 degree bends, and it is what a human uses. So:
- Phase A (first): headless batch driver in our KiCad fork (old fork item 6, RouteNets). It takes a net list, walks each net's ratsnest and drives PNS as a person would: StartRouting(pad), Move(target), FixRoute, pairs through the diff-pair placer. PNS_KICAD_IFACE_BASE is already separate from the GUI (pcbnew/router/pns_kicad_iface.h:60, pns_router.h:180-208), so the entry point is small. Code lives in the fork (GPL, fine), one branch per change, sent upstream.
- Phase B: eeroute orchestrates (MIT, stock-API side): net order, layer choice, retries, the per-net guard, our house rules (traps 11-18), DRC gate. It calls the fork; on stock KiCad it falls back to kicad-autoroute.
- Phase C (later, only if the bench shows PNS cannot finish what Freerouting does): the from-scratch Rust engine for the hard cases (global planning, rip-up). The clean-room rule holds there; it does not apply inside the fork.
- Gates: USB 90 ohm pair matched to 0.15 mm, CAN as a pair, one rusefi stage finished with DRC 0 and no regressions. The Phase 0 bench stays the judge.
- Spike first (local only, no go-ahead needed): build the fork headless, route one net and one pair on a copy of rusefi-sti-2004. GitLab push still waits for the owner.
Phases (from scratch, order superseded by the section above)¶
Each phase ends with a gate on the bench. A missed gate twice means stop and rethink, not push on.
0. Bench. Replay rusefi-sti-2004 stages 2–8 from their commits. Freerouting's 173 DSN test boards are out (eeroute reads .kicad_pcb, so they cannot be shared fixtures). Second set later: KiCadRoutingTools' corpus of real .kicad_pcb boards, local only, after checking its license covers the board files. Record completion, per-net opens, DRC vs the board's own rules, vias, length, 45°, teardrops and time for the baselines (Topola could not run on our board). The hand-finished rusefi board is the quality reference. Needs no go-ahead.
1. Repos (owner go): EmbeddedEra/eeroute (MIT) and the KiCad fork.
2. Model, rules, I/O. Read our boards, compute clearances and rules.
- Gate: our checker and KiCad's DRC agree on every bench board in the routing categories: clearance, track width, via, hole and annular ring, hole clearance, edge clearance, connectivity. Courtyard and silkscreen checks are out of scope.
- Time box: 6 weeks.
- Fork change 1 starts here.
3. One net at a time. Walk around, then shove, 45°, vias.
- Gate: rusefi stage 2 nets routed one by one, DRC 0, no other net broken.
- Time box: 8 weeks.
- Phases 2 and 3 are the first real measure of pace. Re-estimate the rest after them.
4. Batch. Global planner, negotiated congestion, rip-up with the guard inside. Gate: stages 3–8 complete at least as well as Freerouting, DRC 0, 0 regressions, faster than 13 min per stage.
5. Pairs, length, fanout. Gate: USB as a 90 Ω pair matched to 0.15 mm, CAN as a pair, a DDR-style length group within tolerance, plane fan-out like autoroute.py fanout.
6. House rules (traps 11–18). Gate: bench parity with kicad-autoroute on every metric, then it retires.
7. Plugin and packaging. Toolbar button, dialog, one commit per stage; PCM package; CI on Windows, macOS and Linux with KiCad 10.0.x and 11 nightly.
8. AI planner behind the confidentiality gate. Gate: it improves completion or time on the bench, or it is dropped.
9. Fork changes 2–5, each only when a gate needs it, each sent upstream.
KiCad 11 and upstream contributions¶
Read from KiCad master 33cdb12 (2026-10-15, version 10.99) and kicad-python main (0.9.0.dev0).
What 11 gives us (so we do not build it):
- Headless: kicad-cli api-server; kipy KiCad(headless=True, file_path=…) starts it. Open/save/close documents through the API.
- Undoable edits: BeginCommit/EndCommit, CreateItems/UpdateItems/DeleteItems for tracks, vias, zones (already in 10).
- Rules, custom rules (structured), net classes plus the new net-class assignments, stackup, RefillZones (synchronous headless).
- ImportSpecctraSession and RunBoardJobExportSpecctraDsn as API calls (added 2026-10-14), working headless inside our own commit.
- Tuning patterns (meanders) as API types — but creating one does not regenerate its tracks headless.
What 11 still lacks:
- No DRC command in the API (stays kicad-cli pcb drc).
- No routing command at all: KiCad's push-and-shove router (PNS) is cursor-driven and not reachable by plugins. No issue or MR exists for it. Its board interface is already free of the GUI, so a headless "route these nets" entry point is technically possible.
- SES import still deletes every unlocked track and via; locked ones survive. Cheap fix for trap 1 today: lock every other net's copper before import, unlock after.
- DSN export writes only the default class width and clearance, per-class vias and keepouts; custom rules, diff-pair gaps and per-layer rules are dropped.
- SWIG is gone from master: KRT's ActionPlugin will not load in 11. Plugins are python (own venv) or exec (any binary, our launcher picks the platform build).
Design consequences: the plugin is an IPC plugin from day one; KiCad 10 runs it in the GUI, KiCad 11 also headless. No DSN/SES plumbing or headless launcher of our own. Diff pairs, length matching and batch routing stay in our engines, because nothing upstream is coming.
Contributions we can send (GitLab MR, GPL-3.0-or-later, no CLA found):
1. keep_existing option on ImportSpecctraSession, plus the missing kipy wrapper. Small.
2. DSN export of per-class rules and diff-pair gaps through KiCad's rule engine; rule areas as regions (next to open issues #24077, #25751). Medium.
3. A RegenerateGenerators API command, so API-made tuning patterns become real meanders headless. Small–medium.
4. Longer term, a RouteNets API command driving KiCad's own PNS router — real push-and-shove, native, maintained by KiCad. Needs maintainer agreement on the developer list before any code. In our build it is fork change 6. eeroute benches against it rather than depending on it; upstream it needs the maintainers first.
Idea: auto-placement and layer count (owner, 2026-10-16 pm; scout report)¶
Goal: place and rotate parts so routing is easier, and suggest a layer count. Verdict: feasible, small first version; build only the scorer and the rotation gate.
- Best candidate, test before building: KiCadRoutingTools place_optimize (MIT, docs/placement-optimization.md): 90-degree rotation, same-footprint swaps, nudges, group moves, skips locked footprints; no bottom-side flip. Its routing was rejected 2026-10-16, its placement was never tried. Others: kicad-tools (rjwalters, MIT, force/GA/CMA-ES, young), KiCraft (licence unclear, 0 stars), pyplacer (BSD-3, found rotation moves made results worse: gate rotation).
- Methods: RUDY/RISA congestion estimates (from memory, unverified); DREAMPlace/OpenROAD are IC placers, wirelength core only; RL papers have no code.
- Layer count: no tool exists. Rules of thumb only (BGA rows/2 minus 2; about 50 parts 2 layers, 200 for 4, 500 for 6-8, unverified). Plan: demand = total HPWL x detour factor vs capacity = area / track pitch x signal layers x utilisation, validated on our finished boards.
- First version: read-only scorer (HPWL + crossings) on a board copy; try rotations/swaps on unlocked non-connector parts; judged by the bench and DRC. Connectors, holes, Hellen modules stay locked (SOP-021).
- Risks: ECU placement is driven by connector, thermal, EMI and mechanical limits a wirelength score cannot see; layer estimates need several finished boards to validate.
- Not verified: kicad-tools and KiCraft maturity beyond the GitHub API; Konnect (AGPL, uses an LLM, check data path); whether KiCad dropped its legacy autoplacer.
Log¶
- 2026-10-26: Owner: collapse fork branches into one and host privately on GitHub. NAS fork now has one branch
eeroute(5dee2990, all 18 old linear tips are ancestors); unshallowed from upstream GitLab (read-only) and pushed to private EmbeddedEra/kicad-fork. No GitLab push. - 2026-10-19: USB pair/skew tooling started: read-only
pair_audit.pyand--usb-paireditor trial harness built; real coupled U1→M1 attempts and a single-ended D− fallback both blocked by VBAT B.Cu at the M1 pads (details in the handoff above); next is a human/bounded VBAT reroute, not a rule change. - 2026-10-19: Implemented bounded physical45° corridor/heading search;145Python/43editor checks and isolated saved stock/full-report fixture gates pass. Real C1/M1/R43 trials still reject safely with exact original models/source; pushed heads and native-shove next work are in the current handoff above.
- 2026-10-19: Diagnosed all three retained native waypoint failures with exact head traces and a causal M1 checkpoint-omission control; continuation still fails, no real copper retained.39editor/66Python checks pass; source bytes unchanged. Current handoff above records backed-up heads and bounded geometry/cancellation next work.
- 2026-10-19: Implemented distinct physical copper arrivals and bounded angular feasibility;133Python/70editor checks and three stock/full fixture audits pass. Four real trials fail safely with exact model/source preservation; STI stays115opens. Backed-up heads, evidence and native-stage diagnosis pickup are in the current handoff above.
- 2026-10-19: Implemented native reference-owner association;311 editor checks,112 Python tests and ten integer boundary controls pass. Three real R40 candidates pass complete checks under byte-exact original project/rules; R53±1mm now reject safely. Original STI stays115opens. Backed-up heads, evidence and copper-target planning pickup are in the current handoff above.
- 2026-10-19: Continued STI/native handoff; diagnosed stock cosmetic DRC truncation, added complete-report acceptance guards and separate full-fork diagnostics, and verified four saved candidates with zero new full findings.112 tests pass; board stays115opens. Reference-owner association is next; current handoff/evidence above.
- 2026-10-18 — Native finishing checkpoint cd8189c3/f349f6b/60bbc01: three uncut-STI connected moves pass routing/teardrops, unsafe branches/contacts reject,206 editor+61 Python checks; strict silk finish still fails. Source unchanged. Next reference fields and shared-junction repair.
- 2026-10-18: native live routing session milestone passed (32GUI+7regression checks; stock10.0.7 zero opens/errors on fixture). Source/heads/evidence and remaining scope are in the current handoff above; STI unchanged.
- 2026-10-16: Research: Freerouting code map, KRT code map and trial on a copy of rusefi-sti-2004 (15 nets routed but 70 DRC errors and 1 regression under default settings; USB pair failed), landscape and AI evidence, KiCad 11 master read (API has SES import/DSN export and headless mode, still no routing command). Plan written as a proposal; nothing created on GitHub.
- 2026-10-16: Stage 2, KiCadRoutingTools strict (2 runs): 7/7 routed, 0 new DRC, 11 vias, 838 mm, 22 min each, GND pour islands 1→3 (kicad-autoroute 1→2; 7 vias, 680 mm, under 3 min). Full matrix (all stages × both × 2) running as user unit
eeroute-bench-matrix. - 2026-10-16: Bench built and validated (
~/eeroute/bench, VALIDATION.md: references reproduce commit-message counts, negative control flags). Stage 2: kicad-autoroute 7/7 routed, 0 new DRC, 158–167 s, run-to-run length noise 0.2 mm. Bench found anopens.pybug (open pairs with one Hellen module pad were dropped): fixed in kicad-lib; rusefi def43ad has 49 real opens, not 26. - 2026-10-16: Private repo https://github.com/EmbeddedEra/eeroute created (owner go), checkout
~/eeroute; boards are gitignored and never committed. - 2026-10-16: KiCad forked to https://gitlab.com/embeddedera/kicad (public, forked from kicad/code/kicad, default branch master). GitLab token: legacy PAT
plex-claudeof user adiaz40 (api, write_repository, read_api), expires 2027-10-15, in~/.config/gitlab/env(0600) and Vaultwarden "GitLab – plex-claude". - 2026-10-16: Phase 0 started (owner go): bench harness being built in local
~/eeroute(boards never committed). Topola tried and dropped as a baseline (panics on our DSN). - 2026-10-16: Owner: build from scratch, no patching; fork KiCad where it makes the tool better. Plan reworked: eeroute in Rust (MIT, clean room), old engines become bench baselines, KiCad fork with upstream MRs.
- 2026-10-16 pm: Owner re-prioritised: human-like routing (push, shove, diff pairs) first, via KiCad's own PNS in our fork; the from-scratch engine becomes Phase C. Checked KiCad master: PNS_KICAD_IFACE_BASE is GUI-free and Start/Move/FixRoute are callable.
- 2026-10-16 pm (continuation): fork cloned shallow to
~/kicad-fork(local, not pushed). Found the spike base:qa/tools/pns/pns_log_player.cppalready runs a headless PNSROUTERon a loaded board (PCB_IO_KICAD_SEXPR::LoadBoard,StartRouting/Move/FixRoute,GetUpdatedItems), andpcbnew/router/pns_kicad_iface.h:163PNS_KICAD_IFACEhas a realCommit()into aBOARD_COMMIT. So the driver is a new qa-style tool (qa/tools/pns/pns_route_nets.cpp): load board, per net walk ratsnest pairs,StartRouting(pad)→Move(target)→FixRoute, pairs via the diff-pair placer, commit,SaveBoard. Build recipe:qa/fedora-rawhide/Dockerfile(fedora:rawhide + dnf deps), targetsqa_pns_regressions/pns_debug_tool; no compiler on the host. Build BLOCKED: auto-mode classifier denied~/eeroute/work/kicad-build.sh(docker build of fedora:rawhide + compile of cloned external code); needs the owner's permission rule or a manual run. The script was NEVER written at first (the denial covered the file write too; a second write was denied as well). Owner tried! ~/eeroute/work/kicad-build.shin a plain bash prompt: no such file, and!is only a Claude Code prefix. Nothing was run. - 2026-10-16 eve: headless PNS driver spike built (fork
0f702f87, local): PNS push-and-shove and real diff-pair mode, 0 regressions, 0 new DRC, but nets left open (no via fanout/waypoints yet). See Open handoff. - 2026-10-16 21:20: Independent Codex review: keep PNS, add session state machine + via/layer escape + bounded rip-up (ranked plan in
~/eeroute/work/briefs/approach_review.out). PCBWorld-Engine (GPL, KiCad 9.0.8, single-track only) studied: take techniques (via intent, checkpoints, deferred deletion), not the code (~/eeroute/research/pcbworld-study.md). Implementation worker (days 1-5) launched, brief~/eeroute/work/briefs/pns_session.md. - 2026-10-16 22:10: Session v2 (fork d080de96, local): state machine, explicit pairs, escape planner, bounded rip-up, 7 fixtures pass, but real boards NOT improved (stage 2 5→6 opens under stricter gate, USB 3+3, CAN 4+4, 0 regressions). Human reference routes are long/winding; our driver has no path planner. Diagnostic launched: replay the human route vertices through PNS (
~/eeroute/work/briefs/pns_replay.md) to separate engine defect from missing planner; this is the day-14 abandon-gate test. Placement is not the bottleneck on the bench (same parts as the human route). - 2026-10-17: Owner shared the "PCB Layout Style & AI Routing Strategy" document (3 anonymised boards). Adapted in
~/eeroute/research/layout-style-adaptation.md: bench already is its step 1, net classes/45°/teardrops exist; new = style score column on the bench (bench/style_metrics.py, Codex worker running, briefwork/briefs/style_metrics.md), aesthetic cost terms go into the path planner + a tidy pass (PNS has no cost function), inner-layer signal use is a penalty not a ban, decap < 2 mm into the placement scorer. - 2026-10-17: Swap-pass prototype (eeroute
0e5d255, re-checked): 11 declared groups (34 M1 pads + 4 injector drivers), crossings 277→244 (−11.9%), MST length −0.3%; routing mixed: analog opens 22→17 but length 1218→1588 mm and vias 35→47; driver inputs opens 15→16. Neither stage completes; geometry gain real, routing gain unproven. Firmware diff inswap/firmware-pin-map.md. Style metrics tool (81273fb): rusefi grid .25 mm 85.7%, 45° share 43.9%, bundles 18%, one qualifying decap (median 2.9 mm); Universal Tester boards are free-placed (grid 1.6-35%). - 2026-10-17: Housekeeping and contract: s2 GND island recorded in stages.toml; J2 USB-C pad order confirmed interleaved (A6/B6 and A7/B7 bridges cross); eeroute committed locally (GitHub push denied by classifier); two Sonnet workers launched (zero-open replay gate; pair-placer failure reasons).
- 2026-10-17: Board format fixed: fork
pcb downgrade --target 10.0yields a 20260206 file stock 10.0.7 opens; START round-trip and CAN replay output DRC-match on stock (project files must sit beside the board for DRC). - 2026-10-17: KiCad fork remotes changed:
origin= private bare repo on the NAS/mnt/storage/EmbeddedEra/Github/kicad-fork.git(Mac:git clone /Volumes/EmbeddedEra/Github/kicad-fork.git, branch eeroute/pns-route-nets; shallow repo, receive.shallowUpdate on); GitLab is now remotegitlabwith its push URL disabled until the owner says. Pushed branches: master, eeroute/pns-route-nets, eeroute/pair-diag (push each branch separately; a multi-branch push fails on the shallow repo). - 2026-10-17: Pair-placer diagnostics done (fc1799fe on eeroute/pair-diag, pushed to NAS origin; table in kicad-fork
qa/tools/pns/session-tests/PAIR_DIAG.md, reproduce withpair_diag_aggregate.py). 139 failed pair actions over usb/can x normal/replay: shove_failed 31, walk_all_failed 37, no_gateway_fit 29, post_shove_collision 23, stale-trace 7, fix_empty 10. Normal-USB J2 fits all use the A7 D- pad lying between A6 and B6. Defects found, unfixed: tryWalkDp always returns true (empty pair is shoved), aWindCw unused, routeHead returns a stale trace on no-fit, OBSTACLE::m_pos unset. Tool is non-deterministic (replay CAN 40-44 actions across runs; pointer-ordered OBSTACLE::operator< suspected, unverified). Next: fix defects 1 and 3, rerun the 4 cases, then split J2 D+/D- into single nets or a layer change in the driver. - 2026-10-17: Mac/Windows builds:
eeroute/tools/build-fork-mac.sh(also at NASGithub/build-fork-mac.sh) builds kicad-cli + pcbnew kiface + pns_route_nets via Homebrew and prints the build time; UNTESTED on the owner's M1 Air. Server is a 4-core i5-7500T built at -j2 nice 19, so the Mac should be faster. Windows: not started; options are MSYS2 on a Windows PC or a GitHub Actions runner (needs the fork on GitHub); CLI-only first. - 2026-10-17: Zero-open replay gate NOT met (fork 7743b2c9, pushed to NAS origin). s2 human reference +
--replay-chamfer yes: 11 -> 1 open; kicad-autoroute-1/-2 with--replay-align-nm 200: 11 -> 1; krt-1: 11 -> 2; s3a KA-1 (19 nets, align 6000): 24 -> 5. 0 regressions (s2 only the accepted GND island), 0 new DRC. Fixes: same-net virtual via blocked by the START physical_clearance rule (4 lines in pns_item.cpp); off-axis reference stubs (50 nm-5 um) became 90-degree jogs ->--replay-align-nm; connectivity-guard tie-break + copper-anchor retry; new options --replay-chamfer, --replay-align-nm, --dump-rejected. Remaining +12V_ETB: a track-on-track T leaves a 90-degree angle, which fails the dru track_angle min 135 (the references have the same warning; grade.py counts errors only so it never sees it). References also add 5 GND stitching vias (9 on s3a) that heal zone islands; without them connectivity_guard fires. Planner conclusions: connect branches through a pad or via, never a track-on-track T; emit GND stitching vias wherever its copper cuts a zone. Undiagnosed: KRT-1 rejections, s3a MIL/SPARE_LS1/WG. 200 and 6000 nm are by trial. Verify:PNS_ORACLE_CASES=s2-ka1-align python3 -I -c "import oracle_verify as o; o.main()"in session-tests. - 2026-10-19: Native intersection-angle guard completed and backed up; exact saved STI corridor rejects at its 45° branch before Apply. 409 editor/125 Python/514 numerical controls and three stock-clean fixture joins pass. Real source remains 115 opens; current handoff records the next target-selection/topology step.
- 2026-10-20: Cow Robot whole-board run (stripped copy, 4 layers allowed, 75 min): opens 198→53 (stock DRC on the downgraded result confirms: 53 unconnected, violations identical to the human board, the one clearance is the original J11 A6/A7 pair). Only 26 of 58 nets with human copper are fully connected; GND 13 and +3.3V 4 opens remain; 11 nets got zero copper (SPI, LEDs, RX4, J6 pins). Router used 4 vias against the human's 90, so signals stay single-layer, the biggest gap. Detours on completed nets: AN0 173 mm vs 19 (9x, 76 segments), D+ 3.1x, IRQ 2.5x, VBUS 1.9x; most other completed nets match the human length within 10%. Series-R pairs:
pair_audit.py --chainplus automatic R/L/FB* discovery in the front door (75 tests pass; real-engine auto-chain not run). USB chain skew on the human board = 0.114 mm, verdict AMBIGUOUS (J11 has two candidate pads per member). Lessons: strip teardrop zones too (they are fixed same-net copper and block every net); keywordUSBmatches only /USB_DET, use explicit net names; stale/tmpDRC files misled me once, always read the DRC next to the board. - 2026-10-20: Why the router used 4 vias on the Cow board (investigated). NOT a via bug: via plans run and fail only at the last move into a crowded pad; MISO/SCK route alone on the empty board with no via. Causes found: (1) planes ignored: GND on In1, +3.3V on In2, router drew them as surface copper (GND 483 seg, 3.3V 132). Angel's strategy, verified on the human board: passive pad -> one stub -> plane via (0.7-2.3 mm); IC power pin -> short trace to a decoupling-cap pad that has the via. Built
tools/plane_connect.py(+23 tests, 98 pass total): 71 vias (GND 41, 3.3V 30), DRC identical to baseline ONLY after zone refill (stale fills show 131 false clearance/hole findings), so route from a refilled copy (kicad-cli pcb drc --refill-zones --save-board). (2) order: running previously failing nets first took opens 127->44->35 and completed MISO/MOSI/SCK/CSN/LEDs/RX4; routes then fail elsewhere (AN0/AN1/IRQ/NRST/CAN2). (3) layer balance is the real gap: human track length F.Cu 680 / B.Cu 637 / inner 104; tool F.Cu 1025 / B.Cu 42 / inner 11. The router always tries direct on F.Cu first, fills it, and the via fallback fails at the pad. Results: no planes 198->53 (26 nets done); planes+alpha order 198->44; planes+failing-first order 198->35 (36 nets done). Next: layer assignment (prefer B.Cu or via-first for long nets, balance F/B), path straightening on wiggly routes (J2 pins 39-42 seg, AN0 173 mm), wire plane_connect + refill into the front door, IC power pin -> cap rule. - 2026-10-20 (later): Layer balancing tried. Added
--prefer-layer NET=LAYERand--balance-layerstotools/eeroute_route.py(passes--layerto the router per single-net unit; 3 new tests, 33 pass). Result on the Cow board (planes + failing-first order): 127->35 opens, 34/55 nets, B.Cu 42->76 mm, F.Cu 1025->868 mm: no real change. Reason: the router's--layeronly reorders the layer list; direct plans need BOTH pads on the layer, so SMD nets can only reach B.Cu through via plans. Report tally over the whole run: via 1278 tries + reverse-via 1262 tries = 1 success (901+886 fail attarget_move, ~360 atvia_escape, ~70 each at start/arrival anchor and via_arrival); direct succeeded 76. So the via plan is broken or badly parameterised, not unused. A worker is debuggingroutePlaninpns_route_nets.cpp(uncommitted, fork branch eeroute/waypoint-diagnosis). Run takes ~62 min for 55 nets. - 2026-10-20 (via fix): ROOT CAUSE of the router's via failures found (worker, verified by me):
routePlantarget_move (pns_route_nets.cpp~2140) replaced the end pad withGetNearestRatnestAnchor(); after a via the head touches the stub it just fixed, so PNS returned that own stub as the nearest 'connected copper', the target became the head itself and FixRoute did nothing. Fix: requirenearestItem->Parent() != nullptr. Also added--via-first yes(via plans before direct) and debug envPNS_ONLY_STRATEGY,PNS_VIA_DEBUG; rebuilt with~/eeroute/work/viadebug-build.sh(incremental, container name viadebug-build; old binary kept aspns_route_nets.before-viadebug). Fork edits uncommitted on eeroute/waypoint-diagnosis. Front door passes--layer L --via-first yesfor nets given--prefer-layer/--balance-layers. RESULT on Cow board (planes-refilled, failing-first order, --balance-layers,work/cow-robot/balance2): 127->18 opens, 50 of 55 nets complete (was 36), 127 vias (71 plane + 56 signal), wall 32 min (was 62). Track mm: F.Cu 911, B.Cu 333, In1 180, In2 28 (human 680/637/38/66). Independent check: fork downgrade + installed kicad-clidrc --refill-zones= 95 violations (92 lib_footprint, 2 courtyard, 1 clearance = the human board's baseline) and 18 unconnected. Still open: USB pairs /D+ /D- and /_D+ /_D- (1-2 opens each), CANH/CANL (2 each), GND 6, +3.3V 4. Signals now run on In1 (180 mm) through the GND plane layer; human uses 38 mm, so consider keeping In1/In2 for planes (exclude from signal layers or deprioritise) and check plane splits. Next: run the bench + unit suites against the new binary through the front door, finish the 18 opens (pairs, plane islands via more plane vias), exclude plane layers for signals by default, commit fork + eeroute when the owner says. - 2026-10-20 (plane layers, power-first, teardrops): front door now removes plane layers (filled netted inner zones) from signal layers (
--signals-on-planesto keep them;plane_layers_excludedin result.json). balance3 (plane layers excluded): 20 opens, 48/55 complete, F 968 / B 547 mm, 130 vias. balance4 (+ power nets first,/tmp/er/order3.txt: GND, +3.3V, +5V, +12V, VBUS, then signals): 17 opens, 49/55, F 816 / B 788 (human 680/637/38/66), 134 vias, 38 min, no inner-layer signals; independent DRC (work/pns-spike/chk3) = baseline (98 lib_footprint, 2 courtyard, 1 pad-pad clearance on J11 A6/A7, 0 routing violations), 17 unconnected. ROOT CAUSE of the 8 stuck IC power pins (GND U1.2 U2.4 U4.5 U5.1, +3.3V U1.5 U4.4 U5.2 U6.4): the router's input board carried 144 derived teardrop zones (saved by the kicad-cli refill); the ratsnest edge for the pin ended on a teardrop (labelcopper:xxxx, uuid not in the file),findAnchorcannot resolve a zone, so no move is ever tried (actions: [], failurearrival_anchor) although the cap pad is 0.6-1.2 mm away on clear F.Cu. Fix:eeroute_route.Env.strip_teardropsremoves teardrop zones from each router input copy (strip_routing.strip_teardrops, 2 new tests, 59 pass); result.json recordsteardrops_stripped_from_router_input; zone refill regenerates them. RESULT balance5 (same command as balance4, fix on): 127->7 opens, 50/55 nets, F 919 / B 625 mm, 129 vias, 37 min; all GND/+3.3V/CAN complete; independent DRC (chk4) = baseline, 7 unconnected. Left: USB pairs /D+ /D- (1) and /_D+ /_D- (3) (router names fixed pads D11, D3, R25, R28, J11 as blockers: placement), /~{CSN} and /IRQ (U3 header to U4.9/.10, congestion), /NRST (1). Next run balance6 = hard nets right after power (/tmp/er/order4.txt). Human IC power pattern still to match: short 2.6-5 mm trace to the decoupling cap pad that carries the plane via. RESULT balance6 (hard nets right after power): 6 opens, 51/55, F 849 / B 704, 141 vias, 30 min, DRC (chk5) baseline; CSN/IRQ/NRST now complete but /TX2 and /CAN2_TX open instead (U4 fan-out), USB pairs unchanged (blocked by fixed pads D11/D3/R25/R28). - 2026-10-20 (pair->single fallback): ROOT CAUSE of the USB opens: the front door forces
--pairfor /D+- and /_D+-, but after the trunk the remaining connections are single-ended stubs and ties (D11.3<->R25.2, J11 A6<->B6, A7<->B7) that pair mode cannot place; run as--netsfrom the same checkpoints they close in 2 short direct segments (ablationwork/pns-spike/abl/{b1,c2}). The earlier blocker theory (VBUS consuming the corridor, fixed pads) was wrong. Fix inroute_unit(eeroute_route.py): a pair unit with opens after its 3 pair tries is finished byroute_triesas single nets (bundle dirsuNN-stryK,single_fallbackin result.json, 3 tests). RESULT balance7: 127->3 opens, 52/55, 31 min, USB /D+- /_D+- and CAN complete, /TX2 now complete too, independent DRC = baseline. Left: /AN0, /AN1, /CAN2_TX (U4 fan-out). Pair audit for the fallback pairs is UNKNOWN/AMBIGUOUS (see handoff). tester-report-sync incident was a lost exec bit onreporting/tools/sync_tester_reports.py(chmod +x, fixed). - 2026-10-20 (unblock + angles): unblock now accepts the router's plain-string ripup blockers (it never ran on U4 failures before); AN0 live unblock accepted. Pair audits moved after all units. Owner asked: no turn sharper than 90 degrees and no via straight out of a pin; measured on balance7 (20 acute junctions, 11 near-pad vias vs human 2); enforcement is the next task (see Handoff).
- 2026-10-30: Mac build prep (owner asked; Fable + Codex gpt-6-astra read-only reviews agreed). eeroute branch
mac-buildef1fb23: build scripts (mac/windows/docker) now buildeeroute_global;eeroute_env.fork_cli_in_container()finds the native Mac kicad-cli inbuild/mac/kicad/KiCad.app/Contents/MacOS; numpy added to requirements-dev (test_place_opt failed without it); 285 tests pass. Mac setup:~/kicad-fork(eeroute branch, blobless clone),~/kicad-lib->~/Documents/GitHub/KiCadsymlink, uv installed,~/.config/eeroute/config.toml(build/mac, runner native, stock kicad-cli 10.0.6). First Mac build running, log~/kicad-fork-build-mac.log. Open risks: Homebrew wxWidgets 3.3.4 vs KiCad's macos-wx-3.2 fork; editor smoke test is Linux-only, gate 5 on Mac is manual. - 2026-10-30 (later): Mac build fixes committed on eeroute
mac-build(09ab953, d761326, 4e6049f): brew glslang/spirv-tools/spirv-cross (RHI shaders) + unixodbc (nanodbc);-DNGSPICE_LIB_NAME=ngspice(Findngspice wants libngspice.so.0 on all UNIX);-DOCC_INCLUDE_DIR=$PFX/include/opencascade;KICAD_USE_CMAKE_FINDPROTOBUF=OFF(FindProtobuf misses abseil, libkiapi link failed). Configure passes. Compile now stops at kinng: KiCad 10 needs nng 1.x headers, brew has only 2.1 (uninstalled it). Uncommitted script change builds nng v1.12.3 from the nanomsg GitHub tarball into build/mac/deps; running it was blocked by the auto-mode classifier, waiting on owner OK. - 2026-10-30 (evening): First Mac build works (owner approved the nng 1.x source build): 17 min 34 s on the M1. Fork branch
eeroute-mac-cli0aa39311 (pns_route_nets finds kicad-cli in KiCad.app); eeroutemac-build9df6881 (nng, scipy, STATUS/WORKLOG). Mac config grades with the fork's kicad-cli (stock 10.0.6 cannot read 20260901). Proof: suite ecc83 8/8 nets, 0 opens, 0 new DRC (= Linux); front door 2/2 nets, source unchanged. Not pushed or merged yet; pcbnew GUI on Mac not tried. - 2026-10-30 (night): Full fork KiCad suite builds on the Mac (owner wanted schematic + everything, not only PCB): extra targets kicad, eeschema, gerbview, pl_editor, pcb_calculator, cvpcb + bitmap archive (missing archive = '?' toolbar icons), +8 min. Launcher
~/.local/bin/kicad-fork(KICAD_RUN_FROM_BUILD_DIR=1, KICAD10_ libs from /Applications/KiCad). Fork settings~/Library/Preferences/kicad/10.99= copy of 10.0 (first-run import did not trigger; old attempts kept as 10.99.bak). Fork is 749 commits behind upstream master (base 2026-10-16), 69 ahead, 21 files changed on both sides. Owner asked about a 'KiCad Craft' / Rust or C rewrite: recommended no rewrite (2.16 M lines C++); wrap the fork as editor + CLI + MCP (community KiCad MCP servers exist), Rust only for new engine parts; awaiting owner decision. - 2026-10-30 (night, later): Merged + pushed on owner's OK: eeroute main f0b2e41 (rebased onto another session's c663eef; worklog conflict kept both), fork eeroute 0aa39311. eeroute main already depended on unmerged fork branch eeroute-nets-file (global_route always passes --nets-file), so the Mac builds from local fork branch
mac-local(eeroute + nets-file, not pushed); ecc83 mac5 100 %. Merge eeroute-nets-file into fork eeroute after its nf1 measurement. - 2026-10-30 (night): Fork synced with upstream KiCad master b4f36da3cc on branch
eeroute-upstream-sync1287cbc05d (749 commits; 1 conflict, StartRouting params, both kept). Mac rebuild 26 min (+ Homebrew sqlite). Self-tests pass; ecc83 100 %, pic_programmer 97 % 2 opens 0 new DRC (= pre-sync). Not pushed yet; gate-1 rusefi regression set not run (needs the server). Lesson: never cut lines out of CMakeCache.txt (parse errors twice); delete the cache.
Open¶
- Verified checkpoint 2026-10-18 (supersedes stale heads/running-worker claims below): clean
~/eeroutemain4fe19d6,~/kicad-forkeeroute/pns-route-nets7743b2c9,~/kicad-fork-pairdiageeroute/pair-diagfc1799fe. Fork branches backed up to NAS origin; no GitLab push authorized. No routing/build jobs observed. Independent read-only GPT-6.1 Sol audit confirmed committed reports: normal/replay remaining opens s2 6/1 (from 11), USB 6/6 (from 8), CAN 8/8 (from 8). Pair diagnostics branch diverges atd6de79c7and is not integrated with latest replay fixes. Smallest next router step: integrate diagnostics, fix empty-pair return and stale-trace reset, rerun four USB/CAN normal/replay cases with unchanged guards; then legal ETB pad/via-junction replay and GND-island healing before the corridor planner. This session reran stock DRC only on the STI layout trial; PNS results were checked against saved committed reports, not rerun. - Swap pass (owner technique, 2026-10-17): Angel fixes hard routing by going back to the schematic and swapping functionally equivalent pins/channels/gates (analog inputs, GPIO with the same peripheral, driver channels, connector order) to remove crossings, then remaps in firmware. Build it as a layer above the router: per-board swap-group file (what is interchangeable and why, from datasheet pin-function tables), cost = ratsnest crossings + length, anneal on the ratsnest (cheap, no routing), emit the netlist change for schematic + PCB and a firmware pin-map diff. Run before routing, and again on a net the router cannot complete. Survey done 2026-10-17 (
~/eeroute/research/swap-and-ai-survey.md): no mature KiCad tool; borrow kicad-tools' declared-swap-group model, STM32 AF data from embassy stm32-data (licence unverified), LLM only proposes/tie-breaks and is benchmarked against the plain scorer; kipy cannot edit headless on KiCad 10, so write netlist changes at file level. Bench: swap groups for rusefi-sti-2004 need the MCU/firmware pin table. - Quick wins in today's
kicad-autoroute(kicad-libscripting/pcb/), independent of the plugin: use Freerouting's--router.job_timeoutinstead oftimeout(a timed-out job still writes its result), and an inner-layer.ruleslayer_rule (active off)instead of stripping layers. Verify each on a rusefi stage replay. - Phase 0 bench: needs no go-ahead (local only, on board copies). Replay rusefi-sti-2004 stages 2–8 plus Freerouting's test boards; metrics as in Phase 0.
- Check kicad-ipc-rs / kicad-api-rs (license, KiCad 10/11 coverage, activity) before phase 2.
- 2026-10-17: guards calibrated to reference baseline (d6de79c7); replay stage 2 11→1 open, 0 regressions; normal mode still 6; planner is the gap. Fork output is format 20260901 (stock 10.0.5 cannot open).
- Style document follow-ups (2026-10-17): metrics tool done and verified (
bench/results/style/SUMMARY.md); write the house style spec (grid, direction mix, bundle pitches, via rows, decap) from OUR measured values; ask the owner which boards were A/B/C and whether their layout files can join the local corpus (never committed). See~/eeroute/research/layout-style-adaptation.md. - Swap pass next: the geometry win does not yet make routing finish. Re-run the swap arms after the path planner exists (PNS can then use the shorter ratsnest); decide per group with the owner before any schematic/firmware change (ADC3, PC13 drive, timer limits are unproven). Check the
embassy-rs/stm32-datalicence before vendoring pin data. - 2026-10-17: floorplan scorer built (
~/kicad-lib/scripting/pcb/floorplan_score.py, kicad-lib branchfloorplan-scorerf8e2054/a28e3ad, 14 tests). KiCadRoutingToolsplace_optimize(MIT) has no score-only mode and did not flag M1 east, so we built only the missing parts: per-net HPWL, width-weighted cut overload, edge pressure, grid compliance, pitch symmetry. Baseline rusefiSTI2004 at 4 layers: HPWL 14566 mm, 3731 crossings, overload 15.9 tracks over 5 cuts, edge short 1.35 mm, 128/130 origins off whole mm. Worse boards score worse; the real commit pair 09694d9→feca203 (M1 east widened) takes overload 15.9→4.3. Limits: straight ratsnest, weak at predicting individual failed nets (AUC 0.05–1.0 by case), place_optimize's wirelength win did not fix overload (19.1), so accept a plan on overload/edge short first, wirelength second. Layout v2 floorplan worker running onlayout-v2. - 2026-10-18: Completed source/product review and negative-control checks; current completion contract and five gated milestones are in Review and completion plan above. Two newly reproduced defects (report path overwrites input; bus-name progress rejected), missing explicit layer policy, unresolved pair bugs and inconsistent benchmark house gates precede planner/product work. No source fixes, full routing runs or release claimed.
- 2026-10-18: Completed the five execution fixes authorized by the owner; current handoff above records commits, exact negative/positive controls, 50 Python tests, 20 native controls, 7 mechanism fixtures and five stock-validated real reruns. Four constrained read-only GPT-6.1 Sol medium helpers drafted/reviewed; primary integrated, corrected and ran all native verification. Codex capacity observed 3% weekly (fresh codex-app-server); CLI cost unavailable. Planner/full product gate remains unmet.
- 2026-10-18: Continued the executor-closure work; current handoff above records committed ground repair, USB/CAN decomposition,11read-only GPT-6.1 Sol medium helpers, independently graded incomplete real outcomes and the remaining ground/topology gate.
- 2026-10-18: Owner reaffirmed full native AI/KiCad PCB workflow and visual-quality goal; corrected stale Rust roadmap/front page, verified real editor IPC commits/undo/cancel/save, fixed seeded-fragment retries, recorded remaining native-session/planning/delivery work in current handoff. Shared preference memory updated.
- 2026-10-18: Continued after accidental stop; VectorCraft0.13.1 now has a measured, registered H1a planning prototype. Six source/unit controls, five negative artifact checks and real native/export fidelity checks pass; PCB unchanged. Commits/artifact locations and remaining typed-intent/native-session bridge are in current handoff.
- 2026-10-18: Continued native controller implementation:15GUI controller checks,32native regression checks,5unit tests and7real STI bridge checks pass; independently audited partial bridge improvement, full finish gate still false. Sources backed up and current handoff updated.
- 2026-10-18: Added native board-analysis tool, verified21controller GUI/13unit/9real checks and independent stock audit; code reviewed by two read-only GPT-6.1 Sol medium helpers. Source/docs backed up, original board untouched; next native execution tools are track/via starts and layer changes.
- 2026-10-18: Completed native straight-track/via start tools;29anchor GUI+32session+21controller+18unit checks pass, independent stock fixture targets close. Real STI attempt rejects cleanly with no retained copper; all source branches backed up, no GitLab push. Current handoff records next staged via/layer contract.
- 2026-10-18: Completed bounded automatic corridor/via planning, native paired transitions and single/pair/skew tuning;135actual-editor checks+40Python tests pass, independent stock fixture gates pass except teardrops. Current-H1a held-out module route remains unresolved within bounded search; source board untouched. Current handoff records backed-up commits,5read-only GPT-6.1 Sol medium reviews and next precise-breakout gate.
- 2026-10-18: Routed current-H1a USB_D+ module connection with precise automatic alternatives and native PNS shove; saved stock audit passes, real opens116→115, one via, no new findings/regressions. Board branch268b447 and tool heads backed up;136GUI+7real+50unit checks, exact one-Undo/Redo, read-only native reopen and visual review pass. Two read-only GPT-6.1 Sol medium reviews; current handoff retains USB pair/electrical and whole-board limits.
- 2026-10-18: Added the native copper-target/drag/placement checkpoint above, backed-up tool heads and independent stock results; strict connected-placement/finishing failures remain explicit and source STI is unchanged.
- 2026-10-18: Completed native reference finishing and certified junction repair on STI copies; R40 reference silk checks and R53 upward connected move pass. Backed-up heads,250 editor/61 Python checks, strict M1 gate and remaining bounded cases are in the current handoff above.
- 2026-10-19: Front door
~/eeroute/tools/eeroute_route.py(+tests, 21 pass with a fake runner) written by a helper; real STI runs NOT done (one USB try: incomplete, 12 segments / 2 vias, touched USB_VBUS); kicad-cli 10.0.7 cannot read the router's 20260901 output, so each try is downgraded via the fork kicad-cli in docker (unverified). INCIDENT: the helper randocker ps -q | xargs docker killand stopped all ~63 containers; restored by name within minutes, all healthy, VPN and ee-network-check verified. See memoryhelpers-never-kill-docker-wide. - 2026-10-19: Front door
tools/eeroute_route.pyproven end to end on a copy of STI (docs/native-workflow.md): audit runs on the router's downgraded copy, router containers named and cancelled by name. Real results: USB pair opens 2→1 (D- still open), VBUS 1→1, CAN pair 1→1, +5V 3→3, board 115→115; every other net unchanged, source hash unchanged. The driver works; the router makes no legal progress on blocked connections, so the next gate is the unblock step (obstacles named in result.json: BOOT0 pad, THRESHOLD_VR_CR, M3.G). Not committed. - 2026-10-19: Unblock step (
tools/eeroute_unblock.py, hook ineeroute_route.py,--no-unblock) built; 55 tests pass (mocked). Real STI result: no opens closed by it. USB_D- tried PWR_EN then THRESHOLD_VR_CR rip-up, both rolled back (target still 1 open); needs_human: BOOT0 pad P23.1. CAN: every named obstacle is a fixed pad (M3.G, J3.8, M3.W2, M3.W9), so human/placement. +5V: report names no obstacle. Accept path never ran for real. Server containers intact after the helper (64 up). Next: understand why D-/CAN_L/+5V stall with no named movable obstacle (arrival-anchor failures, ~30 mm CAN link) via the nativeplan/preview-planpath; then audit-gated retry, native-editor hand-off. Not committed. - 2026-10-19: Cow Robot Board 004B (
~/Cow_Robot_Board/hardware/Robot-Cow-004B, 4-layer, one Default netclass 0.3048/0.254 pair gap, human-routed, DRC 0 opens) used as a calibration test on a stripped copy (~/eeroute/work/cow-robot/;orig/is the untouched reference,tools/strip_routing.pymakes the bare copy). FIRST RUN ROUTED NOTHING: 488 leftover teardrop zones count as fixed same-net copper (copper:<uuid>anchors) and block everything, even a 2-pad net; strip teardrops with the tracks. Clean run, 79 s, 198→184 opens, stock DRC identical to the human board (the one J11 A6/A7 clearance is original): CAN pair CANH/CANL complete in 1 try (9.69/9.14 mm vs human 8.85/8.32; skew 0.546 vs human 0.526 mm, both miss 0.15, the tool has no tune loop yet); USB 4→1 and +5V 11→4 incomplete, blockers are fixed pads (D11/D12 ESD diodes, R25/R26). Gap found: USB here is J11→R25/R26 (series R)→U4 with D11/D12 on the second stage, so no two footprints carry both members; the front door's pair-end model and audit give 'no end footprints'. KeywordUSBmatched /USB_DET only; use net names. - 2026-10-20 (shape guard): owner rule enforced in the router (min 90 degree corners, no signal via within 1.0 mm of own-net pad, shoved vias included); balance9 from bare2 with prepare + power-first defaults: 198->5 opens, 0 acute, 0 near-pad vias, DRC = human baseline; USB skew ~0.3 mm. See Handoff.
- 2026-11-10: Ornament 2mm tree test (31 nets). Front door got ground pour, fast-fail,
--short-first,--bulk(Freerouting bulk pass, then PNS finisher): 70 opens -> 3 (all GND pad-to-pour) in 2.6 min, against 15 left after 87 min net by net. Target 0 opens < 10 min not met: GND stubs before bulk or rip-and-reroute after it trade GND opens for signal opens. Full handoff with measurements, uncommitted files and next action:~/eeroute/work/ornament-2mm/HANDOFF.md. eeroute branchfeat/ground-pour-fast-faillocal only (cc75f4d); fork patch inpns_route_nets.cppuncommitted. - 2026-11-10 (evening): automatic pour repair after the bulk pass committed locally in eeroute (
pour_repair,plane_connect.py --stub-only): 2mm tree 70 opens -> 1 (Net-(D4-DOUT)), all GND connected, 4.4 min; 0 reached once by hand (~6 min). Variants, cause and next steps in~/eeroute/work/ornament-2mm/HANDOFF.md. - 2026-11-10 (late): 2mm tree still 70 -> 1 open in 4.4 min. Cause found by plot: D5 is walled in by the wide +4.8V and +BATT tracks inside the single-layer zone; C7 is not the cause. Zero opens reached by hand in ~6.5 min (remove +BATT and D5-DOUT, Freerouting, drop a 0.19 mm loose end, finisher). Four automatic versions of that were worse and are not in the code; only the loose-end trim is committed (eeroute, local). Details: ~/eeroute/work/ornament-2mm/HANDOFF.md.
- 2026-11-10 (night): 2mm tree routes to 0 opens automatically in 6 min 57 s (out-auto11; eeroute local commit 17eae97: one-net rip-and-reroute round + finisher runs --stitch-ground yes). Not clean yet: 16 new DRC errors (7 GND vias in the no-via keepout, 3 copper-to-edge, 3 tracks under 0.2 mm, 3 thermals), 60 angle warnings. Next task: remove those, repeat 3x, make --bulk default, push to GitHub. Handoff: ~/eeroute/work/ornament-2mm/HANDOFF.md (last section).