Worked examples

Each project here exists twice, generated from one description by tools/make_examples.py:

  what it is
as-generated/ a deterministic negative control: deliberately incomplete title/part metadata and unreviewed layout details
reviewed/ the rebuilt baseline accepted by its gate and project-specific contracts, with documented exceptions

A repository full of good designs proves only that good designs pass. The pair is the evidence: that the rules catch what they claim to catch, and that fixing what they report converges.

Gate acceptance is not production sign-off. These are worked examples, not hardware-validated reference designs. In particular, the FPGA still has decoupling-distance exceptions; signal return paths, application-specific power/thermal budgets and EMC remain engineering review and measurement work.

./bin/eda.sh gate examples/buck-5v/reviewed     --policy examples/buck-5v/gate.toml --text
./bin/eda.sh gate examples/buck-5v/as-generated --policy examples/buck-5v/gate.toml --text

Regenerate the projects with:

docker run --rm -u $(id -u):$(id -g) -v "$PWD:/work" -w /work \
  -e PYTHONPATH=/work/src -e HOME=/tmp/eda-home \
  --entrypoint python3 eda-toolkit:9.0.9 tools/make_examples.py examples/

and the images below with tools/example_images.py, which runs the same two renders for every variant and writes them as JPEG:

docker run --rm -u $(id -u):$(id -g) -v "$PWD:/work" -w /work \
  -e PYTHONPATH=/work/src -e HOME=/tmp/eda-home \
  --entrypoint python3 eda-toolkit:9.0.9 tools/example_images.py examples/

The generator reads KiCad’s own symbol and footprint libraries, so these are the real parts, not simplified copies — which is why it runs inside the container.

After the five were built, the whole set was read the way an engineer would read it — circuit theory, layout physics, readability — and every finding was either turned into a rule, fixed in reviewed/, or answered with a reasoned waiver in the project’s gate.toml. That pass, with what each finding became, is REVIEW.md.

Three columns, not two

Each comparison below has three, and the leftmost is the honest one.

column what it is
first edition the board as it came out of the generator the day it was written, before any finding had been read. Recovered from this repository’s own history — one git show per file, no editing — and rendered with today’s renderer so the only difference is the design
as-generated what the generator produces now when told to skip the review. It is much better than the first edition, because twenty rounds of findings were built into the generator itself rather than patched into the output
reviewed the same design with the review applied: what passes eda gate

The middle column is the part that is easy to miss. A tool that only fixed its own output would leave the first column and the second identical; the distance between them is the review turned into code, and it arrives before anyone runs the gate. The distance between the second and the third is what the gate still had to catch on the day.

The first editions are ea93330 (buck-5v), e7ad2d7 (motor-driver), b4d66f2 (pico-carrier), 3f796a7 (opamp-filter) and aee7401 (fpga-audio).

CI requires all five reviewed/ projects to pass and every as-generated/ negative control to fail for its intended missing-title and part-specification defects. It checks that neither the schematic nor board stage was skipped. The uploaded JSON verdicts and KiCad reports, not stale finding counts in this historical walkthrough, are the authority for each revision.

What each of them still carries is a waiver, and a waiver here is a decision with the argument attached rather than a finding hidden. Package escape necks, board-only decoupling heuristics and deliberately exposed module rails remain visible there.

All five are two-layer boards, and that is a requirement rather than an outcome. Layer count is the one board parameter that changes the price of a prototype run outright, so a design that will not close on two layers grows a few millimetres of FR4 before it grows a stack. The motor driver pays for that with one waiver — route.return_path, measured at 12.8 mm and 10.4 mm against a 10 mm limit, on two logic lanes crossing under the bridge outputs — and the waiver says what a faster design should do instead. CI checks the two-layer stack and that the ground pour on B.Cu is still mostly one piece.

Two rules keep the rest of it honest. layout.pour_edge_cut reports copper that eats through the outermost millimetre of the ground pour: the rim is what the board radiates into and what every edge-hugging track returns through, and a mounting hole may interrupt it where a route may not. route.under_package and route.via_under_package keep other nets’ tracks and vias out from under the integrated circuits and connectors, where nothing can be probed, inspected or reworked once the part is down. All three are warnings that the ai-generated policy blocks on: a shipped board may carry them and be right, a generated one has no argument for them.

