@openmixer/plugin-qualify 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.
- package/LICENSE +674 -0
- package/README.md +203 -2
- package/bin/plugin-qualify.mjs +77 -0
- package/dist/clap-loader.d.ts +147 -0
- package/dist/clap-loader.js +252 -0
- package/dist/clap-loader.js.map +1 -0
- package/dist/clap-qualify.d.ts +126 -0
- package/dist/clap-qualify.js +272 -0
- package/dist/clap-qualify.js.map +1 -0
- package/dist/host-profiles.d.ts +42 -0
- package/dist/host-profiles.js +106 -0
- package/dist/host-profiles.js.map +1 -0
- package/dist/hosting-codes.d.ts +62 -0
- package/dist/hosting-codes.js +72 -0
- package/dist/hosting-codes.js.map +1 -0
- package/dist/hosting-suitability.d.ts +354 -0
- package/dist/hosting-suitability.js +654 -0
- package/dist/hosting-suitability.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +26 -0
- package/dist/loader.js +9 -0
- package/dist/loader.js.map +1 -0
- package/dist/lv2-loader.d.ts +7 -0
- package/dist/lv2-loader.js +50 -0
- package/dist/lv2-loader.js.map +1 -0
- package/dist/measurement/cli.d.ts +168 -0
- package/dist/measurement/cli.js +353 -0
- package/dist/measurement/cli.js.map +1 -0
- package/dist/measurement/fixtures.d.ts +23 -0
- package/dist/measurement/fixtures.js +129 -0
- package/dist/measurement/fixtures.js.map +1 -0
- package/dist/measurement/format.d.ts +483 -0
- package/dist/measurement/format.js +627 -0
- package/dist/measurement/format.js.map +1 -0
- package/dist/measurement/guards.d.ts +33 -0
- package/dist/measurement/guards.js +61 -0
- package/dist/measurement/guards.js.map +1 -0
- package/dist/measurement/index.d.ts +36 -0
- package/dist/measurement/index.js +39 -0
- package/dist/measurement/index.js.map +1 -0
- package/dist/measurement/merge.d.ts +143 -0
- package/dist/measurement/merge.js +315 -0
- package/dist/measurement/merge.js.map +1 -0
- package/dist/measurement/plan.d.ts +127 -0
- package/dist/measurement/plan.js +141 -0
- package/dist/measurement/plan.js.map +1 -0
- package/dist/measurement/probe-json.d.ts +61 -0
- package/dist/measurement/probe-json.js +253 -0
- package/dist/measurement/probe-json.js.map +1 -0
- package/dist/measurement/provenance.d.ts +91 -0
- package/dist/measurement/provenance.js +173 -0
- package/dist/measurement/provenance.js.map +1 -0
- package/dist/measurement/sweep-jsonl.d.ts +40 -0
- package/dist/measurement/sweep-jsonl.js +154 -0
- package/dist/measurement/sweep-jsonl.js.map +1 -0
- package/dist/measurement/vocabulary.d.ts +125 -0
- package/dist/measurement/vocabulary.js +217 -0
- package/dist/measurement/vocabulary.js.map +1 -0
- package/dist/qualify-cli.d.ts +83 -0
- package/dist/qualify-cli.js +224 -0
- package/dist/qualify-cli.js.map +1 -0
- package/dist/qualify-declarations.d.ts +98 -0
- package/dist/qualify-declarations.js +76 -0
- package/dist/qualify-declarations.js.map +1 -0
- package/dist/qualify-options.d.ts +22 -0
- package/dist/qualify-options.js +36 -0
- package/dist/qualify-options.js.map +1 -0
- package/dist/rating.d.ts +32 -0
- package/dist/rating.js +30 -0
- package/dist/rating.js.map +1 -0
- package/dist/tool-paths.d.ts +12 -0
- package/dist/tool-paths.js +29 -0
- package/dist/tool-paths.js.map +1 -0
- package/fixtures/clap-scan/controls.json +8 -0
- package/fixtures/clap-scan/fake.json +16 -0
- package/fixtures/clap-scan/mixed.json +18 -0
- package/fixtures/zero-path.lv2/manifest.ttl +3 -0
- package/fixtures/zero-path.lv2/zero-path.c +85 -0
- package/fixtures/zero-path.lv2/zero-path.ttl +21 -0
- package/package.json +91 -3
- package/src/clap-loader.ts +393 -0
- package/src/clap-qualify.ts +387 -0
- package/src/host-profiles.ts +122 -0
- package/src/hosting-codes.ts +71 -0
- package/src/hosting-suitability.ts +982 -0
- package/src/index.ts +19 -0
- package/src/loader.ts +30 -0
- package/src/lv2-loader.ts +50 -0
- package/src/measurement/cli.ts +458 -0
- package/src/measurement/fixtures.ts +141 -0
- package/src/measurement/format.ts +1064 -0
- package/src/measurement/guards.ts +64 -0
- package/src/measurement/index.ts +39 -0
- package/src/measurement/merge.ts +434 -0
- package/src/measurement/plan.ts +219 -0
- package/src/measurement/probe-json.ts +271 -0
- package/src/measurement/provenance.ts +220 -0
- package/src/measurement/sweep-jsonl.ts +173 -0
- package/src/measurement/vocabulary.ts +298 -0
- package/src/qualify-cli.ts +277 -0
- package/src/qualify-declarations.ts +117 -0
- package/src/qualify-options.ts +49 -0
- package/src/rating.ts +53 -0
- package/src/tool-paths.ts +32 -0
- package/tools/_declarations.py +21 -0
- package/tools/benchmark.py +2462 -0
- package/tools/gen-python-declarations.d.mts +4 -0
- package/tools/gen-python-declarations.mjs +92 -0
- package/tools/hosting-sweep.mjs +51 -0
- package/tools/lv2-measure.mjs +204 -0
- package/tools/lv2_presets.py +132 -0
- package/tools/scan.py +432 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **The LV2 plugin measurement interchange format** — one measurement run, on one
|
|
3
|
+
* machine, in one document.
|
|
4
|
+
*
|
|
5
|
+
* `lv2:latency` is a declared port and it is often wrong: the 958-plugin sweep behind
|
|
6
|
+
* `data/catalog.json` found 57 declared-vs-measured mismatches. Measured figures exist in
|
|
7
|
+
* several projects and are shared by none of them, because there is no shape to share them
|
|
8
|
+
* in. This is that shape. The schema is the deliverable; the prober is replaceable.
|
|
9
|
+
*
|
|
10
|
+
* ## The three rules the format exists to enforce
|
|
11
|
+
*
|
|
12
|
+
* 1. **A figure carries the conditions it was taken under.** {@link MeasurementRun} is not
|
|
13
|
+
* optional metadata and is not a footer — it is the document's other half. A cost
|
|
14
|
+
* figure without its host, governor, block and stimulus is a decoration; a latency
|
|
15
|
+
* figure without the plugin's version is a trap, because the next release moves the FFT
|
|
16
|
+
* window and the number silently becomes a lie.
|
|
17
|
+
* 2. **Unknown is a value.** A plugin that could not be measured carries a REASON from a
|
|
18
|
+
* controlled vocabulary ({@link ./vocabulary.ts}) and no number at all. Nothing in this
|
|
19
|
+
* format has a default: an absent field means "not determined", never zero and never
|
|
20
|
+
* false. Readers here return `undefined` rather than inventing.
|
|
21
|
+
* 3. **Per rate, always.** Latency does not scale one way and cost does not scale at all
|
|
22
|
+
* predictably — 19 plugins in the reference sweep fit neither scaling law, and 48 cost
|
|
23
|
+
* figures grow with the SQUARE of the rate. A single-rate document is a claim about one
|
|
24
|
+
* rate and the format will not let it pretend otherwise: every figure lives under an
|
|
25
|
+
* explicit rate key.
|
|
26
|
+
*
|
|
27
|
+
* ## Version strategy
|
|
28
|
+
*
|
|
29
|
+
* `formatVersion` is `"<major>.<minor>"`.
|
|
30
|
+
*
|
|
31
|
+
* - **major** changes when a reader written for the previous major would MISREAD a
|
|
32
|
+
* document: a field removed, renamed, re-scaled, or given a new meaning. A reader that
|
|
33
|
+
* meets a higher major must refuse the document ({@link readMeasurementDocument} does),
|
|
34
|
+
* because the alternative is silently consuming figures it has misunderstood.
|
|
35
|
+
* - **minor** changes are additive only: new optional fields, new vocabulary terms. A
|
|
36
|
+
* reader at minor N reads minor N+1 fine, ignoring what it does not know — and is TOLD
|
|
37
|
+
* it is doing so ({@link MeasurementReadResult.forwardMinor}), so a UI can say "this
|
|
38
|
+
* document was written by a newer tool" instead of quietly dropping data.
|
|
39
|
+
*
|
|
40
|
+
* Unknown fields are preserved nowhere and that is deliberate: a reader that round-trips
|
|
41
|
+
* fields it does not understand invites a consumer to depend on them. Re-emit from the
|
|
42
|
+
* original document, never from a parse.
|
|
43
|
+
*/
|
|
44
|
+
import { type HostIsolation, type HostLatencyHandling, type MeasurementDimension, type VerdictRating, MEASUREMENT_DIMENSIONS } from "./vocabulary.js";
|
|
45
|
+
/** The `format` discriminator every document carries. Never absent, never anything else. */
|
|
46
|
+
export declare const MEASUREMENT_FORMAT_ID = "lv2-plugin-measurements";
|
|
47
|
+
/** The major this build writes and is guaranteed to understand. */
|
|
48
|
+
export declare const MEASUREMENT_FORMAT_MAJOR = 1;
|
|
49
|
+
/** The minor this build writes. Additive-only within {@link MEASUREMENT_FORMAT_MAJOR}.
|
|
50
|
+
* `1.1` added the three hosting dimensions — {@link StabilityMeasurement},
|
|
51
|
+
* {@link RtSafetyMeasurement}, {@link FeaturesMeasurement} — which a 1.0 reader skips. */
|
|
52
|
+
export declare const MEASUREMENT_FORMAT_MINOR = 2;
|
|
53
|
+
/** The version string this build writes, e.g. `"1.0"`. */
|
|
54
|
+
export declare const MEASUREMENT_FORMAT_VERSION = "1.2";
|
|
55
|
+
/** A parsed `formatVersion`. */
|
|
56
|
+
export interface FormatVersion {
|
|
57
|
+
readonly major: number;
|
|
58
|
+
readonly minor: number;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Parse a `"<major>.<minor>"` version, or `undefined` when it is not one.
|
|
62
|
+
*
|
|
63
|
+
* Deliberately strict — two non-negative integers and nothing else. A version we cannot
|
|
64
|
+
* parse is a document we cannot claim to understand, and guessing at `"1"` or `"1.0.0"`
|
|
65
|
+
* would be exactly the sloppiness a version field exists to prevent.
|
|
66
|
+
*/
|
|
67
|
+
export declare function parseFormatVersion(value: unknown): FormatVersion | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* The machine a run happened on.
|
|
70
|
+
*
|
|
71
|
+
* Every field except {@link hostname} is optional and every one of them is absent when it
|
|
72
|
+
* could not be READ — the prober reports what `/proc` and `/sys` told it and nothing else.
|
|
73
|
+
* `realtimePriority` and `memoryLocked` are the exception and are required, because
|
|
74
|
+
* "we did not try" and "we tried and were not allowed" are both `false` and a consumer
|
|
75
|
+
* comparing two runs must not have to guess which.
|
|
76
|
+
*/
|
|
77
|
+
export interface MeasurementHost {
|
|
78
|
+
/** The machine's name. The single field an operator recognises. */
|
|
79
|
+
readonly hostname: string;
|
|
80
|
+
readonly kernel?: string;
|
|
81
|
+
readonly os?: string;
|
|
82
|
+
readonly cpuModel?: string;
|
|
83
|
+
readonly cpuCount?: number;
|
|
84
|
+
/** The core the prober pinned itself to, when it pinned at all. */
|
|
85
|
+
readonly pinnedCpu?: number;
|
|
86
|
+
/** What kind of core that was — `performance` / `efficiency` on a hybrid CPU. */
|
|
87
|
+
readonly pinnedCpuKind?: string;
|
|
88
|
+
readonly pinnedCpuMaxKHz?: number;
|
|
89
|
+
/** The cpufreq governor in force, read at run time. `performance` and `powersave`
|
|
90
|
+
* produce materially different cost figures on the same silicon. */
|
|
91
|
+
readonly governor?: string;
|
|
92
|
+
readonly turboEnabled?: boolean;
|
|
93
|
+
/** Whether the prober ran with realtime scheduling. Required: `false` is a finding. */
|
|
94
|
+
readonly realtimePriority: boolean;
|
|
95
|
+
/** Whether the prober's pages were locked. Required, same reason. */
|
|
96
|
+
readonly memoryLocked: boolean;
|
|
97
|
+
/** Versions of what did the probing (`{ python: "3.14.6", lilv: "0.24.26" }`). */
|
|
98
|
+
readonly toolchain?: Readonly<Record<string, string>>;
|
|
99
|
+
}
|
|
100
|
+
/** How the latency figures in this document were taken. */
|
|
101
|
+
export interface LatencyMethod {
|
|
102
|
+
/** A {@link ../vocabulary.METHOD_TERMS} code — `impulse-onset` or `declared-port`. */
|
|
103
|
+
readonly method: string;
|
|
104
|
+
readonly blockFrames: number;
|
|
105
|
+
/** What was fed in, in words. An onset measured through an opened gate is not the same
|
|
106
|
+
* measurement as one through a bare impulse, and the difference must be legible. */
|
|
107
|
+
readonly stimulus: string;
|
|
108
|
+
/** Whether latency-bearing controls were swept to find parameter-dependent latency. */
|
|
109
|
+
readonly paramSweep: boolean;
|
|
110
|
+
}
|
|
111
|
+
/** How the cost figures in this document were taken. */
|
|
112
|
+
export interface CostMethod {
|
|
113
|
+
/** A {@link ../vocabulary.METHOD_TERMS} code — `block-time-percentile`. */
|
|
114
|
+
readonly method: string;
|
|
115
|
+
readonly blockFrames: number;
|
|
116
|
+
/** Blocks timed and then DISCARDED before the steady-state window opened. */
|
|
117
|
+
readonly warmupBlocks: number;
|
|
118
|
+
readonly minTimedBlocks?: number;
|
|
119
|
+
readonly maxTimedBlocks?: number;
|
|
120
|
+
/** Wall-time budget per plugin per rate, after which timing stops early. */
|
|
121
|
+
readonly targetSecondsPerPlugin?: number;
|
|
122
|
+
/** The percentile the headline figure is quoted at. The mean hides the tail that xruns. */
|
|
123
|
+
readonly percentile: number;
|
|
124
|
+
readonly stimulus: string;
|
|
125
|
+
/** How control ports were set — the operating point the figure describes. */
|
|
126
|
+
readonly controls: string;
|
|
127
|
+
/** Lower bound on the harness's own per-block overhead. Plugins near it are too cheap
|
|
128
|
+
* to rank apart, and it is REPORTED rather than subtracted. */
|
|
129
|
+
readonly timingFloorNsPerBlock?: number;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* One measurement run: who measured, on what, how, when, and over which rates.
|
|
133
|
+
*
|
|
134
|
+
* A document is exactly one run, on purpose. Merging figures taken on different machines
|
|
135
|
+
* is a consumer's decision with a consumer's caveats ({@link ./merge.ts}); baking it into
|
|
136
|
+
* the document would make a merged file indistinguishable from a measured one.
|
|
137
|
+
*/
|
|
138
|
+
export interface MeasurementRun {
|
|
139
|
+
/** Stable identifier for this run — referenced by a merged view to name a figure's source. */
|
|
140
|
+
readonly id: string;
|
|
141
|
+
/** ISO 8601, with offset. */
|
|
142
|
+
readonly measuredAt: string;
|
|
143
|
+
/** Which dimensions this run covered. A plugin carrying no figure for a dimension NOT
|
|
144
|
+
* listed here was never asked; one carrying none for a dimension that IS listed has a
|
|
145
|
+
* reason. That distinction is why the field exists. */
|
|
146
|
+
readonly dimensions: readonly MeasurementDimension[];
|
|
147
|
+
/** Every sample rate the run measured at, ascending. */
|
|
148
|
+
readonly rates: readonly number[];
|
|
149
|
+
readonly tool: {
|
|
150
|
+
readonly name: string;
|
|
151
|
+
readonly version: string;
|
|
152
|
+
};
|
|
153
|
+
readonly host: MeasurementHost;
|
|
154
|
+
readonly method: {
|
|
155
|
+
readonly latency?: LatencyMethod;
|
|
156
|
+
readonly cost?: CostMethod;
|
|
157
|
+
};
|
|
158
|
+
/** Wall-clock duration of the run. The only honest basis for estimating the next one. */
|
|
159
|
+
readonly elapsedSeconds?: number;
|
|
160
|
+
/** Free-text label an operator gave the run ("after installing LSP 1.2.22"). */
|
|
161
|
+
readonly label?: string;
|
|
162
|
+
/**
|
|
163
|
+
* Always `true` on a run carrying cost figures: the numbers RANK plugins on the machine
|
|
164
|
+
* named above and do not predict another. Kept as an explicit field rather than a
|
|
165
|
+
* convention so a consumer cannot claim it never saw the caveat.
|
|
166
|
+
*/
|
|
167
|
+
readonly relativeRankingOnly: boolean;
|
|
168
|
+
/** The caveat in prose, for anyone reading the file directly. */
|
|
169
|
+
readonly note: string;
|
|
170
|
+
}
|
|
171
|
+
/** What the prober saw of the plugin's ports. Cheap, portable, and it decides suitability. */
|
|
172
|
+
export interface PluginTopology {
|
|
173
|
+
readonly audioInputs: number;
|
|
174
|
+
readonly audioOutputs: number;
|
|
175
|
+
readonly midiInputs?: number;
|
|
176
|
+
readonly controlInputs?: number;
|
|
177
|
+
}
|
|
178
|
+
/** One rate's latency reading. */
|
|
179
|
+
export interface LatencyAtRate {
|
|
180
|
+
readonly frames: number;
|
|
181
|
+
readonly ms: number;
|
|
182
|
+
/** Set when this reading needed an ESCALATED stimulus (a gate that had to be opened).
|
|
183
|
+
* The figure then describes that operating point, not the plugin at rest. */
|
|
184
|
+
readonly stimulus?: string;
|
|
185
|
+
/**
|
|
186
|
+
* Set when the reading was taken but REJECTED as evidence, with a vocabulary code
|
|
187
|
+
* ({@link ../vocabulary.EXCLUSION_TERMS}). The number stays — this is the measurement
|
|
188
|
+
* record — but a consumer must not resolve a rate to it.
|
|
189
|
+
*/
|
|
190
|
+
readonly excludedReason?: string;
|
|
191
|
+
}
|
|
192
|
+
/** The controls that move a plugin's latency, and the span they move it over. */
|
|
193
|
+
export interface MeasuredParamSweep {
|
|
194
|
+
readonly rate: number;
|
|
195
|
+
readonly defaultFrames: number;
|
|
196
|
+
readonly minFrames: number;
|
|
197
|
+
readonly maxFrames: number;
|
|
198
|
+
/** Per-control span, keyed by control-port symbol. */
|
|
199
|
+
readonly controls: Readonly<Record<string, {
|
|
200
|
+
readonly minFrames: number;
|
|
201
|
+
readonly maxFrames: number;
|
|
202
|
+
}>>;
|
|
203
|
+
}
|
|
204
|
+
/** Everything one run learned about one plugin's latency. */
|
|
205
|
+
export interface LatencyMeasurement {
|
|
206
|
+
/** Readings keyed by sample rate in Hz as a decimal string (`"96000"`). May be empty. */
|
|
207
|
+
readonly perRate: Readonly<Record<string, LatencyAtRate>>;
|
|
208
|
+
/** A {@link ../vocabulary.SCALING_TERMS} code, derived from {@link perRate}. */
|
|
209
|
+
readonly scalingClass?: string;
|
|
210
|
+
/** A {@link ../vocabulary.SCALING_NOTE_TERMS} code: why the class is not the naive fit. */
|
|
211
|
+
readonly scalingNote?: string;
|
|
212
|
+
/** Symbol of the plugin's own `lv2:latency` port, when it has one. */
|
|
213
|
+
readonly declaredPortSymbol?: string;
|
|
214
|
+
/** What that port CLAIMED, in frames. Kept next to the measurement, never instead of it. */
|
|
215
|
+
readonly declaredFrames?: number;
|
|
216
|
+
/** Set when declared and measured disagree beyond rounding — a data-quality flag on the
|
|
217
|
+
* PLUGIN, and the single most useful thing this format publishes. */
|
|
218
|
+
readonly declaredMismatch?: boolean;
|
|
219
|
+
readonly paramSweep?: MeasuredParamSweep;
|
|
220
|
+
/** An {@link ../vocabulary.UNMEASURED_TERMS} code. Set exactly when {@link perRate} is
|
|
221
|
+
* empty; a figure and a reason are mutually exclusive. */
|
|
222
|
+
readonly unmeasuredReason?: string;
|
|
223
|
+
/** The prober's own words for that reason, kept verbatim. The CODE is what a merger
|
|
224
|
+
* branches on; this is what a human reads when the code is too coarse to debug with. */
|
|
225
|
+
readonly unmeasuredDetail?: string;
|
|
226
|
+
}
|
|
227
|
+
/** One rate's cost reading. Per SAMPLE, so it is independent of the block size. */
|
|
228
|
+
export interface CostAtRate {
|
|
229
|
+
readonly nsPerSampleMedian: number;
|
|
230
|
+
/** The percentile named in {@link CostMethod.percentile} — what a bad block costs. */
|
|
231
|
+
readonly nsPerSamplePercentile: number;
|
|
232
|
+
readonly nsPerSampleMin?: number;
|
|
233
|
+
readonly nsPerSampleMax?: number;
|
|
234
|
+
/** Timed blocks the statistics were computed over, warm-up already dropped. */
|
|
235
|
+
readonly blocks: number;
|
|
236
|
+
readonly blockFrames: number;
|
|
237
|
+
/** Median over the DISCARDED warm-up blocks: the evidence that discarding was warranted
|
|
238
|
+
* rather than assumed. */
|
|
239
|
+
readonly warmupNsPerSampleMedian?: number;
|
|
240
|
+
/** Timed, but emitted silence under a stimulus that should have passed — the figure may
|
|
241
|
+
* describe a short-circuit path rather than the DSP. */
|
|
242
|
+
readonly silentOutput?: boolean;
|
|
243
|
+
}
|
|
244
|
+
/** Everything one run learned about one plugin's CPU cost. */
|
|
245
|
+
export interface CostMeasurement {
|
|
246
|
+
/** Readings keyed by sample rate in Hz as a decimal string. May be empty. */
|
|
247
|
+
readonly perRate: Readonly<Record<string, CostAtRate>>;
|
|
248
|
+
/** An {@link ../vocabulary.UNMEASURED_TERMS} code, set exactly when {@link perRate} is empty. */
|
|
249
|
+
readonly unmeasuredReason?: string;
|
|
250
|
+
/** The prober's own words for that reason, kept verbatim. See {@link LatencyMeasurement.unmeasuredDetail}. */
|
|
251
|
+
readonly unmeasuredDetail?: string;
|
|
252
|
+
}
|
|
253
|
+
/** Instantiate/destroy cycles, as run. */
|
|
254
|
+
export interface LifecycleReading {
|
|
255
|
+
readonly cycles: number;
|
|
256
|
+
readonly instantiated: number;
|
|
257
|
+
readonly failed: number;
|
|
258
|
+
}
|
|
259
|
+
/** The process's thread count around the plugin's first instance. */
|
|
260
|
+
export interface ThreadReading {
|
|
261
|
+
readonly before: number;
|
|
262
|
+
readonly afterFirstInstantiate: number;
|
|
263
|
+
readonly afterAllFreed?: number;
|
|
264
|
+
readonly leaked?: number;
|
|
265
|
+
}
|
|
266
|
+
/** A crash as observed. Attribution — whose fault — is the consumer's, from its own table. */
|
|
267
|
+
export interface CrashReading {
|
|
268
|
+
readonly signal?: string;
|
|
269
|
+
readonly topFrame?: string;
|
|
270
|
+
/** The error class a sanitizer build reported, verbatim (`"heap-buffer-overflow"`). */
|
|
271
|
+
readonly sanitizer?: string;
|
|
272
|
+
/** Basename of the shared object the faulting frame lies in (`"zero-path.so"`). */
|
|
273
|
+
readonly frameObject?: string;
|
|
274
|
+
}
|
|
275
|
+
/** One rate of a continuous soak: the render as it went, window by window. */
|
|
276
|
+
export interface SoakAtRate {
|
|
277
|
+
/** The process died at this rate. Every other field is then absent. */
|
|
278
|
+
readonly died?: boolean;
|
|
279
|
+
readonly instantiated?: boolean;
|
|
280
|
+
readonly seconds?: number;
|
|
281
|
+
readonly blockFrames?: number;
|
|
282
|
+
readonly windows?: number;
|
|
283
|
+
readonly nonFiniteWindows?: number;
|
|
284
|
+
readonly silentWindows?: number;
|
|
285
|
+
readonly firstRmsDbfs?: number;
|
|
286
|
+
readonly lastRmsDbfs?: number;
|
|
287
|
+
readonly minRmsDbfs?: number;
|
|
288
|
+
readonly rssStartKb?: number;
|
|
289
|
+
readonly rssMaxKb?: number;
|
|
290
|
+
readonly rssGrowthKb?: number;
|
|
291
|
+
}
|
|
292
|
+
/** A continuous soak, per rate, with whether the controls were swept while it ran. */
|
|
293
|
+
export interface SoakReading {
|
|
294
|
+
readonly sweptParams: boolean;
|
|
295
|
+
readonly perRate: Readonly<Record<string, SoakAtRate>>;
|
|
296
|
+
}
|
|
297
|
+
/** Everything one run learned about one plugin SURVIVING. Readable when it has a lifecycle. */
|
|
298
|
+
export interface StabilityMeasurement {
|
|
299
|
+
readonly lifecycle?: LifecycleReading;
|
|
300
|
+
readonly threads?: ThreadReading;
|
|
301
|
+
readonly crashes?: readonly CrashReading[];
|
|
302
|
+
readonly soak?: SoakReading;
|
|
303
|
+
readonly unmeasuredReason?: string;
|
|
304
|
+
readonly unmeasuredDetail?: string;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* What an interposer counted INSIDE `run()`. `rate` and `blockFrames` are the pair it ran at —
|
|
308
|
+
* a count is a property of the code paths the run took there. Readable when all three counts
|
|
309
|
+
* are present.
|
|
310
|
+
*/
|
|
311
|
+
export interface RtSafetyMeasurement {
|
|
312
|
+
readonly rate?: number;
|
|
313
|
+
readonly blockFrames?: number;
|
|
314
|
+
readonly blocks?: number;
|
|
315
|
+
readonly repeats?: number;
|
|
316
|
+
/** The interposer was actually loaded. A zero from an absent interposer counts nothing. */
|
|
317
|
+
readonly interposer?: boolean;
|
|
318
|
+
/** The controls were swept across their ranges while counting. */
|
|
319
|
+
readonly swept?: boolean;
|
|
320
|
+
/** False when the plugin has no control a sweep could move. */
|
|
321
|
+
readonly sweepApplicable?: boolean;
|
|
322
|
+
readonly allocationsInRun?: number;
|
|
323
|
+
readonly allocationsMin?: number;
|
|
324
|
+
readonly locksInRun?: number;
|
|
325
|
+
readonly syscallsInRun?: number;
|
|
326
|
+
/** Repeats disagreed. */
|
|
327
|
+
readonly variable?: boolean;
|
|
328
|
+
readonly unmeasuredReason?: string;
|
|
329
|
+
readonly unmeasuredDetail?: string;
|
|
330
|
+
}
|
|
331
|
+
/** What the plugin DEMANDS of a host, read off the instance the run loaded. */
|
|
332
|
+
export interface FeaturesMeasurement {
|
|
333
|
+
readonly required?: readonly string[];
|
|
334
|
+
readonly optional?: readonly string[];
|
|
335
|
+
readonly cvPorts?: number;
|
|
336
|
+
readonly unmeasuredReason?: string;
|
|
337
|
+
readonly unmeasuredDetail?: string;
|
|
338
|
+
}
|
|
339
|
+
/** One plugin as one run saw it. */
|
|
340
|
+
export interface MeasuredPlugin {
|
|
341
|
+
readonly uri: string;
|
|
342
|
+
readonly name?: string;
|
|
343
|
+
/**
|
|
344
|
+
* The plugin's own version (`lv2:minorVersion.lv2:microVersion`), when it declares one.
|
|
345
|
+
* A latency figure attached to no version is a trap: the next release moves
|
|
346
|
+
* the window and the figure becomes wrong without becoming absent.
|
|
347
|
+
*/
|
|
348
|
+
readonly version?: string;
|
|
349
|
+
/** The LV2 bundle it came from — what an operator greps for when a figure surprises them. */
|
|
350
|
+
readonly bundle?: string;
|
|
351
|
+
/** The plugin's declared LV2 class label, verbatim. */
|
|
352
|
+
readonly lv2Class?: string;
|
|
353
|
+
readonly topology: PluginTopology;
|
|
354
|
+
readonly latency?: LatencyMeasurement;
|
|
355
|
+
readonly cost?: CostMeasurement;
|
|
356
|
+
readonly stability?: StabilityMeasurement;
|
|
357
|
+
readonly rtSafety?: RtSafetyMeasurement;
|
|
358
|
+
readonly features?: FeaturesMeasurement;
|
|
359
|
+
}
|
|
360
|
+
/** What a host spends on one plugin instance: the P95 core-fraction ceiling, at its rate and quantum. */
|
|
361
|
+
export interface HostBudget {
|
|
362
|
+
readonly coreFractionCeiling: number;
|
|
363
|
+
readonly rate: number;
|
|
364
|
+
readonly quantum: number;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* A HOST PROFILE (`2026-09-25-plugin-qualify.md` §3a; §3b here): the host a verdict was judged
|
|
368
|
+
* against — only what differs between hosts. A document carries every profile its verdicts name,
|
|
369
|
+
* in full, so a verdict against a project's own profile is reproducible from the document alone.
|
|
370
|
+
*/
|
|
371
|
+
export interface HostProfile {
|
|
372
|
+
readonly id: string;
|
|
373
|
+
/** The LV2 feature URIs the host passes to `instantiate`. */
|
|
374
|
+
readonly lv2Features: readonly string[];
|
|
375
|
+
/** The CLAP host extensions it offers; `null` for a host with no CLAP path at all. */
|
|
376
|
+
readonly clapExtensions: readonly string[] | null;
|
|
377
|
+
/** Whether it feeds note/MIDI input — hosts instruments. */
|
|
378
|
+
readonly instruments: boolean;
|
|
379
|
+
readonly cost: HostBudget;
|
|
380
|
+
readonly latency: HostLatencyHandling;
|
|
381
|
+
readonly isolation: HostIsolation;
|
|
382
|
+
}
|
|
383
|
+
/** One plugin's verdict under one host profile (§3b). `host` is a `hostProfiles[].id`. */
|
|
384
|
+
export interface DocumentVerdict {
|
|
385
|
+
readonly uri: string;
|
|
386
|
+
readonly host: string;
|
|
387
|
+
readonly rating: VerdictRating;
|
|
388
|
+
/** `rating === "suitable"`, and nothing else. */
|
|
389
|
+
readonly qualified: boolean;
|
|
390
|
+
/** The code of the reason that decided it; absent for `suitable`. */
|
|
391
|
+
readonly deciding?: string;
|
|
392
|
+
}
|
|
393
|
+
/** A complete document: one run, and what it found. */
|
|
394
|
+
export interface MeasurementDocument {
|
|
395
|
+
readonly format: typeof MEASUREMENT_FORMAT_ID;
|
|
396
|
+
readonly formatVersion: string;
|
|
397
|
+
readonly run: MeasurementRun;
|
|
398
|
+
readonly plugins: readonly MeasuredPlugin[];
|
|
399
|
+
/** v1.2: the hosts the run's verdicts were judged against (§3b). */
|
|
400
|
+
readonly hostProfiles?: readonly HostProfile[];
|
|
401
|
+
/** v1.2: one verdict per plugin per host profile — conclusions, beside the raw blocks. */
|
|
402
|
+
readonly verdicts?: readonly DocumentVerdict[];
|
|
403
|
+
}
|
|
404
|
+
export type HostProfileRead = {
|
|
405
|
+
ok: true;
|
|
406
|
+
profile: HostProfile;
|
|
407
|
+
} | {
|
|
408
|
+
ok: false;
|
|
409
|
+
problem: string;
|
|
410
|
+
};
|
|
411
|
+
/**
|
|
412
|
+
* Read a host profile — a document's, or a project's own file. Every field is required and
|
|
413
|
+
* typed; a missing or mistyped one is refused by name, and a host the reader was not told about
|
|
414
|
+
* is never filled in from a default.
|
|
415
|
+
*/
|
|
416
|
+
export declare function readHostProfile(value: unknown): HostProfileRead;
|
|
417
|
+
/** A refusal to read a document, with the reason in a form a UI can render. */
|
|
418
|
+
export interface MeasurementReadFailure {
|
|
419
|
+
readonly ok: false;
|
|
420
|
+
/** Stable code: `not-an-object` / `wrong-format` / `bad-version` / `future-major` /
|
|
421
|
+
* `missing-run` / `missing-plugins`. */
|
|
422
|
+
readonly problem: string;
|
|
423
|
+
/** English detail for logs. UIs render from {@link problem} plus {@link found}. */
|
|
424
|
+
readonly detail: string;
|
|
425
|
+
/** What was actually there, when the problem is about a value. */
|
|
426
|
+
readonly found?: string;
|
|
427
|
+
}
|
|
428
|
+
/** A document read successfully, plus what the reader had to tolerate to do it. */
|
|
429
|
+
export interface MeasurementReadSuccess {
|
|
430
|
+
readonly ok: true;
|
|
431
|
+
readonly document: MeasurementDocument;
|
|
432
|
+
readonly version: FormatVersion;
|
|
433
|
+
/**
|
|
434
|
+
* Set when the document's minor is AHEAD of this build's: it was written by a newer tool
|
|
435
|
+
* and may carry fields this reader dropped. Everything read is still valid — minors are
|
|
436
|
+
* additive — but a surface should say so rather than imply completeness.
|
|
437
|
+
*/
|
|
438
|
+
readonly forwardMinor?: true;
|
|
439
|
+
/**
|
|
440
|
+
* Plugin entries dropped for being malformed. Never silent: a document that half-parses
|
|
441
|
+
* must say how much of it was lost, or a consumer will read a partial catalogue as a
|
|
442
|
+
* complete one.
|
|
443
|
+
*/
|
|
444
|
+
readonly droppedPlugins: number;
|
|
445
|
+
/**
|
|
446
|
+
* v1.2: verdicts (and host profiles) dropped for being malformed or naming a host the document
|
|
447
|
+
* does not declare — present whenever the document carried either list.
|
|
448
|
+
*/
|
|
449
|
+
readonly droppedVerdicts?: number;
|
|
450
|
+
}
|
|
451
|
+
/** The outcome of {@link readMeasurementDocument}. */
|
|
452
|
+
export type MeasurementReadResult = MeasurementReadSuccess | MeasurementReadFailure;
|
|
453
|
+
/** A lifecycle reading, or `undefined` unless all three counts are there. */
|
|
454
|
+
export declare function readLifecycle(value: unknown): LifecycleReading | undefined;
|
|
455
|
+
/** A thread reading, or `undefined` without its before/after pair. */
|
|
456
|
+
export declare function readThreads(value: unknown): ThreadReading | undefined;
|
|
457
|
+
/** One soak rate. Every field optional: a rate that died carries `died` and nothing else. */
|
|
458
|
+
export declare function readSoakAtRate(value: unknown): SoakAtRate | undefined;
|
|
459
|
+
/**
|
|
460
|
+
* Read an untrusted value — a parsed JSON file, a wire frame — as a measurement document.
|
|
461
|
+
*
|
|
462
|
+
* Never throws and never casts: every field is validated on the way in and the result is
|
|
463
|
+
* built from the pieces that passed. A document whose major exceeds this build's is
|
|
464
|
+
* REFUSED rather than partially read, because a major bump means a field this reader
|
|
465
|
+
* recognises no longer means what it thinks.
|
|
466
|
+
*/
|
|
467
|
+
export declare function readMeasurementDocument(value: unknown): MeasurementReadResult;
|
|
468
|
+
/**
|
|
469
|
+
* Build a document at THIS build's version.
|
|
470
|
+
*
|
|
471
|
+
* The only supported way to create one: it stamps `format` and `formatVersion` so no call
|
|
472
|
+
* site can write a document claiming a version its content does not match, and it sorts
|
|
473
|
+
* plugins by URI so two runs over the same host produce a diffable file.
|
|
474
|
+
*/
|
|
475
|
+
export declare function buildMeasurementDocument(run: MeasurementRun, plugins: readonly MeasuredPlugin[], judged?: {
|
|
476
|
+
readonly hostProfiles: readonly HostProfile[];
|
|
477
|
+
readonly verdicts: readonly DocumentVerdict[];
|
|
478
|
+
}): MeasurementDocument;
|
|
479
|
+
/** Serialise a document: 2-space JSON with a trailing newline, stable across runs. */
|
|
480
|
+
export declare function serialiseMeasurementDocument(document: MeasurementDocument): string;
|
|
481
|
+
/** Every dimension a run may declare, in presentation order. Re-exported for consumers
|
|
482
|
+
* that need the list without reaching into the vocabulary module. */
|
|
483
|
+
export { MEASUREMENT_DIMENSIONS, type MeasurementDimension };
|