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
2 runs on fixture boards from the repository, as the terminal printed them. The second board has a clearance error on purpose.

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.

Rosetta v0.1 from the front: its outline, copper, buttons, the mode knob and its ring ESP32 · back · antenna RP2040 · back L1 R1 Y A X B SHARE OPTIONS CAPTURE HOME ROSETTA v0.1 PS4 PS5 XBOX SWITCH BT

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.

SW1 · R40–R46

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.

J2 · U13 · GPIO16/17

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.

U3

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.

U4 · J5 · J6

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.

U6 · J3 · J4

Triggers that push back

A motor in each trigger module, driven both ways by a DRV8833 for the PS5's trigger effects.

U9 · U10

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.

U5

Gyro aim

A 6-axis IMU for the Switch's and the PlayStation's motion controls.

D2–D6 · U14

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.

Rosetta's front in 3D: the D-pad, the face buttons, the knob with its ring of LEDs, the 2 USB-C ports Rosetta's back in 3D: the RP2040, the ESP32 module over the notch, the ADC, the drivers and the power parts

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.

Every layer of Rosetta on its own, from workcenter render
$ 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
The gates on the example, as the terminal printed them. The 5 warnings are the silkscreen outlines of the 2 USB-C receptacles and the ESP32 module, which hang over the board's edge on purpose. Its README has the spec, the pin plan, every part and why, and what is still open; its design file is the board.

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",
A part is 1 call in a Python file.

divider.kicad_sch

The schematic: R1 and R2 between VCC and GND, C1 across R2, and a 3-pin header
1 command writes the schematic

divider.kicad_pcb

The routed board in 3D: a 3-pin header, R1, R2 and C1
and routes the board. No GUI at any step.

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.

  1. 01

    Design as code

    workcenter design design.py --route builds a schematic and a routed board from a Python file. The file names parts from the KiCad libraries. No GUI at any step.

  2. 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.

  3. 03

    The next step

    workcenter next <project> names the one step that is left, its command, and the fix for the first failure.

  4. 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
The next step for a fixture board whose circuit leaves its bound at a corner, after a run of the gates. A person and an agent of any size read the same 5 lines.

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
  1. 01

    ERC

    KiCad's electrical rules, on the schematic.

  2. 02

    DRC

    KiCad's design rules, on the routed board. --fab adds a fabrication house's limits.

  3. 03

    SIM

    ngspice runs every deck. Each measured value is held to its bound, as written and at each corner.

  4. 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
A fixture board that is inside its bounds as written. It fails at 85 C, and with 2 resistors at opposite ends of 5 %.

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
KiCad project a schematic and a routed board
workcenter gates
ERC · DRC
SIM · EXPORT
4 gates, in order; the last line is the verdict
GREEN
build/fab/ gerbers, drill files, a BOM, a schematic PDF

The engines, all headless

KiCad 10 kicad-cli runs ERC, DRC and the export
ngspice runs every deck at every corner
FreeRouting routes the board

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.