Open source · MIT
An electronics workcenter for agents
Via holds the tools, skills and gates an agent needs to take a KiCad 10 project to fabrication outputs. It runs ERC, DRC, simulation and export, in that order. A board is done when the last line says GREEN.
Via is early: it has no release yet. Where it stands.
$ workcenter gates tests/boards/pass-rc-divider PASS erc 0 errors, 0 warnings [0.27s] PASS drc 0 errors, 0 warnings (0 unconnected, 0 parity) [0.59s] PASS sim 1 decks, 46 corner runs, 0 failures [0.16s] PASS export 4/4 outputs, 22 gerber files [1.21s] GREEN: rc_divider report /home/user/via/tests/boards/pass-rc-divider/build/gates.json $ workcenter gates tests/boards/fail-drc-clearance PASS erc 0 errors, 0 warnings [0.27s] FAIL drc 1 errors, 1 warnings (0 unconnected, 0 parity) [0.63s] - error: clearance: Clearance violation ( clearance 0.2000 mm; actual 0.0500 mm) - warning: track_dangling: Track has unconnected end RED: rc_divider report /home/user/via/tests/boards/fail-drc-clearance/build/gates.json
Example project · examples/universal-controller
One controller. Five consoles. A knob picks.
Rosetta is a game controller that speaks to a PS4, a PS5, an Xbox, a Switch, or anything over Bluetooth. Via took it from a page of notes to a routed 4-layer board in a controller's shape: 140 parts, a knob read as 1 number, and each hard part below. Turn the knob.
The drawing is the board: site/showcase/draw_face.py writes it from the design file and the routed board, in the board's own millimetres. The knob's readings are what workcenter sim measured for each position, from sim/mode_p1.cir to sim/mode_p5.cir.
A knob read as 1 number
Each of the 5 positions taps a resistor ladder. Between detents a pull-down reads near 0, and the firmware drops off the bus before it comes back as the new console's device. 6 decks prove no two positions meet at any corner.
The console's signature
A PS4, a PS5 and an Xbox talk only to a licensed pad. J2 is a USB host port for a licensed donor; the RP2040 runs that second port in software and relays the challenge.
Bluetooth, Classic and BLE
An ESP32 on the RP2040's UART, its antenna over the gap between the grips. The RP2040 holds its reset and boot pins, so 1 cable updates both.
Sticks that do not drift
Hall-effect sticks and triggers on a 12-bit, 8-input ADC that reads against the same rail that feeds them.
Triggers that push back
A motor in each trigger module, driven both ways by a DRV8833 for the PS5's trigger effects.
2 haptic engines
An LRA in each grip, each with a DRV2605L. The chip has 1 fixed I2C address, so each has a bus of its own.
Gyro aim
A 6-axis IMU for the Switch's and the PlayStation's motion controls.
A ring that names the console
5 RGB LEDs round the knob, their data lifted from 3.3 V to the cell's rail by a 74AHCT1G125 so they read it at full charge.
The board KiCad renders
KiCad's own 3D view of the routed board, front and back. The front carries what a hand touches; the back carries the electronics.
$ workcenter gates examples/universal-controller/v0.1 PASS erc 0 errors, 0 warnings [0.64s] PASS drc 0 errors, 5 warnings (0 unconnected, 0 parity) [1.37s] - warning: silk_edge_clearance: Silkscreen clipped by board edge - warning: silk_edge_clearance: Silkscreen clipped by board edge - warning: silk_edge_clearance: Silkscreen clipped by board edge - warning: silk_edge_clearance: Silkscreen clipped by board edge - warning: silk_edge_clearance: Silkscreen clipped by board edge PASS sim 6 decks, 370 corner runs, 0 failures [1.32s] PASS export 4/4 outputs, 24 gerber files [2.07s] GREEN with 5 warnings: rosetta report /home/user/via/examples/universal-controller/v0.1/build/gates.json
Built with Via
Boards, not demos.
Rosetta
A game controller with a knob that picks the console. 160 x 92 mm, 4 layers, 140 parts.
Agentcast
An HDMI stick with far-field microphones: plug it into a TV, talk, and an agent's work shows on the screen. 80 x 30 mm, 8 layers.
The fixture boards
The repository's tests: each passes every gate or fails 1 named gate on purpose.
Design as code
A Python file becomes a board.
design.py
d.add(
"R1",
"Device:R",
"10k",
R_0603,
sch_at=(101.6, 76.2),
pcb_at=(110.0, 106.0),
nets={"1": "VCC", "2": "OUT"},
)
d.add(
"R2",
"Device:R",
divider.kicad_sch

divider.kicad_pcb

A fixture board from the repository, tests/boards/pass-first-design: lines of its design file, then the schematic and the routed board that workcenter design design.py --route writes from the file.
The path
Design. Gate. Fix. Done.
-
01
Design as code
workcenter design design.py --routebuilds a schematic and a routed board from a Python file. The file names parts from the KiCad libraries. No GUI at any step. -
02
4 gates, in order
workcenter gates <project>runs ERC, DRC, SIM and EXPORT. It prints 1 line for each gate. The last line is the verdict. -
03
The next step
workcenter next <project>names the one step that is left, its command, and the fix for the first failure. -
04
Fabrication outputs
Gerbers, drill files, a BOM and a schematic PDF go to
build/fab/. They come from a board that passed every gate.
$ workcenter next tests/boards/fail-sim-corner NEXT The circuit is inside its bound as written and outside it at that corner. Change the circuit: a tighter part, another value, another topology. A bound, a rule or a requirement is never changed to pass: the design changes. RUN workcenter gates tests/boards/fail-sim-corner WHY SIM failed: diode_ref:vref=0.4215933 at 85 C, R1 +5 % (and 5 more corners) not in [0.5, 0.6] (the fixture's reference window, 0.55 V within 0.05 V) READ .claude/skills/simulate/SKILL.md, "Corners" DONE WHEN the last line starts with GREEN
A board is done when the last line says GREEN.
The exit code says the same as the line. A run with an absent engine is never GREEN.
- GREEN exit 0
- RED exit 1
- SKIPPED exit 2
-
01
ERC
KiCad's electrical rules, on the schematic.
-
02
DRC
KiCad's design rules, on the routed board.
--fabadds a fabrication house's limits. -
03
SIM
ngspice runs every deck. Each measured value is held to its bound, as written and at each corner.
-
04
EXPORT
Gerbers, drill files, the BOM and the schematic PDF, written to
build/fab/.
Corners
A bound holds at every corner, or it does not hold.
A circuit that works as written can leave its limits when a part is at the end of its tolerance, or the board is hot. The SIM gate runs those cases before the board is built.
$ workcenter sim tests/boards/fail-sim-corner FAIL sim 2 decks, 32 corner runs, 2 failures, 1 warnings [0.13s] - diode_ref:vref=0.4215933 at 85 C, R1 +5 % (and 5 more corners) not in [0.5, 0.6] (the fixture's reference window, 0.55 V within 0.05 V) - divider:vout=2.625 at R1 -5 %, R2 +5 % (and 9 more corners) not in [2.45, 2.55] (the fixture's output window, 2.5 V within 2 %) - warning: divider: no measure moves with temperature: nothing in the deck or its models carries one
Temperature
Every deck runs at each temperature the project names. The project gives each range with its source.
Tolerance
Each part runs at both ends of its tolerance and half-way to each. Every combination of the ends runs up to 64 runs. Above that, the gate runs a worst case for each bound and the Monte Carlo draws the project names.
Frequency plan
The gate holds every switching frequency and its harmonics against every resonance a deck measured.
Any agent
The knowledge is in the gates, not in the model.
The aim: a small model reaches the same working board as a large one. So each question an engineer asks is a gate that runs, and each next step is a command that names it.
A small model built a board
On 2026-11-11 a small model took a low-pass filter from a paragraph to 0 ERC errors, 0 DRC errors, 14 corner runs and 22 gerber files. It took 38 tool calls and met 8 traps. Each trap but 1 now stops with the line to write.
An engineer reviewed a board
The gates had passed a circuit that fails at 85 C. The engineer found it. That question is now the SIM gate's corners: the fixture above fails at 85 C.
What is still open
1 trap has no gate yet: nothing holds a deck typed by hand to the board's parts. The roadmap names it, and the devlog has each day.
How it works
3 engines, 4 gates, 1 verdict.
design.py
a Python file that names parts from the KiCad libraries
workcenter design
workcenter gates
SIM · EXPORT 4 gates, in order; the last line is the verdict
build/fab/
gerbers, drill files, a BOM, a schematic PDF
The engines, all headless
kicad-cli runs ERC, DRC and the export
The command line itself needs only Python's standard library. workcenter doctor reports each engine's status. A gate whose engine is absent is skipped by name, and that run is never GREEN.
Each procedure is a Markdown file, a skill: design a board, run the gates, simulate, route, price the parts, write the verification package. The contract lists what a project gets.
Run it
Open source under MIT. Run it on your machine.
On Ubuntu 24.04, install.sh installs KiCad 10, ngspice, Java 21 and FreeRouting. The fixture boards in the repository are the first projects to run.
$ git clone https://github.com/ThyFriendlyFox/via.git && cd via $ sudo ./install.sh $ workcenter doctor $ workcenter gates tests/boards/pass-rc-divider
Where it stands. Via is early and has no release yet. The gates, the corners, the frequency plan and workcenter next run on the fixture boards in every check of the repository. The cloud setup script and the Docker build have not run yet. Chip design, from RTL to a mask layout, is in progress on a branch. The status page has the rest.
Take a board to GREEN.
Read the code, run the gates on a fixture board, or open your own project.