@vessel-dsp/compiler 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/README.md +314 -2
  2. package/dist/bake.d.ts +38 -0
  3. package/dist/bake.d.ts.map +1 -0
  4. package/dist/bake.js +101 -0
  5. package/dist/bake.js.map +1 -0
  6. package/dist/bbd-clock.d.ts +171 -0
  7. package/dist/bbd-clock.d.ts.map +1 -0
  8. package/dist/bbd-clock.js +1750 -0
  9. package/dist/bbd-clock.js.map +1 -0
  10. package/dist/block-row.d.ts +51 -0
  11. package/dist/block-row.d.ts.map +1 -0
  12. package/dist/block-row.js +17 -0
  13. package/dist/block-row.js.map +1 -0
  14. package/dist/bypass.d.ts +3 -0
  15. package/dist/bypass.d.ts.map +1 -0
  16. package/dist/bypass.js +228 -0
  17. package/dist/bypass.js.map +1 -0
  18. package/dist/compile.d.ts +24 -0
  19. package/dist/compile.d.ts.map +1 -0
  20. package/dist/compile.js +344 -0
  21. package/dist/compile.js.map +1 -0
  22. package/dist/converter-scale.d.ts +14 -0
  23. package/dist/converter-scale.d.ts.map +1 -0
  24. package/dist/converter-scale.js +133 -0
  25. package/dist/converter-scale.js.map +1 -0
  26. package/dist/couple.d.ts +28 -0
  27. package/dist/couple.d.ts.map +1 -0
  28. package/dist/couple.js +567 -0
  29. package/dist/couple.js.map +1 -0
  30. package/dist/dangling-active-terminal.d.ts +47 -0
  31. package/dist/dangling-active-terminal.d.ts.map +1 -0
  32. package/dist/dangling-active-terminal.js +542 -0
  33. package/dist/dangling-active-terminal.js.map +1 -0
  34. package/dist/declared-program.d.ts +23 -0
  35. package/dist/declared-program.d.ts.map +1 -0
  36. package/dist/declared-program.js +387 -0
  37. package/dist/declared-program.js.map +1 -0
  38. package/dist/denomination.d.ts +48 -0
  39. package/dist/denomination.d.ts.map +1 -0
  40. package/dist/denomination.js +27 -0
  41. package/dist/denomination.js.map +1 -0
  42. package/dist/device-laws.d.ts +237 -0
  43. package/dist/device-laws.d.ts.map +1 -0
  44. package/dist/device-laws.js +3497 -0
  45. package/dist/device-laws.js.map +1 -0
  46. package/dist/diagnostics.d.ts +13 -0
  47. package/dist/diagnostics.d.ts.map +1 -0
  48. package/dist/diagnostics.js +19 -0
  49. package/dist/diagnostics.js.map +1 -0
  50. package/dist/emit.d.ts +8 -0
  51. package/dist/emit.d.ts.map +1 -0
  52. package/dist/emit.js +43 -0
  53. package/dist/emit.js.map +1 -0
  54. package/dist/errors.d.ts +22 -0
  55. package/dist/errors.d.ts.map +1 -0
  56. package/dist/errors.js +39 -0
  57. package/dist/errors.js.map +1 -0
  58. package/dist/firmware-class.d.ts +69 -0
  59. package/dist/firmware-class.d.ts.map +1 -0
  60. package/dist/firmware-class.js +20 -0
  61. package/dist/firmware-class.js.map +1 -0
  62. package/dist/fixtures/circuits.d.ts +1721 -0
  63. package/dist/fixtures/circuits.d.ts.map +1 -0
  64. package/dist/fixtures/circuits.js +13404 -0
  65. package/dist/fixtures/circuits.js.map +1 -0
  66. package/dist/fixtures/expected.d.ts +257 -0
  67. package/dist/fixtures/expected.d.ts.map +1 -0
  68. package/dist/fixtures/expected.js +311 -0
  69. package/dist/fixtures/expected.js.map +1 -0
  70. package/dist/fixtures/index.d.ts +4 -0
  71. package/dist/fixtures/index.d.ts.map +1 -0
  72. package/dist/fixtures/index.js +8 -0
  73. package/dist/fixtures/index.js.map +1 -0
  74. package/dist/fixtures/registry.d.ts +52 -0
  75. package/dist/fixtures/registry.d.ts.map +1 -0
  76. package/dist/fixtures/registry.js +341 -0
  77. package/dist/fixtures/registry.js.map +1 -0
  78. package/dist/generic-tube-fit.d.ts +12 -0
  79. package/dist/generic-tube-fit.d.ts.map +1 -0
  80. package/dist/generic-tube-fit.js +45 -0
  81. package/dist/generic-tube-fit.js.map +1 -0
  82. package/dist/grounded-clock-supply.d.ts +13 -0
  83. package/dist/grounded-clock-supply.d.ts.map +1 -0
  84. package/dist/grounded-clock-supply.js +45 -0
  85. package/dist/grounded-clock-supply.js.map +1 -0
  86. package/dist/identify.d.ts +50 -0
  87. package/dist/identify.d.ts.map +1 -0
  88. package/dist/identify.js +319 -0
  89. package/dist/identify.js.map +1 -0
  90. package/dist/index.d.ts +10 -0
  91. package/dist/index.d.ts.map +1 -0
  92. package/dist/index.js +19 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/inert-controls.d.ts +15 -0
  95. package/dist/inert-controls.d.ts.map +1 -0
  96. package/dist/inert-controls.js +266 -0
  97. package/dist/inert-controls.js.map +1 -0
  98. package/dist/latch-seed.d.ts +53 -0
  99. package/dist/latch-seed.d.ts.map +1 -0
  100. package/dist/latch-seed.js +257 -0
  101. package/dist/latch-seed.js.map +1 -0
  102. package/dist/link.d.ts +7 -0
  103. package/dist/link.d.ts.map +1 -0
  104. package/dist/link.js +270 -0
  105. package/dist/link.js.map +1 -0
  106. package/dist/lower.d.ts +79 -0
  107. package/dist/lower.d.ts.map +1 -0
  108. package/dist/lower.js +1855 -0
  109. package/dist/lower.js.map +1 -0
  110. package/dist/netlist.d.ts +79 -0
  111. package/dist/netlist.d.ts.map +1 -0
  112. package/dist/netlist.js +4746 -0
  113. package/dist/netlist.js.map +1 -0
  114. package/dist/non-executable-clock-drivers.d.ts +8 -0
  115. package/dist/non-executable-clock-drivers.d.ts.map +1 -0
  116. package/dist/non-executable-clock-drivers.js +33 -0
  117. package/dist/non-executable-clock-drivers.js.map +1 -0
  118. package/dist/open-category.d.ts +98 -0
  119. package/dist/open-category.d.ts.map +1 -0
  120. package/dist/open-category.js +111 -0
  121. package/dist/open-category.js.map +1 -0
  122. package/dist/over-driven-node.d.ts +33 -0
  123. package/dist/over-driven-node.d.ts.map +1 -0
  124. package/dist/over-driven-node.js +188 -0
  125. package/dist/over-driven-node.js.map +1 -0
  126. package/dist/part-catalog.d.ts +7 -0
  127. package/dist/part-catalog.d.ts.map +1 -0
  128. package/dist/part-catalog.js +6639 -0
  129. package/dist/part-catalog.js.map +1 -0
  130. package/dist/part-number.d.ts +32 -0
  131. package/dist/part-number.d.ts.map +1 -0
  132. package/dist/part-number.js +80 -0
  133. package/dist/part-number.js.map +1 -0
  134. package/dist/partition.d.ts +7 -0
  135. package/dist/partition.d.ts.map +1 -0
  136. package/dist/partition.js +242 -0
  137. package/dist/partition.js.map +1 -0
  138. package/dist/port-full-scale.d.ts +70 -0
  139. package/dist/port-full-scale.d.ts.map +1 -0
  140. package/dist/port-full-scale.js +235 -0
  141. package/dist/port-full-scale.js.map +1 -0
  142. package/dist/pot-orientation.d.ts +114 -0
  143. package/dist/pot-orientation.d.ts.map +1 -0
  144. package/dist/pot-orientation.js +551 -0
  145. package/dist/pot-orientation.js.map +1 -0
  146. package/dist/power-domain-control.d.ts +40 -0
  147. package/dist/power-domain-control.d.ts.map +1 -0
  148. package/dist/power-domain-control.js +142 -0
  149. package/dist/power-domain-control.js.map +1 -0
  150. package/dist/proxy-declaration.d.ts +121 -0
  151. package/dist/proxy-declaration.d.ts.map +1 -0
  152. package/dist/proxy-declaration.js +50 -0
  153. package/dist/proxy-declaration.js.map +1 -0
  154. package/dist/registry.d.ts +323 -0
  155. package/dist/registry.d.ts.map +1 -0
  156. package/dist/registry.js +192 -0
  157. package/dist/registry.js.map +1 -0
  158. package/dist/sparse-schedule.d.ts +65 -0
  159. package/dist/sparse-schedule.d.ts.map +1 -0
  160. package/dist/sparse-schedule.js +421 -0
  161. package/dist/sparse-schedule.js.map +1 -0
  162. package/dist/speaker-load.d.ts +38 -0
  163. package/dist/speaker-load.d.ts.map +1 -0
  164. package/dist/speaker-load.js +97 -0
  165. package/dist/speaker-load.js.map +1 -0
  166. package/dist/stage-coverage.d.ts +21 -0
  167. package/dist/stage-coverage.d.ts.map +1 -0
  168. package/dist/stage-coverage.js +124 -0
  169. package/dist/stage-coverage.js.map +1 -0
  170. package/dist/stamp-partition.d.ts +314 -0
  171. package/dist/stamp-partition.d.ts.map +1 -0
  172. package/dist/stamp-partition.js +472 -0
  173. package/dist/stamp-partition.js.map +1 -0
  174. package/dist/supply-reference.d.ts +19 -0
  175. package/dist/supply-reference.d.ts.map +1 -0
  176. package/dist/supply-reference.js +66 -0
  177. package/dist/supply-reference.js.map +1 -0
  178. package/dist/supply-short.d.ts +32 -0
  179. package/dist/supply-short.d.ts.map +1 -0
  180. package/dist/supply-short.js +118 -0
  181. package/dist/supply-short.js.map +1 -0
  182. package/dist/supply-stamps.d.ts +51 -0
  183. package/dist/supply-stamps.d.ts.map +1 -0
  184. package/dist/supply-stamps.js +363 -0
  185. package/dist/supply-stamps.js.map +1 -0
  186. package/dist/terminal-role.d.ts +109 -0
  187. package/dist/terminal-role.d.ts.map +1 -0
  188. package/dist/terminal-role.js +46 -0
  189. package/dist/terminal-role.js.map +1 -0
  190. package/dist/terminal-roles.d.ts +59 -0
  191. package/dist/terminal-roles.d.ts.map +1 -0
  192. package/dist/terminal-roles.js +92 -0
  193. package/dist/terminal-roles.js.map +1 -0
  194. package/dist/transformer.d.ts +181 -0
  195. package/dist/transformer.d.ts.map +1 -0
  196. package/dist/transformer.js +476 -0
  197. package/dist/transformer.js.map +1 -0
  198. package/dist/types.d.ts +3157 -0
  199. package/dist/types.d.ts.map +1 -0
  200. package/dist/types.js +11 -0
  201. package/dist/types.js.map +1 -0
  202. package/dist/unbounded-operating-point.d.ts +35 -0
  203. package/dist/unbounded-operating-point.d.ts.map +1 -0
  204. package/dist/unbounded-operating-point.js +181 -0
  205. package/dist/unbounded-operating-point.js.map +1 -0
  206. package/dist/unexecuted-region.d.ts +3 -0
  207. package/dist/unexecuted-region.d.ts.map +1 -0
  208. package/dist/unexecuted-region.js +122 -0
  209. package/dist/unexecuted-region.js.map +1 -0
  210. package/dist/unimplemented-control-role.d.ts +3 -0
  211. package/dist/unimplemented-control-role.d.ts.map +1 -0
  212. package/dist/unimplemented-control-role.js +61 -0
  213. package/dist/unimplemented-control-role.js.map +1 -0
  214. package/dist/unreachable-output.d.ts +3 -0
  215. package/dist/unreachable-output.d.ts.map +1 -0
  216. package/dist/unreachable-output.js +97 -0
  217. package/dist/unreachable-output.js.map +1 -0
  218. package/dist/unreadable-terminal-role.d.ts +67 -0
  219. package/dist/unreadable-terminal-role.d.ts.map +1 -0
  220. package/dist/unreadable-terminal-role.js +409 -0
  221. package/dist/unreadable-terminal-role.js.map +1 -0
  222. package/dist/unverified-pinout.d.ts +27 -0
  223. package/dist/unverified-pinout.d.ts.map +1 -0
  224. package/dist/unverified-pinout.js +121 -0
  225. package/dist/unverified-pinout.js.map +1 -0
  226. package/package.json +75 -5
