## drandyhaas/KiCadRoutingTools — v0.21.1…v0.21.2

_348+ commits._

### Features
- **corpus: add manifest_set1.json -- the one set that never had one** (207c531)

### Fixes
- **#665: fix pad-array cache poisoning by plane-tap window copies** (29d1e01)
- **#597: carry the dash-digit net fix into py_placer/place_route_loop.py** (568b18c)
- **Merge pull request #641 from theironchef/fix/617** (321fcfd)
- **Merge pull request #643 from theironchef/fix/621** (45aee00)
- **Merge pull request #640 from edgehero/fix-fixture-boards-recipes** (806f33c)
- **Merge pull request #638 from edgehero/fix-636-owner-regression** (e8dbdb2)
- **placement: port the #628 swallow-probe fix to py_placer (from PR #636)** (1bf5c31)
- **fix: check_rigid_consistency.py died on `import cli_banner`; relax the #582 guard** (0f45ba2)
- **stress: fix the SyntaxError that broke every importer of redo_stress_test** (332c296)
- **Merge origin/main: #572 lap authority + the #577 fix ports** (1beaeb3)

### Backend
- **release 0.21.2: publish the current crate's binaries** (06a5f68)
- **gui-parity ledger: audit 972131a4..0fd0a991, five gates green, no gap** (2470fb9)
- **stress: score separately-launched cloud arms with rank_arms (#663)** (0fd0a99)
- **#663: --defaults coerced every numeric arm knob to float, which pyo3 rejects** (affa717)
- **#653: log the active KICAD_* env knobs at startup and in JSON_SUMMARY** (3bb4bb7)
- **#654: the KiCad-refill cross-check now always says what it did** (750e10a)
- **GUI: the exact-fill refill re-launched pcbnew instead of running a script** (d4831b9)
- **gui-parity ledger: audit bd5d8d39..972131a4, five gates green, one vacuity named** (357c1ed)
- **#549: octolinear smoothing handed back the track-rule clearance it was routed with** (972131a)
- **AI tab: rename to "AI Driven", and make the Placement side match the Routing side** (2d3f2ab)
- **tests: 25 of 26 red tests were not FAILING, they were not RUNNING (#522 reorg + skill merge)** (ed77909)
- **skills: state the outline imperative, and stop naming corpus boards in user-facing guidance** (76fc1ee)
- **check_complete: board_score moved with the skill merge, so every run reported a false INCOMPLETE** (1eb8b24)
- **gui-parity ledger: record the placement merge (bd5d8d39), all five gates re-run on main** (b5cca3c)
- **Merge placement into main: py_placer reorg, placement engine, six routing reverts** (bd5d8d3)
- **gui-parity ledger: audit ac3b5dcc..f78f3298 on placement, one gap fixed, five gates green** (229d461)
- **#459 parity gap: --group/--group-scope/--group-by must be STEP KEYS, not params** (f78f329)
- **stress/cloud: build the image when the crate is ahead of the release, and stop the corpus check lying** (d546817)
- **Merge main into placement: 0.21.1 release, parity ledger, river rip-resistance** (3a8a130)
- **revert six placement-only routing changes that lose to main on the corpus** (6166a98)
- **stress/cloud: build the image when the crate is ahead of the release, and stop the corpus check lying** (c444f1b)
- **global-plan: river members feed bus_member_net_ids (rip-resistance was inert)** (9413298)
- **Merge main into placement: fanout/cap-opt engine fixes, dogbone gate** (4accece)
- **skills: plan-pcb-routing is main's again; the convergence loop moves to the** (ca6bb45)
- **skill: keep the routing skill's ending routing-shaped, not placement-shaped** (a93a174)
- **Merge main into placement: fold the -c section, drop the auto-merged duplicate** (3b8ecec)
- **Merge main into placement: the four skill fixes ported from here** (7369dbb)
- **skill: the pour gate is GONE -- stop citing it as what enforces pour-first** (2c69a4c)
- **Merge main into placement: ai_plan prompt contract + once-after-all cap cadence** (280e1d9)
- **Merge branch 'main' into placement-merge-0818** (b2a49e5)
- **fanout_gui: drop the duplicate _fanout_status, keeping the #621-accurate one** (b95a5aa)
- **merge repair: drop a dead duplicate run_one() left by the merge** (9672b47)
- **merge repair: restore #508's immediate-reconnect strip consume, de-dup the refill** (ef26370)
- **#597: route.py parsed args TWICE, the first time without the dash-digit pin** (45ef186)
- **#655: zero-copper backstop -- enroll bucket-invisible phase-3 victims in the final reconciliation** (479158d)
- **#649: reject layer-blind escalated welds (cross-layer link, zero vias)** (89ebcdf)
- **#651: env kill switch for the reconciliation rip-authority escalation** (ff985e7)
- **#522: shim py_router/place_fanout_clearance.py -- 172 recorded chains need it** (2eec99d)
- **corpus: verify every set1 raw_url -- 10 of 15 needed `main`, not `master`** (b98151d)
- **tests: restore the #223 fanout fab-floor clamp test (dropped as collateral)** (5078fd2)
- **Merge placement into main (35 conflicts resolved) -- routing unchanged** (b3d720b)
- **#664: bring placement's fanout front up to main's (progress, log tee, gates)** (fc0388f)
- **#621: delete the wall-clock budget facility repo-wide (krt_deadline)** (275c6e5)
- **#621: fanout escapes cancel from the GUI, on a worker thread -- no --deadline** (4f49f6b)
- **#617 follow-up: the declared copper-to-hole floor must survive the CHAIN** (75ff86e)
- **621: --help names the mid-rescue shape too (deadline_unrescued)** (4850bae)
- **test+comment: pin the microshift acceptance-gate trade on declaring boards** (0220410)
- **#621: replace three carried-over numbers with re-measured ones** (172cfc5)
- **#621 test: find the mid-rescue budget instead of hard-coding one** (58f6aa2)
- **#617: read the board's min_hole_clearance where the engine CHOOSES where copper goes** (2cfaef6)
- **#621 review fixes: a cancelled fanout must not report a budget as a clearance failure** (5dbf1c5)
- **tests: finish the --allow-bare-pads removal (5832e4eb left 4 sites)** (1e63c74)
- **tests: fixture_boards could not build ANY recipe (#522 debris)** (2b8821b)
- **#621: --deadline for bga_fanout / qfn_fanout, with a coherent partial** (34c9c38)
- **tests: point classify_connection_width at the real py_placer (#522 debris)** (475e449)
- **tests: unbreak the A/B replay harness and 6 stale reds (#522 debris)** (d61b043)
- **placement: thread the #628 own-ring exemption into the cap repair pass** (994a17c)
- **placement: the swallow probe must skip a part's OWN milled ring (#628)** (0d4e94e)
- **tests: make the fresh-clone and cp1252 failures from PR #634/#635 real gates** (d9b0256)
- **gui-parity ledger: correct the "Routing engine untouched" claim** (cae9796)
- **Merge pull request #633 from edgehero/placement-gui-481** (bf9eef0)
- **Placement sub-tab: readable full-width status, column fits** (ac28969)
- **Placement sub-tab: own Model/Effort dropdowns** (5b3e03e)
- **gui-parity ledger: 2026-08-14 addendum for the Placement sub-tab wave** (e7980f1)
- **GUI: Placement sub-tab -- Claude-driven place / place+route + beautify** (b07450e)
- **beautify_labels (#481): RefLabel parsing, engine, targeted writer, CLI** (a2e9699)
- **diff_pair_custody: demote on no coupled trunk copper kept, not on member count** (2b7875c)
- **tests: three suites graded the host, not the code** (d6d7b6e)
- **loop driver: the fence named what a teammate must not OPEN, nothing about what it might WRITE** (14792c8)
- **loop driver: a repeated --accept-* flag silently REPLACED the first occurrence** (c0f8607)
- **board_score: the net-name audit reported a parseable board unparseable, in-process** (ec09c52)
- **fab-floor origin: the RELAXED banner baselined on the immediate input, and the GUI hid severity silencing in a count** (37bce49)
- **instruments: check_drc could not be read by a machine, and check_channels graded a board against two constants** (ca1055b)
- **review skill: the checker roster could not see a bad via, and the command gates cover the moved stage bodies** (4ac459c)
- **routing skill: board_score published scores off the wrong board, and DRC parse failures graded as clean** (59bc62d)
- **placement skill: read-mandates became gates, and a congestion threshold was withdrawn on measurement** (96bbad4)
- **loop driver: L5's printed close-out command could not run as printed, and a NO-SCORE shipped anyway** (ee6c3ad)
- **stress rig: run 19 lost its qualification JSON, and run 18 staged an undecidable subject** (774d878)
- **legality: a pile seed licensed any smaller pad intersection** (5d35a11)
- **check_assembly: three parts stacked at one origin graded buildable (blocking 0)** (77ca188)
- **tests: the reconstruct suite imports the #522 layout, and the focus test pins the refusal-free behavior** (4f1638e)
- **CLAUDE.md: what a placement run is FOR, before anyone grades one** (8f7feec)
- **stress rig: the truth fence had three open channels, and tip's fence_audit is a SyntaxError this repairs** (6a39fb9)
- **film: it printed 377 frames over a GIF that holds 348, and fell back to GIF in silence** (7b3ded3)
- **converge: a loop could declare itself finished without ever being measured to be** (e8c71ee)
- **deadline: a placement search had no clock, and --limit never bounded the sweep** (c9e2fed)
- **placement instruments: the top-priority defect was measured against a bounding box, not the outline** (cc1bbe8)
- **reseat: a repair search that starts from the current pose knows nothing at 40 mm** (046b433)
- **reconstruct: the ladder trusted any equal-cost move, and a frame was anything with no pins** (f5cee87)
- **port main's parallel run_all to placement, keeping the TIME/utf8 split** (e3bf258)
- **dash-hint test: probe argparse instead of assuming it rejects -12V** (0bca1cf)
- **adapt: krt_capabilities learns the #522 layout (upstream-latent: failed at pristine 0f45ba2b)** (8f22242)
- **route_render: a caption PIL clipped is a fab blocker the picture never showed** (8edabd7)
- **the four routing mains: stop on their own budget, and stop reporting success falsely** (207e45b)
- **a board's own -12V net is an argparse death, not a routing result** (4b4098a)
- **the test runner tells a timeout from a failure, and reads its children as utf8** (600fbe9)
- **net_rescue: the width histogram counted copper outside the rescue's own scope** (6eb8306)
- **plane joiner: the open-space fallback seeded strap ends on bare board, 3-5mm from the region** (3d7347b)
- **plane_fragility: the one call site that dereferenced refill_islands' documented None** (a4f5ff7)
- **route_summary: a real KiCad contradiction merged to oracle_check 'skipped'** (077d8b6)
- **kicad_oracle: the timeout memo keyed on a path every chain step renames** (e2bc248)
- **check_weird: KiCad flagged 64 dangling vias, this tool flagged 0** (f961d61)
- **check_connected: a phantom zone layer joined the copper census, and 69/70 graded as 70/70** (a3b2316)
- **the GUI took a declared 0 as a real hole-to-hole floor, and the CLI did not** (e8a7a52)
- **qfn underpad: the candidate test could not see the copper the run had laid** (98bd0fa)
- **the BGA underpad escape never read the board's drill floor at all** (7303cd8)
- **the router never read min_hole_clearance; its own checker already did** (ef276b5)
- **a declared 0 is UNSET on the routing half too, through one resolver** (b3e148b)
- **build_router: skip the prebuilt when rust_router/ differs from main (#615)** (7d5d142)
- **Merge main into placement: #582 + stress-harness fixes** (0e363c6)
- **stress prep: RemoveNative, so a multi-board strip survives past board 1** (cccb1b1)
- **.gui-parity-checked: port main's 2026-08-06 audit entry** (4550019)
- **plugin: RemoveNative, never Remove -- Remove() corrupts pcbnew headless** (9fc294b)
- **rp2350 live-chain gate: point the CLI leg at py_router/ (#522 debris)** (bcbeb9d)
- **GUI parity: plan globs reach net_name_patterns; un-break the kwarg gate** (ba9b2e2)
- **Merge PR #576's loop driver, with its routing half pointed at the skill** (9d4a75f)
- **Remove the board-derived pour min-thickness: it was CLI-only (closes #579)** (cd19d03)
- **Merge PR #576 (run-8 phase 1): six fixes, our skill, no routing driver** (bec03cf)
- **the loop between the halves gets a driver, and its three guards** (b16f3a1)
- **scan for the rest of that text, and lint the class so it cannot come back** (bf328ee)
- **the placement gate is a MEASUREMENT, not a default to skip** (aade767)
- **run-8 validation: the legality half works, the recovery half is blocked, and** (8600bd0)
- **skill S4: progressive disclosure for the routing references** (5fbf49e)
- **A3: the suspect bit is data, not prose (and its proposed use is refuted)** (1da9ba9)
- **A1: the hole-pattern fit, on the patterns real boards use** (3abe289)
- **E5 airwire vectors: REFUTED, and kept as the measurement** (528d5f8)
- **route_planes: the minimum web comes from the board, not a constant** (7167204)
- **ILP: the status quo is the fallback, not a forbidden state** (4a30349)
- **Merge origin/main: the #578 fragment-gate port comes back, from main** (c096fa0)
- **repair: try the pair's other member, and keep the move proportionate** (5eda148)
- **a custom pad's copper is its primitives, not a box around its anchor** (06e83c2)
- **Remove the #549 A-2 fragment gate for main parity (filed to port as #578)** (e00b292)
- **skill S3b: the routing chain is COMPUTED from the board, not recited** (255af97)
- **skill S3: the placement workflow gets a driver, not just a document** (afc92a0)
- **skill S2: split into placement, routing, and the combined one** (211446e)
- **skill S1: remove the contradictions before splitting it** (10b9ca9)
- **the wrong-basin gates move inside the engine's accept test** (de113d8)
- **E8: a starved escape face is a gate only as a DELTA** (9e4c6fd)
- **E7: a rigid restore cannot make two differently-moved parts touch** (8d46774)

_Recap by [Repo Wrapped](https://repowrapped.com/gh/drandyhaas/KiCadRoutingTools?utm_source=github-action)._