All five carry what a board needs to be made as well as to work: the ground pour is filled by KiCad’s own filler against the board’s own rules, every through-hole land is relieved thermally, every track fillets into the land it enters, and each board has its M3 mounting holes and three fiducials for the assembly machine to align to. The holes clear the screw rather than the hole - a pan head on a washer is seven millimetres across, and a screw terminal’s wires want two more - which is why the count varies: four where the board has four free quarters, two on the boards whose left edge is a connector and a row of resistors. Three that hold a board flat beat four that hold one side of it. Rounds seventeen and eighteen in REVIEW.md are where that came from, and what it cost.

When these were made, and by what

Both variants carry it in their title block, in the comment fields, on the schematic and on the board:

(comment 1 "generated 2026-09-05 by Claude Code")
(comment 2 "from tools/make_examples.py in sabas0ba/kicad_skills")

It matters most on as-generated. That variant is a record of what a generator of this vintage actually produced, and the point of keeping it is to be able to say later how much has changed — which needs a date on it. The stamp is frozen in GENERATED_ON / GENERATED_BY at the top of the generator rather than read from the clock, so regenerating an unchanged design still produces an unchanged file; bump them when you regenerate, or pass --generated-on / --generated-by.

The design’s own title, date, rev and company are a separate thing and stay empty on as-generated — a generator that leaves them blank is one of the findings, and the stamp deliberately does not paper over it.

buck-5v — 12 V to 5 V at 2 A

LM2596S-5, catch diode, output inductor, screw terminals in and out, and a fuse and a TVS between the input terminal and everything else.

Under KiCad’s own ERC and DRC, and the ai-generated policy:

  verdict schematic (e/w/i) board (e/w/i)
reviewed PASS, 1 finding waived 0 / 0 / 0 0 / 0 / 5
as-generated FAIL, blockers retained — —
first edition FAIL, 45 blocking — —

The three, side by side

Everything below is this repository’s own output — eda sch render and eda pcb render, run on the three variants and cropped. Nothing is drawn by hand.

The schematic. Left is the first edition; middle is what the generator leaves today; right is after the loop. The empty title block, the parts stacked on each other at the bottom right, and the absence of any note explaining a single value are all visible before reading one finding:

first edition as-generated reviewed
schematic, first edition schematic, as generated schematic, reviewed

The board, front copper. The same circuit, the same nets. On the left the power rails are routed at signal width, J1 and D1 sit at 37°, and several tracks simply stop in mid-air. On the right the power copper is 1.0 mm, every part is square to the grid, and each ground stub ends in a via:

first edition as-generated reviewed
board front, first edition board front, as generated board front, reviewed

The board, back copper. This is the ground plane, and the reason the floorplan is what it is. Only the two screw terminals are through-hole, and both sit outside the pour, so the bottom layer carries nothing but the plane and the vias dropping into it. Ground now pours on both faces — the front copper joins through-hole only, so no thermal spoke is hostage to a crowded pad — and a ring of stitching vias around the rim ties the two planes together where edge noise wants a short way home. The generated variant has no pour at all:

first edition as-generated reviewed
board back, first edition board back, as generated board back, reviewed

Both variants also produce a complete fabrication package — eda pcb fab examples/buck-5v/reviewed -o fab/ writes the gerbers, the Excellon drill file, the pick-and-place and the BOM.

What separates them, and which check finds it:

in as-generated found by
symbols and wires off the 1.27 mm grid readability.off_grid_pin / _wire / _label, and KiCad’s own erc.endpoint_off_grid
no PWR_FLAG on the externally supplied rails erc.power_pin_not_driven
two symbols dropped on the same spot readability.overlapping_symbols
no title block, no design notes readability.title_block, spec.no_design_notes
no tolerance / voltage / current rating, no MPN spec.missing_rating, spec.missing_part_number
capacitors chosen without derating the rail spec.voltage_derating
no ESR stated on the output capacitor the regulator’s loop depends on spec.missing_esr
no ground pour layout.no_ground_plane
parts off the placement grid, turned to 37 degrees layout.off_grid_placement, layout.odd_rotation
power routed at signal width track.thin_power
routing left half finished route.stub, route.acute_angle

reviewed carries one waiver, in buck-5v/gate.toml: layout.decoupling_distance on U1.4, because that pin is FB — a sense input, not a supply — and the rule cannot tell the two apart from the board alone. That is the mechanism working as intended: the finding is not silenced, it is answered.

