Hauksbee

Evidence

Known-fault validation

The full record. It is published here rather than in the tree: the hunt it came out of examines a board that is not public. File paths and test names in the text point into the repository.

Before trusting hauksbee to find unknown bugs on famous boards (the docs/evidence/FAMOUS_SWEEP.md campaign), we calibrate it against boards with known faults, specifically faults that were fixed between two public design revisions. The revision history is ground truth: "hauksbee flags revision N for exactly the thing revision N+1 fixed" is the strongest calibration the tool can have, and a documented fix that hauksbee misses tells us, with citation, where its reach ends.

This is governed by the same project meta-lesson (docs/evidence/BUG_HUNT.md): a surprising result is presumed a defect in OUR tool until chased to ground truth. Here it cuts the other way too: a tempting "hauksbee could catch this" was measured against the clean corpus and rejected when it would have manufactured false positives. The honest negative is part of the calibration.

Method

For each documented fault we:

  1. Find the prior-art citation (changelog entry, issue, PR, errata, handbook).
  2. Classify it: in scope for hauksbee (electrical connectivity, missing pulls, component ratings/stress, undefined power-up states, copper shorts/clearance, regulator derating) vs out of scope (RF, mechanical, firmware, EMC, transient thermal).
  3. For in-scope faults, obtain the FAULTY and FIXED revision design files and run hauksbee's toolchain (--report, --drc, --lint, headless stress) on both, recording one of three honest outcomes:
    • FLAGGED (gold row): hauksbee flags the faulty revision for the fault and goes clean on the fixed revision. Encoded as a CI test where the corpus files allow.
    • MISSED, check-class exists: improve the check to catch the real fault without manufacturing false positives elsewhere (re-run the famous-corpus clean sweep to prove no regression), or, if that is impossible, record exactly why and keep the honest miss.
    • MISSED, out of reach: state precisely what hauksbee would need to model.

Two revision pairs carry this comparison. The upstream, pinned revision and licence of every fetchable board is corpus.toml, which scripts/fetch-corpus.sh reads:

Which of those you can actually obtain. corpus.toml pins ZSWatch DevKit 1.2.0 and 1.2.1 and both Watchy revisions, so a plain fetch-corpus.sh gets them. It does not pin DevKit 1.1.0, and the DISPLAY-EN row below is the 1.1.0-to-1.2.0 comparison, so that row's faulty side is held only in the maintainers' corpus and is not reproducible from this repository today. The tag exists upstream under the same GPL-3.0 terms as the revisions we do pin, so this is a gap in corpus.toml rather than a licensing obstacle; adding it would make the row reproducible.

corpus.toml also carries a "Local-only boards" section listing what is deliberately absent and why, including reconstructions that are not ours to redistribute. When a row below cites one of those, it is evidence you can read but not re-run.

The native design files for the other boards in the matrix (Olimex ESP32-EVB rev A..L, MNT Reform motherboard 2.0/2.5/3.0, ZSWatch mainboard, Watchy v3) come from the famous sweep and are all pinned in corpus.toml.

Fault matrix

