32 KiB
Audio Coverage Roadmap
Closing the gap between the component library and what the guitar, amplifier, pedal and hi-fi communities actually build.
Status: proposed
Scope: diylc-library (components.passive, .tube, .electromechanical, .guitar,
.connectivity), one bug fix in diylc-core/serialization
Companion: the sibling roadmaps in this directory, which apply the same method to other packages
1. Goal
The audio side of the library has never had a systematic coverage pass, and it is the side the
sample corpus is made of. The headline number: three components in the Tubes category,
against 50 corpus projects that place a tube socket.
This document inventories what ships, measures what people hand-draw because it does not exist, and sorts the additions into three tiers by evidence of demand rather than by appetite.
2. Decisions that govern this roadmap
| # | Decision | Choice | Rationale |
|---|---|---|---|
| D1 | What counts as a gap | A part people are drawing by hand today. Measured from label text in diylc-regression-data/input, not from catalogue browsing. |
Keeps the roadmap honest. Several things that looked like gaps from the palette listing (pedal enclosures, EMG pickups, Filtertrons, Tele single coils, the switch-type matrix) turned out to ship already; several things that look exotic turn out to be in 25 projects. |
| D2 | Evidence vs. judgement | Tiers 1 and 2 are measured. Tier 3 is reasoned and labelled as such. | The corpus is overwhelmingly pedal and amp. It cannot speak to hi-fi demand at all, and saying so is better than inventing a number. See §4.3. |
| D3 | New class vs. new enum constant | Same footprint family → enum constant; different structure → new class. | Already the established paradigm, and diylc/CLAUDE.md states the preference directly: a Version property on one class beats a class per revision. |
| D4 | Where iron lives | One Transformer class with a Type property (power / output / choke), in components.passive. Not three classes. |
A bell-end power transformer, an output transformer and a choke share an outline, a mounting footprint and a tap-strip; they differ in tap count and label. AudioTransformer already sits in passive and already extends AbstractMultiPartComponent, so the neighbour is right there. |
| D5 | Backward compatibility | Append-only enums, unchanged defaults, unchanged control-point counts for existing variants. | .diy files are a public contract. Only §6.1 touches serialization, and it restores compatibility rather than risking it. |
| D6 | Pre-set data before components | Where a variant or a datasheet row closes a gap, it ships before any new class. | Both are configuration rather than Java, and both are already covered by tests. §4.2 shows which of the most-placed parts have neither. |
| D7 | Composable parts are not components | If a part can be assembled from existing components and saved as a building block, it ships as a factory building block, not a new class. | A new class has to be drawn, transformed, tested, BOM-policed and supported forever, and it crowds the toolbox. A building block is data. The test is whether the assembly is electrically and visually honest without a new primitive: a push-pull pot is a pot plus a DPDT switch and passes, a chassis transformer is not a rectangle plus lugs and fails. See §5.4. |
Explicitly out of scope
- The dormant classes outside the packages in scope above. They were parked deliberately
("comment out future stuff",
27eb2599) and belong to the roadmaps for their own packages. - Any electrical simulation: load-line plotting is in scope only because the component already exists (§6.2), impedance and frequency-response maths are not.
- Speaker cabinet and enclosure mechanical drawing beyond what
ChassisPanelalready does. - Bumping the project version, packaging, and the deployment build.
3. Current coverage
169 components register at runtime. 208 classes carry a @ComponentDescriptor, but 39 have it
commented out and are invisible to the annotation scan.
| Category | Active | Category | Active | |
|---|---|---|---|---|
| Schematic Symbols | 30 | Semiconductors | 12 | |
| Electro-Mechanical | 28 | Guitar | 12 | |
| Passive | 17 | Boards | 12 | |
| Connectivity | 16 | Misc | 10 | |
| Displays & Outputs | 13 | Controllers | 10 | |
| Shapes | 4 | Tubes | 3 | |
| SMD | 2 |
What is genuinely strong and should not be touched: the switch matrix (ToggleSwitchType runs SPST
through 5PDT with both on/on/on variants; sealed and open rotaries; Lever, LP, Freeway 3x3,
Schaller Megaswitch in six types, S1), the pickup models (SingleCoilPickup does Strat and Tele
bodies with rod/rail/dual-rail poles, HumbuckerPickup does PAF/Mini/Filtertron with an EMG-style
variant), and resistor coverage (22 variants from 1/4 W carbon comp to 100 W ceramic).
3.1 Dormant classes relevant to audio
Five of the 39 fall inside this roadmap's scope. Two are finished code behind a commented annotation; three are not.
| Class | State | To revive |
|---|---|---|
passive/AirCoreInductor |
Body live, 4 commented descriptor lines | Uncomment + restore 4 imports |
passive/BoxTrimmer |
Body live, 9 commented lines, BoxTrimmerTransformer exists |
Uncomment + restore imports |
misc/Loadline |
Body live, 4 commented lines, extends AbstractBoard |
Uncomment + restore imports |
passive/AxialAirCoreInductor |
Stub — 69 of 70 lines commented, and it extended AbstractFilmCapacitor |
Rewrite, or delete the file |
smd/SOT23Package |
Stub — 427 of 449 lines commented | Out of scope here |
4. Evidence
4.1 Method
diylc-regression-data/input holds 709 .diy projects (cloud 379, v1 284, user-files 34,
diy-fever 12). The 284 v1-format files carry no class names, so component tallies reflect the 425
modern files; label-text searches cover all 709.
Searching raw file text does not work. Image components store base64, and three-letter
queries hit it: XLR returned 17 "files" of which zero were real. Every figure below comes from
extracting <text>, <title>, <name>, <value> and <description> element contents first
(118,619 strings) and searching those.
4.2 Two coverage mechanisms, and which parts have neither
Pre-set data reaches a component two ways, and they are easy to confuse.
Variants (variants.xml, 176 of them) are saved property sets, authored per class, listed under
Apply Variant.
Datasheets are parametrized part catalogues: a <ClassName>.datasheet CSV on the classpath, one
row per real orderable part, listed under Apply Model. Five capacitor classes have one, and
between them they carry 3,539 rows covering 34 models — each row a part number, a voltage, a
capacitance, exact body dimensions in mm, and body/border/label/marker colours:
| Class | Rows | Models |
|---|---|---|
RadialFilmCapacitor |
1,523 | 14 (WIMA MKS-4/MKP-4/FKP/FKS, Orange Drop 225P/715P/716P, Epcos MKT) |
RadialElectrolytic |
833 | 7 (Panasonic FC/FM/NHG, Nichicon FW/FG/KZ, Elna Silmic II) |
AxialElectrolyticCapacitor |
455 | 4 (F&T Typ A, Sprague TVA Atom, Illinois TTA, JJ ANH) |
RadialMicaCapacitor |
433 | 7 (Cornell Dubilier CD10 through CD42, CDV19/CDV30) |
AxialFilmCapacitor |
293 | 2 (Solen Fast, Mallory 150) |
Capacitors are therefore already covered, at a fidelity no variant could reach — exact
dimensions per value and voltage, not a size estimate. Any roadmap item proposing to "add
Mallory 150 / Orange Drop / WIMA variants" is proposing something that shipped long ago; an
earlier draft of this document made exactly that mistake by counting only variants.xml.
The gap is the classes with neither mechanism:
| Component | Placements | Variants | Datasheet |
|---|---|---|---|
Resistor |
2,388 | 22 | — |
RadialFilmCapacitor |
754 | 0 | 1,523 rows |
RadialElectrolytic |
602 | 0 | 833 rows |
TransistorTO92 |
363 | 0 | none |
DiodePlastic |
324 | 0 | none |
RadialCeramicDiskCapacitor |
283 | 0 | none |
PotentiometerPanel |
195 | 2 | none |
RadialCeramicDiskCapacitor is the odd one out among the capacitors — the only one of the six with
no datasheet and no variants, despite 283 placements. TantalumCapacitor and MultiSectionCapacitor
are likewise uncovered.
The opportunity is that the datasheet mechanism is generic and nothing outside capacitors uses
it. IDatasheetSupport is a one-method interface in diylc-core implemented by exactly those five
classes; see §5.3 for what extending it costs.
4.3 Hand-written labels for parts that do not exist
Out of 709 projects:
| Part | Files | Mentions | Status in library |
|---|---|---|---|
Output transformer (OT, "output transformer") |
27 / 10 | 84 / 10 | absent |
Power transformer (PT, "power transformer") |
15 / 12 | 37 / 25 | absent |
| Speaker | 25 | 45 | absent, not even a symbol |
| Choke | 21 | 38 | absent |
| 3PDT / stomp / footswitch | 12 / 6 / 5 | 12 / 8 / 8 | electrically yes, body no |
| Push-pull pot | 12 | 15 | absent |
| Standby | 10 | 31 | covered by toggle |
| LDR | 10 | 15 | absent as a discrete part |
| Reverb tank | 6 | — | absent |
| 1590-series enclosure | 5 | 5 | covered — ChassisPanel variants |
Corroborating signals: 15 files assemble a transformer by hand from TransformerCoil pieces, and
88 fall back to shapes.Rectangle.
Zero hits, and therefore Tier 3 at best: xlr, binding post, piezo, jazzmaster,
lipstick, varitone, concentric, vactrol. The corpus is pedals and amps; its silence on
hi-fi connectors is a property of the sample, not evidence of no demand.
4.4 Why EyeletBoard is used 8 times and discrete turrets 40
EyeletBoard.draw() paints a full grid of eyelets with fillOval. They are decorative: no control
points, no node names, no conductive areas, nothing in the netlist. So builders ignore it and place
individual Turret components, which do carry a node name, on a BlankBoard.
This kills the obvious-looking fix. A Type = Turret flag on EyeletBoard would inherit the same
flaw, and a full grid is wrong for turret boards anyway — they are drilled per design. The real fix
is selectable, connected positions on both boards, which is a composite-building-blocks.md-scale
design question. Tier 3, §8.6.
5. Shared mechanics
5.1 Component discovery
Classpath annotation scan in ComponentProcessor.getComponentTypes(). Any class carrying
@ComponentDescriptor is picked up automatically — no manifest, no registry. Uncommenting an
annotation is the entire registration step.
5.2 Serialization safety
Per diylc/CLAUDE.md: never rename or delete a serialized field or change its type; fields added
to an existing class deserialize as null in old files, so default in the getter, not the
constructor; serialVersionUID stays 1L.
Worth knowing before touching ProjectFileManager: its anonymous MapperWrapper overrides
shouldSerializeMember and drops elements that map to no field, so unknown fields in old files
are already tolerated. It does not override realClass, so an unknown class throws. That
asymmetry is the whole of §6.1.
5.3 Variant and datasheet mechanics
Variants live in diylc-core/src/main/resources/import-defaults/variants.xml, an
XStream-serialized Map<String, List<Template>> keyed by fully-qualified class name.
FactoryVariantsTest (diylc-library/src/test/java/org/diylc/components/) loads it, resolves every
key, and asserts every template names a live property and applies cleanly — so a stale variant fails
the build rather than failing silently at runtime.
Datasheets are cheaper than they look. DatasheetService.loadDatasheet(Class) resolves
/datasheets/<SimpleClassName>.datasheet off the classpath and splits each line on \s*,\s*.
Adding one to a component is therefore two steps and no registration:
- implement
IDatasheetSupport.applyModel(String[] model)on the component, mapping columns onto setters —RadialFilmCapacitor.applyModelis 20 lines and the model to copy; - drop
<ClassName>.datasheetintodiylc-library/src/main/resources/datasheets/.
ComponentPopupMenu.updateApplyModelMenu then builds the Apply Model submenu from
ComponentType.getDatasheet() automatically, and Presenter.applyModelToSelection routes the
chosen row back to applyModel.
Two cautions, both learned from §6.5:
- The column format is positional and unforgiving.
CapacitorDatasheetServicereadsmodel[1].split(" ")[0..1]for voltage andmodel[2].split(" ")[0..1]for value, so a cell must be"<number> <unit>"with exactly one space.4.7uFthrowsArrayIndexOutOfBoundsException;DatasheetRowsTestcatches it. DatasheetServicereads withnew InputStreamReader(in)and no charset, so files are decoded with the platform default. Keep datasheet files ASCII — writeuF, neverµF. Per JEP 400 the default is UTF-8 on the supported JVM, so a latin-1 byte becomes U+FFFD rather than failing loudly.
There is also an unused path worth knowing about: DatasheetService.lookup /
CapacitorDatasheetService.lookup match a datasheet row by approximate value within a tolerance,
for auto-sizing a component from its value. It has no production caller — only
CapacitorDimensionServiceTests — so it is latent infrastructure, not live behaviour.
5.4 Factory building block mechanics
Building blocks are named groups of components, saved from a selection and re-placed as a unit.
BuildingBlockManager.importDefaultBlocks() ships a default set in
diylc-core/src/main/resources/import-defaults/blocks.xml, stored as a List<BuildingBlock>, each
with its name, components (class="list") and createdOn, using the diylc package alias
(aliasPackage("diylc", "org.diylc.components")) for the components. It is merged into the user's
own blocks (the unchanged Map<String, List<IDIYComponent<?>>> under buildingBlocks) by name.
Three ship today, all createdOn 2026-01-01: TO-92 w/ 3 Leads, TO-92 w/ 2 Leads, Title Block.
Authoring one is a UI job, not a code job: place the components, select them, Save as Building
Block, then lift the entry out of the user config into blocks.xml. Running with
-Dorg.diylc.WriteStaticBlocks=true writes the saved block in the bundled shape, dated now. Per D7
this is the default route for anything composable.
The import runs on every start and mirrors VariantManager.importDefaultVariants():
- A block is imported when its
createdOnis after thedefaultBlocksImportDatewatermark (LocalDateTime.MINwhen absent) and the user has no block of that name. A same-named user block is never overwritten or duplicated. - A withdrawn block is a stub with only
nameanddeletedOn; it is removed from the user's blocks and recently used list whendeletedOnis after the watermark. - The watermark becomes the latest
createdOn/deletedOnin the file, never the clock, and never moves backwards, so the order in which versions are launched does not matter. defaultBlocksImportedis still written so that versions before the watermark do not import.
Because configs from those versions have no watermark, the first start of this version offers every factory block once, and a user who deleted one gets it back once.
5.5 Tests
JUnit 4 under diylc-library/src/test/java/org/diylc/, with components/passive/,
components/misc/ and netlist/ already populated. Switching and netlist behaviour is unit-tested
(AbstractSwitchTests and its fifteen subclasses); component rendering is not — that is the
regression suite's job. Any new class with switching belongs in netlist/.
5.6 Build and verify
cd diylc
mvn -pl diylc-library -am test
Baseline at the time of writing, on controllers at 2a08fbe5: diylc-core 148 tests,
diylc-library 331 tests, BUILD SUCCESS, project version 6.6.0.
Everything in Tiers 1 and 2 affects rendering or serialization, so each item also wants a
regression run (.claude/skills/regression-test/SKILL.md) before it is called done. §6.1
specifically cannot be verified any other way.
5.7 Changelog
User-visible additions get a <Change> entry in diylc-core/src/main/resources/update.xml, in the
block for the unreleased version. NEW_FEATURE for components and variants, BUG_FIX for §6.1.
6. Tier 1 — no new components
Enum constants, catalogue data, building blocks and uncommenting. Cheapest fidelity per hour in the roadmap, and where D6 and D7 send most of the work.
§6.1 is a correctness fix and should go first regardless of the rest.
6.1 Restore the ElectrolyticCanCapacitor alias — bug, not a gap
diylc-regression-data/input/cloud/diy/282.diy contains three
<org.diylc.components.passive.ElectrolyticCanCapacitor> elements. That class exists nowhere in
the tree, in any module, or in the vendored jars, and ProjectFileManager.configure() registers no
alias for it. Per §5.2 the file cannot deserialize. It came from the cloud corpus, so real user
files are affected.
This is the exact failure diylc/CLAUDE.md forbids: a shipped component class was renamed or
removed without an XStream alias.
The old element carries name, alpha, value (four nulls), orientation, controlPoints
(three), bodyColor, baseColor, borderColor, labelColor, display, sections (_1) and
diameter (1.0 in). MultiSectionCapacitor is the successor and matches all of it except
sections, which it does not have — it derives section count from value.length.
- In
ProjectFileManager.configure(), beside the existing aliases:xStream.alias("org.diylc.components.passive.ElectrolyticCanCapacitor", MultiSectionCapacitor.class); - Confirm
ClassAliasingMapperwins over class lookup for a dotted element name. If it does not, fall back to restoring a@DeprecatedElectrolyticCanCapacitorthat extendsMultiSectionCapacitorand carries no descriptor — invisible in the palette, resolvable by XStream. sectionsis dropped by the §5.2 mapper, so the file loads but renders four sections where it said one. Decide whether that drift is acceptable or whether it needs a converter that mapssectionsontovaluelength. Recommend accepting it — the old field disagreed with its ownvaluearray, so there is no correct answer to preserve.- Verify by loading
282.diy. A regression run is the only real check.
~2 h. Gate the rest of Tier 1 behind this.
6.2 Revive the three live dormant classes
Per §3.1, AirCoreInductor, BoxTrimmer and Loadline are finished code behind a commented
annotation. AirCoreInductor needs ComponentDescriptor, CreationMethod, IDIYComponent and
SimpleComponentTransformer (the last lives in diylc-core) re-imported; the other two need their
own commented imports restored the same way.
Each one: uncomment, restore imports, compile, check it appears in the palette and draws. Loadline
is the most interesting of the three — tube and transistor load-line plotting is a design feature
no other item here provides, and it is already written.
Then decide AxialAirCoreInductor's fate. It is a 70-line stub that extended AbstractFilmCapacitor,
which is wrong for an inductor. Recommend deleting the file and treating axial air-core as a
variant of a revived AirCoreInductor if it is wanted at all; a commented-out stub inheriting from
the wrong base is worse than nothing.
~3 h for three, plus a decision on the fourth.
6.3 Tube socket bases
TubeSocket.Base covers B9A, OCTAL, B7G and B12C. Jumbo4PinTubeSocket is a separate class scoped
by its own description to 211/805/845. Missing bases the amp community uses constantly:
| Base | Pins | Tubes |
|---|---|---|
| UX4 | 4 | 300B, 2A3, 45, 5U4G |
| UX5 | 5 | type 80, 807 |
| B8G Loctal | 8 | 7N7, 7F7 |
| B9D Magnoval | 9 | EL509, PL519 |
UX4 is the valuable one and the jumbo socket does not substitute — wrong diameter, wrong spacing.
The class is clean to extend: updateControlPoints() switches on base for pin count, spacing and
a hasEmptySpace flag, getBody() switches for body diameter, and draw() carries per-base
keyway branches. Both switches end in default: throw new RuntimeException, so a constant added
without touching both crashes at runtime rather than failing to compile. Add the constant, the
Size constants, and a case in each switch.
One geometry wrinkle: UX4 and UX5 have two large filament pins and two or three small ones, so pin diameter becomes per-pin where today it is per-socket. Scope that before committing to UX4.
Dimensions must come from datasheets, not from this table. ~6-8 h for all four, UX4 first.
6.4 Datasheets for the uncovered high-usage parts
Per §4.2 and D6, and not the capacitor work an earlier draft of this document proposed — that ships already. The parts with neither mechanism, in placement order:
| Class | Placements | What it needs |
|---|---|---|
TransistorTO92 |
363 | BC547/BC557, 2N3904/2N3906, J201, MPF102, BC108, with pinout per part |
DiodePlastic |
324 | 1N4148, 1N400x, 1N5817 |
RadialCeramicDiskCapacitor |
283 | NPO/X7R by value and voltage, with disc diameter |
PotentiometerPanel |
195 | Alpha/Bourns 16 mm and 24 mm bodies by resistance and taper |
RadialCeramicDiskCapacitor is the natural first one: it is a capacitor, so
CapacitorDatasheetService already knows its column layout, and it is the only capacitor class of
the six most-placed with no coverage at all.
TransistorTO92 is the most valuable and the most work, because a transistor datasheet needs a
pinout column that the capacitor format has no equivalent for — the existing E-C-B / E-B-C options
would have to become a datasheet column. Scope that before starting.
Per §5.3 each one is an applyModel implementation plus a CSV. Keep every cell ASCII and every
value "<number> <unit>".
~6-10 h each, RadialCeramicDiskCapacitor first.
6.5 Fix six malformed datasheet rows — done
The four Elna Silmic II 4.7uF cells and the two WIMA MKS-4 0.01µF A/B cells now read 4.7 uF
and 0.01 uF A/B. The A/ B suffix stays: it is how the other WIMA rows give one value two
case sizes, and only the value before it is applied. DatasheetRowsTest applies every row of every
datasheet and rejects non-ASCII bytes, so the next malformed cell fails the build.
6.6 Make factory building blocks reach existing users — done
Factory blocks carry createdOn/deletedOn behind a defaultBlocksImportDate watermark; see §5.4.
6.7 Ship the first factory building blocks
Data, not code, and per D7 this is where composable parts go. Each is authored in the app and lifted
into blocks.xml; each gets a createdOn later than the newest one already in the file.
| Block | Built from | Why |
|---|---|---|
| Push-pull pot | PotentiometerPanel + MiniToggleSwitch (DPDT) |
12 files, 15 mentions — the highest-demand composable in the corpus |
| Transformer symbol | TransformerCoil x2 + TransformerCore |
15 files already hand-assemble exactly this; closes most of §8.1's transformer-symbol gap for free |
| Treble bleed | Resistor + RadialFilmCapacitor |
Named network every guitar builder adds; trivial to author |
| Tube gain stage | TubeSocket + grid stopper + cathode R/C + plate R |
50 corpus files place a tube socket and then rebuild this by hand every time |
| Strat / Tele / LP harness | guitar pickups + pots + switch + jack |
guitar components appear in only 8 of 425 files, which may be discoverability as much as demand |
Author the push-pull pot first: it is the one with measured demand, and it is the worked example that proves D7 before the rest follow. ~2-3 h each.
7. Tier 2 — new components, ranked by measured demand
Parts that cannot be composed from what already ships, so D7 does not apply to them. §7.6 is the exception and is kept only to record why it was declined.
7.1 Transformer — the largest single gap
Per D4: one class in components.passive extending AbstractMultiPartComponent, as
AudioTransformer does, with a Type enum of Power / Output / Choke, a bell-end or open-frame
body, and a configurable tap count per winding.
What the absence costs today: no BOM line, no terminals, no netlist presence, and 15 projects
assembling one by hand out of TransformerCoil pieces. Every tube amp has two or three.
Needs: startTrackingContinuityArea around the tap strip, mirroring a neighbouring multi-part
component exactly; an IComponentTransformer for rotation; drawIcon() hand-drawn for the
toolbox; bomPolicy = SHOW_ONLY_TYPE_NAME with the type and tap count in the variant label.
A matching schematic symbol is §8.1. Ship the physical part first — the corpus demand is for layouts. ~16-20 h.
7.2 Speaker
Physical component plus a schematic symbol, 25 files and 45 hand-written mentions, and the only item here that serves amp and hi-fi equally. Round frame with a configurable diameter, two terminals, an impedance property. The symbol is a few hours; the physical part is a day. ~10-12 h for both.
7.3 Chassis Choke
21 files, 38 mentions. Once §7.1 exists this is largely its Choke type plus a single-winding body,
which is the main argument for building it as part of the Transformer class rather than beside it.
RadialInductor and ToroidalInductor are PCB parts and do not substitute. ~4 h if it rides on
§7.1, ~10 h standalone.
7.4 Footswitch
MiniToggleSwitch already provides 3PDT electrically, but draws as a rounded-rectangle mini toggle.
A stomp switch is a round body with a 3x3 lug grid, and pedal layouts are built around that
geometry. ~20 files reference 3PDT, stomp or footswitch by hand.
New class in electromechanical, reusing ToggleSwitchType so the netlist behaviour and the
existing MiniToggleSwitchTests patterns carry over. Add a momentary SPST variant for
non-latching relay bypass. Needs a netlist/ test per §5.5. ~10-12 h.
7.5 Photoresistor, and Vactrol as a building block
10 files, 15 mentions, and nothing discrete in the library — the only LDR class, LDRSensorModule,
is a dormant breakout board rather than the bare part. Blocks tremolo, optical compressors and Uni-Vibe-style circuits.
Split by D7:
Photoresistoris a new component. It is a two-lead primitive with its own body and no existing part to compose it from, so it has to be drawn. ExtendsAbstractLeadedComponent.- Vactrol is then an
LEDplus aPhotoresistor, which is a factory building block (§6.7), not a class. A real vactrol is exactly those two parts in opaque heatshrink, so the assembly is honest.
~5 h for the component, then an hour to author the block.
7.6 Push-pull pot — declined as a component
Kept here as a record of the decision, because it is the case D7 exists for.
12 files and 15 mentions make this the best-evidenced composable part in the corpus, and an earlier
draft of this roadmap proposed a PushPullPotentiometer class extending AbstractPotentiometer.
That is the wrong answer. A push-pull pot is a PotentiometerPanel and a DPDT MiniToggleSwitch,
both of which already draw correctly, already report the right terminals, and already have netlist
coverage — MiniToggleSwitchTests alone is 15 cases. A new class would re-derive all of it and add
a toolbox entry.
It ships as a factory building block instead: §6.7, first in the list.
The same reasoning retires the alternative that was also considered, a switch property on
PotentiometerPanel. Besides D7, it would change the control-point count of a class placed 195
times in the corpus, which §5.2 forbids for existing files.
8. Tier 3 — reasoned, not measured
Per D2 these have no corpus evidence. Ordered by how confident the judgement is, not by cost.
8.1 Schematic symbol holes. Thirty symbols with specific omissions: no speaker (§7.2), no
momentary/push-button switch (only SwitchLatchingSymbol), no single transformer symbol with taps
(only the TransformerCoil + TransformerCore primitives), no tetrode or beam-power tube and no
dual-triode envelope for a 12AX7, no LDR, opto-isolator, thermistor, varistor, relay or SCR/Triac,
and no crystal symbol although a physical CrystalOscillator ships. GroundSymbol offers only
Default and Triangle with no chassis-versus-earth distinction, which matters for amp safety
drawings. The IC symbol's 3- and 5-contact forms do serve as an op-amp triangle. Each is a few
hours.
Two of these are not component work at all. The transformer symbol is TransformerCoil twice
plus TransformerCore, so per D7 it is a factory building block (§6.7) and needs no class. The
opto-isolator symbol is likewise an LEDSymbol facing a photoresistor symbol, once the latter
exists. That leaves the momentary switch as the one genuinely missing primitive here, and the
first worth drawing.
8.2 Hi-fi connectors. XLR (3-pin, male and female, chassis and PCB), binding post, banana, speakON, 5-pin DIN, BNC. Zero corpus hits, high confidence from domain knowledge, and the weakest domain in the library — there is no hi-fi category at all.
8.3 Speaker crossover parts. Reviving AirCoreInductor in §6.2 is the prerequisite and the
cheapest hi-fi win available. Beyond it: a mains toroid (ToroidalInductor is an inductor, not a
transformer with taps) and non-polar film caps at crossover values.
8.4 Modern power packages. No TO-247, TO-264 or TO-3P, so a current power amp cannot be drawn;
vintage TO-3 ships. Plus a bolt-on heatsink — HeatSinkResistor is a resistor, not a heatsink.
8.5 Pickups not covered. Jazzmaster (a wide soapbar, geometrically distinct from the P-90), Danelectro lipstick, Rickenbacker toaster, gold foil, piezo/under-saddle. All zero corpus hits, which is why they sit below the Tier 2 guitar item despite looking more visible.
8.6 Connected board positions. The EyeletBoard problem from §4.4: selectable eyelet and
turret positions that carry node names and reach the netlist. Benefits both boards and would
retire the discrete-turret workaround in 40 projects. Design question, not a component.
8.7 Mains safety parts. NTC inrush thermistor, MOV, X and Y capacitors. One corpus file mentions NTC.
8.8 Reverb tank. Accutronics-style, 6 files. Simple geometry, narrow audience.
8.9 Dual-concentric pot. Jazz Bass, '62 Strat, Rickenbacker. Zero corpus hits, and per D7 not a
component: two PotentiometerPanel instances sharing a position is a factory building block, so it
joins the §6.7 list rather than this one once someone asks for it.
9. Sequencing
| Step | Items | Rough effort |
|---|---|---|
| 1 | §6.1 alias fix, verified by regression run | 2 h |
| 2 | §6.7 push-pull pot block, as the D7 worked example | 3 h |
| 3 | §6.2 revive three, decide on the fourth | 3 h |
| 4 | §6.3 UX4, then UX5 / B8G / B9D | 6-8 h |
| 5 | §6.4 datasheets, RadialCeramicDiskCapacitor first |
6-10 h each |
| 6 | §6.7 remaining blocks: transformer symbol, treble bleed, tube gain stage, harnesses | 2-3 h each |
| 7 | §7.1 Transformer, with §7.3 Choke as its third type |
20-24 h |
| 8 | §7.2 Speaker, physical plus symbol | 10-12 h |
| 9 | §7.4 Footswitch | 10-12 h |
| 10 | §7.5 Photoresistor, then the Vactrol block |
6 h |
Steps 1-3 are about a day: the alias bug cleared and the highest-demand composable shipped as data rather than as a class. Step 7 is the one that matters most.
Three items that looked like components when this roadmap started are now data (§6.7, §7.6, §8.9), and one is split into a primitive plus a block (§7.5). That is D7 working as intended; expect the same question of anything added to Tier 2 later.
Figures are estimates for scoping, not commitments, and every dimension in this document is an estimate until it is checked against a datasheet (§6.3, §6.4).