@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
package/README.md CHANGED
@@ -1,3 +1,315 @@
1
- # Temporary Holding Version
1
+ # @vessel-dsp/compiler
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Headless circuit compiler turning `.vdsp` / `CircuitDocument` schematic data into compiled simulation `Program` ROMs for VesselDSP. Pure TypeScript, deterministic, rate independent, headless. It depends on `@vessel-dsp/core` and `js-yaml` only.
4
+
5
+ ## Install
6
+
7
+ `@vessel-dsp/compiler` 0.1.0 is not yet on npm. Until the release is published, use the package from this repository's workspace rather than an install command that will 404.
8
+
9
+ ```bash
10
+ # once published:
11
+ bun add @vessel-dsp/compiler
12
+ ```
13
+
14
+ ## Minimal example
15
+
16
+ Compile takes `.vdsp` source text and an optional part registry. It returns a result, never a throw, for a document it cannot use.
17
+
18
+ ```ts
19
+ import { readFileSync } from "node:fs";
20
+ import { compile, pedalPartCatalog } from "@vessel-dsp/compiler";
21
+
22
+ const source = readFileSync("big-muff-pi.vdsp", "utf8");
23
+ const result = compile(source, { registry: pedalPartCatalog });
24
+
25
+ if (result.status === "ok") {
26
+ console.log("Compiled program blocks:", result.program.blocks.length);
27
+ console.log("Warnings:", result.warnings.length);
28
+ } else {
29
+ console.error("Compilation refused:", result.reasons);
30
+ }
31
+ ```
32
+
33
+ ```text
34
+ Compiled program blocks: 1
35
+ Warnings: 0
36
+ ```
37
+
38
+ ## Which registry to pass
39
+
40
+ `compile(source, options)` defaults to `emptyRegistry` when no registry is given. The empty registry models only device class laws (resistor, capacitor, plain op-amp stages) and refuses parts that need catalog identity. `pedalPartCatalog` is the corpus facing catalog injected by scripts and tests.
41
+
42
+ Measured over the 121 top level packets in the artifacts corpus (read only, not committed):
43
+
44
+ - `emptyRegistry`: 74 of 121 compile.
45
+ - `pedalPartCatalog`: 119 of 121 compile. The two refusals are `boss-dd-3t.vdsp` and `boss-dd-5.vdsp`.
46
+
47
+ Use `pedalPartCatalog` for real pedal packets. Use `emptyRegistry` only to measure what works with no part knowledge, or for synthetic device class probes.
48
+
49
+ One refusal shape, for `boss-dd-3t.vdsp` with `pedalPartCatalog`:
50
+
51
+ ```text
52
+ [{ "stage": "device-laws", "device": "U1",
53
+ "reason": "integrated circuit has no model: its part number
54
+ \"undisclosed-roland-boss-dsp\" matched no registry entry, and a declared
55
+ class may not supply a macro for a part whose identity is unresolved" }]
56
+ ```
57
+
58
+ ## Result shape
59
+
60
+ `compile` returns `CompileResult`:
61
+
62
+ - `{ status: "ok", program, warnings }`. Warnings never block a program. A pedal with a dead knob is still worth hearing, but it must not be silent about it.
63
+ - `{ status: "unsupported", reasons }`. Each reason is a `CompileRefusal` with `stage` (`netlist`, `device-laws`, `partition`, `lower`, or `link`), `device` (component id or null), and a human readable `reason`. `device-laws` means a part could not be modelled; any other stage means the document itself does not support a program.
64
+
65
+ Only a `StageRefusal` becomes a result. Anything else (a compiler bug) propagates as a throw, because a bug reported as an unsupported pedal would be a wrong answer about someone's circuit.
66
+
67
+ `compileToArtifact(source, options)` runs `compile` and pairs it with `emit`: `{ result, artifact }` where `artifact` is null unless the result is ok. `decode(text)` parses an artifact back into a `Program`.
68
+
69
+ `readNetlist(source, options?)` runs the first stage only: document text to a law free `Netlist` with `inputJack`/`outputJack` overrides and `parseQuantity` for SI values (`10k` to 10000).
70
+
71
+ ## The Program
72
+
73
+ A `Program` is rate independent: it carries no sample rate. The runtime supplies the rate at `prepare()`. Top level keys:
74
+
75
+ `formatVersion` (always 1), `requiredOperators`, `requiredModels`, `costPredictors`, `blocks`, `order`, `controls`, `ports`, `supplyReference`, `portReferenceVolts`, `portFullScaleVolts`, `stageCoverage`, `portImpedanceOhms`.
76
+
77
+ `requiredOperators` lists every stamp operator the executed blocks present, sorted, so a runtime can refuse by name at load. `requiredModels` lists every macro DSP model id the same way. `order` is the block execution order. `supplyReference` is `negative-ground`, `positive-ground`, `dual-rail`, or `unpowered`, derived from `dc-source` signs. `stageCoverage` is `instrument`, `preamp`, `speaker-electrical`, or `miked`.
78
+
79
+ `big-muff-pi.vdsp` with `pedalPartCatalog` compiles to 1 block (`order: ["analog:0"]`) with stamp kinds `dc-source` 1, `bjt` 4, `conductance` 22, `capacitor` 13, `controlled-conductance` 6, `diode` 4, `input-source` 1. Controls are `IN`, `OUT`, `SUSTAIN`, `TONE`, `VOLUME`, each 0..1 with default 0.5.
80
+
81
+ Each `mna` block carries `nodeIds` (row to source node map), `auxCount`, `stamps`, `stampPartition`, `sparseSchedule`, `stateCount`, `linear`, `controlFree`, `eliminate`, input/output nodes, and operating point seeds. A `macro` block carries a DSP `modelId` with parameters instead of stamps.
82
+
83
+ ## Supply stamps
84
+
85
+ `resolveSupplyStamps(source, program)` joins the document power domains to the compiled `dc-source` stamps. It takes the same source text the program was compiled from and returns `{ supplies, refused }`. Only rails in an external DC domain with `derivation: direct` and `role: main-supply` are candidates; derived rails (divider, regulator, inverter, doubler, isolated) keep their compiled values.
86
+
87
+ Measured over the same 121 packets with `pedalPartCatalog`: 54 packets resolve at least one supply. Refusals by entry: `derived-rail` 60, `no-power-section` 42, `no-stamp-for-rail` 17, `mains-ac-source` 7, `unknown-source-kind` 4, plus 2 packets that do not compile at all. `big-muff-pi` resolves `VPLUS_RAIL` with role `main-supply`.
88
+
89
+ The closed refusal reasons are `no-power-section`, `mains-ac-source`, `unknown-source-kind`, `source-kind-conflict`, `derived-rail`, `rail-not-main-supply`, `no-stamp-for-rail`, `ambiguous-stamp`. Callers switch on `reason`; `detail` is prose for a human report.
90
+
91
+ ## Public exports
92
+
93
+ ### Compile
94
+
95
+ | Name | What it is |
96
+ | --- | --- |
97
+ | `compile` | Compile, or explain the refusal. Never throws for a document it cannot use. |
98
+ | `compileToArtifact` | `compile` plus `emit`, paired as `{ result, artifact }`. |
99
+ | `CompileOptions` | `{ registry, inputJack, outputJack }`. |
100
+ | `CompileResult` | `CompileSuccess` or `CompileFailure`. |
101
+ | `CompileSuccess` | `{ status: "ok", program, warnings }`. |
102
+ | `CompileFailure` | `{ status: "unsupported", reasons }`. |
103
+ | `CompileRefusal` | `{ stage, device, reason }`. |
104
+ | `CompilerStage` | `netlist`, `partition`, `lower`, or `link`. |
105
+ | `StageRefusal` | Stage error that becomes a result instead of a throw. |
106
+ | `LoweringError` | A device declared with a terminal count lowering cannot stamp. |
107
+ | `NetlistError` | Netlist stage refusal. |
108
+ | `PartitionError` | Partition stage refusal. |
109
+ | `LinkError` | Link stage refusal. |
110
+
111
+ ### Artifacts
112
+
113
+ | Name | What it is |
114
+ | --- | --- |
115
+ | `emit` | Render a `Program` to its serializable `Artifact`. |
116
+ | `decode` | Parse an artifact back into a `Program`. |
117
+ | `Artifact` | The serializable form of a compiled program. |
118
+
119
+ ### Netlist
120
+
121
+ | Name | What it is |
122
+ | --- | --- |
123
+ | `readNetlist` | First stage only: source text to a law free `Netlist`. |
124
+ | `ReadNetlistOptions` | `{ inputJack, outputJack }` overrides. |
125
+ | `Netlist` | Devices, controls, ports, and port metadata before laws attach. |
126
+ | `Device` | One lowered device with kind, identity, terminals, and parameters. |
127
+ | `DeviceId` | Component id string. |
128
+ | `DeviceKind` | What a device is electrically (resistor through power-amp, plus sources, rails, gates, BBD, compandor). |
129
+ | `DeviceIdentity` | Evidence a part is recognised by, in stage 2 order. |
130
+ | `DeviceLaw` | A lumped constitutive relation the solver can stamp. |
131
+ | `DeviceResolution` | Per device law outcome, including unsupported with reason. |
132
+ | `LawedNetlist` | Netlist plus per device resolutions. |
133
+ | `parseQuantity` | Parse a quantity to SI base units (`10k` to 10000). |
134
+ | `dcConductingKinds` | DC conducting device kinds, for asking whether a node potential is generated. |
135
+ | `deviceCountByNode` | How many devices touch each node. |
136
+ | `DeclaredWinding` | One declared coil: role, name, terminals in coil order. |
137
+ | `DeclaredWindingImpedance` | One rated coil impedance in ohms. |
138
+
139
+ ### Registries
140
+
141
+ | Name | What it is |
142
+ | --- | --- |
143
+ | `emptyRegistry` | No part knowledge; measures what device class laws alone cover. |
144
+ | `pedalPartCatalog` | The catalog corpus facing scripts inject. |
145
+ | `PartRegistry` | `{ entries }` consulted by stage 2. |
146
+ | `PartEntry` | One catalog entry binding identity to a model. |
147
+ | `PartModel` | Law, sections, or macro model a part supplies. |
148
+ | `PartSection` | One section of a multi section part with its terminals. |
149
+ | `PartPinout` | Terminal order a sections model indices assume. |
150
+ | `PartIdentity` | A resolved part identity. |
151
+ | `MacroPartModel` | Macro model a registered part supplies. |
152
+ | `MacroPortRoles` | Where a macro ports sit, named by terminal role. |
153
+ | `MacroBarePinPositions` | Physical terminal positions for pin numbered documents. |
154
+ | `registryEntryFor` | Entry lookup for a device. |
155
+ | `registryModelFor` | Model lookup for a device. |
156
+ | `registryLawFor` | The law a registered part supplies for a device. |
157
+ | `terminalWithRole` | Index of the terminal whose role matches, or null. |
158
+ | `pinoutMatches` | Whether a device declares the order an entry was written against. |
159
+ | `foldPartId` | Fold case and separators (`MN-3007` is `mn3007`). |
160
+ | `foldToken` | Fold case and whitespace for typed vocabulary values. |
161
+ | `recordedReads` | What each owner consulted. |
162
+ | `withRecordedReads` | Run a function with registry reads recorded. |
163
+ | `clearRecordedReads` | Forget everything recorded so far. |
164
+
165
+ ### Supply stamps
166
+
167
+ | Name | What it is |
168
+ | --- | --- |
169
+ | `resolveSupplyStamps` | Join document power domains to compiled `dc-source` stamps. |
170
+ | `SupplyAddress` | `{ blockIndex, sourceIndex }` naming one `dc-source` stamp. |
171
+ | `ResolvedSupply` | Address plus rail id, role, and nominal volts. |
172
+ | `RefusedSupply` | Unmapped rail with reason and human readable detail. |
173
+ | `SupplyResolution` | `{ supplies, refused }`. |
174
+ | `SupplyRefusalReason` | Closed union of eight refusal reasons. |
175
+
176
+ ### Partition, lower, link, couple
177
+
178
+ | Name | What it is |
179
+ | --- | --- |
180
+ | `partition` | Split the lawed netlist into regions. |
181
+ | `Partitioning` | Regions plus dependencies. |
182
+ | `Region` | One coupled subgraph with its kind. |
183
+ | `RegionKind` | `linear`, `nonlinear`, or `macro`. |
184
+ | `lower` | Turn regions into MNA stamp blocks. |
185
+ | `lowerRegion` | Lower one region. |
186
+ | `link` | Order blocks and attach program metadata. |
187
+ | `couple` | Wire coupled and parameter ports after every region exists. |
188
+ | `Coupled` | Coupled seam record. |
189
+ | `blockNodeIndex` | A block row index for a source node id, or null. |
190
+
191
+ ### Stamps, blocks, program
192
+
193
+ | Name | What it is |
194
+ | --- | --- |
195
+ | `Program` | The compiled ROM: operators, models, blocks, order, controls, ports, supply reference, scales, coverage. |
196
+ | `Block` | One `mna` solve block or one `macro` DSP block. |
197
+ | `Stamp` | One MNA contribution (conductance through switch families). |
198
+ | `OperatorKind` | A stamp kind a runtime must implement. |
199
+ | `StampPartition` | Compile time linear, control, constant, and dynamic partition. |
200
+ | `SparseSchedule` | Compile time static sparse elimination schedule. |
201
+ | `stampControl` | Control a stamp reads, or null. |
202
+ | `stampNeedsNewton` | Whether a stamp needs the Newton loop. |
203
+ | `stampNodes` | Every node a stamp names. |
204
+ | `stampInSourceNodes` | A stamp read back in document node ids. |
205
+ | `withStamp` | Add a stamp with linear and controlFree re-derived. |
206
+ | `Ports` | Input and output node ids. |
207
+ | `CostPredictors` | Rate independent cost facts for admission. |
208
+ | `OperatingPointSeed` | A nodeset style hint for the DC solve start. |
209
+ | `NodeId` | Electrical node number. |
210
+ | `GROUND` | Node 0. |
211
+ | `ModulationPort` | Solved node read every sample to scale a delay macro. |
212
+ | `ParameterPort` | Scalar another operator derives. |
213
+ | `MacroModel` | A region the compiler does not solve. |
214
+ | `MacroClockControl` | Clock control mapping for a macro block. |
215
+ | `SUPPLY_SOURCE_OHMS` | Default series impedance of a pedal supply in ohms (1). |
216
+ | `STATE_SLOTS_PER_REACTIVE_ELEMENT` | State slots a reactive element companion model needs. |
217
+ | `THERMAL_VOLTAGE` | Room temperature thermal voltage for the diode law. |
218
+ | `SILICON_SATURATION_CURRENT` | Default silicon saturation current. |
219
+ | `SILICON_FORWARD_BETA` | Default silicon forward beta (100). |
220
+ | `SWITCH_ON_OHMS` | Closed switch resistance. |
221
+ | `SWITCH_OFF_OHMS` | Open switch resistance. |
222
+
223
+ ### Controls and tapers
224
+
225
+ | Name | What it is |
226
+ | --- | --- |
227
+ | `Control` | Compiled control with id, taper, default position, role, label. |
228
+ | `ControlId` | Control id string. |
229
+ | `TaperKind` | `linear`, `logarithmic`, `reverse-logarithmic`, or `reverse-linear`. |
230
+
231
+ ### Clocks, timing, delay lines
232
+
233
+ | Name | What it is |
234
+ | --- | --- |
235
+ | `deriveBbdDelayFromNetlist` | Derive BBD delay from the clock network. |
236
+ | `derivePt2399DelayFromNetlist` | Derive PT2399 delay from the VCO resistor network. |
237
+ | `deriveM50195DelayFromNetlist` | Derive M50195P delay from the oscillator network. |
238
+ | `pt2399DelayFromOhms` | PT2399 delay formula from VCO resistance. |
239
+ | `identifyClockFamily` | Clock driver family for a device. |
240
+ | `ClockDriverFamily` | BBD clock family. |
241
+ | `clockTimingNodes` | Nodes a clock driver timing network hangs on. |
242
+ | `activeTimingDrivers` | Devices actively steering a timing network. |
243
+ | `ACTIVE_TIMING_KINDS` | Device kinds that can drive a timing node. |
244
+ | `findClockDriverDevice` | Locate the clock driver device. |
245
+ | `resolveClockModulationSource` | Node an LFO reaches a modulated BBD clock on. |
246
+ | `ClockModulationSource` | That modulation source. |
247
+ | `BbdClockDerivationOutcome` | BBD clock derivation result. |
248
+
249
+ ### Device helpers
250
+
251
+ | Name | What it is |
252
+ | --- | --- |
253
+ | `attachDeviceLaws` | Stage 2: attach laws to every netlist device. |
254
+ | `declaredRegulatorTerminals` | Output and reference terminals of a regulator model. |
255
+ | `isElectricallyIsolated` | Whether every device node is private to itself. |
256
+ | `opampInputDistances` | Op-amp input network distances. |
257
+ | `quietDistances` | Quiet network distances. |
258
+ | `optocouplerBinding` | Which rung decided an optocoupler wiring. |
259
+ | `otaPackageOrderAssumed` | Whether an OTA used DIP-8 package order. |
260
+ | `potTerminals` | Potentiometer terminal roles. |
261
+ | `synthesizeOpampImplicitBias` | Add implicit DC bias where topology leaves it undefined. |
262
+ | `stageCoverage` | Amplification stage coverage read from lowered stamps. |
263
+
264
+ ### Diagnostics (warnings and their finders)
265
+
266
+ | Name | What it is |
267
+ | --- | --- |
268
+ | `CompileWarning` | Union of every warning shape. |
269
+ | `findInertControls` | A pot whose wiper touches nothing is a fixed resistor. |
270
+ | `InertControlWarning` | Why a control cannot move the circuit. |
271
+ | `ControlPositionDisagreesWarning` | Panel and component disagree about a control setting. |
272
+ | `findUnboundedOpamps` | Op-amps with no DC feedback path. |
273
+ | `UnboundedOpampWarning` | Op-amp on the signal path with no DC feedback. |
274
+ | `findSupplyShorts` | Diodes forward across a supply. |
275
+ | `SupplyShortWarning` | A diode wired forward across a supply. |
276
+ | `findOverDrivenNodes` | Nodes driven by more than one ideal source. |
277
+ | `OverDrivenNodeWarning` | Over-determined node. |
278
+ | `findDanglingActiveTerminals` | Active terminals on untouched nodes. |
279
+ | `DanglingActiveTerminalWarning` | Active device terminal that cannot conduct. |
280
+ | `findGroundedClockSupplies` | Clock drivers with supply at ground. |
281
+ | `ClockDriverSupplyAtGroundWarning` | Clock driver rendering no clock at all. |
282
+ | `findUnimplementedDeviceLaws` | Devices with no law, told by name. |
283
+ | `UnimplementedDeviceLawWarning` | Device class with no law. |
284
+ | `UnimplementedControlRoleWarning` | Control role with no law: accepted, lowered, ignored. |
285
+ | `findUnreachableOutput` | Outputs reachable by nothing. |
286
+ | `OutputPortUnreachableWarning` | Program executing no blocks. |
287
+ | `OutputPortNotTransformerCoupledWarning` | Output measured before the power stage. |
288
+ | `NoOutputTransformerWarning` | Amp shaped document with no output transformer. |
289
+ | `findUnexecutedRegions` | Regions the program never executes. |
290
+ | `UnexecutedActiveRegionWarning` | Active region never executed. |
291
+ | `findIcsNotExecuted` | Declared ICs the program does not execute. |
292
+ | `IcNotExecutedWarning` | Declared but unexecuted IC. |
293
+ | `findElectricallyIsolatedIcs` | Identification devices with all private nodes. |
294
+ | `ElectricallyIsolatedIcWarning` | Isolated device given an open law. |
295
+ | `OpenIcReason` | Why a device got an open law. |
296
+ | `findPowerDomainControls` | Power hardware exposed as sweepable controls. |
297
+ | `PowerDomainControlWarning` | Power domain hardware outside the audio graph. |
298
+ | `findNonExecutableClockDrivers` | Clock drivers bypassed by the MNA solver. (finder; warning type `NonExecutableClockDriverWarning`) |
299
+ | `NonExecutableClockDriverWarning` | Clock behavior not modelled. |
300
+ | `NonExecutableSupportShellWarning` | Support shell preserved but not executed. |
301
+ | `findGenericTubeFits` | Tubes lowered with device class fits. (finder; types below) |
302
+ | `GenericTubeFitWarning` | Tube lowered without a catalog match. |
303
+ | `GenericSpeakerProfileWarning` | Speaker load with a generic driver profile. |
304
+ | `findUnverifiedPinoutBindings` | Parts wired by declaration order. (finder; type below) |
305
+ | `UnverifiedPinoutBindingWarning` | Terminals placed by order, not assertion. |
306
+ | `UnreadableTerminalRoleWarning` | Terminals wired by order after an unplaceable spelling. |
307
+ | `UnroutableThrowWarning` | Switch throw alone on its node. |
308
+ | `InterfaceOrSourceOnlyWarning` | Interface or source only component. |
309
+ | `UnconnectedBehaviorComponentWarning` | Behavior component with no connection to the audio graph. |
310
+ | `NotPopulatedWarning` | DNP position with nothing fitted. |
311
+ | `NotPopulatedWithoutValueWarning` | DNP component with no value. |
312
+ | `LedgerDivergenceWarning` | Nodes ledger and terminal refs disagree. |
313
+ | `DeclaredDelayWarning` | Delay typed into the source, not derived. |
314
+ | `ModulationNotModelledWarning` | Wired modulation input admission refused. |
315
+ | `TaperNotExecutableWarning` | Taper that renders linear. |
package/dist/bake.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ import type { Block } from "./types";
2
+ /** Why a block's realisation may not be baked, naming the block and the failing flag. */
3
+ export type BakeRefusal = {
4
+ readonly block: string;
5
+ readonly reason: string;
6
+ };
7
+ export type BakeDecision = {
8
+ readonly outcome: "may-bake";
9
+ } | {
10
+ readonly outcome: "refused";
11
+ readonly refusal: BakeRefusal;
12
+ };
13
+ /**
14
+ * May this block's realisation be derived once and reused across samples?
15
+ *
16
+ * Only when `linear && controlFree` both hold. Checked in that order because a `linear`
17
+ * failure is the more fundamental one -- a nonlinear block has no fixed realisation to derive
18
+ * at all, `linear && controlFree` or not -- so naming it first is the more useful refusal even
19
+ * though a real block can fail either independently (see `bake.test.ts`'s four-quadrant cases,
20
+ * matching `lower.test.ts`'s own `resistorDivider`/`potDivider`/`switchedDivider`/
21
+ * `diodeClipper` fixtures for `linear`/`controlFree`'s own precedent).
22
+ */
23
+ export declare function bakeDecisionFor(block: Extract<Block, {
24
+ readonly kind: "mna";
25
+ }>,
26
+ /**
27
+ * Whether every supply this block references holds still across the samples the realisation
28
+ * would be reused over.
29
+ *
30
+ * `true` for every block the compiler builds today — a rail is ground or a node inside this
31
+ * block's own matrix, and sag inside the matrix is already exact in a linear realisation.
32
+ * The reducer must pass `false` the day a rail is driven from outside the block, whichever
33
+ * shape that plumbing takes.
34
+ */
35
+ supply?: {
36
+ readonly static: boolean;
37
+ }): BakeDecision;
38
+ //# sourceMappingURL=bake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bake.d.ts","sourceRoot":"","sources":["../src/bake.ts"],"names":[],"mappings":"AAmDA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,yFAAyF;AACzF,MAAM,MAAM,WAAW,GAAG;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,YAAY,GACrB;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAA;CAAE,GAChC;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC;AAElE;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC9B,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CAAE,CAAC;AAC/C;;;;;;;;GAQG;AACH,MAAM,GAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CAAqB,GACrD,YAAY,CA6Bd"}
package/dist/bake.js ADDED
@@ -0,0 +1,101 @@
1
+ // Spec clause 5: `linear && controlFree`, jointly, as the precondition for baking a
2
+ // realisation -- and NOTHING ELSE. No new field: `Block.linear` and `Block.controlFree`
3
+ // already exist (`src/compiler/types.ts`, computed in `lower.ts` from the exhaustive,
4
+ // `never`-bound `stampNeedsNewton`/`stampControl` classifiers), and this file is the
5
+ // instruction to read them as a conjunction rather than separately, at the one place a
6
+ // reduction may ever be baked and reused across samples.
7
+ //
8
+ // **This gate has no caller today, and that is honest rather than incomplete.** Nothing in
9
+ // this compiler reduces an `mna` block into a state-space realisation -- that is step 5's own
10
+ // future work, and it does not exist yet (build-order steps 4 and 5 are unbuilt; clause 4,
11
+ // declared output nodes, has nothing to govern until something reduces). `bakeDecisionFor`
12
+ // is the precondition function that future stage MUST call before it bakes anything, and it
13
+ // is written, tested and wired into the plan's documentation now rather than left implicit,
14
+ // so the precondition cannot be forgotten the day someone builds the reducer. The insertion
15
+ // point is between `lower`/`couple` and `link` in `compile.ts`'s pipeline -- after a block's
16
+ // stamps are final and its `linear`/`controlFree` flags are settled, before anything is
17
+ // scheduled to run.
18
+ //
19
+ // **Why the conjunction, not either flag alone.** `linear-core-state-space-gate` measured
20
+ // H0 (a linear reduction is exact) fully holding, and H2 ("linear is sufficient on its own")
21
+ // false: its `load-shift` mutation -- a realisation derived at one load, evaluated at
22
+ // another -- errs 80% of the signal (`4.19e-02` against a `5.23e-02` reference), because a
23
+ // pot sits *inside* the matrix a realisation is derived from, so a knob move is exactly the
24
+ // kind of topology change a baked realisation cannot track without re-deriving. `linear`
25
+ // alone would have let that 80% error ship silently -- see `bake.test.ts`'s reproduction of
26
+ // this shape on `potDivider`, and `pipeline.test.ts` for the same shape measured end to end.
27
+ //
28
+ // **The supply gap, closed 2026-09-02 as a third condition rather than a fourth flag.** A block
29
+ // can be `linear && controlFree` and still reference a rail that moves with the audio, because
30
+ // `controlFree` is about controls and says nothing about supply state. This used to be recorded
31
+ // here as an undecided gap waiting on the rail-port design. It is not: the gate can refuse the
32
+ // case without knowing how a moving rail will eventually be *plumbed*, and refusing it is what
33
+ // the reducer needs.
34
+ //
35
+ // **Why the caller supplies the answer instead of the block carrying a flag.** A rail that moves
36
+ // cannot cross a block boundary today -- every supply a block references is either ground or a
37
+ // node inside its own matrix, and a rail sagging *inside* a linear block is already exact in the
38
+ // realisation, because the sag is part of the linear system being derived. So a `supplyStatic`
39
+ // field on `Block` would be a constant `true` with no way to test it, and this repository's
40
+ // evidence rule asks for a check that can fail for the reason claimed. Taking it as an argument
41
+ // makes the future reducer state the answer at the point it bakes, keeps the Program contract
42
+ // (and its C++ mirror) unchanged, and is testable today by passing `false`.
43
+ //
44
+ // **What is still open is the port design, and it is deliberately not decided here.** Whether a
45
+ // dynamic rail becomes a fourth port kind or folds into `coupled` is an architecture question
46
+ // whose evidence does not exist yet -- `thoughts/shared/experiments/power-domain-rail-sag-contract/`
47
+ // was written to produce exactly that evidence (sign convention, multi-rail summing, the DK
48
+ // rail-in/current-out contract) and has never been run. This gate is correct under either answer:
49
+ // whichever one lands sets `supply.static` to `false` for a block whose rail is driven from
50
+ // outside it.
51
+ /**
52
+ * May this block's realisation be derived once and reused across samples?
53
+ *
54
+ * Only when `linear && controlFree` both hold. Checked in that order because a `linear`
55
+ * failure is the more fundamental one -- a nonlinear block has no fixed realisation to derive
56
+ * at all, `linear && controlFree` or not -- so naming it first is the more useful refusal even
57
+ * though a real block can fail either independently (see `bake.test.ts`'s four-quadrant cases,
58
+ * matching `lower.test.ts`'s own `resistorDivider`/`potDivider`/`switchedDivider`/
59
+ * `diodeClipper` fixtures for `linear`/`controlFree`'s own precedent).
60
+ */
61
+ export function bakeDecisionFor(block,
62
+ /**
63
+ * Whether every supply this block references holds still across the samples the realisation
64
+ * would be reused over.
65
+ *
66
+ * `true` for every block the compiler builds today — a rail is ground or a node inside this
67
+ * block's own matrix, and sag inside the matrix is already exact in a linear realisation.
68
+ * The reducer must pass `false` the day a rail is driven from outside the block, whichever
69
+ * shape that plumbing takes.
70
+ */
71
+ supply = { static: true }) {
72
+ if (!block.linear) {
73
+ return {
74
+ outcome: "refused",
75
+ refusal: {
76
+ block: block.id,
77
+ reason: `block "${block.id}" is not linear, so it has no fixed realisation to derive at all -- it must keep iterating Newton every sample`,
78
+ },
79
+ };
80
+ }
81
+ if (!block.controlFree) {
82
+ return {
83
+ outcome: "refused",
84
+ refusal: {
85
+ block: block.id,
86
+ reason: `block "${block.id}" is linear but not control-free, so a realisation baked at one control position would be wrong at another -- linear-core-state-space-gate measured this at 80% of the signal (load-shift). Refuse to bake and keep solving the per-sample stamps until re-derivation on control change or knob-grid tabulation exists`,
87
+ },
88
+ };
89
+ }
90
+ if (!supply.static) {
91
+ return {
92
+ outcome: "refused",
93
+ refusal: {
94
+ block: block.id,
95
+ reason: `block "${block.id}" is linear and control-free but references a supply that moves with the audio, and a realisation derived at one rail voltage is wrong at another for the same reason a pot position is -- the rail sits inside the matrix the realisation is derived from. Refuse to bake until the rail is an input the realisation can be re-derived against`,
96
+ },
97
+ };
98
+ }
99
+ return { outcome: "may-bake" };
100
+ }
101
+ //# sourceMappingURL=bake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bake.js","sourceRoot":"","sources":["../src/bake.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,wFAAwF;AACxF,sFAAsF;AACtF,qFAAqF;AACrF,uFAAuF;AACvF,yDAAyD;AACzD,EAAE;AACF,2FAA2F;AAC3F,8FAA8F;AAC9F,2FAA2F;AAC3F,2FAA2F;AAC3F,4FAA4F;AAC5F,4FAA4F;AAC5F,4FAA4F;AAC5F,6FAA6F;AAC7F,wFAAwF;AACxF,oBAAoB;AACpB,EAAE;AACF,0FAA0F;AAC1F,6FAA6F;AAC7F,sFAAsF;AACtF,2FAA2F;AAC3F,4FAA4F;AAC5F,yFAAyF;AACzF,4FAA4F;AAC5F,6FAA6F;AAC7F,EAAE;AACF,gGAAgG;AAChG,+FAA+F;AAC/F,gGAAgG;AAChG,+FAA+F;AAC/F,+FAA+F;AAC/F,qBAAqB;AACrB,EAAE;AACF,iGAAiG;AACjG,+FAA+F;AAC/F,iGAAiG;AACjG,+FAA+F;AAC/F,4FAA4F;AAC5F,gGAAgG;AAChG,8FAA8F;AAC9F,4EAA4E;AAC5E,EAAE;AACF,gGAAgG;AAChG,8FAA8F;AAC9F,qGAAqG;AACrG,4FAA4F;AAC5F,kGAAkG;AAClG,4FAA4F;AAC5F,cAAc;AAcd;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAC9B,KAA+C;AAC/C;;;;;;;;GAQG;AACH,SAAuC,EAAE,MAAM,EAAE,IAAI,EAAE;IAEvD,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO;YACN,OAAO,EAAE,SAAS;YAClB,OAAO,EAAE;gBACR,KAAK,EAAE,KAAK,CAAC,EAAE;gBACf,MAAM,EAAE,UAAU,KAAK,CAAC,EAAE,gHAAgH;aAC1I;SACD,CAAC;IACH,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QACxB,OAAO;YACN,OAAO,EAAE,SAAS;YAClB,OAAO,EAAE;gBACR,KAAK,EAAE,KAAK,CAAC,EAAE;gBACf,MAAM,EAAE,UAAU,KAAK,CAAC,EAAE,wTAAwT;aAClV;SACD,CAAC;IACH,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACpB,OAAO;YACN,OAAO,EAAE,SAAS;YAClB,OAAO,EAAE;gBACR,KAAK,EAAE,KAAK,CAAC,EAAE;gBACf,MAAM,EAAE,UAAU,KAAK,CAAC,EAAE,iVAAiV;aAC3W;SACD,CAAC;IACH,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;AAChC,CAAC"}
@@ -0,0 +1,171 @@
1
+ import type { ControlId, Device, DeviceKind, Netlist, NodeId, TaperKind } from "./types";
2
+ import type { PartRegistry } from "./registry";
3
+ export type ClockDriverFamily = "CD4047" | "MN3101" | "MN3102" | "unknown";
4
+ export type BbdClockDerivationOutcome = {
5
+ readonly outcome: "derived";
6
+ readonly delaySeconds: number;
7
+ readonly rOhms: number;
8
+ readonly cFarads: number;
9
+ readonly fClockHz: number;
10
+ readonly family: ClockDriverFamily;
11
+ readonly stages: number;
12
+ readonly formulaConstant: number;
13
+ readonly offsetSeconds: number;
14
+ readonly controlId: ControlId | null;
15
+ readonly taper: TaperKind;
16
+ readonly ohmsAtControlMin: number;
17
+ readonly ohmsAtControlMax: number;
18
+ } | {
19
+ readonly outcome: "no-clock-driver";
20
+ } | {
21
+ readonly outcome: "refused";
22
+ readonly reason: string;
23
+ };
24
+ /**
25
+ * Device kinds that can *drive* a timing node rather than merely set it.
26
+ *
27
+ * A resistor, a capacitor and a potentiometer are the timing network — a pot is a knob, which is
28
+ * control-rate and already carried as `controlId`. Anything on this list sitting on the timing
29
+ * network is steering it at signal rate, which is how an LFO reaches a BBD clock.
30
+ *
31
+ * `diode` is deliberately absent: `mxr-carbon-copy` clamps its CD4047 timing node with `D5` to
32
+ * ground, and a clamp is not a modulator. `ic` is absent because the clock driver is itself an
33
+ * `ic` on its own timing pins.
34
+ */
35
+ export declare const ACTIVE_TIMING_KINDS: ReadonlySet<DeviceKind>;
36
+ /**
37
+ * Devices actively steering this clock driver's timing network, by id.
38
+ *
39
+ * The discrimination this exists for, measured 2026-08-29 across the corpus: `mxr-carbon-copy`
40
+ * and `boss-dm-2` carry only resistors, capacitors and pots on their timing nodes, so their
41
+ * derived delays are a property of the circuit. `boss-ce-5` carries **`Q8`'s emitter directly
42
+ * on oscillator node 42**, so the frequency is set by that transistor's current and the R·C
43
+ * arithmetic reads a resistor that is not what times the oscillator.
44
+ */
45
+ export declare function activeTimingDrivers(clockDevice: Device, netlist: Netlist, registry: PartRegistry): readonly string[];
46
+ export type ClockModulationSource = {
47
+ readonly outcome: "resolved";
48
+ readonly steeredBy: string;
49
+ readonly node: NodeId;
50
+ } | {
51
+ readonly outcome: "none";
52
+ } | {
53
+ readonly outcome: "refused";
54
+ readonly reason: string;
55
+ };
56
+ /**
57
+ * The node an LFO reaches a modulated BBD clock on: the steering device's **control** terminal.
58
+ *
59
+ * **Not the timing node.** That is the point of this function. The timing node carries the
60
+ * oscillator's own waveform at the clock rate — for a chorus, ~100 kHz, far above anything a
61
+ * 48 kHz host can sample — so reading it as a modulation source would alias garbage into the
62
+ * delay length. The control terminal carries the LFO and nothing else: on `boss-ce-2` that is
63
+ * `Q5`'s base, which the DEPTH pot reaches through `Q4` and `D1`.
64
+ *
65
+ * Deliberately strict. Exactly one steering device with one resolvable control terminal, and
66
+ * that terminal may not sit on ground or on the timing network itself. Anything else refuses by
67
+ * name and leaves the delay static, because a guess here is a sweep of the wrong thing.
68
+ */
69
+ export declare function resolveClockModulationSource(bbdDevice: Device, netlist: Netlist, registry: PartRegistry): ClockModulationSource;
70
+ /**
71
+ * The nodes a clock driver's own timing network hangs on, by family.
72
+ *
73
+ * Exported because `scripts/report-bbd-architecture.ts` asks the same question and answered it
74
+ * with a private copy of the role vocabulary, which **under-reported immediately**: an MN3102
75
+ * spelling its pins `oscillatorinput`/`oscillatoroutput`/`clockcontrol` (`boss-bf-2`) matched
76
+ * none of the canonical `ox1`/`ox2`/`ox3` tokens, so a driver the compiler finds read as absent
77
+ * in the report. One enumerator, shared, is the same conclusion `stampNodes` reached.
78
+ *
79
+ * Role tokens are compared as whole values after folding, never by substring, and the MN310x
80
+ * branch admits the registry's own alias spellings through `oscillatorRoleTokens`.
81
+ */
82
+ export declare function clockTimingNodes(device: Device, registry: PartRegistry): ReadonlySet<NodeId>;
83
+ export declare function identifyClockFamily(device: Device): ClockDriverFamily;
84
+ export declare function findClockDriverDevice(netlist: Netlist): Device | null;
85
+ export declare function deriveBbdDelayFromNetlist(bbdDevice: Device, netlist: Netlist, stages: number, registry: PartRegistry): BbdClockDerivationOutcome;
86
+ /**
87
+ * Open-OX2 clock law (CE-2 first slice, 2026-09-28).
88
+ *
89
+ * The form the MN3101 datasheet never drew: OX2 left open (N.C.) and a single
90
+ * relaxation loop across the OX1→OX3 inverter pair — a timing capacitor
91
+ * between OX3 and OX1 charged through a pull-up on OX1 and discharged by a
92
+ * transistor the OX3 side drives, with the LFO setting the restart level
93
+ * through a diode. `boss-ce-2` v1.25 is the corpus case.
94
+ *
95
+ * Family constants (per-packet R and C come from the netlist):
96
+ * - `OPEN_OX2_VDD_VOLTS`: the driver's supply span, 9 V nominal from the
97
+ * packet's own supply port (the filtered rail measures 8.967 V, 0.4%,
98
+ * disclosed and immaterial beside the threshold sensitivity).
99
+ * - `OPEN_OX2_VTH_VOLTS`: solved 2026-09-28 for 110 kHz at the measured
100
+ * Depth-0 divider level 1.565 V against a scope specimen (DIYstompboxes
101
+ * 2011, one rough forum reading, disclosed), inside the 3.4–5.6 V window
102
+ * the MN3101 p.60 example table allows. Re-solve, never tune: procedure in
103
+ * `docs/todo-archive/2026-09-28-ce2-clock-law-analysis.md`.
104
+ * - `OPEN_OX2_VF_VOLTS`: generic-diode forward drop, disclosed typical.
105
+ * - `OPEN_OX2_FLOOR_VOLTS`: yank-minus-kick bottom under datasheet OX3 drive
106
+ * (R_OH ≈ 667 Ω / R_OL ≈ 500 Ω from the I_OH2/I_OL2 minimums): a ~7 V OX3
107
+ * step kicks ~6.2 V through 47 pF against cited diode maxima, landing near
108
+ * −5.3 V — below the D1 floor at every measured divider level, so the D1
109
+ * branch binds everywhere. Same max() form as the analysis, not a deletion.
110
+ */
111
+ export declare const OPEN_OX2_VDD_VOLTS = 9;
112
+ export declare const OPEN_OX2_VTH_VOLTS = 4.757;
113
+ export declare const OPEN_OX2_VF_VOLTS = 0.65;
114
+ export declare const OPEN_OX2_FLOOR_VOLTS = -5.35;
115
+ export type OpenOx2ClockLawOutcome = {
116
+ readonly outcome: "recognized";
117
+ readonly slowNode: NodeId;
118
+ readonly steeredBy: string;
119
+ readonly rOhms: number;
120
+ readonly cFarads: number;
121
+ } | {
122
+ readonly outcome: "absent";
123
+ readonly reason: string;
124
+ };
125
+ /**
126
+ * Recognise the open-OX2 relaxation form by connectivity, never by names or
127
+ * prose: an MN3101-family driver with OX2 touched by nothing but its own pin,
128
+ * one capacitor bridging OX3 to OX1, one pull-up resistor from OX1 to the
129
+ * driver's own supply pin, one resistor from OX3 into a grounded-emitter
130
+ * transistor's base, that transistor's collector on a pulled-up discharge
131
+ * node back into OX1 through a diode, and one diode from OX1 into a
132
+ * divider-fed slow node.
133
+ *
134
+ * Anything else is `absent` with the failed check named, and the caller falls
135
+ * back to the existing star/gate derivation untouched. In particular the
136
+ * pre-rewire collapse (capacitor on OX1–OX2), a transistor sitting directly
137
+ * on a timing node (the modulated refusal owns that), and the MN3102 two-leg
138
+ * form (a different oscillator; no corpus case) all read absent here.
139
+ */
140
+ export declare function deriveOpenOx2ClockLaw(netlist: Netlist, registry: PartRegistry): OpenOx2ClockLawOutcome;
141
+ /**
142
+ * Princeton Technology PT2399 delay time from external VCO resistance R (in Ohms).
143
+ *
144
+ * Princeton PT2399 datasheet, TABLE 1: RESISTOR/DELAY TIME VALUES (V1.4, Oct 2005;
145
+ * the same 28-row table is reprinted in V1.1, V1.2 and V1.6; the packet-local V1.9,
146
+ * Mar 2022, drops the table but still says "Please refer to Table 1 for the
147
+ * Resistor/Delay Time values"). Note 1 under the table states R in Ohms, so the
148
+ * last row's "0.5" is 0.5 Ohms (pin 6 effectively grounded, 31.3 ms), not 0.5 kOhms.
149
+ *
150
+ * The table is linear in R: with pin 6 held at 2.5 V, the page
151
+ * https://electrosmash.mas-effects.com/pt2399-analysis gives
152
+ * I_pin6 (mA) = 28.65 / (Delay_ms - 29.70), i.e. Delay_ms = 29.70 + 11.46 * R_kOhms,
153
+ * which reproduces all 28 cited rows within 4 ms (worst row: 27.6 kOhms, 346.0 ms
154
+ * predicted vs 342 ms cited). That linear form is what this returns.
155
+ *
156
+ * Deliberately no clamp and no hard maximum: the cited source states none. Quality
157
+ * degrades past about 350 ms (the table's own THD reaches 1.0% at 342 ms), but the
158
+ * delay keeps growing with R, so this extrapolates the line rather than clipping it.
159
+ */
160
+ export declare function pt2399DelayFromOhms(rOhms: number): number;
161
+ /**
162
+ * Derives delaySeconds and dynamic control mapping for PT2399 digital delay chips
163
+ * by tracing the series resistor/potentiometer network connected to VCO Pin 6.
164
+ */
165
+ export declare function derivePt2399DelayFromNetlist(device: Device, netlist: Netlist): BbdClockDerivationOutcome;
166
+ /**
167
+ * Derives delaySeconds and dynamic control mapping for Mitsubishi M50195P digital echo LSI
168
+ * by tracing the inverter oscillator timing network connected to CLK1.
169
+ */
170
+ export declare function deriveM50195DelayFromNetlist(device: Device, netlist: Netlist): BbdClockDerivationOutcome;
171
+ //# sourceMappingURL=bbd-clock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bbd-clock.d.ts","sourceRoot":"","sources":["../src/bbd-clock.ts"],"names":[],"mappings":"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"}