@@ -0,0 +1,266 @@
1
+ // Controls the document wires so that they provably cannot affect the circuit.
2
+ //
3
+ // Three reasons, and the third arrived with the execution schedule: a floating pot wiper, a
4
+ // device joined to nothing, and a control whose every element sits in a region the program
5
+ // does not execute. All three are structural -- connectivity, or the program's own `order`.
6
+ //
7
+ // This is a *warning* stage, not a refusal. A pedal with a dead knob still renders, and
8
+ // refusing it would cost 11 compiling corpus packets to fix a defect the user can hear
9
+ // around. What was not acceptable is shipping one silently: `ibanez-ts808` rendered
10
+ // bit-identical output at `Drive=0.1` and `Drive=0.9` and nothing in the compile result
11
+ // said why.
12
+ //
13
+ // The evidence is connectivity and nothing else. No component name, no `Description`,
14
+ // no role prose -- the same discipline the rest of the pipeline holds to.
15
+ import { potTerminals, stampControl } from "./lower.js";
16
+ /**
17
+ * A pot whose wiper node touches nothing else is electrically a fixed resistor.
18
+ *
19
+ * The two halves of the track are in series with a floating junction between them, so
20
+ * whatever the wiper takes from one half it gives back to the other and their sum is
21
+ * the whole track no matter where the knob sits. The pot still stamps, still solves,
22
+ * and still moves its two conductances -- it just cannot change any node voltage, which
23
+ * is why listening finds it and the matrix does not.
24
+ *
25
+ * A wiper tied back to one of its own ends is a rheostat and genuinely varies, so it
26
+ * has degree 2 and is correctly not reported here.
27
+ */
28
+ export function findInertControls(netlist, program) {
29
+ const degree = new Map();
30
+ for (const device of netlist.devices) {
31
+ for (const node of device.nodes) {
32
+ degree.set(node, (degree.get(node) ?? 0) + 1);
33
+ }
34
+ }
35
+ const byControl = new Map();
36
+ for (const device of netlist.devices) {
37
+ if (device.control === null) {
38
+ continue;
39
+ }
40
+ const bound = byControl.get(device.control);
41
+ if (bound === undefined) {
42
+ byControl.set(device.control, [device]);
43
+ }
44
+ else {
45
+ bound.push(device);
46
+ }
47
+ }
48
+ const unscheduled = controlsOnlyInUnscheduledBlocks(program);
49
+ const scheduledControls = controlsInScheduledBlocks(program);
50
+ // Controls a program reads through a port rather than a stamp. Their devices are genuinely
51
+ // dead to the circuit and that is not a defect: a mode pot read by a high-impedance input
52
+ // has a wiper driving nothing, and it still chooses which program runs. Reporting it would
53
+ // be a wrong answer about someone's circuit, which is the bar this whole file sets.
54
+ const portedControls = controlsReadThroughPorts(program);
55
+ const warnings = [];
56
+ for (const control of netlist.controls) {
57
+ const bound = byControl.get(control.id) ?? [];
58
+ if (bound.length === 0) {
59
+ continue;
60
+ }
61
+ if (portedControls.has(control.id)) {
62
+ continue;
63
+ }
64
+ // Every device the control drives must be dead for the claim to hold. One live gang, or a
65
+ // switch sharing the control, and the knob does something -- reporting it then would be a
66
+ // wrong answer about someone's circuit, which is worse than the silence this replaces. The
67
+ // narrower per-device fact is still visible in `devices`.
68
+ //
69
+ // **Two ways to be dead, and the second was missing.** A pot with a floating wiper is the
70
+ // original case. The other is a device with *every* terminal unwired, which the pot rule
71
+ // cannot express: `boss-od-3`'s `EFFECT_FOOTSWITCH` drives a switch whose three nodes are all
72
+ // degree 1, so the footswitch is bound to a real device that is connected to nothing, and the
73
+ // control read as fine. That packet's whole signal path is gated by JFETs whose gates the
74
+ // footswitch network should drive, so this is the control warning the pedal most needed.
75
+ const allPots = bound.every((device) => device.kind === "potentiometer" || device.kind === "rheostat");
76
+ const allDead = bound.every((device) => (device.kind === "potentiometer" && wiperIsFloating(device, degree)) ||
77
+ isFullyDisconnected(device, degree));
78
+ // **Three ways to be dead, and the device-level ones are reported first.** A floating
79
+ // wiper or a device joined to nothing is a fact about the circuit and is what a source
80
+ // reader can act on; sitting in an unexecuted region is a fact about the schedule, and
81
+ // only applies to audio potentiometers/rheostats (power contacts, bypass latches, and
82
+ // indicators naturally reside in DC/power regions outside the scheduled audio blocks).
83
+ if (!allDead && (!allPots || !unscheduled.has(control.id))) {
84
+ continue;
85
+ }
86
+ const disconnected = bound.filter((device) => isFullyDisconnected(device, degree));
87
+ const isDisconnected = disconnected.length === bound.length;
88
+ const reasonKind = !allDead
89
+ ? "unscheduled-region"
90
+ : isDisconnected
91
+ ? "disconnected"
92
+ : "floating-wiper";
93
+ // A structural defect is one that breaks an executed block (a scheduled part of the audio path).
94
+ // Controls for unselected channels or unexecuted regions are warned about but do not refuse compilation.
95
+ const isStructuralDefect = allDead && (program.order.length === 0 || scheduledControls.has(control.id));
96
+ warnings.push({
97
+ code: "control-cannot-affect-circuit",
98
+ control: control.id,
99
+ reasonKind,
100
+ isStructuralDefect,
101
+ devices: bound.map((device) => device.id),
102
+ detail: !allDead
103
+ ? `control "${control.id}" cannot affect the circuit: ` +
104
+ `${bound.length === 1 ? "the element it varies sits" : "every element it varies sits"} ` +
105
+ "in a region the program does not execute, because nothing in that region " +
106
+ "can reach the output jack"
107
+ : isDisconnected
108
+ ? `control "${control.id}" cannot affect the circuit: ` +
109
+ `${bound.length === 1 ? "the device it drives has" : "every device it drives has"} ` +
110
+ "every terminal connected to nothing else, so the device is present in the " +
111
+ "document and joined to no circuit at all"
112
+ : `control "${control.id}" cannot affect the circuit: ` +
113
+ `${bound.length === 1 ? "its potentiometer has" : "every potentiometer it drives has"} ` +
114
+ "a wiper node connected to nothing else, so the two track halves stay in " +
115
+ "series and their sum is fixed at every knob position",
116
+ });
117
+ }
118
+ return warnings;
119
+ }
120
+ /**
121
+ * Controls whose every stamp sits in a block the program does not execute.
122
+ *
123
+ * The third way a knob can be provably dead, and the one only the emitted program can see:
124
+ * the device is wired, its stamps are real, and the region they are in owns neither jack and
125
+ * feeds nothing that does — so `link` leaves it out of `order` and the runtime never solves
126
+ * it. Measured on the corpus: 11 controls across 5 packets, `boss-od-1`'s battery contact and
127
+ * `boss-sd-1`'s effect switch being the two that no earlier rule catches.
128
+ *
129
+ * **A control with no stamp at all is deliberately not this warning.** 74 declared controls
130
+ * are in that position and almost all of them are jacks, LED indicators and DC-adaptor
131
+ * entries that were never knobs — a separate question about what belongs in
132
+ * `program.controls`, and warning about them would bury these 11 in 74 rows of noise.
133
+ *
134
+ * Reads `program.order` rather than re-deriving reachability, because the program's own
135
+ * statement of what executes is the fact being reported.
136
+ */
137
+ /**
138
+ * Controls a block reads through a port: a clock control, or a router choosing a program.
139
+ *
140
+ * Neither changes the conductance matrix, so every structural test in this file calls them
141
+ * dead. They are not: the control reaches the program by another road.
142
+ */
143
+ function controlsReadThroughPorts(program) {
144
+ const ported = new Set();
145
+ for (const block of program.blocks) {
146
+ if (block.kind !== "macro" && block.kind !== "composed")
147
+ continue;
148
+ const clock = block.clockControl?.controlId;
149
+ if (clock !== undefined)
150
+ ported.add(clock);
151
+ if (block.kind === "composed" && block.router !== null && block.router !== undefined) {
152
+ ported.add(block.router.controlId);
153
+ }
154
+ // A scanned or tapped line's control moves the program's delay without varying any device.
155
+ if (block.kind === "composed") {
156
+ for (const position of block.positions) {
157
+ for (const line of Object.values(position.lines)) {
158
+ if (line.sweep !== undefined)
159
+ ported.add(line.sweep.controlId);
160
+ }
161
+ }
162
+ }
163
+ // And a swept gain's knob moves a mix term the same way.
164
+ if (block.kind === "composed") {
165
+ for (const position of block.positions) {
166
+ for (const op of position.ops) {
167
+ if (op.op !== "mix")
168
+ continue;
169
+ for (const term of op.terms) {
170
+ if (term.sweep !== undefined)
171
+ ported.add(term.sweep.controlId);
172
+ }
173
+ }
174
+ }
175
+ }
176
+ }
177
+ return ported;
178
+ }
179
+ function controlsOnlyInUnscheduledBlocks(program) {
180
+ const scheduled = new Set(program.order);
181
+ const stampedAnywhere = new Set();
182
+ const stampedInScheduled = new Set();
183
+ for (const block of program.blocks) {
184
+ if (block.kind === "macro" || block.kind === "composed") {
185
+ const controlId = block.clockControl?.controlId;
186
+ if (controlId !== undefined) {
187
+ stampedAnywhere.add(controlId);
188
+ if (scheduled.has(block.id)) {
189
+ stampedInScheduled.add(controlId);
190
+ }
191
+ }
192
+ continue;
193
+ }
194
+ if (block.kind !== "mna") {
195
+ continue;
196
+ }
197
+ for (const stamp of block.stamps) {
198
+ const control = stampControl(stamp);
199
+ if (control === null) {
200
+ continue;
201
+ }
202
+ stampedAnywhere.add(control);
203
+ if (scheduled.has(block.id)) {
204
+ stampedInScheduled.add(control);
205
+ }
206
+ }
207
+ }
208
+ for (const control of stampedInScheduled) {
209
+ stampedAnywhere.delete(control);
210
+ }
211
+ return stampedAnywhere;
212
+ }
213
+ /**
214
+ * Controls whose stamps sit in at least one block the program executes in `order`.
215
+ */
216
+ function controlsInScheduledBlocks(program) {
217
+ const scheduled = new Set(program.order);
218
+ const inScheduled = new Set();
219
+ for (const block of program.blocks) {
220
+ if (!scheduled.has(block.id)) {
221
+ continue;
222
+ }
223
+ if (block.kind === "macro" || block.kind === "composed") {
224
+ const controlId = block.clockControl?.controlId;
225
+ if (controlId !== undefined) {
226
+ inScheduled.add(controlId);
227
+ }
228
+ continue;
229
+ }
230
+ if (block.kind !== "mna") {
231
+ continue;
232
+ }
233
+ for (const stamp of block.stamps) {
234
+ const control = stampControl(stamp);
235
+ if (control !== null) {
236
+ inScheduled.add(control);
237
+ }
238
+ }
239
+ }
240
+ return inScheduled;
241
+ }
242
+ /**
243
+ * A device joined to no circuit: every terminal it declares is touched by nothing else.
244
+ *
245
+ * Ground is deliberately *not* excused here, unlike in the dangling-terminal rule. A device with one
246
+ * terminal on ground and the rest unwired still cannot do anything, and a control driving it still
247
+ * cannot be heard -- which is the claim this file makes.
248
+ */
249
+ function isFullyDisconnected(device, degree) {
250
+ return (device.nodes.length > 0 &&
251
+ device.nodes.every((node) => (degree.get(node) ?? 0) <= 1));
252
+ }
253
+ function wiperIsFloating(device, degree) {
254
+ if (device.nodes.length !== 3) {
255
+ return false;
256
+ }
257
+ // The same terminal-order reading the lowering stage uses, imported rather than
258
+ // repeated: a second copy of "which lug is the wiper" would drift from the one that
259
+ // decides what actually gets stamped, and then the warning would describe a
260
+ // different circuit from the program. Only the wiper is read here, which is decided
261
+ // before `potTerminals` ever consults connectivity, so two empty maps are exact -- not a
262
+ // stand-in -- for what this caller needs.
263
+ const [, wiper] = potTerminals(device, new Map(), new Map());
264
+ return (degree.get(wiper) ?? 0) === 1;
265
+ }
266
+ //# sourceMappingURL=inert-controls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inert-controls.js","sourceRoot":"","sources":["../src/inert-controls.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,4FAA4F;AAC5F,2FAA2F;AAC3F,4FAA4F;AAC5F,EAAE;AACF,wFAAwF;AACxF,uFAAuF;AACvF,oFAAoF;AACpF,wFAAwF;AACxF,YAAY;AACZ,EAAE;AACF,sFAAsF;AACtF,0EAA0E;AAE1E,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAUrD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAChC,OAAgB,EAChB,OAAgB;IAEhB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACtC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC/C,CAAC;IACF,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,GAAG,EAAuB,CAAC;IACjD,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACtC,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YAC7B,SAAS;QACV,CAAC;QACD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACzB,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpB,CAAC;IACF,CAAC;IAED,MAAM,WAAW,GAAG,+BAA+B,CAAC,OAAO,CAAC,CAAC;IAE7D,MAAM,iBAAiB,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAE7D,2FAA2F;IAC3F,0FAA0F;IAC1F,2FAA2F;IAC3F,oFAAoF;IACpF,MAAM,cAAc,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAC;IAEzD,MAAM,QAAQ,GAA0B,EAAE,CAAC;IAC3C,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,SAAS;QACV,CAAC;QACD,IAAI,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;YACpC,SAAS;QACV,CAAC;QACD,0FAA0F;QAC1F,0FAA0F;QAC1F,2FAA2F;QAC3F,0DAA0D;QAC1D,EAAE;QACF,0FAA0F;QAC1F,yFAAyF;QACzF,8FAA8F;QAC9F,8FAA8F;QAC9F,0FAA0F;QAC1F,yFAAyF;QACzF,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAC1B,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,eAAe,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,CACzE,CAAC;QACF,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAC1B,CAAC,MAAM,EAAE,EAAE,CACV,CAAC,MAAM,CAAC,IAAI,KAAK,eAAe,IAAI,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACpE,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CACpC,CAAC;QACF,sFAAsF;QACtF,uFAAuF;QACvF,uFAAuF;QACvF,sFAAsF;QACtF,uFAAuF;QACvF,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YAC5D,SAAS;QACV,CAAC;QACD,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAC5C,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CACnC,CAAC;QACF,MAAM,cAAc,GAAG,YAAY,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC;QAC5D,MAAM,UAAU,GAA6D,CAAC,OAAO;YACpF,CAAC,CAAC,oBAAoB;YACtB,CAAC,CAAC,cAAc;gBACf,CAAC,CAAC,cAAc;gBAChB,CAAC,CAAC,gBAAgB,CAAC;QACrB,iGAAiG;QACjG,yGAAyG;QACzG,MAAM,kBAAkB,GACvB,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;QAE9E,QAAQ,CAAC,IAAI,CAAC;YACb,IAAI,EAAE,+BAA+B;YACrC,OAAO,EAAE,OAAO,CAAC,EAAE;YACnB,UAAU;YACV,kBAAkB;YAClB,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;YACzC,MAAM,EAAE,CAAC,OAAO;gBACf,CAAC,CAAC,YAAY,OAAO,CAAC,EAAE,+BAA+B;oBACtD,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,4BAA4B,CAAC,CAAC,CAAC,8BAA8B,GAAG;oBACxF,2EAA2E;oBAC3E,2BAA2B;gBAC5B,CAAC,CAAC,cAAc;oBACf,CAAC,CAAC,YAAY,OAAO,CAAC,EAAE,+BAA+B;wBACtD,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,4BAA4B,GAAG;wBACpF,4EAA4E;wBAC5E,0CAA0C;oBAC3C,CAAC,CAAC,YAAY,OAAO,CAAC,EAAE,+BAA+B;wBACtD,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,mCAAmC,GAAG;wBACxF,0EAA0E;wBAC1E,sDAAsD;SACzD,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH;;;;;GAKG;AACH,SAAS,wBAAwB,CAAC,OAAgB;IACjD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAa,CAAC;IACpC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACpC,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU;YAAE,SAAS;QAClE,MAAM,KAAK,GAAG,KAAK,CAAC,YAAY,EAAE,SAAS,CAAC;QAC5C,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACtF,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;QACD,2FAA2F;QAC3F,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC/B,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;gBACxC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;oBAClD,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;wBAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBAChE,CAAC;YACF,CAAC;QACF,CAAC;QACD,yDAAyD;QACzD,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC/B,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;gBACxC,KAAK,MAAM,EAAE,IAAI,QAAQ,CAAC,GAAG,EAAE,CAAC;oBAC/B,IAAI,EAAE,CAAC,EAAE,KAAK,KAAK;wBAAE,SAAS;oBAC9B,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;wBAC7B,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;4BAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;oBAChE,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC;AAED,SAAS,+BAA+B,CACvC,OAAgB;IAEhB,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACzC,MAAM,eAAe,GAAG,IAAI,GAAG,EAAa,CAAC;IAC7C,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAa,CAAC;IAChD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACpC,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACzD,MAAM,SAAS,GAAG,KAAK,CAAC,YAAY,EAAE,SAAS,CAAC;YAChD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC7B,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBAC/B,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;oBAC7B,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBACnC,CAAC;YACF,CAAC;YACD,SAAS;QACV,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YAC1B,SAAS;QACV,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YAClC,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;YACpC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACtB,SAAS;YACV,CAAC;YACD,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC7B,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC7B,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACjC,CAAC;QACF,CAAC;IACF,CAAC;IACD,KAAK,MAAM,OAAO,IAAI,kBAAkB,EAAE,CAAC;QAC1C,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,eAAe,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,SAAS,yBAAyB,CACjC,OAAgB;IAEhB,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACzC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAa,CAAC;IACzC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACpC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;YAC9B,SAAS;QACV,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACzD,MAAM,SAAS,GAAG,KAAK,CAAC,YAAY,EAAE,SAAS,CAAC;YAChD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC7B,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAC5B,CAAC;YACD,SAAS;QACV,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YAC1B,SAAS;QACV,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YAClC,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;YACpC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;gBACtB,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC1B,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,WAAW,CAAC;AACpB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,mBAAmB,CAC3B,MAAc,EACd,MAAmC;IAEnC,OAAO,CACN,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QACvB,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAC1D,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CACvB,MAAc,EACd,MAAmC;IAEnC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACd,CAAC;IACD,gFAAgF;IAChF,oFAAoF;IACpF,4EAA4E;IAC5E,oFAAoF;IACpF,yFAAyF;IACzF,0CAA0C;IAC1C,MAAM,CAAC,EAAE,KAAK,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,IAAI,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;IAC7D,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,53 @@
1
+ import type { Netlist, NodeId, OperatingPointSeed } from "./types";
2
+ /**
3
+ * A cross-coupled pair, by connectivity alone.
4
+ *
5
+ * **Seed a base, never a collector.** This is the whole reason the rule is written in terms
6
+ * of bases. A seed enters the solve only through the nonlinear devices' linearisation
7
+ * points, and perturbing a *collector* creates no differential base drive: measured at every
8
+ * magnitude from `1e-9` to `5 V`, a collector seed returns the symmetric solution in an
9
+ * identical 16 iterations, so it does exactly nothing. A caller cannot be trusted to know
10
+ * that, which is why this function emits the seeds rather than exposing the pair.
11
+ *
12
+ * Four structural requirements, and each one is load-bearing:
13
+ *
14
+ * 1. **A direct resistor each way**, collector to the opposite base. All three corpus pairs
15
+ * are wired this way, two of them with a speed-up capacitor in parallel. Requiring a
16
+ * direct element rather than a path is most of what keeps this from over-matching.
17
+ * 2. **A shared emitter node.** The corpus pairs share ground, ground, and a common node
18
+ * that is not ground. Two transistors that merely have resistors between them do not.
19
+ * 3. **Rails are not routes.** Neither collector nor base may be a rail. Without this the
20
+ * supply links every collector to every base and the rule dissolves: measured, a census
21
+ * lacking it reported one packet as having **12** cross-coupled pairs among four
22
+ * transistors. The same exclusion `findUnboundedOpamps` and `switchesClosedForPortPath`
23
+ * make. An emitter *on* a rail is expected and fine — two of the three pairs sit on
24
+ * ground.
25
+ * 4. **Exclusivity.** A transistor in more than one candidate pair makes every pair it is in
26
+ * ambiguous, and an ambiguous latch is not seeded. Guessing which pair is the real one
27
+ * from a tie-break would put the pedal in a state chosen by node numbering.
28
+ *
29
+ * **Which side, with a floor.** `engagedBase` derives the side that engages the effect from
30
+ * connectivity, and when it cannot, this falls back to the lower base node id — deterministic,
31
+ * meaningless, and still a *physical* state rather than the midpoint. The fallback is the
32
+ * floor, not the intent: a latch nobody can read out is better left latched somewhere than
33
+ * left halfway.
34
+ *
35
+ * What this still does not give is **toggling**. The seed decides where the circuit powers up;
36
+ * it is not an input the control plane can move, so a footswitch cannot change it. That needs
37
+ * the latched-state operator with a state entry — see the runtime plan's S4 initial-state item.
38
+ */
39
+ export type BistableLatch = {
40
+ readonly controlId: string | null;
41
+ readonly engagedBase: NodeId;
42
+ readonly bypassedBase: NodeId;
43
+ readonly emitter: NodeId;
44
+ readonly engagedCollector: NodeId;
45
+ readonly bypassedCollector: NodeId;
46
+ readonly engagedTransistorId: string;
47
+ readonly bypassedTransistorId: string;
48
+ };
49
+ export declare function findBistableLatches(netlist: Pick<Netlist, "devices"> & {
50
+ readonly controls?: readonly import("./types").Control[];
51
+ }): readonly BistableLatch[];
52
+ export declare function findLatchSeeds(netlist: Netlist): readonly OperatingPointSeed[];
53
+ //# sourceMappingURL=latch-seed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"latch-seed.d.ts","sourceRoot":"","sources":["../src/latch-seed.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAU,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAY3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,MAAM,aAAa,GAAG;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;CACtC,CAAC;AAEF,wBAAgB,mBAAmB,CAClC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG;IAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,OAAO,SAAS,EAAE,OAAO,EAAE,CAAA;CAAE,GAC9F,SAAS,aAAa,EAAE,CA6G1B;AAED,wBAAgB,cAAc,CAC7B,OAAO,EAAE,OAAO,GACd,SAAS,kBAAkB,EAAE,CAY/B"}
@@ -0,0 +1,257 @@
1
+ // Operating-point seeds for cross-coupled bistables.
2
+ //
3
+ // A symmetric cross-coupled BJT pair has **three** DC solutions: two latched and the
4
+ // unstable midpoint between them. Newton started from `fill(0)` begins exactly on the
5
+ // symmetry axis and returns the midpoint, which is a valid solution of the system and a
6
+ // state no physical pedal holds. Nothing is failing to converge, so no iteration cap and
7
+ // no tolerance touches it — the initial guess *chooses the answer*, and the only fix is to
8
+ // make that guess data rather than a convention.
9
+ //
10
+ // Measured on the corpus: three compiled packets sit on the axis, at `1.74723 / 1.74723`,
11
+ // `1.78645 / 1.78645` and `3.13363 / 3.13363`, their two halves agreeing to between `2e-11`
12
+ // and `4e-6`. One of them is the corpus's only ngspice `disagrees`, one is near-silent, and
13
+ // the third **agrees with ngspice at `corr=1.0000`** — because the deck carries the same
14
+ // stamps and ngspice converges to the same midpoint. Parity cannot see this class.
15
+ //
16
+ // See `docs/troubleshootings/boss-hm-2-linear-where-ngspice-distorts.md` for what the
17
+ // midpoint does downstream: both collectors half-high bias every switching JFET
18
+ // identically, so the bypass and effect paths conduct at once and which dominates is
19
+ // settled by rounding.
20
+ /**
21
+ * Enough forward bias to put one base into conduction and let the pair fall to that side.
22
+ *
23
+ * Measured rather than chosen: seeding a single base at this value converges to a latched
24
+ * state in 16 iterations, the same count as the symmetric solve, and the mirrored seed
25
+ * reaches the mirrored state. Its exact value is not delicate — it has to be a base-emitter
26
+ * voltage that conducts, and anything that does selects the same basin.
27
+ */
28
+ const LATCH_SEED_VOLTS = 0.65;
29
+ export function findBistableLatches(netlist) {
30
+ const rails = new Set([0]);
31
+ for (const device of netlist.devices) {
32
+ if (device.kind === "voltage-source" || device.kind === "rail") {
33
+ for (const node of device.nodes) {
34
+ rails.add(node);
35
+ }
36
+ }
37
+ }
38
+ const transistors = netlist.devices.flatMap((device) => {
39
+ if (device.kind !== "bjt") {
40
+ return [];
41
+ }
42
+ const collector = terminal(device, "collector");
43
+ const base = terminal(device, "base");
44
+ const emitter = terminal(device, "emitter");
45
+ if (collector === null || base === null || emitter === null) {
46
+ return [];
47
+ }
48
+ // Requirement 3, on the two nodes that carry the state. The emitter is exempt.
49
+ if (rails.has(collector) || rails.has(base)) {
50
+ return [];
51
+ }
52
+ return [{ id: String(device.id), collector, base, emitter }];
53
+ });
54
+ const coupledBetween = (a, b) => a === b ||
55
+ netlist.devices.some((device) => device.kind === "resistor" &&
56
+ device.nodes.length === 2 &&
57
+ device.nodes.includes(a) &&
58
+ device.nodes.includes(b));
59
+ const pairs = [];
60
+ for (let i = 0; i < transistors.length; i += 1) {
61
+ for (let j = i + 1; j < transistors.length; j += 1) {
62
+ const a = transistors[i];
63
+ const b = transistors[j];
64
+ if (a === undefined || b === undefined) {
65
+ continue;
66
+ }
67
+ if (a.emitter !== b.emitter) {
68
+ continue;
69
+ }
70
+ if (!coupledBetween(a.collector, b.base) ||
71
+ !coupledBetween(b.collector, a.base)) {
72
+ continue;
73
+ }
74
+ pairs.push({ a, b });
75
+ }
76
+ }
77
+ // Requirement 4. A transistor in two candidate pairs makes all of them ambiguous.
78
+ const appearances = new Map();
79
+ for (const pair of pairs) {
80
+ for (const member of [pair.a, pair.b]) {
81
+ appearances.set(member.id, (appearances.get(member.id) ?? 0) + 1);
82
+ }
83
+ }
84
+ const latches = [];
85
+ for (const pair of pairs) {
86
+ if ([pair.a, pair.b].some((m) => (appearances.get(m.id) ?? 0) > 1)) {
87
+ continue;
88
+ }
89
+ // Prefer the side that engages the effect. Falling back to the lower base id keeps a
90
+ // latch this cannot read out of the unphysical midpoint, which is the seed's floor.
91
+ const engagedBaseNode = engagedBase(netlist, rails, pair.a, pair.b) ??
92
+ Math.min(pair.a.base, pair.b.base);
93
+ const engaged = engagedBaseNode === pair.a.base ? pair.a : pair.b;
94
+ const bypassed = engagedBaseNode === pair.a.base ? pair.b : pair.a;
95
+ const bypassControls = (netlist.controls ?? []).filter((c) => c.role === "bypass");
96
+ let controlId = bypassControls.length === 1 ? (bypassControls[0]?.id ?? null) : null;
97
+ if (controlId === null) {
98
+ for (const dev of netlist.devices) {
99
+ if (dev.kind === "switch" && dev.control !== null) {
100
+ controlId = dev.control;
101
+ break;
102
+ }
103
+ }
104
+ }
105
+ latches.push({
106
+ controlId,
107
+ engagedBase: engaged.base,
108
+ bypassedBase: bypassed.base,
109
+ emitter: pair.a.emitter,
110
+ engagedCollector: engaged.collector,
111
+ bypassedCollector: bypassed.collector,
112
+ engagedTransistorId: engaged.id,
113
+ bypassedTransistorId: bypassed.id,
114
+ });
115
+ }
116
+ return latches;
117
+ }
118
+ export function findLatchSeeds(netlist) {
119
+ const latches = findBistableLatches(netlist);
120
+ const seeds = [];
121
+ const seeded = new Set();
122
+ for (const latch of latches) {
123
+ if (seeded.has(latch.engagedBase)) {
124
+ continue;
125
+ }
126
+ seeded.add(latch.engagedBase);
127
+ seeds.push({ node: latch.engagedBase, volts: LATCH_SEED_VOLTS });
128
+ }
129
+ return seeds;
130
+ }
131
+ /**
132
+ * Which member of the pair is high when the pedal is **engaged**, or `null` when that is not
133
+ * derivable from connectivity.
134
+ *
135
+ * Ported from the old spine's `discrete-bypass-latch.ts`, whose rule this repository already
136
+ * paid for: a latch's side is a control fact, and the two questions that decide it are which
137
+ * switches each collector gates, and which of those switch paths carries the audio the user
138
+ * adjusts. Every refusal below is one the old rule names, and returning `null` for all of
139
+ * them is deliberate — an undecidable side must fall back to *a* physical state rather than a
140
+ * confident guess, because being wrong here silently bypasses the pedal.
141
+ *
142
+ * Two edge sets, and the split matters:
143
+ *
144
+ * - **DC edges are resistors and diodes only.** Capacitors are excluded because a bistable's
145
+ * own cross-coupling capacitors, plus every supply bypass capacitor, would bridge the whole
146
+ * circuit and make both collectors appear to gate everything.
147
+ * - **Audio edges include capacitors**, since that is how a signal path is coupled, but
148
+ * exclude the switch channels themselves so a side cannot reach the controls *through* its
149
+ * counterpart.
150
+ *
151
+ * Both floods are blocked by rails, the same exclusion the rest of this file makes.
152
+ */
153
+ function engagedBase(netlist, rails, a, b) {
154
+ const dcEdges = edgesOf(netlist, ["resistor", "diode"]);
155
+ const switches = netlist.devices.flatMap((switching) => {
156
+ if (switching.kind !== "jfet" && switching.kind !== "mosfet") {
157
+ return [];
158
+ }
159
+ const gate = terminal(switching, "gate");
160
+ const drain = terminal(switching, "drain");
161
+ return gate === null || drain === null
162
+ ? []
163
+ : [{ id: String(switching.id), gate, drain }];
164
+ });
165
+ const gatedBy = (collector) => {
166
+ const reached = flood(collector, dcEdges, rails);
167
+ return switches
168
+ .filter((candidate) => reached.has(candidate.gate))
169
+ .map((candidate) => candidate.id)
170
+ .sort();
171
+ };
172
+ const gatedA = gatedBy(a.collector);
173
+ const gatedB = gatedBy(b.collector);
174
+ // Neither side drives a switch: not a switched bypass latch at all. A latch driving only
175
+ // an indicator lands here, correctly.
176
+ if (gatedA.length === 0 && gatedB.length === 0) {
177
+ return null;
178
+ }
179
+ // Both sides gate the same switches, so the sides are indistinguishable.
180
+ if (gatedA.length === gatedB.length &&
181
+ gatedA.every((id, index) => id === gatedB[index])) {
182
+ return null;
183
+ }
184
+ const switchIds = new Set([...gatedA, ...gatedB]);
185
+ const audioEdges = edgesOf(netlist, [
186
+ "resistor",
187
+ "capacitor",
188
+ "inductor",
189
+ "potentiometer",
190
+ ]).filter((edge) => !switchIds.has(edge.id));
191
+ // A control-bound device is one a knob varies, which is a typed field rather than a name.
192
+ const controlNodes = netlist.devices.flatMap((device) => device.control === null ? [] : device.nodes);
193
+ const reachesAControl = (gated) => gated.some((switchId) => {
194
+ const found = switches.find((candidate) => candidate.id === switchId);
195
+ if (found === undefined) {
196
+ return false;
197
+ }
198
+ const reached = flood(found.drain, audioEdges, rails);
199
+ return controlNodes.some((node) => reached.has(node));
200
+ });
201
+ const engagedA = reachesAControl(gatedA);
202
+ const engagedB = reachesAControl(gatedB);
203
+ if (engagedA === engagedB) {
204
+ return null;
205
+ }
206
+ return engagedA ? a.base : b.base;
207
+ }
208
+ /** Undirected two-terminal edges of the given kinds; a pot contributes one per terminal pair. */
209
+ function edgesOf(netlist, kinds) {
210
+ const wanted = new Set(kinds);
211
+ const edges = [];
212
+ for (const device of netlist.devices) {
213
+ if (!wanted.has(device.kind)) {
214
+ continue;
215
+ }
216
+ for (let i = 0; i < device.nodes.length; i += 1) {
217
+ for (let j = i + 1; j < device.nodes.length; j += 1) {
218
+ const a = device.nodes[i];
219
+ const b = device.nodes[j];
220
+ if (a !== undefined && b !== undefined && a !== b) {
221
+ edges.push({ a, b, id: String(device.id) });
222
+ }
223
+ }
224
+ }
225
+ }
226
+ return edges;
227
+ }
228
+ /** Reachable nodes, never entering a blocked node. */
229
+ function flood(start, edges, blocked) {
230
+ if (blocked.has(start)) {
231
+ return new Set();
232
+ }
233
+ const seen = new Set([start]);
234
+ const queue = [start];
235
+ while (queue.length > 0) {
236
+ const node = queue.pop();
237
+ for (const edge of edges) {
238
+ const other = edge.a === node ? edge.b : edge.b === node ? edge.a : null;
239
+ if (other === null || seen.has(other) || blocked.has(other)) {
240
+ continue;
241
+ }
242
+ seen.add(other);
243
+ queue.push(other);
244
+ }
245
+ }
246
+ return seen;
247
+ }
248
+ /** A terminal's node by role token, or `null` when the role is absent or repeated. */
249
+ function terminal(device, role) {
250
+ const found = device.identity.terminalRoles.flatMap((candidate, index) => candidate === role ? [index] : []);
251
+ if (found.length !== 1) {
252
+ return null;
253
+ }
254
+ const node = device.nodes[found[0]];
255
+ return node === undefined ? null : node;
256
+ }
257
+ //# sourceMappingURL=latch-seed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"latch-seed.js","sourceRoot":"","sources":["../src/latch-seed.ts"],"names":[],"mappings":"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"}
package/dist/link.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { StageRefusal } from "./errors";
2
+ import type { Block, Control, Partitioning, Ports, Program, ProgramBypass } from "./types";
3
+ export declare class LinkError extends StageRefusal {
4
+ constructor(message: string);
5
+ }
6
+ export declare function link(blocks: readonly Block[], partitioning: Partitioning, controls: readonly Control[], ports: Ports, portImpedanceOhms?: Program["portImpedanceOhms"], portDeclaredFullScaleVolts?: Program["portImpedanceOhms"], bypass?: ProgramBypass): Program;
7
+ //# sourceMappingURL=link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../src/link.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAIxC,OAAO,KAAK,EACX,KAAK,EACL,OAAO,EAGP,YAAY,EACZ,KAAK,EACL,OAAO,EACP,aAAa,EACb,MAAM,SAAS,CAAC;AAEjB,qBAAa,SAAU,SAAQ,YAAY;gBAC9B,OAAO,EAAE,MAAM;CAG3B;AAED,wBAAgB,IAAI,CACnB,MAAM,EAAE,SAAS,KAAK,EAAE,EACxB,YAAY,EAAE,YAAY,EAC1B,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,KAAK,EAAE,KAAK,EAGZ,iBAAiB,GAAE,OAAO,CAAC,mBAAmB,CAG7C,EAGD,0BAA0B,GAAE,OAAO,CAAC,mBAAmB,CAGtD,EACD,MAAM,GAAE,aAAoC,GAC1C,OAAO,CAkDT"}