Board Rev pair (faulty -> fixed) Documented fault Source In scope hauksbee check Outcome
ZSWatch Watch-DevKit-HW 1.2.0 -> 1.2.1 Missing pull-ups on the RTC-side I2C bus (PCA9306 SDA2/SCL2 -> RV-8263) PR/issue #158; CHANGELOG 1.2.1 Yes (missing-pull) missing_i2c_pullup FLAGGED (gold row)
Watchy v1.5 -> v2.0 e-paper RES# on a Hi-Z-capable GPIO, no pull-up; display will not enter deep sleep issue #14; PR #17 Yes (undefined power-up state) boot-coverage (firmware co-sim) EXECUTED (Round 5): boot-coverage run on the real Watchy v1.5 board under the Espressif QEMU ESP32 backend, two-sided GREEN/RED. The display-init firmware drives RES (GPIO9) HIGH a few hundred ms into an 800 ms run, well inside the assertion's 700 ms deadline (PASS); a firmware that never touches GPIO9 leaves RES at 0.000 V and never reaches 2.6 V (FAIL). Reduced firmware, labelled honestly (see below).
ZSWatch Watch-DevKit-HW 1.1.0 -> 1.2.0 DISPLAY-EN (NTS0104 OE) on a Hi-Z-capable GPIO, no pull-up; display undefined at power-up issue #123 Yes (undefined power-up state) (control-pin class) MISSED, not structurally separable (decidable with firmware co-sim; ZSWatch's nRF5340 has no backend and cannot cheaply get one, because no nRF5340 platform exists in Renode or on its master and renode-infrastructure has no SPU model, no nRF53 IPC model and nothing for the network core, so there is nothing to vendor the way RP2040's models were. nRF52840 is the closest proven Nordic part; see docs/cosim/MCU.md)
Olimex ESP32-EVB rev D -> rev E Ethernet PHY 50 MHz REF_CLK on GPIO0 (a strapping pin) present at reset; ESP32 randomly enters download mode Olimex HARDWARE changelog, rev E; ESP-IDF Ethernet API Yes (undefined power-up state / strapping) strap_pin FLAGGED (caught; honest reach limit: fires on every rev incl. the fixed ones, the rev-E fix is not netlist-visible)
MNT Reform motherboard 2.5 -> 3.0 LTC4020 ILIMIT pin unprogrammed; charger over-draws (75-88 W from a 60 W brick) Handbook, Revised Versions; GitLab #19 Yes (component-rating / missing-program-resistor) LTC4020 behavioural converter (ltc4020_overdraws_on_mb25_and_is_clean_on_mb30) FLAGGED (gold row)
MNT Reform motherboard 2.0 -> 2.5 LTC4020 RNG/SS toggled to gate charge current destabilises the same IC's system DC/DC community.mnt.re #177 Yes (power-sequencing) LTC4020 behavioural FSM (ltc4020_rng_ss_destabilises_only_when_driven) FLAGGED (gold row)
MNT Reform motherboard 2.0 -> 2.5 Current leak through the LTC6803-4 cell-balancing network when a cell position is empty Handbook, Revised Versions Yes (reverse-current) LTC6803-4 behavioural leak law (ltc6803_leaks_on_mb20_and_is_clean_on_mb25) FLAGGED (gold row)
ZSWatch Watch-DevKit-HW 1.2.0 -> 1.2.1 nPM1300 SHPHLD internal pull-up to VSYS also wired to an MCU GPIO -> VSYS feeds the GPIO in sleep issue #155 Yes (over-voltage / reverse path) nPM1300 behavioural internal pull (npm1300_shphld_feeds_gpio_on_120_and_is_clean_on_121) FLAGGED (gold row)
ODrive v2 inverter Inverter45attempt.PcbDoc -> Inverter.PcbDoc (sibling layouts, same pinned rev) GND <-> AGND copper overlap on F.Cu at x=153.2, y=183.9 (gap -1.0 mm), forbidden by the file's own rule set (only ShortCircuit rule is the Altium default, scope All/All, ALLOWED=FALSE, so Altium's DRC flags the same overlap; no scoped allowance sanctions a ground join) The design files themselves: both layouts at ODriveHardware 079f30ac, v2/; the "faulty -> fixed" ground truth is the repo shipping the final Inverter.PcbDoc short-clean beside the attempt, not a changelog citation Yes (copper short) Altium DRC shorts (odrive_v2_attempt_ground_short_flagged_and_final_is_clean) FLAGGED (gold row); found live 2026-08-08 when the corpus fetch first materialised the file, adjudicated same day (odrive_v2 is known_good = false so the silence sweep defers to this row)
MWGEN-G1 signal generator single revision (no revision pair: the pin is the upstream head as fetched, and no later fix was looked for) Six pad-to-pad copper overlaps on F.Cu: D503 (SMAJ48CA TVS in an SMA body, 2.5 x 1.8 mm pads) over D203 pad 1 (GND <-> REFOUT_SEC, -0.150 mm at x=144.24, y=90.60), over D202 and D203 pad 3 (REFIN_GND <-> Net-(D202-Pad3), -0.060 mm at x=140.09 and x=142.36) and over the inner pads of R204 and C205 (REFIN_GND <-> REFIN, -0.015 mm at x=140.09 and x=141.75); J206's Laird BMI-S-205-F fence pad over FB204 pad 1 (GND <-> Net-(FB204-Pad1)) The design file itself at MWGEN-G1 2fc77c90; ground truth is that the design's own rule set forbids all six (MWGEN-G1.kicad_pro sets shorting_items and clearance to error with an empty drc_exclusions) and KiCad 9.0.3's own kicad-cli pcb drc on the same file reports the same six shorting_items violations, same net pairs, same pad anchors Yes (copper short) KiCad DRC shorts (mwgen_g1_pad_overlap_shorts_match_kicads_own_drc) FLAGGED (gold row); found live 2026-08-09 by the external-pool iteration qc/evidence/runs/2026-08-09-external-01.json, adjudicated same day, and pinned into corpus.toml as known_good = false so this row owns the file
OpenEnergyMonitor emonTx V3.4.5 single revision (fabrication output as the oracle) Reported GND <-> AGND shorts on BOTH copper layers at x=34.41, y=43.20, from the same-rank overlap of the two pour outlines The design file, its shipped gerbers and its schematic at EmonTx3 411de961, hardware/V3.4.5/; ground truth is net attribution over Eagle's own CAM output (a filled region holding vias of both nets, on each layer) plus the .sch segment tying the AGND supply symbol to the GND one Yes (copper short) Eagle pour overlap (emontx_ground_tie_is_real_copper_on_both_layers_and_both_are_reported) TRUE ABOUT THE COPPER; SCHEMATIC INTENT IS CONTEXT ONLY: both layers really do join the two nets and the schematic declares the pair, but carries no board coordinate that authorizes either contact. Both remain serious pending board-local authority. The F.Cu report also names the wrong location (the pour crossing, not the trace). A proposed isolate narrowing was measured and reverted. Detail below
MNT Reform laptop (system) all motherboard revisions Standby battery drain: LPC + keyboard MCU stay powered through an always-on 3V3 rail with no hardware cutoff community.mnt.re #559 No (primary fix is firmware deep-sleep; the always-on rail is by design) n/a OUT OF SCOPE
Watchy v3 USB_DET (GPIO21) held HIGH ~500 ms after wake -> immediate re-wake loop Watchy firmware #265 No (firmware wake-source logic vs ESP32-S3 boot pin behaviour) n/a OUT OF SCOPE
Olimex ESP32-EVB rev C -> C1, H1 -> I ESP_EN / reset RC timing tuning (R38, C28, R24, C31) for start-up reliability Olimex HARDWARE changelog No (RC start-up timing margin, not a connectivity/rating defect) n/a OUT OF SCOPE

Summary statistic

The count below is of faults documented in a revision history, which is what this page was built to calibrate against. Three rows in the matrix are a different thing and are deliberately not counted in it: the ODrive, MWGEN-G1 and emonTx copper rows are adjudications of findings hauksbee raised live, on boards nobody had filed a fault against, and their ground truth is a sibling layout, a second tool's DRC, or the board's own fabrication output rather than a changelog. They are in the matrix because the adjudication has to live somewhere the next reader finds it, and each carries its own test.

In-scope known faults validated: 8. Caught statically by hauksbee: 6; executed by firmware co-sim: 1; remaining static miss: 1. The original I2C pull-up gold row; the Olimex ESP32-EVB GPIO0 strapping fault caught by the strap_pin lint; and the four power-IC faults the behavioural device-model framework now carries (detailed below). The two "control input on a Hi-Z GPIO with no pull" rows are not structurally separable from benign twins on a static netlist (proven below) and so are honest static misses; they become decidable by running the firmware (the boot-coverage assertion). Round 5 executed the Watchy row that way: on the real Watchy v1.5 board under the Espressif QEMU ESP32 backend, two-sided GREEN/RED (see the firmware-boot section below). The ZSWatch DISPLAY-EN row remains a static miss because ZSWatch's nRF5340 has no co-sim backend yet (docs/cosim/MCU.md).

No check was forced and no false positive was shipped to inflate the count: the strap lint fires on exactly the Olimex GPIO0 clock and nothing else across the whole famous corpus, the behavioural models add physics, not findings, on clean boards (behavioral_parts_do_not_manufacture_faults_on_clean_board), and the Watchy boot-coverage execution is two-sided (it goes RED on a firmware that does not drive the net), so it is a measurement, not a vacuous pass.

Every catch is a true catch against ground truth, and every miss is annotated with exactly what hauksbee would need to model. A tool that claimed to catch the LTC4020 overdraw from the netlist alone would be lying; the model is what earns the catch.

The honest reach limit on the strap catch. hauksbee flags the GPIO0 fault on every Olimex ESP32-EVB revision in the corpus (rev B through L), not just the pre-fix ones. That is not a false positive: the rev-E fix is a time-domain power-sequencing change (FET3/FET4 gate PHY power via OSC_DIS so the PHY does not drive GPIO0 until the oscillator has stabilised), and it does not alter the GPIO0 net - the 50 MHz oscillator (CR1) still reaches GPIO0 through R36 (10R) in the rev-L netlist, byte-for-byte as in rev D (verified from the files). A static netlist check cannot see the fix, so it correctly reports the netlist-visible fault on all revisions and we say so plainly rather than forcing a "clean on the fixed rev" the netlist does not support. The clean two-sided discriminator (fires on a clock-strapped GPIO0, silent on a pull-up-strapped one) is proven on a constructed pair instead - see the strap-catch detail below.

