@vessel-dsp/compiler 0.0.0-stage → 0.3.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 +118 -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 +348 -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 +3513 -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 +1742 -0
  63. package/dist/fixtures/circuits.d.ts.map +1 -0
  64. package/dist/fixtures/circuits.js +13896 -0
  65. package/dist/fixtures/circuits.js.map +1 -0
  66. package/dist/fixtures/expected.d.ts +261 -0
  67. package/dist/fixtures/expected.d.ts.map +1 -0
  68. package/dist/fixtures/expected.js +315 -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 +15 -0
  127. package/dist/part-catalog.d.ts.map +1 -0
  128. package/dist/part-catalog.js +6712 -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,109 @@
1
+ /**
2
+ * **What a terminal IS — typed, with per-terminal evidence.**
3
+ *
4
+ * **The defect:** 1260 terminals across the corpus's open devices carry `role: pin`. One value.
5
+ * `vinl` is indistinguishable from `A0`, `SUM` from `VCC`. It is the fourth instance of the
6
+ * missing-field defect and the worst of them, because the first three blocked a classification, a
7
+ * declaration and an ownership claim while **this one blocks a MEASUREMENT**: the fidelity
8
+ * counterfactual must bridge a device's audio-bearing terminals, cannot tell which those are, and
9
+ * therefore bridges all of them — a DRAM's address bus included — giving an upper bound (37) where
10
+ * a number was wanted.
11
+ *
12
+ * **The remediation is a pattern this repository already trusts, not a new design.** The triode
13
+ * registry carries per-parameter `source` citations plus an entry-level
14
+ * `terminalProfileEvidenceStatus`; `CLAUDE.md` names it the provenance template for the whole
15
+ * registry. This is that template extended to the classes that never got it.
16
+ */
17
+ export type TerminalRole =
18
+ /** Carries signal. The only role the audio-path counterfactual may bridge. */
19
+ "audio" | "supply" | "ground"
20
+ /** Address lines: a bus, never a signal path. */
21
+ | "address"
22
+ /** Data lines, parallel or serial. */
23
+ | "data" | "clock"
24
+ /** Mode, enable, reset, chip-select: a parameter stream, not audio. */
25
+ | "control"
26
+ /**
27
+ * **Added because the corpus forces it.** A DAC's `VREF` sets full-scale output: it is neither a
28
+ * supply rail nor a signal, and calling it either loses the distinction that makes it useful.
29
+ * `RKM14L492-103F` in `boss-dd-2` and `boss-dsd-3` carries one.
30
+ */
31
+ | "reference";
32
+ /**
33
+ * **How a role is known. Two kinds, and the second is deliberately weaker.**
34
+ *
35
+ * `direct-source` is the strong kind: a datasheet pinout, a service-note terminal map, or the
36
+ * packet's own declared node role (for `supply`/`ground`, cited to the node ledger) — the way the
37
+ * triode registry cites a Koren coefficient.
38
+ *
39
+ * **`packet-validated-shell` is circumstantial and is named so it reads that way**: it is not the
40
+ * source document saying what a terminal is, it is *our own packet's deck* having pushed a signal
41
+ * through it. It buys the shells that have **no part number at all** —
42
+ * `boss-st-2:ANALOG_INPUT_SUPPORT` can never have a datasheet — at the price of being admissible
43
+ * for exactly one role.
44
+ *
45
+ * **It is asymmetric, and the direction is the unwelcome one.** A deck can only ever ADD an audio
46
+ * terminal, never remove one, so this kind can move a device from `drops` to `stays` and never the
47
+ * reverse. A ceiling that improves because of it is a bug, not a result.
48
+ */
49
+ export type TerminalRoleEvidenceKind = "direct-source" | "packet-validated-shell";
50
+ /**
51
+ * **A role and the evidence for it. Both required — a role without evidence is refused.**
52
+ *
53
+ * **The banned inference is reading the pin's NAME.** `vinl` looking like audio and `A0` looking
54
+ * like an address is exactly the substring classification `CLAUDE.md` forbids, and it is forbidden
55
+ * because **a pin name is a label, not a fact**. It is also how this corpus already went wrong once:
56
+ * four tremolo optocouplers are wired from their terminals' *names*, one of the two surviving name
57
+ * vocabularies.
58
+ */
59
+ export type TerminalRoleEvidence = {
60
+ readonly kind: "direct-source";
61
+ readonly role: TerminalRole;
62
+ /** The cited authority: a datasheet pinout, a service-note terminal map, a node ledger. */
63
+ readonly source: string;
64
+ } | {
65
+ /**
66
+ * **Admissible for `audio` and nothing else.** Address, data and clock are not evidenceable
67
+ * this way at all: a deck that drives a bus line proves nothing about what the line carries,
68
+ * and supply/ground need no deck because the packet already declares them.
69
+ */
70
+ readonly kind: "packet-validated-shell";
71
+ readonly role: "audio";
72
+ /** The packet-local deck, by path. It must exist and must have passed. */
73
+ readonly deck: string;
74
+ /**
75
+ * **The specific path through this terminal that the deck exercises**, as the deck's own
76
+ * driven node and probed node. "The deck covers this component" is NOT this field: the
77
+ * claim is that signal passes through *this terminal*, and a component-level citation
78
+ * cannot support a per-terminal claim.
79
+ */
80
+ readonly signalPath: string;
81
+ };
82
+ /** Strong or circumstantial. Reported wherever a populated profile is summarised. */
83
+ export declare function evidenceStrength(e: TerminalRoleEvidence): "source" | "circumstantial";
84
+ /**
85
+ * **`unpopulated` is a first-class state and a CORRECT outcome**, not a gap. If a role cannot be
86
+ * evidenced it stays unpopulated, and a consumer must handle that rather than guess.
87
+ */
88
+ export type TerminalProfile = {
89
+ readonly status: "unpopulated";
90
+ readonly reason: string;
91
+ } | {
92
+ readonly status: "populated";
93
+ /** Keyed by the terminal's declared name. */
94
+ readonly terminals: Readonly<Record<string, TerminalRoleEvidence>>;
95
+ };
96
+ export declare function validateTerminalProfile(p: TerminalProfile): readonly string[];
97
+ /**
98
+ * **The terminals the audio-path counterfactual may bridge.**
99
+ *
100
+ * **Returns `null` when the profile is unpopulated**, and the caller must then fall back to
101
+ * bridging everything and **report its result as an upper bound** — which is exactly what the
102
+ * ceiling of 37 is today. The distinction between "measured" and "bounded" lives here.
103
+ *
104
+ * **A device needs at least TWO audio terminals to bridge anything.** One audio pin connects
105
+ * nothing, which is why an R2R ladder with eleven data lines and a single `SUM` should leave the
106
+ * audio path once its profile is populated.
107
+ */
108
+ export declare function audioTerminals(p: TerminalProfile | undefined): readonly string[] | null;
109
+ //# sourceMappingURL=terminal-role.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-role.d.ts","sourceRoot":"","sources":["../src/terminal-role.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,YAAY;AACvB,8EAA8E;AAC5E,OAAO,GACP,QAAQ,GACR,QAAQ;AACV,iDAAiD;GAC/C,SAAS;AACX,sCAAsC;GACpC,MAAM,GACN,OAAO;AACT,uEAAuE;GACrE,SAAS;AACX;;;;GAIG;GACD,WAAW,CAAC;AAEf;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,wBAAwB,GAAG,eAAe,GAAG,wBAAwB,CAAC;AAElF;;;;;;;;GAQG;AACH,MAAM,MAAM,oBAAoB,GAC7B;IACA,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACvB,GACD;IACA;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,wBAAwB,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEL,qFAAqF;AACrF,wBAAgB,gBAAgB,CAC/B,CAAC,EAAE,oBAAoB,GACrB,QAAQ,GAAG,gBAAgB,CAE7B;AAED;;;GAGG;AACH,MAAM,MAAM,eAAe,GACxB;IAAE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3D;IACA,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,6CAA6C;IAC7C,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;CAClE,CAAC;AAEL,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,eAAe,GAAG,SAAS,MAAM,EAAE,CA8B7E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,eAAe,GAAG,SAAS,GAAG,SAAS,MAAM,EAAE,GAAG,IAAI,CAKvF"}
@@ -0,0 +1,46 @@
1
+ /** Strong or circumstantial. Reported wherever a populated profile is summarised. */
2
+ export function evidenceStrength(e) {
3
+ return e.kind === "direct-source" ? "source" : "circumstantial";
4
+ }
5
+ export function validateTerminalProfile(p) {
6
+ if (p.status === "unpopulated")
7
+ return p.reason.trim() === ""
8
+ ? ["`unpopulated` must say why -- an unexplained absence is indistinguishable from an oversight"]
9
+ : [];
10
+ const out = [];
11
+ for (const [name, e] of Object.entries(p.terminals)) {
12
+ if (e.kind === "direct-source") {
13
+ if (e.source.trim() === "")
14
+ out.push(`terminal "${name}" declares role \`${e.role}\` with no source -- A ROLE ASSIGNED WITHOUT EVIDENCE IS REFUSED. Cite a datasheet pinout, a service-note terminal map, or the packet's declared node role.`);
15
+ continue;
16
+ }
17
+ // Checked at runtime as well as in the types: registry entries are authored as data and can
18
+ // reach this function from JSON, where the union's discrimination buys nothing.
19
+ if (e.role !== "audio")
20
+ out.push(`terminal "${name}" claims role \`${e.role}\` from \`packet-validated-shell\` -- REFUSED. That kind evidences \`audio\` and nothing else: address, data and clock are not evidenceable from a deck at all, and supply/ground are already evidenced by the packet's declared node role.`);
21
+ if (e.deck.trim() === "")
22
+ out.push(`terminal "${name}" claims \`packet-validated-shell\` with no deck -- REFUSED. Name the packet-local deck that passed.`);
23
+ if (e.signalPath.trim() === "")
24
+ out.push(`terminal "${name}" claims \`packet-validated-shell\` with no signal path -- REFUSED. "The deck covers this component" cannot support a per-terminal claim; name the driven node and the probed node.`);
25
+ }
26
+ return out;
27
+ }
28
+ /**
29
+ * **The terminals the audio-path counterfactual may bridge.**
30
+ *
31
+ * **Returns `null` when the profile is unpopulated**, and the caller must then fall back to
32
+ * bridging everything and **report its result as an upper bound** — which is exactly what the
33
+ * ceiling of 37 is today. The distinction between "measured" and "bounded" lives here.
34
+ *
35
+ * **A device needs at least TWO audio terminals to bridge anything.** One audio pin connects
36
+ * nothing, which is why an R2R ladder with eleven data lines and a single `SUM` should leave the
37
+ * audio path once its profile is populated.
38
+ */
39
+ export function audioTerminals(p) {
40
+ if (p === undefined || p.status === "unpopulated")
41
+ return null;
42
+ return Object.entries(p.terminals)
43
+ .filter(([, e]) => e.role === "audio")
44
+ .map(([name]) => name);
45
+ }
46
+ //# sourceMappingURL=terminal-role.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-role.js","sourceRoot":"","sources":["../src/terminal-role.ts"],"names":[],"mappings":"AAyFA,qFAAqF;AACrF,MAAM,UAAU,gBAAgB,CAC/B,CAAuB;IAEvB,OAAO,CAAC,CAAC,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC;AACjE,CAAC;AAcD,MAAM,UAAU,uBAAuB,CAAC,CAAkB;IACzD,IAAI,CAAC,CAAC,MAAM,KAAK,aAAa;QAC7B,OAAO,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE;YAC5B,CAAC,CAAC,CAAC,6FAA6F,CAAC;YACjG,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;QACrD,IAAI,CAAC,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;YAChC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE;gBACzB,GAAG,CAAC,IAAI,CACP,aAAa,IAAI,qBAAqB,CAAC,CAAC,IAAI,6JAA6J,CACzM,CAAC;YACH,SAAS;QACV,CAAC;QACD,4FAA4F;QAC5F,gFAAgF;QAChF,IAAK,CAAC,CAAC,IAAqB,KAAK,OAAO;YACvC,GAAG,CAAC,IAAI,CACP,aAAa,IAAI,mBAAmB,CAAC,CAAC,IAAI,6OAA6O,CACvR,CAAC;QACH,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;YACvB,GAAG,CAAC,IAAI,CACP,aAAa,IAAI,sGAAsG,CACvH,CAAC;QACH,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE;YAC7B,GAAG,CAAC,IAAI,CACP,aAAa,IAAI,qLAAqL,CACtM,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,CAA8B;IAC5D,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,aAAa;QAAE,OAAO,IAAI,CAAC;IAC/D,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;SAChC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC;SACrC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * How a supply names the terminal it drives, and the terminal it returns through.
3
+ *
4
+ * **These were two pairs, not one, until 2026-09-01.** `device-laws.ts` (stage 3, building the
5
+ * drive/return node sets that decide whether a jack contact engages onto a stranded supply
6
+ * node) accepted `{positive, hot, plus}` / `{negative, return}`. `lower.ts` (stage 5, choosing
7
+ * which end of a supply a stamp drives) accepted those plus `{line, anode, +}` /
8
+ * `{cathode, minus, neutral}`. Same question, two answers, three tokens apart -- so a supply
9
+ * declaring `line` was a drive terminal when lowering and invisible to stage 3, which decides
10
+ * jack engagement.
11
+ *
12
+ * Resolved as the **union**, because both stages are asking which end is which and the wider
13
+ * vocabulary is the more complete answer to that question, not a laxer one.
14
+ *
15
+ * **Measured before merging, so this is a consolidation and not a behaviour change.** Across
16
+ * all 142 corpus packets, only five role tokens appear on a `voltage-source` at all:
17
+ * `positive` (46), `negative` (45), `hot` (1), `return` (1) and `midpoint` (1). None of the six
18
+ * divergent tokens occurs, so no packet's classification moves. The divergence was latent, and
19
+ * the point of merging it now is that the next packet spelling a rail `line` gets one answer
20
+ * instead of two.
21
+ *
22
+ * `midpoint` is deliberately in neither set: a centre tap is not a drive end or a return end,
23
+ * and the stages that consult these are asking about polarity.
24
+ */
25
+ export declare const supplyDriveRoles: ReadonlySet<string>;
26
+ /**
27
+ * Role tokens that name a terminal's **position or its winding half, and deliberately carry no
28
+ * electrode evidence** — so a resolver that cannot orient a device from them has read the
29
+ * document correctly rather than failed to.
30
+ *
31
+ * **Why this set exists.** Four asymmetric-device resolvers in `lower.ts` fall back to
32
+ * declaration order when they cannot read a role, and that fallback serves two situations one
33
+ * predicate could not previously tell apart: a document of bare lugs, which carries no
34
+ * orientation evidence and where declaration order is all there is, and a document that *named*
35
+ * its terminals in a spelling the resolver does not know, where declaration order is a guess.
36
+ * The first is correct and must stay silent; the second is the silent-mis-wiring class the
37
+ * 2026-09-02 census measured. This set is the escape hatch that separates them.
38
+ *
39
+ * Every entry is named as exactly this case by a resolver's own docstring already, and each is
40
+ * corpus-evidenced: `diodeTerminals` says it "falls back to declaration order for `a,b` and bare
41
+ * **`a`/`b` removed 2026-09-03, because a diode is not a symmetric device.** They were here for
42
+ * "a diode's bare `a`/`b` lugs, which carry no orientation evidence" -- and that reading is wrong
43
+ * in a way a mutation study caught: reversing such a diode moves the program, so declaration
44
+ * order is not "the document's intended reading", it is a guess this set was suppressing the
45
+ * warning about. What remains is genuinely symmetric: the two ends of a winding, where either
46
+ * order is the same circuit.
47
+ *
48
+ * The older note read: `diodeTerminals` says "falls back to declaration order for `a,b` and bare
49
+ * "`ac_a`/`ac_b` and `heater_a`/`heater_b` are symmetric windings that name no polarity ... so
50
+ * both fall back to declaration order" (`fender-5f1-champ`'s filament bus and `hiwatt-dr103`'s
51
+ * heater bus). Nothing here feeds a resolver, so adding a token changes no wiring — it only
52
+ * declares that this spelling's fallback was intended.
53
+ *
54
+ * A `lugN` or bare `pinN` needs no entry: `terminalRoleToken` already folds a bare pin number to
55
+ * `null`, and a `lug1`/`lug3` names a pot's track end, which `potTerminals` resolves by its own
56
+ * quiet-distance rule rather than by orientation.
57
+ */
58
+ export declare const orientationFreeRoles: ReadonlySet<string>;
59
+ //# sourceMappingURL=terminal-roles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-roles.d.ts","sourceRoot":"","sources":["../src/terminal-roles.ts"],"names":[],"mappings":"AAsBA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAO/C,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAKnD,CAAC"}
@@ -0,0 +1,92 @@
1
+ // Terminal-role vocabularies shared by more than one stage.
2
+ //
3
+ // **Why this file exists.** The corpus spells one terminal many ways, and the design rule is
4
+ // refuse rather than guess, so every stage that has to recognise a role carries a closed set of
5
+ // the spellings it accepts. That is correct. What is not correct is two stages carrying their
6
+ // *own* closed set for the *same* question, because the two then drift: the sets below were
7
+ // found defined twice, under the same names, in different files, and two of them had already
8
+ // diverged by three tokens each.
9
+ //
10
+ // The rule this file establishes: **a role vocabulary consulted by more than one stage lives
11
+ // here, once.** A vocabulary only one stage reads stays with that stage -- it cannot drift
12
+ // against anything. This is phase 1 of R1 in
13
+ // `thoughts/shared/plans/archive/2026-08-31-v2-complexity-reduction-plan.md`, and it is deliberately
14
+ // the half that needs nothing from `@vessel-dsp/core`: consolidating duplication that already
15
+ // exists inside this repository. Canonicalising the spellings themselves, upstream, is the
16
+ // other half and is still gated on the core parser contract.
17
+ //
18
+ // Nothing here is a substring or regex test. Every set is compared as whole values against
19
+ // tokens `terminalRoleToken` has already folded (case dropped, separators stripped, a leading
20
+ // `pinN` removed), which is what keeps this on the right side of the repository's rule against
21
+ // reading authored prose.
22
+ /**
23
+ * How a supply names the terminal it drives, and the terminal it returns through.
24
+ *
25
+ * **These were two pairs, not one, until 2026-09-01.** `device-laws.ts` (stage 3, building the
26
+ * drive/return node sets that decide whether a jack contact engages onto a stranded supply
27
+ * node) accepted `{positive, hot, plus}` / `{negative, return}`. `lower.ts` (stage 5, choosing
28
+ * which end of a supply a stamp drives) accepted those plus `{line, anode, +}` /
29
+ * `{cathode, minus, neutral}`. Same question, two answers, three tokens apart -- so a supply
30
+ * declaring `line` was a drive terminal when lowering and invisible to stage 3, which decides
31
+ * jack engagement.
32
+ *
33
+ * Resolved as the **union**, because both stages are asking which end is which and the wider
34
+ * vocabulary is the more complete answer to that question, not a laxer one.
35
+ *
36
+ * **Measured before merging, so this is a consolidation and not a behaviour change.** Across
37
+ * all 142 corpus packets, only five role tokens appear on a `voltage-source` at all:
38
+ * `positive` (46), `negative` (45), `hot` (1), `return` (1) and `midpoint` (1). None of the six
39
+ * divergent tokens occurs, so no packet's classification moves. The divergence was latent, and
40
+ * the point of merging it now is that the next packet spelling a rail `line` gets one answer
41
+ * instead of two.
42
+ *
43
+ * `midpoint` is deliberately in neither set: a centre tap is not a drive end or a return end,
44
+ * and the stages that consult these are asking about polarity.
45
+ */
46
+ export const supplyDriveRoles = new Set([
47
+ "positive",
48
+ "plus",
49
+ "hot",
50
+ "line",
51
+ "anode",
52
+ "+",
53
+ ]);
54
+ /**
55
+ * Role tokens that name a terminal's **position or its winding half, and deliberately carry no
56
+ * electrode evidence** — so a resolver that cannot orient a device from them has read the
57
+ * document correctly rather than failed to.
58
+ *
59
+ * **Why this set exists.** Four asymmetric-device resolvers in `lower.ts` fall back to
60
+ * declaration order when they cannot read a role, and that fallback serves two situations one
61
+ * predicate could not previously tell apart: a document of bare lugs, which carries no
62
+ * orientation evidence and where declaration order is all there is, and a document that *named*
63
+ * its terminals in a spelling the resolver does not know, where declaration order is a guess.
64
+ * The first is correct and must stay silent; the second is the silent-mis-wiring class the
65
+ * 2026-09-02 census measured. This set is the escape hatch that separates them.
66
+ *
67
+ * Every entry is named as exactly this case by a resolver's own docstring already, and each is
68
+ * corpus-evidenced: `diodeTerminals` says it "falls back to declaration order for `a,b` and bare
69
+ * **`a`/`b` removed 2026-09-03, because a diode is not a symmetric device.** They were here for
70
+ * "a diode's bare `a`/`b` lugs, which carry no orientation evidence" -- and that reading is wrong
71
+ * in a way a mutation study caught: reversing such a diode moves the program, so declaration
72
+ * order is not "the document's intended reading", it is a guess this set was suppressing the
73
+ * warning about. What remains is genuinely symmetric: the two ends of a winding, where either
74
+ * order is the same circuit.
75
+ *
76
+ * The older note read: `diodeTerminals` says "falls back to declaration order for `a,b` and bare
77
+ * "`ac_a`/`ac_b` and `heater_a`/`heater_b` are symmetric windings that name no polarity ... so
78
+ * both fall back to declaration order" (`fender-5f1-champ`'s filament bus and `hiwatt-dr103`'s
79
+ * heater bus). Nothing here feeds a resolver, so adding a token changes no wiring — it only
80
+ * declares that this spelling's fallback was intended.
81
+ *
82
+ * A `lugN` or bare `pinN` needs no entry: `terminalRoleToken` already folds a bare pin number to
83
+ * `null`, and a `lug1`/`lug3` names a pot's track end, which `potTerminals` resolves by its own
84
+ * quiet-distance rule rather than by orientation.
85
+ */
86
+ export const orientationFreeRoles = new Set([
87
+ "aca",
88
+ "acb",
89
+ "heatera",
90
+ "heaterb",
91
+ ]);
92
+ //# sourceMappingURL=terminal-roles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-roles.js","sourceRoot":"","sources":["../src/terminal-roles.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,EAAE;AACF,6FAA6F;AAC7F,gGAAgG;AAChG,8FAA8F;AAC9F,4FAA4F;AAC5F,6FAA6F;AAC7F,iCAAiC;AACjC,EAAE;AACF,6FAA6F;AAC7F,2FAA2F;AAC3F,6CAA6C;AAC7C,qGAAqG;AACrG,8FAA8F;AAC9F,2FAA2F;AAC3F,6DAA6D;AAC7D,EAAE;AACF,2FAA2F;AAC3F,8FAA8F;AAC9F,+FAA+F;AAC/F,0BAA0B;AAE1B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAwB,IAAI,GAAG,CAAC;IAC5D,UAAU;IACV,MAAM;IACN,KAAK;IACL,MAAM;IACN,OAAO;IACP,GAAG;CACH,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAwB,IAAI,GAAG,CAAC;IAChE,KAAK;IACL,KAAK;IACL,SAAS;IACT,SAAS;CACT,CAAC,CAAC"}
@@ -0,0 +1,181 @@
1
+ import { type Device, type DeviceLaw, type NodeId } from "./types";
2
+ /**
3
+ * How many `(device, terminal)` references touch each node across the whole netlist.
4
+ *
5
+ * Existence evidence, not a corpus-wide cost: `transformerWindings` is the only reader, and
6
+ * only when its own terminal grouping turns up a winding with a single, unpaired end. A node
7
+ * this stage never asks about -- ground, or any node two ordinary components share -- costs
8
+ * nothing extra to have counted.
9
+ */
10
+ export declare function nodeOccurrenceCounts(devices: readonly Device[]): Map<NodeId, number>;
11
+ /**
12
+ * A transformer's windings, as coupled pairs against its primary.
13
+ *
14
+ * **Positional order was silently wrong on 45 of the corpus's 55 transformers.** This read
15
+ * `device.nodes` as exactly `[primary+, primary-, secondary+, secondary-]` and dropped every
16
+ * terminal past the fourth without a word. Only 10 transformers have four terminals; the rest
17
+ * have five to thirteen. `tycobrahe-octavia` is the compiled case: its `T1` is
18
+ * `[primarya, primaryb, secondarya, secondaryct, secondaryb, shieldnc]`, so the old reading
19
+ * coupled `(secondarya, secondaryct)` — **half the secondary** — and left `secondaryb` driven by
20
+ * nothing. Since `secondarya` and `secondaryb` each feed a diode anode with the centre tap
21
+ * grounded, that is a full-wave rectifier reduced to half-wave, which is the difference between
22
+ * an octave-up pedal and a fuzz.
23
+ *
24
+ * Grouping is by **terminal-role token compared whole** against the closed table below, never by
25
+ * substring or by position. A role this table does not name is a refusal rather than a guess: a
26
+ * winding assembled from tokens nobody has checked would silently couple the wrong pair, which is
27
+ * the failure this replaces.
28
+ *
29
+ * A centre tap splits its winding into two halves in series. Each half carries half the turns, so
30
+ * its primary-to-half ratio is **twice** the declared one — the declared `Ratio` is read as
31
+ * primary-to-*full*-winding, which is the conventional statement for a tapped secondary and is
32
+ * documented here because the source cannot say which it meant.
33
+ *
34
+ * **One declared ratio is applied to every secondary.** `declaredRatio` is exact for a single
35
+ * secondary, tapped or not, whose halves genuinely share a ratio -- which is every transformer
36
+ * that reaches the coupled path, because a ratio is only ever derived for a transformer whose
37
+ * primary carries signal.
38
+ *
39
+ * ## Driven windings: a power transformer needs no primary
40
+ *
41
+ * **A transformer the source specifies by its winding voltages is lowered as one `ac-source` per
42
+ * winding, and its primary is not modelled at all.** Decided 2026-08-14 after measuring the amp
43
+ * corpus; `netlist.ts`'s `transformerWindingVolts` carries the argument for why the primary side
44
+ * is not needed and this function is where that is executed.
45
+ *
46
+ * The physics: the B+ rail comes from the HV secondary through the rectifier and the filter
47
+ * network, and a cap-input rectifier's behaviour is fixed by the secondary RMS, the rectifier
48
+ * drop and the load. The primary is the mains side, and nothing downstream of it is audio. So a
49
+ * winding whose own RMS the source states can be driven directly, and the mains number the
50
+ * previous formulation demanded -- unstateable for the multi-tap primaries this corpus is full of
51
+ * -- drops out.
52
+ *
53
+ * **What this models that the coupled form did not.** `vox-ac15-top-boost` declares no supply
54
+ * component at all: under the coupled form its power transformer's primary would be driven by
55
+ * nothing and its B+ would solve to zero, a wrong answer that renders. A driven winding asserts
56
+ * the voltage the source states regardless of what the mains side of the document contains.
57
+ *
58
+ * **What this does not model, plainly.** A real transformer sags: winding resistance, leakage
59
+ * inductance and core loss make B+ fall as the power tubes draw, and that sag is part of how an
60
+ * amp sounds. **No source in this corpus states any of those**, so none of them is modelled and
61
+ * none is guessed. The series impedance is `SUPPLY_SOURCE_OHMS`, the repository's existing
62
+ * one-ohm default for every supply, carried on the law -- it is there so an ideal source cannot
63
+ * deliver unbounded current into a rectifier, and it is **not** a winding resistance: a real HV
64
+ * winding is tens to hundreds of ohms. Read a B+ from this model as the no-load rail. Likewise
65
+ * there is no cross-winding coupling (loading the HV winding does not disturb the heaters) and
66
+ * nothing on the mains side can switch the amp off any more, because there is no longer a path
67
+ * from the mains inlet to the secondaries; the standby switch, which sits after the rectifier,
68
+ * is unaffected.
69
+ *
70
+ * **The centre tap is two sources about the tap, not one across the winding.** A `370-0-370`
71
+ * winding with the tap grounded gets `V(a) - V(tap) = E` and `V(tap) - V(b) = E`, so `a` and `b`
72
+ * swing in antiphase about the tap at 370 V RMS each -- which is what makes the full-wave
73
+ * rectifier downstream full-wave. One 740 V source across `a`-`b` would leave the tap
74
+ * undetermined and the rectifier fed wrongly. The pairing is the same one the coupled form
75
+ * already uses, and the stated voltage is per half by the corpus's own convention, so it is
76
+ * applied to each half directly and never doubled.
77
+ *
78
+ * **Every winding must have a voltage if any does.** A transformer on this path has no ratio to
79
+ * fall back on by construction, so a winding with no typed voltage is refused by name rather than
80
+ * silently dropped or defaulted -- `fender-bassman` and `marshall-jtm45` both state their
81
+ * rectifier-heater winding only as prose ("assumed from GZ34 heater, unprinted on selected
82
+ * schematic"), and that is a source gap to report, not a number to invent.
83
+ *
84
+ * **A tapped primary swaps which winding is the shared reference, added 2026-08-14.** Three
85
+ * amp output transformers (`fender-5e3-deluxe-tweed`, `marshall-jtm45`, `vox-ac30-top-boost`)
86
+ * declare a **centre-tapped primary** -- the ordinary push-pull shape, plate-to-plate with the
87
+ * centre tap at B+ -- which the original code refused outright: "no single reference winding".
88
+ * That refusal is still correct for a primary tapped *and* carrying no other complete winding to
89
+ * pivot on, but a push-pull output transformer always has one: the secondary. Every winding on
90
+ * one core shares the same volts-per-turn, so if `declaredRatio = Vprimary_full / Vsecondary_full`
91
+ * (exactly what the impedance-pair rung above already derives), then for a centre tap splitting
92
+ * the primary into two equal halves:
93
+ *
94
+ * V(half) = V(primary_full) / 2
95
+ * V(secondary_full) = V(primary_full) / declaredRatio = (2 * V(half)) / declaredRatio
96
+ * => V(secondary_full) = (2 / declaredRatio) * V(half)
97
+ *
98
+ * So stamping the secondary as the shared reference and each primary half as a winding coupled
99
+ * to it needs `2 / declaredRatio` per half -- applied directly, not doubled again. Verified
100
+ * against `fender-5e3-deluxe-tweed`'s own numbers: `declaredRatio = sqrt(8000/8) ≈ 31.62`,
101
+ * swapped ratio `2/31.62 ≈ 0.0632`, and `0.0632 * 15.81 = 1` confirms the round trip
102
+ * (`1/0.0632 ≈ 15.81 = declaredRatio/2`, the per-half ratio the un-swapped direction would have
103
+ * used).
104
+ *
105
+ * The swap needs **exactly one** other complete, untapped winding to pivot on; zero or several is
106
+ * refused rather than guessed. It cannot arise on the driven path, which models no primary.
107
+ *
108
+ * **A winding with only one declared end is dropped if connectivity says it is dangling, and
109
+ * refused by name otherwise -- also 2026-08-14.** `vox-ac30-top-boost`'s output transformer
110
+ * declares a `secondary_8` terminal beside its typed, wired `secondary_16` -- "8 Ω alternate
111
+ * source-visible tap" in the source's own words, a second impedance tap on the same core with no
112
+ * companion end of its own. Its node touches no other component anywhere in the document (checked
113
+ * against `nodeOccurrenceCounts`, connectivity rather than a spelling), so nothing this stage
114
+ * could stamp for it would change the solved circuit, and it is dropped exactly as `shieldnc`
115
+ * already is. This is deliberately **not** a blanket rule for the `secondary8`/`secondary_8`
116
+ * spelling: a future packet that actually wires an alternate tap to a load has a node other
117
+ * components touch, and that packet refuses by name instead -- a real multi-tap secondary in use
118
+ * is a different device law this stage does not have, not a winding to silently drop.
119
+ */
120
+ export type LoweredWinding = {
121
+ readonly kind: "coupled";
122
+ readonly primaryPlus: number;
123
+ readonly primaryMinus: number;
124
+ readonly plus: number;
125
+ readonly minus: number;
126
+ readonly turnsRatio: number;
127
+ } | {
128
+ readonly kind: "driven";
129
+ readonly plus: number;
130
+ readonly minus: number;
131
+ readonly amplitudeVolts: number;
132
+ readonly frequencyHz: number;
133
+ readonly sourceOhms: number;
134
+ };
135
+ /**
136
+ * Nodes where a transformer meets a **selector switch** and nothing else.
137
+ *
138
+ * Connectivity, never a spelling: a node qualifies when the devices touching it are one
139
+ * transformer and one or more plain `switch`es, with no third kind. An output transformer's
140
+ * impedance selector is exactly that shape -- `hiwatt-dr103`'s `SW_IMPEDANCE_SELECTOR` has one
141
+ * `common` and three throws, each throw sharing a node with one secondary tap of
142
+ * `T_OUTPUT_TH7549_2`.
143
+ *
144
+ * **`switch` and not `selector`, deliberately, and this is the whole boundary of this rule.** This
145
+ * repository already models a real 1-of-N selector as its own device kind: `selector` stamps every
146
+ * throw of every pole and lets a control decide which conducts, resolving its position from a
147
+ * declared `Options:` list against `Position:`. That is the right answer for an ohms selector -- it
148
+ * makes the taps *switchable*, so the reflected load changes with the selection, which is the half
149
+ * that matters for power-stage distortion.
150
+ *
151
+ * These two packets do not reach it: they declare `kind: switch` with a `SelectedState: 16 ohms`
152
+ * property rather than the `Options`/`Position` pair the classifier reads. So this rule is the
153
+ * narrow, exact thing that can be said meanwhile -- an unselected throw is open, and an open
154
+ * secondary contributes nothing -- and it **stops applying by construction** the moment a packet
155
+ * declares its selector properly, because a `selector` is not a `switch` and this predicate counts
156
+ * only the latter. It cannot defeat the better model; it is superseded by it.
157
+ */
158
+ export declare function selectorTapNodes(devices: readonly Device[]): ReadonlySet<NodeId>;
159
+ /**
160
+ * The coils this transformer declares, reduced to the pairs of nodes a stamp needs.
161
+ *
162
+ * **Read, not reconstructed.** Until `@vessel-dsp/core@0.6.35` a transformer's terminals gave no
163
+ * statement of which coil each belonged to, so this stage grouped them with a 110-entry table
164
+ * keyed on folded terminal spellings -- `primaryplus`, `hvreda345vac`, `powertubeheatercenter0v`.
165
+ * That table covered the corpus because it was written from the corpus, one packet at a time, and
166
+ * a transformer nobody had transcribed yet arrived as a refusal naming a winding class instead of
167
+ * the real fault, which was that the document never said what its coils were.
168
+ *
169
+ * Everything the table encoded beyond the grouping stays where it was:
170
+ *
171
+ * - **`end: a|b|tap` is coil order plus each terminal's own role.** The ends are the terminals
172
+ * whose declared role is `winding`; a `windingTap` sits physically where the list puts it.
173
+ * - **`end: unconnected` was never winding structure.** A tap the document wires to nothing is a
174
+ * connectivity fact, read from `terminalOccurrences` below exactly as before.
175
+ * - **`end: selectable` likewise.** Which mains tap is live is a property of the selector switch,
176
+ * and the refusal below still names that switch rather than the transformer.
177
+ */
178
+ export declare function transformerWindings(device: Device, law: Extract<DeviceLaw, {
179
+ kind: "transformer";
180
+ }>, terminalOccurrences: ReadonlyMap<NodeId, number>, selectorTaps: ReadonlySet<NodeId>): readonly LoweredWinding[];
181
+ //# sourceMappingURL=transformer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transformer.d.ts","sourceRoot":"","sources":["../src/transformer.ts"],"names":[],"mappings":"AAaA,OAAO,EAAU,KAAK,MAAM,EAAE,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAE3E;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQpF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4GG;AACH,MAAM,MAAM,cAAc,GACvB;IACA,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC3B,GACD;IACA,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEL;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC,CA6BhF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,mBAAmB,CAClC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,OAAO,CAAC,SAAS,EAAE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC,EAChD,mBAAmB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAChD,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAC/B,SAAS,cAAc,EAAE,CA4M3B"}