What building it changed in the toolkit

Laying out a real board found four things the rules and the parser had wrong:

motor-driver — dual H-bridge, DRV8833PW, 2 × 0.5 A RMS

Two brushed DC motors, screw terminals out, an eight pin logic header, the charge pump and bypass capacitors, and a fuse and a TVS on the motor supply. The PW package is rated at 0.5 A RMS per bridge at VM = 5 V and 25 °C, not the 1.5 A of the thermally enhanced PWP/RTY packages. Confirm temperature and motor stall current for the actual load. TI DRV8833 datasheet.

  verdict schematic (e/w/i) board (e/w/i)
reviewed PASS, gate exceptions documented 0 / 0 / 0 0 / 0 / 5
as-generated FAIL, blockers retained — —
first edition FAIL, 43 blocking — —

The policy retains two waiver decisions in motor-driver/gate.toml, covering the intentional grounded sense pins and the small set of logic nets drawn as named connections. Grounding AISEN/BISEN disables PWM current regulation; overcurrent fault shutdown is not a 0.5 A current regulator. REVIEW.md is the pass that decided them.

first edition as-generated reviewed
schematic, first edition schematic, as generated schematic, reviewed
board front, first edition board front, as generated board front, reviewed
board back, first edition board back, as generated board back, reviewed

The back layer is the ground pour and the four logic lanes that cross under the bridge outputs, which leaves the front free for the supply row and the local bypass. VM reaches the driver down a stated front-side spine rather than through a plane: the board is two-layer on purpose, and the spine is what that decision looks like in copper.

What this one is honest about

The first rebuild still placed C2/C3/C4 about 12 mm from their IC pins. That was a consequence of the chosen long escape fan, not an unavoidable TSSOP constraint. The follow-up puts all three capacitors beside the supply row, drops the logic locally to B.Cu, and takes the IC grounds straight into the back-layer pour through their own vias. The decoupling-distance waiver is removed; the normal 5 mm limit applies. The generated board measures 2.69 mm from VM to C2, 2.88 mm from VINT to C4, and 3.37 mm from VCP to C3 (pad centres, not complete current-loop lengths).

C2 is now 10 µF on VM, C4 2.2 µF on VINT, and the 10 nF C3 remains between VCP and VM. R1 is removed: VINT is only bypassed, and J4.7 nFAULT requires a host-side 10 kΩ pull-up to 3.3 V. The example-specific CI contract checks these values and exact connections in both schematic and board. It does not verify effective MLCC capacitance under DC bias, thermal performance, motor protection or EMC; those remain application-specific design work.

What building it changed in the toolkit

pico-carrier — Raspberry Pi Pico, every pin broken out

A carrier board: the module, two twenty-pin headers beside it, and a 5 V input that reaches VSYS through a resettable fuse and then the Schottky the Pico datasheet asks for.

  verdict schematic (e/w/i) board (e/w/i)
reviewed PASS, 9 findings waived 0 / 0 / 0 0 / 0 / 4
as-generated FAIL, blockers retained — —
first edition FAIL, 50 blocking — —

Under KiCad’s own checks reviewed has no errors and no unconnected items, on 9.0.9 and 10.0.4 — one lib_footprint_mismatch on the module and two silkscreen warnings are all that is left. The gate findings are answered in pico-carrier/gate.toml; the schematic-side decoupling rule now reads pin electrical types, so VBUS — a rail the module drives — is no longer asked for a capacitor at all.

first edition as-generated reviewed
schematic, first edition schematic, as generated schematic, reviewed
board front, first edition board front, as generated board front, reviewed
board back, first edition board back, as generated board back, reviewed

What this one is honest about

Most of a carrier is one job done forty times, and the findings are about the few places where it is not:

What building it changed in the toolkit

opamp-filter — 1 kHz Sallen-Key low pass, single 5 V

Two MCP6001 singles: one is the filter, the other buffers the half-rail the filter is referenced to. The supply comes in through a fuse and a TVS, and the output leaves through a 100 ohm isolation resistor before its coupling capacitor.

  verdict schematic (e/w/i) board (e/w/i)
reviewed PASS, 5 findings waived 0 / 0 / 0 0 / 0 / 4
as-generated FAIL, blockers retained — —
first edition FAIL, 33 blocking — —

