framework: passive + diode terminals mandatory - #32
Conversation
Continuation of the LED.cathode regression fix (#29). Resistor, Capacitor, Inductor, and every Diode subclass had their terminals declared `mandatory=False` with the same "structurally inert under graph evaluation" justification — letting demos declare BOM-only timing / decoupling / pull-up parts without wiring them, then ship breadboard SVGs and netlists where those parts sat orphaned. A real resistor / capacitor / inductor / diode with a dangling terminal does nothing. Marking the ports mandatory closes the silent regression at the framework layer. - `src/components/passives/{resistor,capacitor,inductor}.py` and every diode subclass under `src/components/diodes/` now declare their terminals `mandatory=True`. - `tests/components/test_{resistor,capacitor,inductor}.py`: regression tests pin the new contract per component. - `tests/components/test_diode_mandatory_terminals.py` (new): parametrized over D1N4001 / D1N4007 / D1N4148 / D1N4728A / D1N4733A / D1N4742A / D1N5817. - Six demos (`backup_power`, `bldc_motor`, `dice`, `doorbell_protector`, `fan_cooling`, `isolated_rs232`) had declared-but-unwired passives — each now wires them either to their datasheet-correct net (e.g. DoorbellProtector's flyback diode anode joins the relay coil_minus net) or as an isolated 0-Ω passthrough with `dynamically_driven=True` where the part is genuinely BOM-only and the voltage-only simulator can't honour its behaviour. Comments at each call site explain which kind. - Test fixtures across CLI, refdes, format-roundtrip, kicad import, and per-format renderer tests updated so they still pin down the specific error each one exists to catch without tripping the new mandatory-pin check first. - All demo docs regenerated; goldens refreshed. After this commit the breadboard SVGs show every BOM part connected to something — no more orphaned caps and resistors floating without jumpers.
There was a problem hiding this comment.
Pull request overview
This PR continues the framework’s “physical-fidelity” enforcement by making passive-component terminals and all discrete diode terminals mandatory, preventing circuits from constructing with BOM-listed parts left electrically floating. It then updates affected demos and fixtures to explicitly wire previously-orphaned passives/diodes (either to a real net or as isolated/pass-through wiring with dynamically_driven=True where the simulator can’t model the analog behavior), and adds regression tests to lock the behavior in.
Changes:
- Enforce
mandatory=TrueforResistor.t1/t2,Capacitor.t1/t2,Inductor.t1/t2, and all discrete diodeanode/cathodeports. - Update multiple demos/exports/goldens to ensure BOM-only parts are no longer left dangling (pass-through or isolated wiring patterns).
- Add new regression tests to ensure floating passives/diodes are refused at
Circuitconstruction time.
Reviewed changes
Copilot reviewed 102 out of 115 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/components/passives/resistor.py | Make resistor terminals mandatory to prevent dangling BOM parts. |
| src/components/passives/capacitor.py | Make capacitor terminals mandatory; update rationale comment. |
| src/components/passives/inductor.py | Make inductor terminals mandatory; align behavior with resistor/capacitor. |
| src/components/diodes/d1n4001.py | Make diode terminals mandatory. |
| src/components/diodes/d1n4007.py | Make diode terminals mandatory. |
| src/components/diodes/d1n4148.py | Make diode terminals mandatory. |
| src/components/diodes/d1n4728a.py | Make diode terminals mandatory. |
| src/components/diodes/d1n4733a.py | Make diode terminals mandatory. |
| src/components/diodes/d1n4742a.py | Make diode terminals mandatory. |
| src/components/diodes/d1n5817.py | Make diode terminals mandatory. |
| tests/components/test_resistor.py | Add mandatory-terminal + dangling-resistor construction regression tests. |
| tests/components/test_capacitor.py | Add mandatory-terminal + dangling-capacitor construction regression tests. |
| tests/components/test_inductor.py | Update existing expectations and add mandatory/dangling construction tests. |
| tests/components/test_diode_mandatory_terminals.py | New parametrized tests covering all discrete diode subclasses. |
| tests/framework/test_refdes.py | Update refdes-collision tests to satisfy new mandatory-terminal validation. |
| tests/framework/test_format_roundtrip.py | Update hand-edited-format fixture to include wiring that satisfies mandatory checks. |
| tests/framework/export/mermaid/test_mermaid_components.py | Update test circuit to include wiring/rail so mandatory resistor terminals are connected. |
| tests/framework/export/dot/test_dot_components.py | Update test circuit to include wiring/rail so mandatory resistor terminals are connected. |
| tests/framework/export/bom/test_bom_components.py | Update header-column test to satisfy mandatory resistor terminal requirements. |
| tests/framework/import_kicad/fixtures/regulator_with_named_rail.net | Fixture tweak to reflect capacitor participation in the VIN net. |
| tests/cli/fixtures/floating_net.py | Adjust fixture to pass mandatory-pin checks while still triggering FloatingNetError. |
| demos/dice/dice.py | Wire previously BOM-only timing/pull-up passives onto a driven rail net to satisfy mandatory pins. |
| demos/dice/docs/Dice.* | Regenerated outputs reflecting new wiring/nets. |
| demos/doorbell_protector/doorbell_protector.py | Wire flyback diode anode + relay coil-minus net; wire BOM-only timing/decoupling passives onto VCC net. |
| demos/doorbell_protector/docs/DoorbellProtector.* | Regenerated outputs reflecting rewiring and updated net structure. |
| demos/fan_cooling/fan_cooling.py | Wire BOM-only zener/resistors as isolated pass-through nets with dynamically_driven=True. |
| demos/fan_cooling/docs/FanCoolingBoard.* | Regenerated outputs reflecting isolated nets for BOM-only parts. |
| demos/fan_cooling/docs/CooledSystem.* | Regenerated outputs reflecting isolated nets in the composed system. |
| demos/bldc_motor/bldc_motor.py | Wire BOM-only bootstrap/decoupling caps + reset pull-up as isolated pass-through nets (dynamically_driven=True). |
| demos/bldc_motor/docs/BLDCSystem.* | Regenerated outputs reflecting new isolated nets for BOM-only passives. |
| demos/bldc_motor/docs/BLDCControllerBoard.* | Regenerated outputs reflecting new isolated nets for BOM-only passives. |
| demos/isolated_rs232/isolated_rs232.py | Wire BOM-only charge-pump/decoupling caps as isolated pass-through nets (dynamically_driven=True). |
| demos/isolated_rs232/docs/IsolatedRS232Board.* | Regenerated outputs reflecting new isolated capacitor nets. |
| demos/isolated_rs232/docs/IsolatedRS232Link.* | Regenerated outputs reflecting new isolated capacitor nets. |
| demos/backup_power/backup_power.py | Add wiring to connect all BOM-only passives onto a driven rail net to satisfy mandatory pins. |
| demos/backup_power/docs/BackupPower.* | Regenerated outputs reflecting new wiring/nets. |
| tests/golden/assembly_guide/dice.md | Updated golden assembly guide reflecting additional jumper instructions. |
| tests/golden/assembly_guide/doorbell_protector.md | Updated golden assembly guide reflecting additional jumper instructions. |
| demos/dice/docs/Dice.breadboard.svg | Updated breadboard SVG reflecting added rail jumpers. |
| tests/golden/breadboard/dice.breadboard.svg | Updated golden breadboard SVG reflecting added rail jumpers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
Continuation of #31 (formerly #29). The same physical-fidelity violation that left
LED.cathodefloating was also present on every other passive component:Resistor,Capacitor,Inductor, and every diode subclass had their terminals declaredmandatory=Falsewith the "structurally inert under graph evaluation" shortcut. Real components with dangling terminals do nothing — they sit on the BOM but contribute nothing to the circuit.Resistor.t1/t2,Capacitor.t1/t2,Inductor.t1/t2, and every Diode subclass'sanode/cathodenowmandatory=True.backup_power,bldc_motor,dice,doorbell_protector,fan_cooling,isolated_rs232) declared BOM-only passives that were never wired. Each now wires them either to their datasheet-correct net (e.g. DoorbellProtector's flyback diode anode joining the relay's coil_minus) or as an isolated 0-Ω passthrough withdynamically_driven=Truewhere the part is genuinely BOM-only and the voltage-only simulator can't model its behaviour. Comments at each call site explain which.tests/components/test_resistor.py,test_capacitor.py,test_inductor.py—test_terminals_are_mandatory+test_floating_*_refused_at_circuit_constructiontests/components/test_diode_mandatory_terminals.py(new file) — parametrized across all 7 diode subclassesAfter this PR, the breadboard SVGs from #28 show every BOM-listed part connected to something — no more orphaned caps and resistors floating without jumpers.
Test plan
uv run pytest— 4382 passed (20 new regression tests).uv run mypy src/ demos/clean.Supersedes the auto-closed #30 (the base branch was deleted when #29/#31 merged).