export: breadboard SVG visualiser — Phase 2.6 - #28
Merged
Conversation
Render any wirebench design as it would sit on a standard solderless breadboard. SVG output sibling to the assembly-guide markdown: the two artefacts share `assembly_guide.placement.place()` so the positions named in the assembly guide's step-by-step instructions match the components' positions in the rendered SVG (acceptance criterion #8 of `.plans/phase-2.6-spec.md`). Subpackage `src/framework/export/breadboard/`: - colors.py — locked palette (red reserved for + rail, black for −, 4-tone analog & digital signal cycles). Cycles are ordinal, not hash-based: adjacent nets on the board get adjacent colours so physically-close jumpers can't collide. - svg.py — coordinate system, inline-style SVG primitives, dynamic canvas width (grows for designs over 63 columns), dynamic viewBox y-offset for designs needing many detour bands. - placement.py — refusal of SMD / Board / multi-Board designs; wraps `assembly_guide.placement.place()`. - routing.py — net walker producing coloured Jumper records. - renderer.py — interval-coloured detour-band allocation, double- detour for cross-bank jumpers (pivots at clear columns so the wire never traverses a chip body's opposite-bank tie strip), pin-name labels on chip bodies, end-cap discs at every plug-in point, per-endpoint row assignment so chained nets land in distinct holes on a shared tie strip. Z-order: surface → jumpers → components, so any jumper section that would pass behind a chip is hidden by the opaque body — visually representing how a real jumper physically routes around the chip. Spec deferrals (`.plans/phase-2.6-spec.md` §12): full-size 63-column board (assembly_guide places parts past col 30, half-size impossible in practice); multi-board composite SVG (multi-Board designs raise `BreadboardIncompatibleError`, matching assembly_guide). Other changes: - `assembly_guide.placement._place_2lead_part` now lands passive leads on row D (near the trough) instead of row A. Leaves rows A–C clear for jumpers terminating on the same tie strip; the assembly-guide goldens stay byte-identical because they use the `any of <pos>A–<pos>E` tie-strip phrasing. - `scripts/render_demo_docs.py` adds `breadboard` to its format loop and emits an SVG refusal stub for designs the visualiser refuses. - 26 new tests under `tests/framework/export/breadboard/` (placement, routing, renderer, byte-identical goldens for 5 demos). - Main README and 12 per-demo READMEs reference the new format. - Every demo's `docs/` folder gains a `<Design>.breadboard.svg`.
This was referenced May 19, 2026
There was a problem hiding this comment.
Pull request overview
Adds a new breadboard SVG exporter that renders wirebench circuits onto a solderless breadboard layout, sharing assembly-guide placement so visual and textual build instructions align.
Changes:
- Added breadboard exporter modules for palette, placement, routing, SVG primitives, and rendering.
- Updated demo-doc generation and README links to include
.breadboard.svgoutputs or refusal stubs. - Added breadboard placement/routing/renderer/golden regression tests.
Reviewed changes
Copilot reviewed 36 out of 67 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/framework/export/breadboard/__init__.py |
Registers the new breadboard exporter adapter. |
src/framework/export/breadboard/colors.py |
Defines locked SVG colors and signal palettes. |
src/framework/export/breadboard/placement.py |
Wraps shared assembly-guide placement and refusal checks. |
src/framework/export/breadboard/renderer.py |
Builds the complete breadboard SVG document. |
src/framework/export/breadboard/routing.py |
Converts nets into routed jumpers with colors and rail handling. |
src/framework/export/breadboard/svg.py |
Provides SVG geometry and primitive string emitters. |
src/framework/export/assembly_guide/placement.py |
Moves 2-lead parts to row D for clearer jumper routing. |
scripts/render_demo_docs.py |
Generates breadboard SVG docs and format-specific refusal stubs. |
tests/framework/export/breadboard/test_breadboard_golden.py |
Adds golden regression coverage for breadboard SVG output. |
tests/framework/export/breadboard/test_placement.py |
Tests placement sharing and refusal behavior. |
tests/framework/export/breadboard/test_renderer.py |
Tests XML validity, structure, determinism, and refusals. |
tests/framework/export/breadboard/test_routing.py |
Tests rail colors and signal palette behavior. |
tests/framework/export/breadboard/__init__.py |
Adds the breadboard test package. |
README.md |
Documents the new export type. |
| Demo README files | Add links to generated breadboard SVG artifacts. |
Demo .breadboard.svg files |
Add generated breadboard renderings or refusal stubs. |
demos/penfold_one_second_timer/docs/* |
Adds generated docs artifacts for OneSecondTimer. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+308
to
+314
| rail_marker_top = '+TOP' | ||
| rail_marker_bot = '+BOT' | ||
| elif net_polarity is False: | ||
| color = RAIL_MINUS_JUMPER | ||
| kind = JumperKind.RAIL_MINUS | ||
| rail_marker_top = '-TOP' | ||
| rail_marker_bot = '-BOT' |
Comment on lines
+10
to
+15
| Refusal (acceptance criterion 7 in `.plans/phase-2.6-spec.md`): | ||
| - SMD parts (`is_breadboard_compatible == False`) | ||
| - Top-level `Board` (one populated PCB) | ||
| - Multi-board designs (nested `Board`s — v1 defers; spec §12) | ||
| - Designs whose placement exceeds 63 positions | ||
|
|
- Rail jumpers always route to the TOP rails. The shared assembly guide instructs builders to wire only the top `+`/`-` rails to the supply; standard breadboards keep top and bottom rail strips electrically isolated, so a chip pin on the bot bank routing to `+BOT`/`-BOT` would show an unpowered connection. Bot-bank pins now cross the trough via the standard cross-bank double-detour, terminating at the powered top rail. Same-column rail verticals (DIP VCC pin at col X to +TOP at the same col X) get hidden in the middle by the chip body's z-order, visually representing the wire arching over the package. - Refusal docstring updated to describe the actual capacity limit: designs that exceed `svg.MAX_POSITIONS` (four daisy-chained full-size breadboards) are refused. Designs that just exceed one board's 63 columns (Dice → 106, DoorbellProtector → 92) are rendered on a canvas wide enough to fit them, with a faint column-break marker every 63 columns. Breadboard goldens for WaterAlarm, Dice, DoorbellProtector refreshed; demo SVGs regenerated.
This was referenced May 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Renders any wirebench design as a self-contained SVG of how it sits on a standard solderless breadboard. Sibling to the existing
assembly_guidemarkdown — the two artefacts shareassembly_guide.placement.place(), so the positions narrated by the step-by-step build instructions match the visible positions in the SVG (acceptance criterion #8 of.plans/phase-2.6-spec.md).src/framework/export/breadboard/: colors / svg / placement / routing / renderer modules.a_1,VSS,q_3) on every chip pin in small white text. DIP pin numbering is the universal convention; pin names are what the design'swire()calls reference.Spec deferrals (locked in
.plans/phase-2.6-spec.md§12):BreadboardIncompatibleError, matching assembly_guide).Test plan
tests/framework/export/breadboard/(placement, routing, renderer, byte-identical goldens for HelloLED / WaterAlarm / Dice / DigitalThermometer / DoorbellProtector).any of <pos>A–<pos>Ephrasing so the row letter never appears literally).uv run pytest— 4360 passed.uv run mypy src/ demos/clean.docs/folder gains a<Design>.breadboard.svg— visual inspection in a browser to confirm the rendering reads correctly. I have not opened the SVGs in a browser; the structural assertions are in tests but a human eyeball pass would catch any aesthetic issues my tests miss.