The four behavioural-model catches:

The behavioural numbers are derived from the real board resistor values read off the corpus (R8, R49, R52) and the documented revision fixes, not asserted.

Gold row detail (FLAGGED)

ZSWatch DevKit: missing I2C pull-ups (1.2.0 -> 1.2.1)

Documented fault. Watch-DevKit-HW CHANGELOG 1.2.1, "Added":

Add missing pull-ups for I2C lines (#158)

Faulty 1.2.0, at the s-expression level (net_probe). The RTC sits on the secondary side of the PCA9306 I2C level translator (IC506), and that side has no resistor to any rail. (The net names below read SDA_2 while the pad func beside them still reads SDA_{2}: net names are unescaped to the spelling the schematic shows, pad function strings are quoted from the file verbatim.)

NET Net-(IC506-SDA_2) (id 145)
   IC506  val='PCA9306JK'   pad=5 func='SDA_{2}'  (level translator, secondary side)
   IC507  val='RV-8263-C8'  pad=1 func='SDA'      (RTC)
NET Net-(IC506-SCL_2) (id 146)
   IC506  val='PCA9306JK'   pad=6 func='SCL_{2}'
   IC507  val='RV-8263-C8'  pad=8 func='SCL'

The PCA9306 is a bare pass-gate with no integrated pull-ups (hauksbee deliberately does not exempt it; only auto-direction translators like the NTS0104 carry their own pulls), so this translated bus genuinely needs its own resistors. hauksbee run ... --lint on 1.2.0:

net-lint: 4 finding(s)
  [low]    missing_i2c_pullup - I2C SDA net '.../MCU/SDA' ... bus breaks out to a header ...
  [low]    missing_i2c_pullup - I2C SCL net '.../MCU/SCL' ... bus breaks out to a header ...
  [medium] missing_i2c_pullup - I2C SDA net 'Net-(IC506-SDA_2)' ... on-board master and peripheral, no pull-up present
  [medium] missing_i2c_pullup - I2C SCL net 'Net-(IC506-SCL_2)' ... on-board master and peripheral, no pull-up present

The two medium findings are exactly the missing RTC pull-ups. (The two Low findings are the primary-side bus breaking out to a 0.1" header, the intended "pulls on the attached module" convention from the famous sweep, correctly Low.)

Fixed 1.2.1, same nets:

NET Net-(IC506-SDA_2) (id 145)
   R505   val='3k3'  pad=1                (added)
   IC506  ...  IC507 ...
NET Net-(IC506-SCL_2) (id 146)
   R504   val='3k3'  pad=1                (added)
   IC506  ...  IC507 ...

hauksbee run ... --lint on 1.2.1: net-lint: no findings. The two 3k3 pull-ups R504/R505 make the lint clean. hauksbee flags exactly what the next revision fixed and stops flagging once it is fixed.

Why this discriminator is real, not a parser quirk. The shipped ZSWatch mainboard (a corpus board) uses the same PCA9306 + RV-8263 topology with the pull-ups present, and hauksbee is clean on it. This is the exact circuit the famous sweep's deep-dive C2 analysed (where the pulls were present and an earlier tool bug had hidden them). The faulty DevKit revision is where the pulls were genuinely absent, and the lint, with its 0201-extra-pad and local-rail fixes from that sweep, now reads it correctly. Cross-checked in zswatch_mainboard_rtc_i2c_is_clean.

External-pool adjudication detail (2026-08-09)

The external-pool iteration qc/evidence/runs/2026-08-09-external-01.json drew five never-before-used boards and two came back with serious shorts: six on the MWGEN-G1 and two on the emonTx V3.4.5. Both are public designs by people who know what they are doing, which is enough to put a short report under suspicion of being our defect until it is chased to the file (the project meta-lesson in BUG_HUNT.md). Chasing them separated cleanly: one set is real copper the designer did not intend, and the other was our reader treating a pour outline as a fill.

One thing this page does not claim about either board is production status. The emonTx V3.4.5 is a released revision of a product line whose six preceding revisions sit beside it in the same tree (V3.2.x and V3.4.0 through V3.4.4); the MWGEN-G1's status is not established here, and neither verdict depends on it. Both are about what is in the design files.

MWGEN-G1: six pad overlaps, and KiCad agrees on all six

The suspicion going in was a deliberate structure: a net tie, a pad-in-pad RF launch, a capacitive stub drawn as a zone. It is none of those. There is no net_tie attribute and no NetTie footprint anywhere in the file, every finding is a pad against a pad with no zone or track involved, and the geometry is a placement collision around the reference-input corner. D503 is the cause of five of the six:

(footprint "Diode_SMD_AKL:D_SMA_TVS" (layer "F.Cu")
  (at 143.3445 90.6395)
  (attr smd)
  (fp_text value "SMAJ48CA" ...)
  (pad "1" smd rect (at -2 0) (size 2.5 1.8) (layers "F.Cu" "F.Paste" "F.Mask")
    (net 125 "/Clock reference/Input Clocks/REFIN_GND") (pinfunction "K") ...)
  (pad "2" smd rect (at 2 0) (size 2.5 1.8) (layers "F.Cu" "F.Paste" "F.Mask")
    (net 1 "GND") (pinfunction "A") ...)

A TVS in an SMA body has 2.5 x 1.8 mm pads, so pad 1 spans x 140.095..142.595, y 89.740..91.540. Everything around it is smaller and closer:

What the reported gap numbers are, and are not. Four of the six are the exact overlap: -0.059614 twice against a true 0.0596 mm (89.7991 - 89.7395), and -0.015386 twice against a true 0.0154 mm (91.5395 - 91.5241). The other two are floors, for two different reasons, and each is far from tight.

Where inset pad polygons interpenetrate, the depth saturates: a roundrect is carried as an inset polygon plus a corner radius, so once the inset polygons cross the reported figure stops at the summed radii. D503/D203's -0.150 mm is that: pad 2 of D503 is a plain rect (radius 0) and D203's pad 1 a roundrect (radius 0.15), summing to the 0.15 reported against a real 0.2905 mm x-overlap.

Where one pad is wholly inside another, a different branch takes over. Containment returns edge.min(0.0) - 1e-6 (drc.rs, the polygon-polygon arm), so a fully engulfed pad reports -0.000001 mm: FB204 inside J206's fence pad measures +0.025 mm of inset-polygon edge distance, the clamp takes it to zero, and the epsilon is all that is left. The most complete overlap on the board therefore carries the smallest number in the table.

So a magnitude here is a lower bound on the overlap, exact in four cases and badly under in two. The sign is what the short verdict rests on, and it is right in all six; the magnitudes are pinned in the test as a regression guard, and are no part of the agreement with KiCad, whose report carries no gap at all for a shorting_items violation.

Ground truth, in the same shape as the ODrive row's: the design's own rules forbid it. MWGEN-G1.kicad_pro sets shorting_items and clearance to error and its drc_exclusions list is empty. Those two severities are KiCad's defaults rather than a deliberate tightening, which is the same strength of claim the ODrive row rests on: the design did not loosen the rule that forbids this, and no exclusion records an instance of it being looked at and accepted. That is weaker than "nobody looked", and it is all the file supports. The test reads the .kicad_pro and asserts all three values, so the claim cannot rot. And KiCad 9.0.3 run over the same file (kicad-cli pcb drc --format json) reports six shorting_items violations, which are the same six net pairs anchored at the same pad centres:

shorting_items | nets /Clock reference/Input Clocks/REFIN_GND and .../REFIN   | (141.3445, 90.6395) (139.86, 91.999114)
shorting_items | nets /Clock reference/Input Clocks/REFIN_GND and .../REFIN   | (141.3445, 90.6395) (141.45, 91.999114)
shorting_items | nets /Clock reference/Input Clocks/REFIN_GND and Net-(D202-Pad3) | (141.3445, 90.6395) (141.7725, 89.499114)
shorting_items | nets /Clock reference/Input Clocks/REFIN_GND and Net-(D202-Pad3) | (141.3445, 90.6395) (139.7975, 89.499114)
shorting_items | nets GND and /Clock reference/REFOUT_SEC                     | (145.3445, 90.6395) (143.6475, 90.449114)
shorting_items | nets GND and Net-(FB204-Pad1)                                | (162.55, 79.1) (161.675, 79.1)

That report is committed, not paraphrased: crates/hauksbee-extract/tests/mwgen_g1_2fc77c90_kicad_9_0_3_shorts.json holds the six violations verbatim (item descriptions, pad UUIDs, positions), and mwgen_g1_2fc77c90_oracle.md beside it records the command, the input hash and what was trimmed. The gold-row test derives its expected set from that file, so the cross-check runs in CI without CI needing KiCad. That pins hauksbee against the recording; it does not re-run KiCad, so a change in KiCad's own reading stays invisible until someone re-runs the recorded command.

KiCad also reports 32 courtyards_overlap and 16 solder_mask_bridge violations board-wide, of which 6 and 6 fall inside the two boxes these shorts sit in (x 140-145 / y 88-93, and x 160-164 / y 77-81). So the crowding is not confined to this corner, but it is concentrated here: the same story from two other directions. What that establishes is that the shipped file is not DRC-green. It does not establish that nobody ran the DRC, or that nobody looked at these and accepted them: a designer can do both without recording an exclusion, and an empty exclusion list only says none was recorded. Six REAL DEFECTS, and a gold row rather than a corpus silence entry.

emonTx V3.4.5: a declared ground tie, reported as a short

The emonTx reported one GND <-> AGND short per copper layer, at the identical coordinate, gap 0.000 mm. That pattern is what a deliberate single-point star ground looks like, so the question was which it was. Both pours are same-rank and their outlines genuinely overlap:

GND   layer 1  isolate 0.3048       ... (33.782, 80.772) (33.647, 43.205) (87.376, 43.205) ...
AGND  layer 1  isolate 0.3048       (34.544, 78.359) (34.409, 42.856) (86.733, 42.856) (86.233, 78.359)
GND   layer 16 isolate 0.3048       ... (33.782, 80.772) (33.647, 43.205) (87.376, 43.205) ...
AGND  layer 16 isolate 0.00030625   (34.409, 42.856) (86.733, 42.983) (86.233, 78.232) (34.544, 78.232)

GND fills below y=43.205 and AGND above y=42.856, so the outlines overlap in a 0.349 mm band on both layers at the corner the finding sits in, and (34.41, 43.20) is where the two rings cross. Nothing in that geometry distinguishes the layers.

The oracle, and the mistake to avoid. This upstream ships its fabrication output beside the design: hardware/V3.4.5/GERBERS emonTx V3.4.5_2021-04-07/CAMOutputs/GerberFiles/. The useful question to ask of it is not "how far apart are the pours" but does any single filled region of the layer contain vias of both nets, which needs no distance and no resolution choice. Answers, by exact point-in-polygon over the dark G36 regions:

The top-layer pour FILLS are 8.236 mm apart, at (42.110, 70.967) to (33.874, 70.997), and that is true and irrelevant: the pours do not meet, and the nets are joined anyway, by routing near U1. An earlier pass of this adjudication measured the pours, concluded "no contact on F.Cu", and was wrong about the layer. Reading only the pours answers a question about pours.

The tie is declared, not inferred. emonTx V3.4.5.sch wires the AGND supply symbol AGND7 (library supply1, deviceset AGND, no package) to the GND supply symbol SUPPLY6 in one segment of net GND. That segment is absent from V3.4.0's schematic and present in V3.4.1 through V3.4.5, the same boundary at which three of the four ground pours were raised from 0.00030625 to 0.3048 mm. A designer drew the tie, in the revision where they started isolating the pours, and kept it for four more releases.

Verdict: both reports are about real copper, and the schematic declares the net pair. hauksbee is right that GND and AGND are in contact on both layers. It is wrong about where on F.Cu, naming the pour-outline crossing rather than the trace. The schematic establishes net-pair intent but cannot locate either board contact, so both observations remain SERIOUS until board-local authority identifies an intended physical join.

Context added by reading the schematic. That input is now a supported companion. --schematic <FILE>, or a .sch sitting beside the board under the board's own name, is read for Eagle's supply-symbol tie construct (hauksbee-extract/src/eagle_sch.rs). A matching contact keeps its layer, location, measured gap, SERIOUS severity and --strict gate while naming AGND7 wired to SUPPLY6 in net GND as context. Run against the fabrication-era files, the parser finds exactly one declared tie in the whole of V3.4.5's schematic, and it covers both layers' contacts; it finds none in V3.4.0, independently reproducing the revision boundary recorded above. With no schematic the finding stays SERIOUS and names the schematic as the contextual upload, and a schematic that declares nothing adds nothing, so V3.4.0's reported contacts stay serious even with its own schematic supplied. Both directions remain opt-in external-corpus adjudications (emontx_ground_tie_is_real_copper_on_both_layers_and_both_are_reported and emontx_v340_schematic_declares_no_tie_so_its_shorts_stay_serious in crates/hauksbee-extract/tests/known_faults.rs); explicit invocation fails when the corpus files are absent. The always-run release contract instead uses the tracked declared and undeclared pairs under crates/hauksbee-extract/tests/fixtures/eagle_ties/. The two pour over-reports below are untouched: they are a fill-reconstruction problem, not a declaration problem.

Historical exploratory sweep over the available Eagle pairs. The first cut admitted any symbol carrying a direction="sup" pin, which is wrong because ordinary Eagle libraries mark a real component's power pins that way. Run over the twelve .brd/.sch pairs on hand it invented declarations on two unrelated designs: 6 on margay_logger/Hardware/Margay.sch (the SD-MMC symbol, 13 pins, 4 of them sup, so an SD socket "declared" ground tied to MISO, 3V3_AUX, SCK_BUF and three more) and 19 on emonTx V3.2.sch (the XBEE symbol, 20 pins, 2 sup), including 3.3V to GND. Any of those would have attached false intent context to a real rail-to-ground short. Requiring a single sup pin, one gate, and no packaged device behind the deviceset takes both boards to zero while leaving the emonTx tie intact. That twelve-pair exploratory sweep was not retained as a release artifact, so it is historical diagnosis, not current completion evidence. The release contract is the always-present declared and undeclared pair under crates/hauksbee-extract/tests/fixtures/eagle_ties/, plus focused structural, packaged-part, multi-pin and library-URN collision tests.

Reproduce with cargo run -p hauksbee-extract --example sch_ties -- <brd> <sch>. The multi-pin case is pinned by a_multi_pin_component_with_supply_pins_declares_nothing and the packaged case by a_packaged_deviceset_is_never_a_supply_symbol, both in crates/hauksbee-extract/src/eagle_sch.rs.

The isolate narrowing: tried, measured, reverted

Before the F.Cu contact was found, the F.Cu report looked like a false positive, and the fix looked like keying the pour-to-pour rule on isolate (report the overlap only when a pour asks for no gap). That change was implemented, measured and then reverted. The measurement is worth keeping, because it is what the next attempt has to beat.

Registering each revision's shipped export against its own drill file (V3.4.0 at (1.2195, 19.6690) mm, worst via residual 0.8 um over 108 vias; V3.4.2 at (1.1430, 19.6690), 0.9 um over 123), then labelling connected copper and asking whether any component holds vias of both nets:

Revision, layer pour isolates contact? outline rule isolate rule
V3.4.0 F.Cu both 0.00030625 no flags: wrong flags: wrong
V3.4.0 B.Cu both 0.00030625 yes flags: right flags: right
V3.4.2 F.Cu both 0.3048 no flags: wrong silent: right
V3.4.2 B.Cu GND 0.3048, AGND 0.00030625 yes flags: right flags: right
V3.4.5 F.Cu both 0.3048 yes (the trace above) flags: right silent: wrong
V3.4.5 B.Cu GND 0.3048, AGND 0.00030625 yes flags: right flags: right

Four of six either way. The narrowing does not improve accuracy; it trades the V3.4.2 F.Cu false positive for a false negative on V3.4.5's F.Cu, and this detector's own note on SHORT_TOUCH_EPS_MM says which side of that trade is worse: "Under-reporting a short is the worst failure this detector has: it is the one finding a board cannot ship with." So the guard came out and the rule is as it was, with its error rate now measured rather than assumed.

Two things the exercise established that survive the revert. A near-zero isolate is necessary but not sufficient for two pours to merge: V3.4.0 sets both top pours to 0.00030625 and their fills stay apart, so no cut through isolate can decide contact, and the 1 um threshold that was chosen inside the measured band was answering a question the band cannot settle. And the outline rule over-reports twice in six, which is now written down in ../about/LIMITATIONS.md with the reason (the fill is never reconstructed) rather than left as a silence.

(V3.4.1 through V3.4.4 are not pinned in corpus.toml, which lands only V3.4.5 and V3.4.0, so the two V3.4.2 rows need a full upstream checkout to re-derive. V3.4.1's archive is a V3.2.1 export; V3.4.3 and V3.4.4 ship gerbers that were not measured.)

Strap-pin catch detail (FLAGGED)

Olimex ESP32-EVB: 50 MHz PHY clock on the GPIO0 strapping pin

Documented fault. GPIO0 is an ESP32 boot strapping pin that must be a stable HIGH at the reset latch window for normal SPI boot; a LOW (or unstable) level sends the chip into download mode. The Olimex ESP32-EVB wires the LAN8710A Ethernet PHY's XTAL1/CLKIN (REF_CLK) to GPIO0, and a free-running 50 MHz oscillator (CR1) drives that net. Espressif states it plainly (ESP-IDF Ethernet API): "GPIO0 is also an important strapping GPIO on ESP32. If GPIO0 samples a low level during power-up, ESP32 will go into download mode [...] This could fail for some PHY device (i.e., it still outputs signal to GPIO0 even in reset state)." Olimex's rev-E changelog adds "L5, FET3, FET4, C29, R50, R51 [...] to enable powering on the PHY after the 50MHz oscillator has been started."

At the s-expression level (net_probe, rev D PCB; identical in rev L). Net /GPIO0/XTAL1/CLKIN carries:

U3 pad 25   ESP-WROOM-32  GPIO0 (the strapping pin)
U4 pad 5    LAN8710A      XTAL1/CLKIN (PHY REF_CLK)
R36 pad 2   10R           -> R36 pad 1 = Net-(CR1-OUT)
R54 pad 1   NA/R0603      -> +3V3   (the would-be GPIO0 pull-up, DNP - not fitted)
CR1         Q50MHz osc    pad 3 = OUT -> R36 -> GPIO0 ;  pad 4 = +3V3 ; pad 1 = OSC_DIS

The 50 MHz oscillator CR1 reaches GPIO0 through the 10R series R36, and the only would-be bias (R54, a pull-up to +3V3) is marked DNP. So the strap pin sees a free-running clock at the reset latch, with no static pull to hold it HIGH: exactly the documented failure.

The new check. A per-part strap table now lives in the model db ([[models.straps]] in crates/hauksbee-models/db/mcu.toml, cited per entry to the Espressif/ST/RP datasheets), and an engine-layer lint (crates/hauksbee-engine/src/checks/straps.rs, run by hauksbee run <board> --lint) examines the net each strap pin sits on. It flags (High) a strap net that a free-running clock source reaches with no override:

[high] strap_pin - U3 strap pin gpio0 (gpio0) net '/GPIO0/XTAL1/CLKIN' carries a
free-running clock source CR1 (Q50MHz/...): a clock present at the reset latch
can mis-strap the part (GPIO0 high = SPI boot (normal); low = download/UART boot.
A 50 MHz clock here randomly enters download mode (Olimex ESP32-EVB))

Calibration (zero false positives, the bar for shipping). Swept across the whole famous corpus, the strap lint fires on exactly the Olimex GPIO0 clock and nothing else. Every other strap on every board is correctly biased and stays silent, and the clean is real (not vacuous) because the parts genuinely resolve and the straps are genuinely examined: Watchy's ESP32 GPIO0 (held by the internal pull-up, no clock), every other Olimex strap (GPIO2 pulled high via the 2.2k RM1, GPIO15 pulled high via R29 10k, GPIO12 pulled low), the RP2040 QSPI_SS/BOOTSEL on rp2040-minimal and SparkFun Thing Plus (flash-CS idle-high), and the nRF/AVR boards that have no ESP32 straps at all. The one false fire the calibration did surface (GPIO15 appearing to "reach" CR1 because its pull-up and the oscillator's VDD share the +3V3 rail) was a real bug in the check's reachability, chased to ground and fixed (the hop now never travels through a rail/ground), in the project meta-lesson pattern. Pinned by crates/hauksbee-engine/tests/strap_lint_corpus.rs (5 corpus-gated tests, including a corpus-wide guard that the lint fires only on the Olimex GPIO0) and checks::straps unit tests (the synthetic two-sided discriminator: a constructed ESP32 board fires when a 50 MHz oscillator reaches GPIO0, goes clean when GPIO0 carries a pull-up instead).

The honest reach limit. As noted in the summary, the lint fires on all Olimex revisions, because the rev-E fix is a time-domain power-sequencing change not visible in the GPIO0 net. The catch is of the netlist-visible fault (a free-running clock source reachable to a strap pin with no override); the time-domain question of when the clock appears relative to the latch window is out of a static netlist check's reach, and the clean two-sided proof is the constructed synthetic pair rather than rev D vs rev L.

Two further reach limits of the wrong-bias arm are recorded so the bar stays honest as the corpus grows. (i) A momentary boot button to a net named GND is indistinguishable, on a static netlist, from a permanent pull-down: a real BOOTSEL/boot-button design routes the button's series resistor to a button node, not directly to GND, so the corpus is clean, but that is incidental to net naming, not a structural guarantee. (ii) A boot-select strap carrying both a correct and a wrong-direction pull (a divider) is netted out and not flagged (the split-divider guard, tested by boot0_with_an_opposing_correct_pull_does_not_fire).

Firmware-boot control-net verification (the Hi-Z control-input class)

The two "control input on a Hi-Z GPIO with no pull" misses below (Watchy RES#, ZSWatch DISPLAY-EN) are not structurally separable on a static netlist - that finding stands. But they become decidable by running the firmware: a new hauksbee-ci assertion kind, boot-coverage, runs the co-sim from reset and requires the MCU to drive the control net to a defined level within a deadline, with no fault during the boot window before it. This turns "we cannot know the intended default" into "watch what the firmware actually does". It is proven two-sided on a constructed AVR board whose MOSFET gate floats at reset (variant-A firmware drives it -> PASS; variant-B never does -> FAIL, naming the net; crates/hauksbee-ci/tests/boot_coverage.rs, documented in docs/ci/CI.md).

Round 5 executed the Watchy row on its own board. The ESP32-PICO-D4 model now maps to the qemu:esp32 backend (the same proven Espressif QEMU path the ESP32 demo uses), so the unmodified Watchy v1.5 board file co-simulates. boot-coverage on the real RES net (GPIO9, no pull in v1.5) is two-sided: a reduced display-init firmware that drives GPIO9 HIGH passes (RES reaches 2.6 V with the boot window clean), and a firmware that drives other GPIOs but never GPIO9 fails (RES observed across [0.000, 0.000] V and named).

The instant it crosses 2.6 V is not a reproducible number, and quoting one as if it were would misrepresent the measurement. QEMU's timing varies run to run: four consecutive runs put the crossing at 336, 272, 280 and 280 ms. What the assertion checks, and what is stable, is the margin: the crossing lands inside the spec's 700 ms deadline_ms every time, with roughly half the window to spare. Read the result as "comfortably in time", not as a timestamp. The firmware is a reduced Watchy display-init stand-in (it reproduces only the e-paper reset behaviour issue #14 is about and maintains the GPIO mailbox the QEMU backend needs; the full GxEPD2 stack would not mirror GPIO, see docs/cosim/MCU.md), so the verdict is labelled a reduced firmware-level validation, not a claim about the full Watchy firmware's exact timing. Pinned by watchy_v15_display_res_driven_passes / _undriven_fails (crates/hauksbee-ci/tests/boot_coverage.rs, corpus + QEMU gated). Full derivation in docs/evidence/FAMOUS_SWEEP.md Round 5.

The ZSWatch DISPLAY-EN row is still not flipped: ZSWatch's nRF5340 has no co-sim backend yet (see docs/cosim/MCU.md), so the mechanism is recorded as ready-for-backends, not applied to it.

Documented-miss detail (MISSED, not structurally separable)

The "control input on a Hi-Z GPIO with no pull" class

Two independently-cited faults share one structure:

hauksbee's existing floating_control_pin check requires the control net to have degree 1 (only the control pin, nothing else). Both faults have degree 2 (the control pin plus a driving GPIO), so the check is silent. That is correct, because the net is not floating in the strict sense; it is driven, just by a pin that goes Hi-Z at power-up / sleep.

Could a structural check catch it? A prospective check was specified ("a dedicated EN/OE/RST/CS pin whose only driver is an MCU GPIO, with no pull resistor to a rail or ground"). Rather than commit it on a hunch, it was implemented as a probe (kept, not thrown away, as crates/hauksbee-extract/examples/ctrl_input_probe.rs) and measured against the faulty board and the clean corpus before any change to net_lint(). On the faulty DevKit 1.1.0 (Dev-Kit.kicad_pcb) the probe fires on five nets:

  DISPLAY-EN   ctrl=[IC304:output-enable] drivers=[M301.A5=P0.20]   <- the documented fault
  DISPLAY-CS   ctrl=[X307:chip-select]    drivers=[M301.B3=...]     <- normal SPI CS, NOT a fault
  DISPLAY-RST  ctrl=[X307:reset]          drivers=[M301.B5=...]     <- normal display reset, NOT a fault
  VIB-EN       ctrl=[IC206:enable]        drivers=[M301.E9=P1.05]   <- haptic driver EN, NOT a fault
  RESETn       ctrl=[X310/M301:reset]                               <- MCU reset button net

Only DISPLAY-EN was fixed by the project. Five clean, shipped corpus boards were swept, and three of them produced 6 false positives between them, every one a correct design (a GPIO-sequenced power-gate, an idle-at-boot SPI CS, an off-at-boot haptic EN). Enumerated so the claim is auditable, not asserted:

Board (clean, shipped) Net the probe wrongly flags Why it is correct
ZSWatch mainboard RESETn (M601 reset) MCU reset, driven by the SWD/USB SBU lines
ZSWatch mainboard DISPLAY-CS (X608 CS) SPI chip-select, idle-high until firmware drives it
ZSWatch mainboard DISPLAY-RST (X608 reset) display reset driven by firmware; brief Hi-Z is harmless
ZSWatch mainboard VIB-EN (IC505 enable) haptic driver; off-at-boot is the desired default
MNT Reform mb 3.0 PCIE1_PWR_EN (U20 enable) intentional GPIO-sequenced PCIe power gate
Watchy v3 Net-(D1-K) (U2 enable) boost-converter EN in its own startup network

The ZSWatch mainboard is the killer: it is structurally identical to the DevKit (same DISPLAY-CS / DISPLAY-RST / VIB-EN topology) but the project never reported any of those as faults. The thing that makes DISPLAY-EN a fault and VIB-EN not a fault is the desired default state of the controlled load (a display that must be on by default vs a vibration motor that must be off by default), which is a behavioural judgment the netlist does not encode. The structures are byte-identical. A check firing here would be precisely the confident false-positive the project meta-lesson forbids, on the very same shipped board. So this is recorded as an honest miss, with the measurement as the evidence that forcing the check would do more harm than good.

The test fixed_control_pull_revisions_are_lint_clean pins that the fixed revisions (Watchy v2.0, DevKit 1.2.0), which carry the pull, raise no floating-control-pin finding, so any future attempt to add such a check must at least stay silent on the corrected designs.

Behavioural-catch detail (FLAGGED via the device-model framework)

These four in-scope faults need a behavioural model of a specific power IC. That model now exists: a declarative behavioural layer (named pins with internal pulls / open-drain / enable semantics; finite state machines; averaged buck/boost/buck-boost converter blocks with programmable input/output current limits; and evalexpr-based current/voltage laws) defined in TOML alongside the DB (crates/hauksbee-models/db/power_ics.toml), stamped and iterated each solver chunk by crates/hauksbee-engine/src/behavioral.rs the same way the configurable power supplies are. The model reads board-programmable resistors off the actual netlist, so the faulty and fixed revisions diverge with no model edit.

Out-of-reach detail (now empty)

Earlier passes of this benchmark listed five in-scope faults as out of reach: the Olimex GPIO0 strapping fault (needed strap tables and clock-at-reset awareness) and the four power-IC faults (needed behavioural models of the LTC4020, LTC6803-4 and nPM1300). All five are now caught: the strap-pin lint covers the first (with its reach limit stated above), and the behavioural device-model framework covers the other four (detail above). The only remaining in-scope misses are the two not-structurally-separable control-pin rows, whose firmware-decidable path is described in the matrix.

Out-of-scope detail

Check improvements committed

A new boot-time check shipped: the strap-pin lint (strap_pin). It carries per-part boot strapping tables in the model db (ESP32 classic / -S2 / -S3 / -C3, STM32 BOOT0, RP2040 QSPI_SS/BOOTSEL, each cited to the part's TRM; AVR documented as having no strapping pin), and flags a strap net a free-running clock reaches with no override, or a strap pulled to the wrong level. It was calibrated to zero false positives on the whole famous corpus (it fires on exactly the Olimex GPIO0 clock) before being trusted, in the same discipline as the famous sweep. The one false fire the calibration surfaced (the GPIO15 rail-shared-with-VDD reachability bug) was fixed at the cause. A new hauksbee-ci assertion kind, boot-coverage, makes the Hi-Z-control-input class decidable by running the firmware (see the detail section above and docs/ci/CI.md).

No check was added or changed by the 2026-08-09 external-pool adjudication, and the attempt is worth recording because it was reverted on evidence. The Eagle pour-to-pour arbitration reads any same-rank outline overlap between two signals as a short. The emonTx V3.4.5 looked like a counterexample, and a narrowing that required a near-zero isolate was implemented; scoring it against the six emonTx layer-instances that ship fabrication data showed it right about four, the same as the rule it replaced, having traded one over-report for a miss on a layer where a trace genuinely joins the nets. The guard came out. What survives is the scoring table in the detail section above, the two over-reports written down in ../about/LIMITATIONS.md, and a named unlocking input for each (Eagle's fill for the over-reports, the schematic's net segments for the net-tie blindness).

The blast radius of the reverted attempt was measured while it existed: it could only change a verdict where two same-rank pours of different signals have overlapping outlines, and a scan of every .brd in board-corpus finds zero such pairs (the Eagle 5 binaries hauksbee refuses are not readable either way, which is what the eurorack_binary_eagle entry is for). The only instances in either tree are the twelve in the emonTx revision family. eagle_drc_corpus, drc_corpus and the 52-test eagle_drc suite are green.

The earlier campaign forced no check. The decisive engineering call it produced was a negative: the prospective "control input on a Hi-Z GPIO" check was specified, measured against the clean corpus (via the committed crates/hauksbee-extract/examples/ctrl_input_probe.rs, so the measurement is re-runnable), shown to manufacture 6 false positives on three of the five swept shipped boards, and rejected. The famous-sweep lint already had the supporting fixes (0201 extra-pad resistor counting, local-rail recognition, pass-gate non-exemption) that let it read the ZSWatch gold row correctly, so no new code was needed to catch the one catchable I2C fault. The false-positive discipline of the sweep is therefore intact: cargo test --workspace is green, including the netlint, drc_corpus, known_faults, strap_lint_corpus, and boot_coverage suites.

Tests added

crates/hauksbee-engine/tests/behavioral_faults.rs (5 tests, corpus-gated the same way: absent corpus skips, HAUKSBEE_REQUIRE_CORPUS=1 makes absence a hard fail). Four are the two-sided power-IC gold rows above (ltc4020_overdraws_on_mb25_and_is_clean_on_mb30, ltc4020_rng_ss_destabilises_only_when_driven, ltc6803_leaks_on_mb20_and_is_clean_on_mb25, npm1300_shphld_feeds_gpio_on_120_and_is_clean_on_121); the fifth (behavioral_parts_do_not_manufacture_faults_on_clean_board) is the false-positive guard; the FIXED Reform mb3.0 binds both behavioural parts and runs a normal solve raising zero behavioural faults. The framework itself is unit-tested in crates/hauksbee-engine/tests/behavioral_framework.rs (8 tests: pulls, FSM + open-drain, expression laws, averaged-converter regulation and limits, and the custom-behaviour escape hatch).

crates/hauksbee-extract/tests/known_faults.rs (7 tests, corpus-gated: absent corpus skips, but HAUKSBEE_REQUIRE_CORPUS=1 makes absence a hard fail so the calibration cannot vacuously pass on a runner that should have the corpus):

crates/hauksbee-engine/tests/strap_lint_corpus.rs (5 tests, corpus-gated): the strap catch. olimex_rev_d_gpio0_clock_flagged (the gold catch), olimex_rev_l_still_fires_fix_is_not_netlist_visible (the honest reach limit, pinned as ground truth), watchy_esp32_straps_are_clean and rp2040_minimal_bootsel_is_clean (known-good, examined-and-clean), and strap_lint_only_fires_on_olimex_gpio0_across_corpus (the corpus-wide zero-false- positive guard). Plus checks::straps unit tests (no corpus): the synthetic two-sided discriminator and the GPIO15 rail-share regression.

crates/hauksbee-ci/tests/boot_coverage.rs (4 tests): the boot-coverage two-sided proof. gate_driven_promptly_passes (variant A drives the floating MOSFET gate) and gate_left_floating_fails_naming_the_net (variant B never does), plus the two Watchy arms on the real v1.5 board under the ESP32 QEMU backend, watchy_v15_display_res_driven_passes and watchy_v15_display_res_undriven_fails (corpus + QEMU gated). Plus a spec-validation guard in spec_and_assertions.rs.

Reproduce

cd hauksbee
BIN=target/release/hauksbee; cargo build --release -p hauksbee-engine
C=../board-corpus/famous

# Gold row: flagged on faulty, clean on fixed.
$BIN run "$C/zswatch_devkit/v1.2.0/ZSWatch-Watch-DevKit.kicad_pcb" --lint   # 2 medium
$BIN run "$C/zswatch_devkit/v1.2.1/ZSWatch-Watch-DevKit.kicad_pcb" --lint   # no findings

# The documented misses (hauksbee is silent; the fix is a pull-up).
cargo run --release -q -p hauksbee-extract --example net_probe \
  "$C/watchy_history/v1.5/Watchy.kicad_pcb" RES        # no R on the RES net
cargo run --release -q -p hauksbee-extract --example net_probe \
  "$C/watchy_history/v2.0/Watchy.kicad_pcb" RES        # R20 present

# Why the control-input check was NOT added: the probe fires on the real fault
# AND on benign twins. 5 hits on the faulty board, 6 false hits on clean ones.
cargo run --release -q -p hauksbee-extract --example ctrl_input_probe \
  "$C/zswatch_devkit/v1.1.0/Dev-Kit.kicad_pcb"         # DISPLAY-EN + 4 benign
cargo run --release -q -p hauksbee-extract --example ctrl_input_probe \
  "$C/zswatch_mainboard/watch/ZSWatch-Watch.kicad_pcb" # 4 false positives, shipped board

# Strap-pin catch: the Olimex GPIO0 50 MHz clock flags; the fix is not
# netlist-visible so it flags every rev. Known-good ESP32/RP2040 boards stay
# silent (the strap lint runs as part of `--lint`).
$BIN run "$C/olimex_esp32/HARDWARE/REV-D/ESP32-EVB_Rev_D.kicad_pcb" --lint  # 1 high: GPIO0 clock
$BIN run "$C/olimex_esp32/HARDWARE/REV-L/ESP32-EVB_Rev_L.kicad_pcb" --lint  # still 1 high (fix not netlist-visible)
$BIN run "$C/watchy_history/v2.0/Watchy.kicad_pcb" --lint                   # no strap_pin finding

# Boot-coverage two-sided demo (the Hi-Z control-input class, decided by firmware).
CI=target/release/hauksbee-ci; cargo build --release -p hauksbee-ci
$CI run crates/hauksbee-ci/examples/boot_gate_pass.toml   # GREEN: gate driven within deadline
$CI run crates/hauksbee-ci/examples/boot_gate_fail.toml   # RED: gate left floating, net named

# The two external-pool copper rows. These two boards sit at the corpus ROOT
# (the fetch layout), not under famous/. MWGEN-G1 flags six pad overlaps; the
# emonTx flags both layers, which its own gerbers say is right about the copper.
R=../board-corpus
$BIN run "$R/mwgen_g1/MWGEN-G1.kicad_pcb" --drc                  # 6 serious
$BIN run "$R/emontx3/hardware/V3.4.5/emonTx V3.4.5.brd" --drc    # 2 shorts, one per layer
$BIN run "$R/emontx3_v340/hardware/V3.4.0/emonTx V3.4.brd" --drc  # 2, and F.Cu is the over-report

# The MWGEN-G1 cross-check, if you have KiCad 9 installed: same six
# shorting_items, same net pairs, same pad anchors.
kicad-cli pcb drc --format json --output mwgen_drc.json \
  --severity-error "$R/mwgen_g1/MWGEN-G1.kicad_pcb"

# CI calibration. HAUKSBEE_REQUIRE_CORPUS=1 makes corpus-absence a hard fail
# instead of a silent skip, so the gold row cannot vacuously green-out.
HAUKSBEE_REQUIRE_CORPUS=1 cargo test -p hauksbee-extract --test known_faults
HAUKSBEE_REQUIRE_CORPUS=1 cargo test -p hauksbee-engine --test strap_lint_corpus
cargo test -p hauksbee-ci --test boot_coverage

What each row rests on

The matrix's mechanisms are pinned by tests, split by whether they need boards on disk. The distinction matters, because a corpus-gated test that quietly passes with no corpus present would report green on zero coverage.

Needing no corpus, so runnable by anyone:

Needing the corpus: the ZSWatch I2C gold row (known_faults), the ODrive, MWGEN-G1 and emonTx copper rows including the V3.4.0 known-false-positive pin (also known_faults), the Olimex strap catches (strap_lint_corpus), the four behavioural power-IC rows (behavioral_faults), and the Watchy QEMU boot-coverage pair. Absent a corpus they early-return; HAUKSBEE_REQUIRE_CORPUS=1 makes a missing corpus a hard failure instead, and the commands for that are in the reproduction section above.

Two honest bounds on that second group. Several of those tests address boards by a board-corpus/famous/<id>/... layout that scripts/fetch-corpus.sh does not produce, so run against a freshly fetched corpus they do not find their boards. And the DISPLAY-EN row's faulty side needs ZSWatch DevKit 1.1.0, which corpus.toml does not pin. Reproducing the matrix end-to-end therefore still needs the maintainers' tree.