## drandyhaas/KiCadRoutingTools — v0.21.5…v0.22.0

_175+ commits._

### Features
- **#826: repoint the two anchors the review fixes moved, and add a row for B1** (7f5eacc)

### Fixes
- **#841: a blind review found the bug the fact-check had talked me into documenting** (b5c567c)
- **#619: the regression test, and the sibling pin the fix moves** (a5b55b6)
- **#830: cmd_record has the same bug, and my "dead risk" claim about it was false** (94c1f9d)
- **#826: re-record the moved row, disclose the stale ones, document the fix** (2e1d29f)

### Backend
- **Merge PR #866: #530: KiCad-standard design rules -- sizes as asked, rules and classes honoured, escalation disclosed (v0.22.0)** (e1f5274)
- **gui-parity-checked: audit head aa9e25d6 (one engine commit since c61dd168, #864, shared; gates re-run)** (a1ae2da)
- **RUNBOOK: after editing recorded manifests, re-upload the sets by hand (the upload stage skips present sets)** (aa9e25d)
- **#530: chained runs pass --clearance-ceiling; recorded manifests rewritten to it; graders read either spelling** (10982fc)
- **v0.22.0: metadata.json entry for the release triple (VERSION and Cargo.toml already 0.22.0)** (9f20914)
- **#864: the under-pad rescue's foreign-copper carve: 433 s -> 7 s on the reproducer** (a960757)
- **design-rules proposal: phase 6 is #865, the under-pad carve cost is #864** (ff9b7b4)
- **gui-parity-checked: audit head c61dd168 (defaults moved on both fronts from one place; parity identical)** (4b39baf)
- **#857/#530: default --fab-tier auto and --escalation fab (completion first), defined in ONE place** (c61dd16)
- **#530 per-net via rungs: sit above the #568 small map, and raw-copper mirrors reach them when it is armed** (34d7058)
- **#530 replay knob: the legacy reading also lowers the RUN clearance to min(Default class, ceiling)** (329a146)
- **#530 replay knobs: KICAD_FAB_TIER_DEFAULT / KICAD_ESCALATION_DEFAULT default the omitted flags** (f31aa46)
- **gui-parity-checked: audit head 1fb9b57e (engine parity re-run identical after the corpus-A/B fixes)** (f374a94)
- **#530: the net rescue's clearance ladder floors at the net's own class clearance too** (1fb9b57)
- **#530: a net's own class clearance floors every automatic clearance descent (corpus A/B: core1106_cam)** (8346fa9)
- **#530: multipoint pairs get their per-pair geometry; KICAD_CLEARANCE_LEGACY_CEILING replay knob** (5cae0ab)
- **#530: a diff pair's coupling gap is floored at its OWN net class's clearance (corpus A/B: schoko, ghoul)** (dbe07fa)
- **gui-parity-checked: audit head 52c83e63 (QFN width gap fixed in-range)** (ac0459a)
- **#861/#530: the fanout log names where its width came from; qfn --width joins the stale-minimum rule** (52c83e6)
- **kicad_drc_compare: check_drc's size classes leave the copper-clearance match, like KiCad's** (ddb7909)
- **gui-parity-checked: audit e239e067..2becea8f (the #530 design-rules branch), one gap fixed in fc36e578** (ccf6798)
- **#530 phase 5: per-net via sizes as N via-legality rungs (grid_router 0.22.0)** (2becea8)
- **#530 GUI parity: a Default-only net takes the run's clearance, not the class's stored one** (fc36e57)
- **#530: the writeback never raises min_clearance above a pad override the run honoured; tools predict at the physical floor** (f1ed806)
- **#530 decision 2: --clearance sets the Default class; --clearance-ceiling caps every class; explicit sizes are checked against the physical fab floor** (1c04439)
- **#530/#770: draw sizes and size floors resolve through the rules -- per net, per layer, from the .kicad_dru too** (5ab3e6a)
- **#530: a pad clearance override replaces the class/rule value, as KiCad's engine does (decision 3)** (44d3c24)
- **#530: the constraint-agreement harness -- KiCad's DRC engine as the resolver's specification** (a86b817)
- **#857/#842: one escalation policy, a hard standard tier, and a ledger every front can read** (9a93311)
- **#530: design_rules.py -- the board's rules resolved in KiCad's order, installed on every engine (report-only)** (1a40eed)
- **#856/#842: the project writeback stops lowering netclass draw sizes and stops touching severities** (a980d6c)
- **Merge PR #853: #849: the lane ledger's whole-board geometry, resolved once per board** (858c174)
- **Merge PR #855: #847: the escape band was not what decided the starved-face gate** (be383c1)
- **Merge PR #852: #620/#618: manage_vias stops being blind to the vias it is placing right now** (d2361a1)
- **#849: the pre-push review's findings, and the one my honesty note got backwards** (ef01fe7)
- **#847: what a blind review and an independent fact-check falsified** (f5bbfce)
- **#847: the gate stops asking whether supply is exactly zero** (1b9400f)
- **#847: preregister the rule, then the calibration run it decides** (9b71e98)
- **#847: the gate's verdict, attributed to faces on both sides of the delta** (79c7058)
- **#847: one escape band, resolved once, with the term that set it named** (a6bb377)
- **#849: the review's findings, fixed and pinned** (3636f9e)
- **#849: the mutation battery -- 13 rows, and what each one would have to break** (109bb4e)
- **#849: the timings, re-taken after the harness turned out to be in them** (ee0edf0)
- **#849: the anchor-channel table reuses the sweep's graded parts** (74102f9)
- **#849: check_channels resolves the board's geometry once per sweep** (c2b3f23)
- **#849: the lane ledger's five whole-board objects, resolved once per BOARD** (ace6d54)
- **#618: the decline disclosure counts PAIRS, not gate calls** (f823889)
- **#620: the twin rule takes the pad's RECTANGLE, not a radius on its long axis** (7b22b51)
- **Merge PR #844: qfn underpad escape: test the pad->via stub against the copper nets_to_route erases (#619, #845)** (72e982d)
- **Merge PR #843: #834/#835: a placement instrument stops charging copper it does not share** (62e8bcf)
- **Merge PR #839: #829: a footprint that draws the board outline is not the placer's to move** (1cda2cb)
- **#841: what an independent fact-check falsified, and the flake that found the rest** (4707b42)
- **#620: what an adversarial review broke, including my own headline** (75eda4b)
- **#618: the coupled diff-pair escape stops emitting ungated via-in-pads** (c6ca1ea)
- **#620: a via refused on ring clearance stops stranding its route** (a4ba990)
- **#620: manage_vias tests the vias of one call against each other** (b0bdd51)
- **#841: the lane ledger stops billing routing for assembly margin** (7b46b29)
- **#841: ...and so is the part doing the escaping, with pads measured by their own edge** (c68f827)
- **#841: a track is obstructed by copper, so a NEIGHBOUR contributes its pad copper** (b6b4918)
- **#845: the stub's clearance test reads the layer the stub is emitted on** (55bc9b9)
- **#841: one obstruction rectangle, chosen on the physics** (79ac348)
- **#619: battery rows for the review's findings, and the typo spelling that discriminates** (0c6c2d0)
- **#619: the pad half CALLS the grader instead of approximating it** (6cec18d)
- **#619: the mutation battery, and the four test holes it found in my own tests** (28fbe2d)
- **#619: the RED arm must FAIL its checks, not die before it reaches them** (ec17bd1)
- **#619: ...and against erased PADS -- pre-registered, then earned on the corpus** (7c7e995)
- **#619: publish the erased set, and a corpus sweep that is re-runnable** (1ade414)
- **#619: ...and against erased SEGMENTS (the half the issue never named)** (c4ffa27)
- **#619: the under-pad stub is tested against erased vias (the via half)** (80a8b4a)
- **#834: re-record the placement A/B baseline -- the two ulx3s rows, and only those** (49fff12)
- **#835: both lane ledgers share one obstruction kernel, and stop double-charging** (7fecb20)
- **#835: a neighbour on the far side of the board is not a blocker, and neither is a frame** (fa5c0bc)
- **#834: a pad pair that shares no copper face stops being a clearance conflict** (4424560)
- **#834/#835: what the two side-blind instruments cost, measured** (3860989)
- **#829: repoint three battery anchors the #726 merge moved, and guard the join** (9613a93)
- **Merge upstream/main (#726) into #829** (a406411)
- **Merge PR #838: #726: a footprint block stops being lost because another one shares its name, and one placement moves one block** (8aa7637)
- **#829: what a hostile fact-check of the PR description found in the CODE** (107a8ae)
- **#829: keep the established --lock sentence for the established case** (29bde1a)
- **#726: three suite reds the branch run found -- one fixture, two re-records** (3a95bc1)
- **#726: what the writer verifier found -- three live defects and a wrong headline** (2eadbdf)
- **#726: what the Phase-1 verifier found, including two suite regressions** (0198607)
- **#829: what a blind adversarial review broke, and the redesign it forced** (64570eb)
- **#726: the last GUI lookup that could not see an ordinal key** (b9055e1)
- **#726: the consumers that kept their own reference-keyed maps** (e16fbc6)
- **#829: the mutation battery, so these numbers can be re-derived** (efbad36)
- **#726: one placement moves one block** (6dc6dff)
- **#829: refuse a write that changed the board outline, whatever produced it** (205cdcc)
- **#829: pin that both parse paths agree on who owns the outline** (d7efc0c)
- **#829: the other four movers, and the test the invariant never had** (d6a37f9)
- **#726: a footprint block is no longer lost because another one shares its name** (fea7a6a)
- **#829: a footprint that draws the board outline is not the placer's to move** (751d27a)
- **#829: the parser records WHICH footprint owns a piece of the outline** (743c8c1)
- **#726: what a duplicate reference costs, and what an identity write does, measured** (9e77cea)
- **Merge PR #827: #713: the wall clock stops deciding which placement wins, and an instrument that could not answer stops reading as a clean verdict** (3fb9c05)
- **Merge PR #832: #830: an unreadable route summary is a non-verdict, not a crash** (8c9f770)
- **Merge PR #833: #826: the portfolio's basin-escape destroys the lattice before the quench sees it** (9688464)
- **Merge PR #825: #708: the placement candidate snap is absolute, and should be a whole number of the board's own grid** (64d8334)
- **#826: record the final battery table (12 rows, 12 killed, 0 broken)** (89a8353)
- **#826: what two independent reviewers found, fixed** (c20b80a)
- **#830: drop the dead finally, and test atomicity by its mechanism** (5104559)
- **#830: the publisher failed STALE, not closed, and the guard crashed on a non-mapping** (25e6abe)
- **#830: an unreadable route summary is a non-verdict, not a crash** (77ae73c)
- **#826: record the battery table (11 rows, 11 killed, 0 broken)** (4ee5fd7)
- **#826: the disclosure row survived a vacuous assertion** (ea82b5c)
- **#826: the arms and the battery** (d771464)
- **#826: the portfolio's jitter lands on the board's own lattice** (a27e870)
- **#826: what the portfolio's jitter does to the board's lattice, measured** (a328639)

### Tests
- **test(#847/#849): re-anchor the three battery rows the merge resolution moved** (e239e06)
- **test(#847): re-record the battery at the reviewed tree** (f4bf129)
- **test(#847): record the battery run, including the run that found holes** (de7d533)
- **test(#847): three holes the battery found, closed rather than re-recorded** (229b5a5)
- **test(#847): the mutation battery, and the row that caught my own hole** (ca47c88)
- **test(#847): the starved-face gate gets a true positive a clean clone can run** (684432b)
- **test(#849): which arm kills which row, and the ordering claim retracted** (65a6c6c)
- **test(#849): the battery result, in the header, from the run** (7439882)
- **test(#849): count the parses across a whole check_channels run, in-process** (b98af00)
- **test(#849): a value recorded from BEFORE the change, because the others cannot be** (1f63c26)
- **test(#849): the hoist is pinned by mechanism, not by a number** (0aa8883)
- **test(#620): re-anchor the 10 rows my own rename staled** (cdd700e)
- **test(#620): three battery rows an audit found misnamed or mis-explained** (2aeb2ad)
- **test(#841): the battery caught my own new arm not biting** (bc9ff1e)
- **test(#618): the site-conflict half is redundant for BOARD copper, measured** (5cbebea)
- **test(#620/#618): three coverage holes the battery found in its own tests** (61342e9)
- **test(#620/#618): the mutation battery, 36 rows** (3f772ca)
- **test(#841): --table D, so the choice of rectangle is re-derivable rather than argued** (b251d3b)
- **test(#841): re-anchor the two battery rows the engine change moved** (bddbf61)
- **test(#834/#835): record the battery table (16 rows, 12 killed, 4 expected survivors, 0 broken)** (1b151ef)
- **test(#835): the union arm compared lanes to millimetres** (3c7a648)
- **test(#834/#835): what the battery found -- three of my own tests did not bite** (69c9741)
- **test(#834): the two ulx3s A/B rows say what moved, and why they predicted it** (527b919)
- **test(#834/#835): the mutation battery, 16 rows** (82ed2b8)
- **test(#726): the battery's two survivors were both real, and both mine** (96e9c06)
- **test(#726): the mutation battery, 18 rows** (a56a786)
- **test(#830): the mutation battery, and the two test gaps it found** (04ae2f8)
- **test(#830): pin the unreadable-summary channel and the publisher** (a51f30e)

### Docs
- **docs(#847): the escape band, documented for the first time** (0f2972e)
- **docs(#620): the census number was the BASE arm, and had no command attached** (02bcb65)
- **docs(#841): the pages that recorded the two rects, re-measured** (65616dc)
- **docs(#835): the skill may not name a corpus board, and my rewrite did** (17b73cd)
- **docs(#834/#835): twelve numbers an independent fact-check falsified, and the pattern** (e6954c3)
- **docs(#834/#835): the pages that described the defect, re-measured** (fb9a9fc)
- **docs(#726): a comment that said the opposite of what the code does** (a106b2e)
- **docs(#726): four claims an independent fact-check falsified, and the headline was one** (321a989)
- **docs(#830): two claims a fact-check falsified** (c026177)

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