@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.
- package/README.md +314 -2
- package/dist/bake.d.ts +38 -0
- package/dist/bake.d.ts.map +1 -0
- package/dist/bake.js +101 -0
- package/dist/bake.js.map +1 -0
- package/dist/bbd-clock.d.ts +171 -0
- package/dist/bbd-clock.d.ts.map +1 -0
- package/dist/bbd-clock.js +1750 -0
- package/dist/bbd-clock.js.map +1 -0
- package/dist/block-row.d.ts +51 -0
- package/dist/block-row.d.ts.map +1 -0
- package/dist/block-row.js +17 -0
- package/dist/block-row.js.map +1 -0
- package/dist/bypass.d.ts +3 -0
- package/dist/bypass.d.ts.map +1 -0
- package/dist/bypass.js +228 -0
- package/dist/bypass.js.map +1 -0
- package/dist/compile.d.ts +24 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +344 -0
- package/dist/compile.js.map +1 -0
- package/dist/converter-scale.d.ts +14 -0
- package/dist/converter-scale.d.ts.map +1 -0
- package/dist/converter-scale.js +133 -0
- package/dist/converter-scale.js.map +1 -0
- package/dist/couple.d.ts +28 -0
- package/dist/couple.d.ts.map +1 -0
- package/dist/couple.js +567 -0
- package/dist/couple.js.map +1 -0
- package/dist/dangling-active-terminal.d.ts +47 -0
- package/dist/dangling-active-terminal.d.ts.map +1 -0
- package/dist/dangling-active-terminal.js +542 -0
- package/dist/dangling-active-terminal.js.map +1 -0
- package/dist/declared-program.d.ts +23 -0
- package/dist/declared-program.d.ts.map +1 -0
- package/dist/declared-program.js +387 -0
- package/dist/declared-program.js.map +1 -0
- package/dist/denomination.d.ts +48 -0
- package/dist/denomination.d.ts.map +1 -0
- package/dist/denomination.js +27 -0
- package/dist/denomination.js.map +1 -0
- package/dist/device-laws.d.ts +237 -0
- package/dist/device-laws.d.ts.map +1 -0
- package/dist/device-laws.js +3497 -0
- package/dist/device-laws.js.map +1 -0
- package/dist/diagnostics.d.ts +13 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +19 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/emit.d.ts +8 -0
- package/dist/emit.d.ts.map +1 -0
- package/dist/emit.js +43 -0
- package/dist/emit.js.map +1 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +39 -0
- package/dist/errors.js.map +1 -0
- package/dist/firmware-class.d.ts +69 -0
- package/dist/firmware-class.d.ts.map +1 -0
- package/dist/firmware-class.js +20 -0
- package/dist/firmware-class.js.map +1 -0
- package/dist/fixtures/circuits.d.ts +1721 -0
- package/dist/fixtures/circuits.d.ts.map +1 -0
- package/dist/fixtures/circuits.js +13404 -0
- package/dist/fixtures/circuits.js.map +1 -0
- package/dist/fixtures/expected.d.ts +257 -0
- package/dist/fixtures/expected.d.ts.map +1 -0
- package/dist/fixtures/expected.js +311 -0
- package/dist/fixtures/expected.js.map +1 -0
- package/dist/fixtures/index.d.ts +4 -0
- package/dist/fixtures/index.d.ts.map +1 -0
- package/dist/fixtures/index.js +8 -0
- package/dist/fixtures/index.js.map +1 -0
- package/dist/fixtures/registry.d.ts +52 -0
- package/dist/fixtures/registry.d.ts.map +1 -0
- package/dist/fixtures/registry.js +341 -0
- package/dist/fixtures/registry.js.map +1 -0
- package/dist/generic-tube-fit.d.ts +12 -0
- package/dist/generic-tube-fit.d.ts.map +1 -0
- package/dist/generic-tube-fit.js +45 -0
- package/dist/generic-tube-fit.js.map +1 -0
- package/dist/grounded-clock-supply.d.ts +13 -0
- package/dist/grounded-clock-supply.d.ts.map +1 -0
- package/dist/grounded-clock-supply.js +45 -0
- package/dist/grounded-clock-supply.js.map +1 -0
- package/dist/identify.d.ts +50 -0
- package/dist/identify.d.ts.map +1 -0
- package/dist/identify.js +319 -0
- package/dist/identify.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/inert-controls.d.ts +15 -0
- package/dist/inert-controls.d.ts.map +1 -0
- package/dist/inert-controls.js +266 -0
- package/dist/inert-controls.js.map +1 -0
- package/dist/latch-seed.d.ts +53 -0
- package/dist/latch-seed.d.ts.map +1 -0
- package/dist/latch-seed.js +257 -0
- package/dist/latch-seed.js.map +1 -0
- package/dist/link.d.ts +7 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +270 -0
- package/dist/link.js.map +1 -0
- package/dist/lower.d.ts +79 -0
- package/dist/lower.d.ts.map +1 -0
- package/dist/lower.js +1855 -0
- package/dist/lower.js.map +1 -0
- package/dist/netlist.d.ts +79 -0
- package/dist/netlist.d.ts.map +1 -0
- package/dist/netlist.js +4746 -0
- package/dist/netlist.js.map +1 -0
- package/dist/non-executable-clock-drivers.d.ts +8 -0
- package/dist/non-executable-clock-drivers.d.ts.map +1 -0
- package/dist/non-executable-clock-drivers.js +33 -0
- package/dist/non-executable-clock-drivers.js.map +1 -0
- package/dist/open-category.d.ts +98 -0
- package/dist/open-category.d.ts.map +1 -0
- package/dist/open-category.js +111 -0
- package/dist/open-category.js.map +1 -0
- package/dist/over-driven-node.d.ts +33 -0
- package/dist/over-driven-node.d.ts.map +1 -0
- package/dist/over-driven-node.js +188 -0
- package/dist/over-driven-node.js.map +1 -0
- package/dist/part-catalog.d.ts +7 -0
- package/dist/part-catalog.d.ts.map +1 -0
- package/dist/part-catalog.js +6639 -0
- package/dist/part-catalog.js.map +1 -0
- package/dist/part-number.d.ts +32 -0
- package/dist/part-number.d.ts.map +1 -0
- package/dist/part-number.js +80 -0
- package/dist/part-number.js.map +1 -0
- package/dist/partition.d.ts +7 -0
- package/dist/partition.d.ts.map +1 -0
- package/dist/partition.js +242 -0
- package/dist/partition.js.map +1 -0
- package/dist/port-full-scale.d.ts +70 -0
- package/dist/port-full-scale.d.ts.map +1 -0
- package/dist/port-full-scale.js +235 -0
- package/dist/port-full-scale.js.map +1 -0
- package/dist/pot-orientation.d.ts +114 -0
- package/dist/pot-orientation.d.ts.map +1 -0
- package/dist/pot-orientation.js +551 -0
- package/dist/pot-orientation.js.map +1 -0
- package/dist/power-domain-control.d.ts +40 -0
- package/dist/power-domain-control.d.ts.map +1 -0
- package/dist/power-domain-control.js +142 -0
- package/dist/power-domain-control.js.map +1 -0
- package/dist/proxy-declaration.d.ts +121 -0
- package/dist/proxy-declaration.d.ts.map +1 -0
- package/dist/proxy-declaration.js +50 -0
- package/dist/proxy-declaration.js.map +1 -0
- package/dist/registry.d.ts +323 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +192 -0
- package/dist/registry.js.map +1 -0
- package/dist/sparse-schedule.d.ts +65 -0
- package/dist/sparse-schedule.d.ts.map +1 -0
- package/dist/sparse-schedule.js +421 -0
- package/dist/sparse-schedule.js.map +1 -0
- package/dist/speaker-load.d.ts +38 -0
- package/dist/speaker-load.d.ts.map +1 -0
- package/dist/speaker-load.js +97 -0
- package/dist/speaker-load.js.map +1 -0
- package/dist/stage-coverage.d.ts +21 -0
- package/dist/stage-coverage.d.ts.map +1 -0
- package/dist/stage-coverage.js +124 -0
- package/dist/stage-coverage.js.map +1 -0
- package/dist/stamp-partition.d.ts +314 -0
- package/dist/stamp-partition.d.ts.map +1 -0
- package/dist/stamp-partition.js +472 -0
- package/dist/stamp-partition.js.map +1 -0
- package/dist/supply-reference.d.ts +19 -0
- package/dist/supply-reference.d.ts.map +1 -0
- package/dist/supply-reference.js +66 -0
- package/dist/supply-reference.js.map +1 -0
- package/dist/supply-short.d.ts +32 -0
- package/dist/supply-short.d.ts.map +1 -0
- package/dist/supply-short.js +118 -0
- package/dist/supply-short.js.map +1 -0
- package/dist/supply-stamps.d.ts +51 -0
- package/dist/supply-stamps.d.ts.map +1 -0
- package/dist/supply-stamps.js +363 -0
- package/dist/supply-stamps.js.map +1 -0
- package/dist/terminal-role.d.ts +109 -0
- package/dist/terminal-role.d.ts.map +1 -0
- package/dist/terminal-role.js +46 -0
- package/dist/terminal-role.js.map +1 -0
- package/dist/terminal-roles.d.ts +59 -0
- package/dist/terminal-roles.d.ts.map +1 -0
- package/dist/terminal-roles.js +92 -0
- package/dist/terminal-roles.js.map +1 -0
- package/dist/transformer.d.ts +181 -0
- package/dist/transformer.d.ts.map +1 -0
- package/dist/transformer.js +476 -0
- package/dist/transformer.js.map +1 -0
- package/dist/types.d.ts +3157 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/dist/unbounded-operating-point.d.ts +35 -0
- package/dist/unbounded-operating-point.d.ts.map +1 -0
- package/dist/unbounded-operating-point.js +181 -0
- package/dist/unbounded-operating-point.js.map +1 -0
- package/dist/unexecuted-region.d.ts +3 -0
- package/dist/unexecuted-region.d.ts.map +1 -0
- package/dist/unexecuted-region.js +122 -0
- package/dist/unexecuted-region.js.map +1 -0
- package/dist/unimplemented-control-role.d.ts +3 -0
- package/dist/unimplemented-control-role.d.ts.map +1 -0
- package/dist/unimplemented-control-role.js +61 -0
- package/dist/unimplemented-control-role.js.map +1 -0
- package/dist/unreachable-output.d.ts +3 -0
- package/dist/unreachable-output.d.ts.map +1 -0
- package/dist/unreachable-output.js +97 -0
- package/dist/unreachable-output.js.map +1 -0
- package/dist/unreadable-terminal-role.d.ts +67 -0
- package/dist/unreadable-terminal-role.d.ts.map +1 -0
- package/dist/unreadable-terminal-role.js +409 -0
- package/dist/unreadable-terminal-role.js.map +1 -0
- package/dist/unverified-pinout.d.ts +27 -0
- package/dist/unverified-pinout.d.ts.map +1 -0
- package/dist/unverified-pinout.js +121 -0
- package/dist/unverified-pinout.js.map +1 -0
- package/package.json +75 -5
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The kinds that separate the mains and HT domains from the audio graph.
|
|
3
|
+
*
|
|
4
|
+
* A supply, a rail and a battery are the domain's origin. A **transformer** is here because its
|
|
5
|
+
* windings couple magnetically rather than electrically: a mains primary and a heater secondary
|
|
6
|
+
* share no node, and treating the device as a conductor would make every rail reachable from the
|
|
7
|
+
* wall socket.
|
|
8
|
+
*/
|
|
9
|
+
const DOMAIN_BOUNDARY_KINDS = new Set([
|
|
10
|
+
"voltage-source",
|
|
11
|
+
"current-source",
|
|
12
|
+
"battery",
|
|
13
|
+
"rail",
|
|
14
|
+
"transformer",
|
|
15
|
+
"ground",
|
|
16
|
+
]);
|
|
17
|
+
/**
|
|
18
|
+
* The devices reachable from the audio ports without crossing a domain boundary.
|
|
19
|
+
*
|
|
20
|
+
* Ground is excluded as a hop for the reason a shared-node test always must: nearly every device
|
|
21
|
+
* touches it, so counting it makes the whole circuit one neighbourhood.
|
|
22
|
+
*/
|
|
23
|
+
function devicesReachableFromPorts(netlist, ports) {
|
|
24
|
+
const byNode = new Map();
|
|
25
|
+
for (const device of netlist.devices) {
|
|
26
|
+
if (DOMAIN_BOUNDARY_KINDS.has(device.kind)) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
for (const node of device.nodes) {
|
|
30
|
+
if (node === 0) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const bucket = byNode.get(node) ?? [];
|
|
34
|
+
bucket.push(device);
|
|
35
|
+
byNode.set(node, bucket);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const seenNodes = new Set(ports);
|
|
39
|
+
const seenDevices = new Set();
|
|
40
|
+
const queue = [...ports];
|
|
41
|
+
while (queue.length > 0) {
|
|
42
|
+
const node = queue.pop();
|
|
43
|
+
for (const device of byNode.get(node) ?? []) {
|
|
44
|
+
if (seenDevices.has(device.id)) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
seenDevices.add(device.id);
|
|
48
|
+
for (const next of device.nodes) {
|
|
49
|
+
if (next !== 0 && !seenNodes.has(next)) {
|
|
50
|
+
seenNodes.add(next);
|
|
51
|
+
queue.push(next);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return seenDevices;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A device exposed as a sweepable user control that is **power-domain hardware**: it sits outside
|
|
60
|
+
* the audio graph and no panel entry claims it.
|
|
61
|
+
*
|
|
62
|
+
* **Why this is reported rather than decided.** Structure cannot tell a user control from fixed
|
|
63
|
+
* hardware on this side of the boundary. A standby switch and a mains fuse are both switches in
|
|
64
|
+
* the supply, and one belongs on the front panel while the other belongs inside the chassis --
|
|
65
|
+
* only the document can say which. So the compiler names the ambiguity and the fix is a
|
|
66
|
+
* `deviceInterface.controls` entry, or a kind that is not a switch.
|
|
67
|
+
*
|
|
68
|
+
* **A rule was attempted and it does not exist.** The promising one was "a switch declaring a
|
|
69
|
+
* `FuseRating` is a fuse, therefore not a control" -- legal evidence, since it reads the presence
|
|
70
|
+
* of a typed property rather than a name, and 26 corpus switches declare one. It fails on its own
|
|
71
|
+
* data: `mesa-boogie-dual-rectifier`'s `SRC_POWER_SWITCH_FUSE_4A`, `orange-rockerverb`'s
|
|
72
|
+
* `SRC_PRIMARY_POWER_SWITCH_AND_FUSE` and `sunn-beta-lead`'s `SRC_POWER_SWITCH_FUSE` are each a
|
|
73
|
+
* power switch *and* a fuse in one two-terminal component, and a power switch is exactly the user
|
|
74
|
+
* control the rule would have suppressed. Narrowing it to "two terminals and no switch-shaped
|
|
75
|
+
* property" keeps all three, because a fuse and a combined switch-and-fuse are structurally
|
|
76
|
+
* identical: two terminals, one closed contact. The difference is whether a human operates it,
|
|
77
|
+
* which is not a fact about the circuit.
|
|
78
|
+
*
|
|
79
|
+
* **Measured 2026-09-04: 67 of the corpus's 958 program controls**, across the packets that
|
|
80
|
+
* model their own power supply. It read 80 before `boss-bf-2`'s three service trims were
|
|
81
|
+
* declared, and 76 before every signal jack became a seed -- see the note in the function. They are mains switches,
|
|
82
|
+
* voltage selectors, and mains, HT and heater fuses -- `vox-ac30-top-boost` alone contributes
|
|
83
|
+
* `SW_MAINS`, a 230/120 selector and four fuses. A fuse offered to a player as a knob to sweep is
|
|
84
|
+
* the shape of the problem.
|
|
85
|
+
*
|
|
86
|
+
* **The traversal's one known imprecision** is at the B+ boundary: it stops at the transformer but
|
|
87
|
+
* not at the rectifier and filter, so B+ nodes stay reachable through a tube's plate. That keeps
|
|
88
|
+
* `SW1_STANDBY` and `S_STANDBY_SWITCH` out of this warning, which is right -- a standby switch is
|
|
89
|
+
* a front-panel control -- and also keeps `F2_HT_FUSE` out, which is not.
|
|
90
|
+
*/
|
|
91
|
+
export function findPowerDomainControls(netlist,
|
|
92
|
+
// A control carries the `role` and `label` its `deviceInterface.controls` entry gave it, so
|
|
93
|
+
// "the panel does not claim this" is exactly both being absent -- no second pass over the
|
|
94
|
+
// document, and no way for the two readings to drift apart.
|
|
95
|
+
controls, ports) {
|
|
96
|
+
// **Every signal jack is a seed, not just the two selected ports.**
|
|
97
|
+
//
|
|
98
|
+
// A `.vdsp` names one input and one output port, and on a two-channel amp that selects one
|
|
99
|
+
// channel: `fender-super-reverb-aa1069`'s port is its *Vibrato* input, so the Normal channel
|
|
100
|
+
// hangs off a jack the traversal never started from. Combined with the transformer cut --
|
|
101
|
+
// which stops the walk re-entering from the speaker-side output port -- the search was
|
|
102
|
+
// effectively one-directional from a single channel's input, and it called three
|
|
103
|
+
// `Normal-Volume` pots and `soldano-slo-100`'s `SRC_PRESENCE_25K` power-domain hardware.
|
|
104
|
+
//
|
|
105
|
+
// Seeding every jack node drops the count from 76 to 67 and rescues all nine. It also rescues
|
|
106
|
+
// `soldano-slo-100`'s `SRC_F1_BPLUS_FUSE`, which is a false negative -- a B+ fuse is not a
|
|
107
|
+
// panel control -- and that trade is deliberate: this warning offers candidates for a human
|
|
108
|
+
// decision, and naming a Presence knob as power hardware costs more than missing one fuse.
|
|
109
|
+
const jackNodes = netlist.devices
|
|
110
|
+
.filter((device) => device.kind === "jack")
|
|
111
|
+
.flatMap((device) => device.nodes)
|
|
112
|
+
.filter((node) => node !== 0);
|
|
113
|
+
const unclaimed = controls.filter((control) => (control.role ?? null) === null && (control.label ?? null) === null);
|
|
114
|
+
if (unclaimed.length === 0) {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
const reachable = devicesReachableFromPorts(netlist, [
|
|
118
|
+
...ports,
|
|
119
|
+
...new Set(jackNodes),
|
|
120
|
+
]);
|
|
121
|
+
const warnings = [];
|
|
122
|
+
for (const { id } of unclaimed) {
|
|
123
|
+
if (reachable.has(id)) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const device = netlist.devices.find((candidate) => candidate.id === id);
|
|
127
|
+
if (device === undefined) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
warnings.push({
|
|
131
|
+
code: "power-domain-control",
|
|
132
|
+
device: id,
|
|
133
|
+
detail: `${device.kind} ${id} is offered as a sweepable control, but it sits outside the ` +
|
|
134
|
+
"audio graph -- unreachable from either port without crossing a supply, rail or " +
|
|
135
|
+
"transformer -- and no `deviceInterface.controls` entry claims it as a panel " +
|
|
136
|
+
"control. Power-domain hardware, not a control: declare it on the panel if a " +
|
|
137
|
+
"player operates it, and give it a kind that is not a switch if one does not.",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return warnings;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=power-domain-control.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"power-domain-control.js","sourceRoot":"","sources":["../src/power-domain-control.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **A declared proxy: a macro that stands in for a part it is not.**
|
|
3
|
+
*
|
|
4
|
+
* `RDD63H101` is a 144-pin microcode-driven digital delay processor bound to
|
|
5
|
+
* `bucket-brigade-delay-line` — an analog charge-transfer model. That binding may well be the right
|
|
6
|
+
* engineering call: removing it makes `boss-dd-2` and `boss-dsd-3` silent, which **trades a stated
|
|
7
|
+
* approximation for an unstated absence** and destroys information. What is wrong today is not the
|
|
8
|
+
* binding; it is that **nothing in the catalog says it is a stand-in**, so it reads as an identity
|
|
9
|
+
* claim, and the packets' own records say that chip's microcode is unavailable.
|
|
10
|
+
*
|
|
11
|
+
* **This is format item 8's shape, one level up.** The author knew the macro was an approximation
|
|
12
|
+
* and had nowhere to say so, so they wrote the only thing the format allowed: a binding that
|
|
13
|
+
* asserts identity. **Every required assertion with no "approximate, because" option manufactures
|
|
14
|
+
* a false claim**, and the coping strategies are the same three — overstate it, omit it and lose
|
|
15
|
+
* the reason, or write prose beside it.
|
|
16
|
+
*
|
|
17
|
+
* **Why this is a separate construct from item 8's reason field, since the unification was asked
|
|
18
|
+
* for.** They share the principle and not the payload. An absent part number needs a reason a
|
|
19
|
+
* *consumer* can branch on — unknown, excluded, not-applicable — and nothing more. A proxy needs
|
|
20
|
+
* three things a *fidelity* query must be able to read separately, and merging them into one
|
|
21
|
+
* free-text annotation would lose exactly the field that does the work: `doesNotReproduce`. **One
|
|
22
|
+
* generic "declared inexactness" bag would be uncheckable, which is the property both constructs
|
|
23
|
+
* exist to provide.** They belong in one format package and not in one field.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* **A thing the model fails to reproduce, written so it can be RUN.**
|
|
27
|
+
*
|
|
28
|
+
* The test: *could someone who has never read this declaration run it and get a number that either
|
|
29
|
+
* agrees or does not?* — *"does not reproduce the microcode's modulation behaviour"* fails that;
|
|
30
|
+
* *"delay time does not track the clock, measured as time-of-first-energy"* passes it.
|
|
31
|
+
*
|
|
32
|
+
* **This is the falsifier, and it is the reason a declaration is not paperwork.** Each entry
|
|
33
|
+
* becomes the **acceptance row for the real block** when someone finally writes it: the measurement
|
|
34
|
+
* is already chosen, the expected value of the stand-in is already recorded, and a real model has
|
|
35
|
+
* to move that number. A prose sentence here gives the next person nothing to run.
|
|
36
|
+
*/
|
|
37
|
+
export type Measurable = {
|
|
38
|
+
/** What is not reproduced. */
|
|
39
|
+
readonly claim: string;
|
|
40
|
+
/** How to measure it, in terms someone who has not read this could execute. */
|
|
41
|
+
readonly measurement: string;
|
|
42
|
+
/** What THIS model yields for that measurement -- the number a real block must move. */
|
|
43
|
+
readonly standIn: string;
|
|
44
|
+
/**
|
|
45
|
+
* **`registered-unrun` is a PRE-REGISTRATION WITHOUT A FIXTURE, and it is not the same thing as
|
|
46
|
+
* a refuted claim.** Nine `subsumes` claims were withdrawn on 2026-09-11 because they were shown
|
|
47
|
+
* **false**; a well-formed measurable that no packet can currently run is **unrun**, which is a
|
|
48
|
+
* different state. Deleting it destroys the evidence that the claim was stated *before* any
|
|
49
|
+
* measurement — and pre-registration is the thing this method exists to make valuable.
|
|
50
|
+
*
|
|
51
|
+
* **What it must never do is look like it ran, or count toward anything measured.**
|
|
52
|
+
*/
|
|
53
|
+
readonly status: "measured" | "registered-unrun";
|
|
54
|
+
/** `registered-unrun` only: what fixture would run it. Required, so the claim names its own cure. */
|
|
55
|
+
readonly fixture?: string;
|
|
56
|
+
/** `registered-unrun` only: withdraw if no fixture exists by this date. A registration with no expiry is a permanent excuse. */
|
|
57
|
+
readonly expires?: string;
|
|
58
|
+
};
|
|
59
|
+
export type ProxyDeclaration = {
|
|
60
|
+
/** The real part and what it actually is — not what the macro models. */
|
|
61
|
+
readonly standsFor: string;
|
|
62
|
+
/** Why the stand-in is defensible at all, in terms a reviewer can disagree with. */
|
|
63
|
+
readonly justification: string;
|
|
64
|
+
/**
|
|
65
|
+
* **What the proxy does NOT reproduce. Required, and required to be non-empty.**
|
|
66
|
+
*
|
|
67
|
+
* A proxy that reproduces everything is not a proxy, it is a model — so an empty list is
|
|
68
|
+
* always an error, never a well-behaved case. This is the field that makes the declaration
|
|
69
|
+
* cost something: an author who cannot name a single thing their stand-in fails to reproduce
|
|
70
|
+
* has not examined it.
|
|
71
|
+
*/
|
|
72
|
+
readonly doesNotReproduce: readonly Measurable[];
|
|
73
|
+
/**
|
|
74
|
+
* **True by construction, so passing proves nothing.** Kept apart from `doesNotReproduce`
|
|
75
|
+
* because listing a structural invariant among measurables inflates the count of things
|
|
76
|
+
* measured — the same inflation this programme spent 2026-09-11 removing. A compiler check is
|
|
77
|
+
* the right home for these; they are recorded here so the boundary is visible.
|
|
78
|
+
*/
|
|
79
|
+
readonly structuralInvariants?: readonly string[];
|
|
80
|
+
};
|
|
81
|
+
export type ProxyViolation = {
|
|
82
|
+
readonly field: string;
|
|
83
|
+
readonly problem: string;
|
|
84
|
+
};
|
|
85
|
+
/** Structural validation. A declaration that cannot fail is not a declaration. */
|
|
86
|
+
export declare function validateProxy(p: ProxyDeclaration): readonly ProxyViolation[];
|
|
87
|
+
/**
|
|
88
|
+
* **A CITED DESIGN-TIME DERIVATION — a different claim from `subsumes`, with a different check.**
|
|
89
|
+
*
|
|
90
|
+
* `subsumes` claims **parametric dependence**: mutate the device's spec and the macro's parameters
|
|
91
|
+
* move. That is the only form a census can trust without reading anything, and it is what
|
|
92
|
+
* `scripts/report-subsumption-mutant.ts` tests.
|
|
93
|
+
*
|
|
94
|
+
* A design-time derivation is legitimate and **the mutant would reject it**, because the value is a
|
|
95
|
+
* literal in the catalog rather than a computed expression. So it gets its own field and its own
|
|
96
|
+
* check: **RECOMPUTATION**. Apply the cited formula to the cited values and require it to reproduce
|
|
97
|
+
* the constant.
|
|
98
|
+
*
|
|
99
|
+
* > **A cited derivation that does not reproduce the constant is a guess with a footnote**, and
|
|
100
|
+
* > recomputation catches that where a citation alone does not.
|
|
101
|
+
*
|
|
102
|
+
* **The nine withdrawn `subsumes` claims could be re-made this way, and cannot be**: the constant
|
|
103
|
+
* is `stages: 1024`, which is the **MN3007's datasheet stage count** — the macro's default. The
|
|
104
|
+
* same 1024 appears in `boss-rv-6`, whose Renesas reverb DSP has a 64Mbit SDRAM and no bucket
|
|
105
|
+
* brigade anywhere. **No formula from three 64Kx1 DRAMs lands on 1024 stages of a device class the
|
|
106
|
+
* pedal does not contain.**
|
|
107
|
+
*/
|
|
108
|
+
export type DerivedFrom = {
|
|
109
|
+
/** Which macro parameter this derives. */
|
|
110
|
+
readonly parameter: string;
|
|
111
|
+
/** The device whose spec determines it, by canonical part id. */
|
|
112
|
+
readonly fromPart: string;
|
|
113
|
+
/** The source, cited to edition and page, as the triode registry cites per parameter. */
|
|
114
|
+
readonly citation: string;
|
|
115
|
+
/** The arithmetic, stated so it can be re-run -- not a pointer at the parts. */
|
|
116
|
+
readonly formula: string;
|
|
117
|
+
/** What the formula yields. Must equal the constant the entry declares. */
|
|
118
|
+
readonly recomputes: number;
|
|
119
|
+
};
|
|
120
|
+
export declare function validateDerivation(d: DerivedFrom, declared: number): readonly ProxyViolation[];
|
|
121
|
+
//# sourceMappingURL=proxy-declaration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-declaration.d.ts","sourceRoot":"","sources":["../src/proxy-declaration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,GAAG;IACxB,8BAA8B;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,wFAAwF;IACxF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;;;;;OAQG;IACH,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,kBAAkB,CAAC;IACjD,qGAAqG;IACrG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,gIAAgI;IAChI,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC9B,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,oFAAoF;IACpF,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B;;;;;;;OAOG;IACH,QAAQ,CAAC,gBAAgB,EAAE,SAAS,UAAU,EAAE,CAAC;IACjD;;;;;OAKG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAElF,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,CAAC,EAAE,gBAAgB,GAAG,SAAS,cAAc,EAAE,CAoC5E;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,WAAW,GAAG;IACzB,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,cAAc,EAAE,CAU9F"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** Structural validation. A declaration that cannot fail is not a declaration. */
|
|
2
|
+
export function validateProxy(p) {
|
|
3
|
+
const out = [];
|
|
4
|
+
if (p.standsFor.trim() === "")
|
|
5
|
+
out.push({ field: "standsFor", problem: "must name the real part and what it actually is" });
|
|
6
|
+
if (p.justification.trim() === "")
|
|
7
|
+
out.push({ field: "justification", problem: "must say why the stand-in is defensible" });
|
|
8
|
+
if (p.doesNotReproduce.length === 0)
|
|
9
|
+
out.push({
|
|
10
|
+
field: "doesNotReproduce",
|
|
11
|
+
problem: "must name at least one thing the proxy fails to reproduce -- a proxy that reproduces everything is a model, not a proxy",
|
|
12
|
+
});
|
|
13
|
+
for (const [i, m] of p.doesNotReproduce.entries()) {
|
|
14
|
+
if (m.status === "registered-unrun") {
|
|
15
|
+
if ((m.fixture ?? "").trim() === "")
|
|
16
|
+
out.push({ field: `doesNotReproduce[${i}].fixture`, problem: "a registered-unrun claim must name the fixture that would run it" });
|
|
17
|
+
if ((m.expires ?? "").trim() === "")
|
|
18
|
+
out.push({ field: `doesNotReproduce[${i}].expires`, problem: "a registration with no expiry is a permanent excuse" });
|
|
19
|
+
}
|
|
20
|
+
// **All three parts are required, because two of them are the falsifier.** A `claim` alone
|
|
21
|
+
// is prose; a `measurement` without a `standIn` cannot be compared; a `standIn` without a
|
|
22
|
+
// `measurement` cannot be reproduced.
|
|
23
|
+
if (m.claim.trim() === "")
|
|
24
|
+
out.push({ field: `doesNotReproduce[${i}].claim`, problem: "is empty" });
|
|
25
|
+
if (m.measurement.trim() === "")
|
|
26
|
+
out.push({
|
|
27
|
+
field: `doesNotReproduce[${i}].measurement`,
|
|
28
|
+
problem: "must say how to measure it -- a claim nobody can run is not a falsifier",
|
|
29
|
+
});
|
|
30
|
+
if (m.standIn.trim() === "")
|
|
31
|
+
out.push({
|
|
32
|
+
field: `doesNotReproduce[${i}].standIn`,
|
|
33
|
+
problem: "must give the number THIS model yields, which is what a real block has to move",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
export function validateDerivation(d, declared) {
|
|
39
|
+
const out = [];
|
|
40
|
+
for (const [k, v] of [["parameter", d.parameter], ["fromPart", d.fromPart], ["citation", d.citation], ["formula", d.formula]])
|
|
41
|
+
if (v.trim() === "")
|
|
42
|
+
out.push({ field: k, problem: "is empty" });
|
|
43
|
+
if (d.recomputes !== declared)
|
|
44
|
+
out.push({
|
|
45
|
+
field: "recomputes",
|
|
46
|
+
problem: `the cited formula yields ${d.recomputes} and the entry declares ${declared} -- a cited derivation that does not reproduce the constant is a guess with a footnote`,
|
|
47
|
+
});
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=proxy-declaration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-declaration.js","sourceRoot":"","sources":["../src/proxy-declaration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import type { DeviceLaw, MacroModel, PartIdentity } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* One section of a multi-section part: a law plus which of the device's terminals it uses.
|
|
4
|
+
*
|
|
5
|
+
* `terminals` are indices into the device's own terminal list, given in the canonical order the
|
|
6
|
+
* law's lowering expects -- two ends for a two-terminal law, `plus, minus, output` for an op-amp.
|
|
7
|
+
* That order is the contract, because a section bypasses the terminal-role lookup lowering does
|
|
8
|
+
* for a whole device: an 8-pin dual op-amp has two outputs and two inverting inputs, so no single
|
|
9
|
+
* role token can name either of them unambiguously.
|
|
10
|
+
*/
|
|
11
|
+
export type PartSection = {
|
|
12
|
+
readonly law: DeviceLaw;
|
|
13
|
+
readonly terminals: readonly number[];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The terminal order a `sections` model's indices assume, as role tokens, index-aligned with the
|
|
17
|
+
* device's own terminal list. `null` means that position carries no role token to check against.
|
|
18
|
+
*
|
|
19
|
+
* **Positional indices are only meaningful against a known declaration order, and the corpus proves
|
|
20
|
+
* two documents can order the same chip differently.** `boss-ce-2b` declares a `TL022CP` as
|
|
21
|
+
* `pin1_OUT_B, pin2_IN_B_MINUS, pin3_IN_B_PLUS, pin4_VEE, pin5_IN_A_PLUS, ...` -- physical pin
|
|
22
|
+
* order -- while `boss-bf-2` declares the same part number grouped by section, as
|
|
23
|
+
* `outA, invertingInputA, nonInvertingInputA, outB, ...`. One entry's `terminals` indices are
|
|
24
|
+
* correct for exactly one of those, and applied to the other they wire an amplifier's output to
|
|
25
|
+
* its input.
|
|
26
|
+
*
|
|
27
|
+
* Without this field that mistake is silent: every index resolves, every section stamps, and the
|
|
28
|
+
* pedal renders a plausible wrong circuit. With it, a document the entry was not written against
|
|
29
|
+
* refuses by name instead. It is the same reasoning as `identify`'s whole-token comparison -- a
|
|
30
|
+
* coverage bound is acceptable, a wrong answer is not.
|
|
31
|
+
*/
|
|
32
|
+
export type PartPinout = readonly (string | null)[];
|
|
33
|
+
/**
|
|
34
|
+
* What the registry knows about a part. Either it reduces to lumped elements the
|
|
35
|
+
* solver can stamp, or it is a DSP algorithm with ports that the runtime implements.
|
|
36
|
+
*
|
|
37
|
+
* **`sections` exists because most real chips are multi-section and neither other arm can express
|
|
38
|
+
* one.** A dual op-amp is two amplifiers on eight pins: not one `law`, which produces a single
|
|
39
|
+
* element, and not a `macro` either, because a macro becomes its own block solved *outside* the MNA
|
|
40
|
+
* system while an op-amp's output depends on the feedback network that depends on its output.
|
|
41
|
+
* Lowering it to a separate DSP block breaks the loop that makes it an amplifier.
|
|
42
|
+
*
|
|
43
|
+
* A `sections` part is expanded by stage 3 into one ordinary device per section, so partitioning,
|
|
44
|
+
* lowering and the runtime see nothing new.
|
|
45
|
+
*/
|
|
46
|
+
/**
|
|
47
|
+
* Where a macro's ports sit, named by terminal role rather than by terminal index.
|
|
48
|
+
*
|
|
49
|
+
* **This is the same problem `PartPinout` solves for `sections`, with a better answer available.**
|
|
50
|
+
* A positional index is only meaningful against a known declaration order, and the corpus orders
|
|
51
|
+
* one bucket brigade three ways: `boss-ce-2` declares `gnd, cp1, in, vgg, vdd, cp2, out1, out2`,
|
|
52
|
+
* `boss-dm-2` declares the same family part as `gnd, cp2, out1, out2, vdd, cp1, in, vgg`, and
|
|
53
|
+
* `electro-harmonix-electric-mistress` declares a two-terminal shell as `input, output`. One entry
|
|
54
|
+
* carrying indices is correct for exactly one of those and silently mis-wires the rest.
|
|
55
|
+
*
|
|
56
|
+
* `sections` cannot escape indices — a dual op-amp has two outputs and two inverting inputs, so no
|
|
57
|
+
* single role token names either unambiguously, which is why that arm refuses on a pinout guard
|
|
58
|
+
* instead. A macro's audio in and audio out are exactly one terminal each, so a role token *does*
|
|
59
|
+
* name them, and resolving by role is order-independent rather than order-checked. One entry then
|
|
60
|
+
* serves every declaration order that names its terminals at all.
|
|
61
|
+
*
|
|
62
|
+
* The bound is the same one `identify` accepts: whole-token comparison cannot invent a match, but
|
|
63
|
+
* the transcriber must have used a spelling listed here. A packet numbering its pins `pin1..pin8`
|
|
64
|
+
* produces no role tokens and is refused by name rather than guessed at.
|
|
65
|
+
*/
|
|
66
|
+
export type MacroPortRoles = {
|
|
67
|
+
/** Aliases for the audio input terminal. Compared as whole folded tokens. */
|
|
68
|
+
readonly audioIn: readonly string[];
|
|
69
|
+
/** Aliases for the audio output terminal. Compared as whole folded tokens. */
|
|
70
|
+
readonly audioOut: readonly string[];
|
|
71
|
+
/**
|
|
72
|
+
* Aliases for a **second** audio output terminal, for a part that has two.
|
|
73
|
+
*
|
|
74
|
+
* A BBD is the case this exists for: an MN3208/BL3208 brings out OUT1 and OUT2, and a design
|
|
75
|
+
* puts a balance trimmer across them because the signal appears on both while the clock
|
|
76
|
+
* feedthrough appears in antiphase, so the pot nulls the clock and keeps the audio. Declaring
|
|
77
|
+
* only OUT1 left OUT2 driven by nothing, and the trimmer then acted as a plain attenuator
|
|
78
|
+
* against whatever bled through its own track -- measured on `mxr-carbon-copy` as **6.0 dB
|
|
79
|
+
* lost across four brigades at the trimmers' centre default against 0.67 dB with the wipers
|
|
80
|
+
* on the driven pin**, which a compandor downstream then squared.
|
|
81
|
+
*
|
|
82
|
+
* Empty (the default for every part that does not set it) means the part has one output, and
|
|
83
|
+
* nothing about the single-output path changes.
|
|
84
|
+
*/
|
|
85
|
+
readonly audioOut2?: readonly string[];
|
|
86
|
+
/**
|
|
87
|
+
* Aliases for the terminal whose network derives the `parameter` port, if the part has one.
|
|
88
|
+
*
|
|
89
|
+
* Optional in effect rather than in type: an empty list means the part has none. Unlike the
|
|
90
|
+
* audio ports, failing to resolve this is not a refusal — `couple.ts` documents an absent
|
|
91
|
+
* `parameter` port as the safe default, where an absent audio port is an amputation.
|
|
92
|
+
*/
|
|
93
|
+
readonly parameter: readonly string[];
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* A macro as the registry states it: everything `MacroModel` carries except the terminal indices
|
|
97
|
+
* and the pedal-specific parameters, which stage 3 resolves per device.
|
|
98
|
+
*
|
|
99
|
+
* Same split as `sections`' op-amp rails, and for the same reason — a registry describes a part,
|
|
100
|
+
* and where its terminals landed and how fast its clock runs are properties of the pedal it was
|
|
101
|
+
* fitted to.
|
|
102
|
+
*/
|
|
103
|
+
/**
|
|
104
|
+
* 0-indexed physical terminal positions for a document that numbers its pins (`pin1`, `pin2`, ...
|
|
105
|
+
* -- `terminalRoleToken` returns null for those) and so carries no role tokens. Only consulted when
|
|
106
|
+
* every terminal is bare; a document that names even one terminal resolves by role, never by these.
|
|
107
|
+
*/
|
|
108
|
+
export type MacroBarePinPositions = {
|
|
109
|
+
readonly audioIn: number;
|
|
110
|
+
readonly audioOut: number;
|
|
111
|
+
readonly parameter?: number;
|
|
112
|
+
};
|
|
113
|
+
export type MacroPartModel = {
|
|
114
|
+
readonly modelId: string;
|
|
115
|
+
/** Part-intrinsic parameters only. Pedal-specific ones are merged in at resolution. */
|
|
116
|
+
readonly parameters: Readonly<Record<string, number>>;
|
|
117
|
+
readonly ports: MacroPortRoles;
|
|
118
|
+
readonly barePinPositions?: MacroBarePinPositions;
|
|
119
|
+
readonly audioPortImpedanceOhms: MacroModel["audioPortImpedanceOhms"];
|
|
120
|
+
readonly parameterReferenceVolts: number | null;
|
|
121
|
+
};
|
|
122
|
+
export type PartModel = {
|
|
123
|
+
readonly kind: "law";
|
|
124
|
+
readonly law: DeviceLaw;
|
|
125
|
+
} | {
|
|
126
|
+
readonly kind: "sections";
|
|
127
|
+
readonly sections: readonly PartSection[];
|
|
128
|
+
/** The declaration order `sections[*].terminals` index into. See `PartPinout`. */
|
|
129
|
+
readonly pinout: PartPinout;
|
|
130
|
+
} | {
|
|
131
|
+
readonly kind: "macro";
|
|
132
|
+
readonly macro: MacroPartModel;
|
|
133
|
+
} | {
|
|
134
|
+
/**
|
|
135
|
+
* A chip whose silicon holds both lumped analog stages and a sampled
|
|
136
|
+
* core (first case: the PT2399's internal filter op-amps around its
|
|
137
|
+
* ADC/RAM/DAC): `sections` expand to ordinary MNA devices exactly as
|
|
138
|
+
* the `sections` arm does, and `core` resolves to the macro block
|
|
139
|
+
* exactly as the `macro` arm does, against the same device. Neither
|
|
140
|
+
* arm can express this alone -- a section is a lumped law and a
|
|
141
|
+
* macro solves outside MNA -- so the entry carries both, and the
|
|
142
|
+
* arms stay exclusive otherwise.
|
|
143
|
+
*/
|
|
144
|
+
readonly kind: "sections-core";
|
|
145
|
+
readonly sections: readonly PartSection[];
|
|
146
|
+
/** The declaration order `sections[*].terminals` index into. See `PartPinout`. */
|
|
147
|
+
readonly pinout: PartPinout;
|
|
148
|
+
/** The sampled core, resolved as a macro against the whole device. */
|
|
149
|
+
readonly core: MacroPartModel;
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* The index of the one terminal whose role matches any of `aliases`, or `null`.
|
|
153
|
+
*
|
|
154
|
+
* `null` for no match and for an ambiguous one. Two terminals answering to the same port role is a
|
|
155
|
+
* declaration this entry cannot address — a `SAD1024`'s two cascaded sections both carry an
|
|
156
|
+
* `input` if a document names them that way — and picking the first would be a guess about which
|
|
157
|
+
* half the audio enters.
|
|
158
|
+
*/
|
|
159
|
+
export declare function terminalWithRole(terminalRoles: readonly (string | null)[], aliases: readonly string[]): number | null;
|
|
160
|
+
/**
|
|
161
|
+
* Does this device declare the terminal order a `sections` entry's indices were written against?
|
|
162
|
+
*
|
|
163
|
+
* Length must match, and every position the entry names must carry that role token. A position the
|
|
164
|
+
* entry leaves `null` is unchecked, which is what lets a part whose document numbers its pins
|
|
165
|
+
* (`pin1`, `pin2`, ... -- `terminalRoleToken` returns null for those) still be expanded.
|
|
166
|
+
*/
|
|
167
|
+
export declare function pinoutMatches(pinout: PartPinout, terminalRoles: readonly (string | null)[]): boolean;
|
|
168
|
+
import type { FirmwareEvidence } from "./firmware-class";
|
|
169
|
+
import type { TerminalProfile } from "./terminal-role";
|
|
170
|
+
import type { ProxyDeclaration } from "./proxy-declaration";
|
|
171
|
+
export type PartEntry = {
|
|
172
|
+
/** Matched whole and case-folded against a device's part number. */
|
|
173
|
+
readonly partIds: readonly string[];
|
|
174
|
+
/** Matched whole against the source's closed type vocabulary. */
|
|
175
|
+
readonly declaredTypes: readonly string[];
|
|
176
|
+
/**
|
|
177
|
+
* Required terminal role tokens. Every group must be satisfied by at least one
|
|
178
|
+
* alias, compared as whole tokens. Empty means this part has no pinout signature.
|
|
179
|
+
*/
|
|
180
|
+
readonly terminalRoleGroups: readonly (readonly string[])[];
|
|
181
|
+
readonly model: PartModel;
|
|
182
|
+
/**
|
|
183
|
+
* **Present when this entry's model is a STAND-IN for a part it is not.** Required for any
|
|
184
|
+
* macro binding a part whose packets declare its behaviour unavailable, and read by
|
|
185
|
+
* `report-record-behaviour.ts`: a firmware-blocked part that executes a macro is a fidelity
|
|
186
|
+
* overclaim **unless** the binding declares itself a proxy and says what it fails to
|
|
187
|
+
* reproduce. Absent means the entry asserts it models the part.
|
|
188
|
+
*/
|
|
189
|
+
readonly proxy?: ProxyDeclaration;
|
|
190
|
+
/**
|
|
191
|
+
* **Whether this chip's behaviour comes from a program — typed, with positive evidence.**
|
|
192
|
+
* Absent means `undetermined`, which is **not** a scope boundary. See `firmware-class.ts`: the
|
|
193
|
+
* compiler's current operative test reads the packet's *record* rather than the chip, so a
|
|
194
|
+
* failed dump search becomes "this contains firmware" with nothing demonstrating it.
|
|
195
|
+
*/
|
|
196
|
+
readonly firmware?: FirmwareEvidence;
|
|
197
|
+
/**
|
|
198
|
+
* **The whole package's own supply current at no load, in amps, from its datasheet.**
|
|
199
|
+
*
|
|
200
|
+
* An `ideal-opamp` takes its rails as numbers and a clock driver's phases are ideal sources, so
|
|
201
|
+
* without this no IC draws anything from the supply: `boss-ce-2`'s battery read 2.7 mA against
|
|
202
|
+
* the 9 mA its service notes print, the missing ~6 mA being its 4558, TL022 and MN3101.
|
|
203
|
+
* `device-laws.ts` lowers it as a conductance across the package's supply pins, sized at the
|
|
204
|
+
* pedal's own rail span, so the draw is right at the rail the pedal runs on. A device that
|
|
205
|
+
* declares one section of a multi-section package draws that section's share.
|
|
206
|
+
*
|
|
207
|
+
* Absent means no datasheet figure is recorded, and the part draws nothing, as before.
|
|
208
|
+
*/
|
|
209
|
+
readonly quiescentSupplyAmps?: number;
|
|
210
|
+
/**
|
|
211
|
+
* **What each terminal IS, with per-terminal evidence.** Absent means `unpopulated`, which is a
|
|
212
|
+
* correct state rather than a gap: a consumer that needs audio terminals must fall back to
|
|
213
|
+
* bridging everything and **report its result as an upper bound**.
|
|
214
|
+
*/
|
|
215
|
+
readonly terminalProfile?: TerminalProfile;
|
|
216
|
+
/**
|
|
217
|
+
* **Which other parts this entry's model ACCOUNTS FOR.** Canonical part ids of devices whose
|
|
218
|
+
* behaviour is inside this model's abstraction, so a consumer can move them out of an engine-gap
|
|
219
|
+
* count **with evidence** rather than by a packet-level guess.
|
|
220
|
+
*
|
|
221
|
+
* **This is the third instance of one defect.** A macro block carries `kind id modelId
|
|
222
|
+
* parameters audioIn audioOut parameter modulation clockControl` and **no way to say what it
|
|
223
|
+
* stands for** — so whoever bound `RDD63H101` to a bucket-brigade had nowhere to write *"and
|
|
224
|
+
* this also accounts for the three DRAMs and the ladder"*, exactly as whoever wrote
|
|
225
|
+
* `undisclosed-roland-boss-dsp` had nowhere to write *"this part has no name"*, and exactly as
|
|
226
|
+
* `open` stood in for five different claims. **Prose in an identity field, a catch-all value,
|
|
227
|
+
* and a macro that cannot name its scope: three coping strategies, one missing field each.**
|
|
228
|
+
*
|
|
229
|
+
* **Deliberately on the ENTRY rather than inside `proxy`**, because subsumption and
|
|
230
|
+
* approximation are independent: `M50195` is a legitimate model of a fixed-function chip — not
|
|
231
|
+
* a proxy at all — and it still subsumes the `4164` DRAM it addresses.
|
|
232
|
+
*
|
|
233
|
+
* **WITHDRAWN ON ITS FIRST DAY, and that is the field working.** Two entries declared
|
|
234
|
+
* `subsumes` on 2026-09-11 -- `RDD63H101` claiming three DRAMs and an R2R ladder, `M50195P`
|
|
235
|
+
* claiming a `4164` -- and all nine claims passed the structural check (12-14 shared nodes) and
|
|
236
|
+
* then **all nine collapsed under the parameter mutant**. `stages: 1024` is a catalog constant
|
|
237
|
+
* and `delaySeconds` comes from the owner's own `DelayMs`; neither moves when the subsumed
|
|
238
|
+
* device's part is changed. The macros are not parameterised by what they claimed to account
|
|
239
|
+
* for, so the claims were decorative. **A shared bus looks identical either way, which is
|
|
240
|
+
* exactly what the structural check cannot see.**
|
|
241
|
+
*
|
|
242
|
+
* **The field stays; the claims go.** Any future `subsumes` entry must pass
|
|
243
|
+
* `scripts/report-subsumption-mutant.ts`: mutate the subsumed device's spec and the claiming
|
|
244
|
+
* macro's parameters must move. **THE DERIVATION IS THE EVIDENCE** -- a subsumption claim that
|
|
245
|
+
* cannot name which macro parameter the subsumed device determines is as empty as a device
|
|
246
|
+
* parameter with no source citation, which the triode registry already knows how to carry.
|
|
247
|
+
*/
|
|
248
|
+
readonly subsumes?: readonly string[];
|
|
249
|
+
/**
|
|
250
|
+
* **Claims that were MADE and REFUTED, kept rather than erased.** "Declared and refuted" is a
|
|
251
|
+
* different failure from "the record said nothing", and a census that collapses them loses the
|
|
252
|
+
* distinction between a packet that never spoke and one that spoke and was wrong.
|
|
253
|
+
*/
|
|
254
|
+
readonly subsumesRefuted?: readonly {
|
|
255
|
+
readonly parts: readonly string[];
|
|
256
|
+
readonly refutedBy: string;
|
|
257
|
+
readonly on: string;
|
|
258
|
+
}[];
|
|
259
|
+
/**
|
|
260
|
+
* **This part is a data converter, and what its full scale is.** Read by `converter-scale.ts`:
|
|
261
|
+
* a program runs in volts, so a DSP between an ADC and a DAC is unity only if the two full
|
|
262
|
+
* scales are equal. The real gain is `dac / adc`, and it is a fact about the two parts, not
|
|
263
|
+
* something a document should have to declare.
|
|
264
|
+
*/
|
|
265
|
+
readonly converter?: ConverterDeclaration;
|
|
266
|
+
};
|
|
267
|
+
/**
|
|
268
|
+
* A converter's full scale, cited. `fullScaleVoltsPeakToPeak` is null when no datasheet was
|
|
269
|
+
* obtained, and `basis` then says what was searched: an unknown full scale is a named gap, never
|
|
270
|
+
* a guessed number.
|
|
271
|
+
*/
|
|
272
|
+
export type ConverterDeclaration = {
|
|
273
|
+
readonly direction: "adc" | "dac";
|
|
274
|
+
readonly fullScaleVoltsPeakToPeak: number | null;
|
|
275
|
+
readonly basis: string;
|
|
276
|
+
};
|
|
277
|
+
export type PartRegistry = {
|
|
278
|
+
readonly entries: readonly PartEntry[];
|
|
279
|
+
};
|
|
280
|
+
export declare const emptyRegistry: PartRegistry;
|
|
281
|
+
/** Run `fn` with registry reads recorded against `owner`. Nesting is honoured; the outer set wins. */
|
|
282
|
+
export declare function withRecordedReads<T>(owner: string, fn: () => T): T;
|
|
283
|
+
/** What each owner consulted. Keyed by the owner string passed to `withRecordedReads`. */
|
|
284
|
+
export declare function recordedReads(): ReadonlyMap<string, ReadonlySet<string>>;
|
|
285
|
+
/** Forget everything recorded so far. Callers that measure one packet at a time need this. */
|
|
286
|
+
export declare function clearRecordedReads(): void;
|
|
287
|
+
export declare function registryEntryFor(registry: PartRegistry, identity: PartIdentity, terminalCount?: number): PartEntry | null;
|
|
288
|
+
export declare function registryModelFor(registry: PartRegistry, identity: PartIdentity, terminalCount?: number): PartModel | null;
|
|
289
|
+
/** Part numbers compare ignoring case and separators: `MN-3007` is `mn3007`. */
|
|
290
|
+
export declare function foldPartId(value: string | undefined | null): string;
|
|
291
|
+
/** Typed vocabulary values compare ignoring case and whitespace, keeping structure. */
|
|
292
|
+
export declare function foldToken(value: string | undefined | null): string;
|
|
293
|
+
/**
|
|
294
|
+
* The law a registered part supplies for a device whose class already names its law kind.
|
|
295
|
+
*
|
|
296
|
+
* This is rung 1 of `identify.ts`'s evidence ladder and **only** rung 1: an exact manufacturer
|
|
297
|
+
* part number, compared whole and case-folded. The other two rungs are deliberately unreachable
|
|
298
|
+
* here.
|
|
299
|
+
*
|
|
300
|
+
* - **Declared type is not consulted.** `Circuit.Pentode` is a *class*, and resolving a class to
|
|
301
|
+
* one part's model would claim every pentode in the corpus is an EL84 — the same reason
|
|
302
|
+
* `part-catalog.ts` leaves `declaredTypes` empty for op-amps.
|
|
303
|
+
* - **Pinout signature is not consulted.** Every pentode has the same electrodes, so a pinout
|
|
304
|
+
* match cannot tell an EL84 from a KT66. That is exactly the case where a wrong identity is
|
|
305
|
+
* worse than none.
|
|
306
|
+
*
|
|
307
|
+
* `lawKind` must match the law the device's class already implies, so an entry can only refine
|
|
308
|
+
* the law that class was going to get. A triode cannot be handed a pentode's fit by a catalog
|
|
309
|
+
* typo; it falls back instead.
|
|
310
|
+
*
|
|
311
|
+
* Returns `null` when nothing matches, which is a correct and expected answer — the caller keeps
|
|
312
|
+
* its device-class default. See `findGenericTubeFits`, which reports every such fallback, because
|
|
313
|
+
* a silent one is what let every amp in the corpus run a 6V6 fit.
|
|
314
|
+
*/
|
|
315
|
+
export declare function registryLawFor(registry: PartRegistry, partNumber: string | null, lawKind: DeviceLaw["kind"]): DeviceLaw | null;
|
|
316
|
+
/**
|
|
317
|
+
* How far a part's output stops short of each supply rail, from the registry entry that names
|
|
318
|
+
* it, or `null` when the part is not in the registry or states none (the rails are then used as
|
|
319
|
+
* they are). Read from either an `ideal-opamp` law entry or the first section of a `sections`
|
|
320
|
+
* entry, because a declared `opamp` device never reaches the section lowering.
|
|
321
|
+
*/
|
|
322
|
+
export declare function registryOpampSwingDrop(registry: PartRegistry, partNumber: string | null): number | null;
|
|
323
|
+
//# sourceMappingURL=registry.d.ts.map
|