reviewed passes KiCad’s own DRC with two silkscreen warnings — no errors, no unconnected items, no parity findings. It also passes its own gate now. The route.wander finding it used to carry was /OUT’s feedback wrap: thirteen millimetres from one side of the op-amp to the other, routed last, taking fifty-six millimetres round the board because everything nearer was already spoken for. Routing it first costs nothing and removes it.

first edition as-generated reviewed
schematic, first edition schematic, as generated schematic, reviewed
board front, first edition board front, as generated board front, reviewed
board back, first edition board back, as generated board back, reviewed

What this one is honest about

The most recognisable generated-schematic trait — every connection a label, not a wire — is now both measured and fixed. readability.label_only counts it (this sheet was 96% labels; KiCad’s own demo sheets pass), and the generator routes the sheet: every one of this design’s eight signal nets is a drawn wire tree, from the jack through the filter to the jack, with junction dots where the trees branch, and the rule no longer fires at all. One label per net survives, because the label is what names the net. This round also added R6 and R7 — the coupling caps’ far sides previously floated, which the new analog.no_dc_path rule now catches from the netlist alone.

fpga-audio — iCE40UP5K to PCM5102A, I2S out

An FPGA, an I2S DAC, the SPI flash the FPGA boots from, a 12 MHz oscillator and a 1.2 V regulator for the core — on two layers. The 3.3 V input is fused and clamped, the clock leaves the oscillator through a series resistor, and the DAC’s mute is held by a pull-down until the configured FPGA releases it.

  verdict schematic (e/w/i) board (e/w/i)
reviewed PASS, gate exceptions documented 0 / 0 / 0 0 / 0 / 5
as-generated FAIL, blockers retained — —
first edition FAIL, 34 blocking — —

Under KiCad’s own checks reviewed is clean: no DRC errors, nothing unconnected, no schematic-parity findings. This is the board that pays for the two-layer rule in area: a 48-pin QFN, a codec, a boot flash and an oscillator escaped on two layers need 100 x 84 mm, where four layers fitted the same circuit into 76 x 58 mm. Area is the cheaper currency. The FPGA, codec, flash and regulator still form one signal-flow block; the line-out and configuration headers sit on the edges they serve, and the +3V3 distribution is outer-layer copper rather than a plane. Four ordered I2S runs cross on B.Cu, and the short +1V2 spine runs there under its own FPGA block.

The earlier engineering pass also fixed four electrical faults that the new floorplan retains: the PCM5102A charge pump is CAPP–CAPM with a VNEG reservoir, VCCPLL is RC-filtered from the core rail, the boot flash has a chip-select pull-up, and the LDO reservoir is 2.2 uF.

first edition as-generated reviewed
schematic, first edition schematic, as generated schematic, reviewed
board front, first edition board front, as generated board front, reviewed
board back, first edition board back, as generated board back, reviewed

These are actual KiCad copper renders. The image utility only removes the empty page margin and converts the format; it does not redraw or rescale copper.

What this one is honest about

Two layers are this example’s design choice, and the area is what it costs. A four-layer stack fits the same circuit into 76 x 58 mm and gives the return path an uncut plane; this board buys neither, and pays in plane cuts, routing tours and 100 x 84 mm of laminate. The trade is stated rather than engineered away, and its long fan and capacitor placement still have room for improvement.

The findings that follow from it, at the scale a 48-pin part gives them:

What building it changed in the toolkit

Five things, each of which had been quietly producing a board that was not the board the schematic described:

What the five of them say together

Three findings appear on every board that has a fine-pitch part, and they all trace to the same fact — the escape from the package eats the distance budget before any component can be placed:

  motor-driver opamp-filter fpga-audio
package TSSOP-16, 0.65 mm SOT-23-5, 0.95 mm QFN-48, 0.5 mm
layout.decoupling_distance 3 4 16
track.thin_power 0.3 mm 0.2 mm 0.2 mm

The two blind spots that showed up from opposite directions — decoupling asked of VBUS, which the module drives, and of VREF, which an op-amp output makes — are closed on the schematic side: analog.missing_decoupling now reads pin electrical types, asks only where a power_in pin is, and never asks on a net an output pin drives. The board file carries no pin types, so the board-side rule keeps its blindness and the waivers say so.

And the two things no rule caught at all became rules in the review round:

REVIEW.md is the full pass: what was found, what each finding became, and the calibration of every new rule against KiCad’s demo corpus.