@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.
Files changed (114) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +203 -2
  3. package/bin/plugin-qualify.mjs +77 -0
  4. package/dist/clap-loader.d.ts +147 -0
  5. package/dist/clap-loader.js +252 -0
  6. package/dist/clap-loader.js.map +1 -0
  7. package/dist/clap-qualify.d.ts +126 -0
  8. package/dist/clap-qualify.js +272 -0
  9. package/dist/clap-qualify.js.map +1 -0
  10. package/dist/host-profiles.d.ts +42 -0
  11. package/dist/host-profiles.js +106 -0
  12. package/dist/host-profiles.js.map +1 -0
  13. package/dist/hosting-codes.d.ts +62 -0
  14. package/dist/hosting-codes.js +72 -0
  15. package/dist/hosting-codes.js.map +1 -0
  16. package/dist/hosting-suitability.d.ts +354 -0
  17. package/dist/hosting-suitability.js +654 -0
  18. package/dist/hosting-suitability.js.map +1 -0
  19. package/dist/index.d.ts +17 -0
  20. package/dist/index.js +20 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/loader.d.ts +26 -0
  23. package/dist/loader.js +9 -0
  24. package/dist/loader.js.map +1 -0
  25. package/dist/lv2-loader.d.ts +7 -0
  26. package/dist/lv2-loader.js +50 -0
  27. package/dist/lv2-loader.js.map +1 -0
  28. package/dist/measurement/cli.d.ts +168 -0
  29. package/dist/measurement/cli.js +353 -0
  30. package/dist/measurement/cli.js.map +1 -0
  31. package/dist/measurement/fixtures.d.ts +23 -0
  32. package/dist/measurement/fixtures.js +129 -0
  33. package/dist/measurement/fixtures.js.map +1 -0
  34. package/dist/measurement/format.d.ts +483 -0
  35. package/dist/measurement/format.js +627 -0
  36. package/dist/measurement/format.js.map +1 -0
  37. package/dist/measurement/guards.d.ts +33 -0
  38. package/dist/measurement/guards.js +61 -0
  39. package/dist/measurement/guards.js.map +1 -0
  40. package/dist/measurement/index.d.ts +36 -0
  41. package/dist/measurement/index.js +39 -0
  42. package/dist/measurement/index.js.map +1 -0
  43. package/dist/measurement/merge.d.ts +143 -0
  44. package/dist/measurement/merge.js +315 -0
  45. package/dist/measurement/merge.js.map +1 -0
  46. package/dist/measurement/plan.d.ts +127 -0
  47. package/dist/measurement/plan.js +141 -0
  48. package/dist/measurement/plan.js.map +1 -0
  49. package/dist/measurement/probe-json.d.ts +61 -0
  50. package/dist/measurement/probe-json.js +253 -0
  51. package/dist/measurement/probe-json.js.map +1 -0
  52. package/dist/measurement/provenance.d.ts +91 -0
  53. package/dist/measurement/provenance.js +173 -0
  54. package/dist/measurement/provenance.js.map +1 -0
  55. package/dist/measurement/sweep-jsonl.d.ts +40 -0
  56. package/dist/measurement/sweep-jsonl.js +154 -0
  57. package/dist/measurement/sweep-jsonl.js.map +1 -0
  58. package/dist/measurement/vocabulary.d.ts +125 -0
  59. package/dist/measurement/vocabulary.js +217 -0
  60. package/dist/measurement/vocabulary.js.map +1 -0
  61. package/dist/qualify-cli.d.ts +83 -0
  62. package/dist/qualify-cli.js +224 -0
  63. package/dist/qualify-cli.js.map +1 -0
  64. package/dist/qualify-declarations.d.ts +98 -0
  65. package/dist/qualify-declarations.js +76 -0
  66. package/dist/qualify-declarations.js.map +1 -0
  67. package/dist/qualify-options.d.ts +22 -0
  68. package/dist/qualify-options.js +36 -0
  69. package/dist/qualify-options.js.map +1 -0
  70. package/dist/rating.d.ts +32 -0
  71. package/dist/rating.js +30 -0
  72. package/dist/rating.js.map +1 -0
  73. package/dist/tool-paths.d.ts +12 -0
  74. package/dist/tool-paths.js +29 -0
  75. package/dist/tool-paths.js.map +1 -0
  76. package/fixtures/clap-scan/controls.json +8 -0
  77. package/fixtures/clap-scan/fake.json +16 -0
  78. package/fixtures/clap-scan/mixed.json +18 -0
  79. package/fixtures/zero-path.lv2/manifest.ttl +3 -0
  80. package/fixtures/zero-path.lv2/zero-path.c +85 -0
  81. package/fixtures/zero-path.lv2/zero-path.ttl +21 -0
  82. package/package.json +91 -3
  83. package/src/clap-loader.ts +393 -0
  84. package/src/clap-qualify.ts +387 -0
  85. package/src/host-profiles.ts +122 -0
  86. package/src/hosting-codes.ts +71 -0
  87. package/src/hosting-suitability.ts +982 -0
  88. package/src/index.ts +19 -0
  89. package/src/loader.ts +30 -0
  90. package/src/lv2-loader.ts +50 -0
  91. package/src/measurement/cli.ts +458 -0
  92. package/src/measurement/fixtures.ts +141 -0
  93. package/src/measurement/format.ts +1064 -0
  94. package/src/measurement/guards.ts +64 -0
  95. package/src/measurement/index.ts +39 -0
  96. package/src/measurement/merge.ts +434 -0
  97. package/src/measurement/plan.ts +219 -0
  98. package/src/measurement/probe-json.ts +271 -0
  99. package/src/measurement/provenance.ts +220 -0
  100. package/src/measurement/sweep-jsonl.ts +173 -0
  101. package/src/measurement/vocabulary.ts +298 -0
  102. package/src/qualify-cli.ts +277 -0
  103. package/src/qualify-declarations.ts +117 -0
  104. package/src/qualify-options.ts +49 -0
  105. package/src/rating.ts +53 -0
  106. package/src/tool-paths.ts +32 -0
  107. package/tools/_declarations.py +21 -0
  108. package/tools/benchmark.py +2462 -0
  109. package/tools/gen-python-declarations.d.mts +4 -0
  110. package/tools/gen-python-declarations.mjs +92 -0
  111. package/tools/hosting-sweep.mjs +51 -0
  112. package/tools/lv2-measure.mjs +204 -0
  113. package/tools/lv2_presets.py +132 -0
  114. package/tools/scan.py +432 -0
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The hosting verdict's message codes (2026-09-04-lv2-hosting-path.md). Codes, never prose: the
3
+ * surface renders the operator's locale.
4
+ *
5
+ * A UNION list file (2026-09-15-no-stored-derivations.md §9): `.gitattributes` marks it
6
+ * `merge=union`, so two lanes that each add a code merge with both kept. Only the table lives
7
+ * here, one `key: 'code',` per line, with keys and codes unique; the registry-hotspots gate
8
+ * refuses anything else.
9
+ */
10
+ export declare const HOSTING_CODE: {
11
+ readonly stabilitySoaked: "hosting.stability.soaked";
12
+ readonly stabilityCrashedAttributed: "hosting.stability.crashed-attributed-plugin";
13
+ readonly stabilityCrashedLive: "hosting.stability.crashed-live-on-this-rig";
14
+ readonly stabilityBelowFloor: "hosting.stability.cycles-below-floor";
15
+ readonly stabilityUnsoaked: "hosting.stability.unsoaked";
16
+ readonly stabilityUnmeasured: "hosting.stability.unmeasured";
17
+ readonly stabilityCrashUnattributed: "hosting.stability.crash-unattributed";
18
+ readonly stabilitySpawnsThreads: "hosting.stability.spawns-threads";
19
+ readonly stabilityLinksGuiToolkit: "hosting.stability.links-gui-toolkit";
20
+ readonly rtSafetyClean: "hosting.rt-safety.clean-under-sweep";
21
+ readonly rtSafetyViolations: "hosting.rt-safety.violations-in-run";
22
+ readonly rtSafetyUnmeasured: "hosting.rt-safety.unmeasured";
23
+ readonly rtSafetyNotReproducible: "hosting.rt-safety.not-reproducible";
24
+ readonly featuresProvided: "hosting.features.all-provided";
25
+ readonly featuresMissing: "hosting.features.required-not-provided";
26
+ readonly featuresDisqualifying: "hosting.features.host-not-provided";
27
+ readonly featuresCv: "hosting.features.cv-ports";
28
+ readonly featuresMidiIn: "hosting.features.midi-in-fed-empty";
29
+ readonly featuresUnscanned: "hosting.features.scan-predates-field";
30
+ readonly costWithinCeiling: "hosting.cost.within-ceiling";
31
+ readonly costAboveCeiling: "hosting.cost.above-ceiling";
32
+ readonly costSpiky: "hosting.cost.spiky-tail";
33
+ readonly costUnmeasured: "hosting.cost.unmeasured-at-rate-and-quantum";
34
+ readonly latencyResolved: "hosting.latency.resolved";
35
+ readonly latencyUnmeasured: "hosting.latency.unmeasured";
36
+ readonly latencyNonconforming: "hosting.latency.nonconforming";
37
+ readonly latencyParameterDependent: "hosting.latency.parameter-dependent";
38
+ readonly topologyMatched: "hosting.topology.matched";
39
+ readonly topologyNoAudioInput: "hosting.topology.no-audio-input";
40
+ readonly topologyNoAudioOutput: "hosting.topology.no-audio-output";
41
+ readonly topologyExtraInputs: "hosting.topology.extra-inputs-fed-silence";
42
+ readonly topologyInstrument: "hosting.topology.instrument";
43
+ readonly topologyWiderThanStrip: "hosting.topology.wider-than-strip";
44
+ readonly clapLatencyMismatch: "hosting.clap.latency-mismatch";
45
+ readonly clapNotAudioEffect: "hosting.clap.not-audio-effect";
46
+ readonly clapHeadlessFailed: "hosting.clap.headless-failed";
47
+ readonly clapNoteInput: "hosting.clap.note-input";
48
+ readonly clapThreadViolation: "hosting.clap.thread-violation";
49
+ readonly clapProcessError: "hosting.clap.process-error";
50
+ readonly clapUnqualified: "hosting.clap.unqualified";
51
+ readonly clapFormatNotHosted: "hosting.clap.format-not-hosted";
52
+ readonly clapExtensionsNotCovered: "hosting.clap.extensions-not-covered";
53
+ readonly outputNonFinite: "hosting.output.non-finite";
54
+ readonly overrideIsolated: "hosting.override.curator-isolated";
55
+ readonly hostProvenanceDiffers: "hosting.provenance.host-differs";
56
+ readonly sweepControlDidNotFire: "hosting.provenance.sweep-control-did-not-fire";
57
+ readonly disabled: "hosting.disabled";
58
+ readonly unjudged: "hosting.unjudged";
59
+ readonly noRealisation: "hosting.no-realisation";
60
+ readonly realisedInProcess: "hosting.realised.in-process";
61
+ readonly linked: "hosting.linked";
62
+ };
@@ -0,0 +1,72 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ // Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
3
+ /**
4
+ * The hosting verdict's message codes (2026-09-04-lv2-hosting-path.md). Codes, never prose: the
5
+ * surface renders the operator's locale.
6
+ *
7
+ * A UNION list file (2026-09-15-no-stored-derivations.md §9): `.gitattributes` marks it
8
+ * `merge=union`, so two lanes that each add a code merge with both kept. Only the table lives
9
+ * here, one `key: 'code',` per line, with keys and codes unique; the registry-hotspots gate
10
+ * refuses anything else.
11
+ */
12
+ export const HOSTING_CODE = {
13
+ stabilitySoaked: 'hosting.stability.soaked',
14
+ stabilityCrashedAttributed: 'hosting.stability.crashed-attributed-plugin',
15
+ stabilityCrashedLive: 'hosting.stability.crashed-live-on-this-rig',
16
+ stabilityBelowFloor: 'hosting.stability.cycles-below-floor',
17
+ stabilityUnsoaked: 'hosting.stability.unsoaked',
18
+ stabilityUnmeasured: 'hosting.stability.unmeasured',
19
+ stabilityCrashUnattributed: 'hosting.stability.crash-unattributed',
20
+ stabilitySpawnsThreads: 'hosting.stability.spawns-threads',
21
+ stabilityLinksGuiToolkit: 'hosting.stability.links-gui-toolkit',
22
+ rtSafetyClean: 'hosting.rt-safety.clean-under-sweep',
23
+ rtSafetyViolations: 'hosting.rt-safety.violations-in-run',
24
+ rtSafetyUnmeasured: 'hosting.rt-safety.unmeasured',
25
+ rtSafetyNotReproducible: 'hosting.rt-safety.not-reproducible',
26
+ featuresProvided: 'hosting.features.all-provided',
27
+ featuresMissing: 'hosting.features.required-not-provided',
28
+ featuresDisqualifying: 'hosting.features.host-not-provided',
29
+ featuresCv: 'hosting.features.cv-ports',
30
+ featuresMidiIn: 'hosting.features.midi-in-fed-empty',
31
+ featuresUnscanned: 'hosting.features.scan-predates-field',
32
+ costWithinCeiling: 'hosting.cost.within-ceiling',
33
+ costAboveCeiling: 'hosting.cost.above-ceiling',
34
+ costSpiky: 'hosting.cost.spiky-tail',
35
+ costUnmeasured: 'hosting.cost.unmeasured-at-rate-and-quantum',
36
+ latencyResolved: 'hosting.latency.resolved',
37
+ latencyUnmeasured: 'hosting.latency.unmeasured',
38
+ latencyNonconforming: 'hosting.latency.nonconforming',
39
+ latencyParameterDependent: 'hosting.latency.parameter-dependent',
40
+ topologyMatched: 'hosting.topology.matched',
41
+ topologyNoAudioInput: 'hosting.topology.no-audio-input',
42
+ topologyNoAudioOutput: 'hosting.topology.no-audio-output',
43
+ topologyExtraInputs: 'hosting.topology.extra-inputs-fed-silence',
44
+ topologyInstrument: 'hosting.topology.instrument',
45
+ // The CLAP arm (2026-09-26-clap-hosting-path.md §3): the SAME dimensions fed from CLAP facts,
46
+ // minted here where the LV2 table has no code for the fact.
47
+ topologyWiderThanStrip: 'hosting.topology.wider-than-strip',
48
+ clapLatencyMismatch: 'hosting.clap.latency-mismatch',
49
+ clapNotAudioEffect: 'hosting.clap.not-audio-effect',
50
+ clapHeadlessFailed: 'hosting.clap.headless-failed',
51
+ clapNoteInput: 'hosting.clap.note-input',
52
+ clapThreadViolation: 'hosting.clap.thread-violation',
53
+ clapProcessError: 'hosting.clap.process-error',
54
+ clapUnqualified: 'hosting.clap.unqualified',
55
+ clapFormatNotHosted: 'hosting.clap.format-not-hosted',
56
+ clapExtensionsNotCovered: 'hosting.clap.extensions-not-covered',
57
+ outputNonFinite: 'hosting.output.non-finite',
58
+ overrideIsolated: 'hosting.override.curator-isolated',
59
+ hostProvenanceDiffers: 'hosting.provenance.host-differs',
60
+ sweepControlDidNotFire: 'hosting.provenance.sweep-control-did-not-fire',
61
+ // The five REALISED reasons. Everything above answers what a plugin EARNED; these answer
62
+ // what the console DID with that, and the difference between the two is the operator's
63
+ // whole question when a slot is not on the fast path.
64
+ disabled: 'hosting.disabled',
65
+ unjudged: 'hosting.unjudged',
66
+ noRealisation: 'hosting.no-realisation',
67
+ realisedInProcess: 'hosting.realised.in-process',
68
+ // A plugin LINKED into the engine (the static-link registry) runs in-process without a
69
+ // verdict: the library's oracles are its proof (`2026-09-26-clap-hosting-path.md` §7).
70
+ linked: 'hosting.linked',
71
+ };
72
+ //# sourceMappingURL=hosting-codes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosting-codes.js","sourceRoot":"","sources":["../src/hosting-codes.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,sDAAsD;AACtD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,eAAe,EAAE,0BAA0B;IAC3C,0BAA0B,EAAE,6CAA6C;IACzE,oBAAoB,EAAE,4CAA4C;IAClE,mBAAmB,EAAE,sCAAsC;IAC3D,iBAAiB,EAAE,4BAA4B;IAC/C,mBAAmB,EAAE,8BAA8B;IACnD,0BAA0B,EAAE,sCAAsC;IAClE,sBAAsB,EAAE,kCAAkC;IAC1D,wBAAwB,EAAE,qCAAqC;IAC/D,aAAa,EAAE,qCAAqC;IACpD,kBAAkB,EAAE,qCAAqC;IACzD,kBAAkB,EAAE,8BAA8B;IAClD,uBAAuB,EAAE,oCAAoC;IAC7D,gBAAgB,EAAE,+BAA+B;IACjD,eAAe,EAAE,wCAAwC;IACzD,qBAAqB,EAAE,oCAAoC;IAC3D,UAAU,EAAE,2BAA2B;IACvC,cAAc,EAAE,oCAAoC;IACpD,iBAAiB,EAAE,sCAAsC;IACzD,iBAAiB,EAAE,6BAA6B;IAChD,gBAAgB,EAAE,4BAA4B;IAC9C,SAAS,EAAE,yBAAyB;IACpC,cAAc,EAAE,6CAA6C;IAC7D,eAAe,EAAE,0BAA0B;IAC3C,iBAAiB,EAAE,4BAA4B;IAC/C,oBAAoB,EAAE,+BAA+B;IACrD,yBAAyB,EAAE,qCAAqC;IAChE,eAAe,EAAE,0BAA0B;IAC3C,oBAAoB,EAAE,iCAAiC;IACvD,qBAAqB,EAAE,kCAAkC;IACzD,mBAAmB,EAAE,2CAA2C;IAChE,kBAAkB,EAAE,6BAA6B;IACjD,8FAA8F;IAC9F,4DAA4D;IAC5D,sBAAsB,EAAE,mCAAmC;IAC3D,mBAAmB,EAAE,+BAA+B;IACpD,kBAAkB,EAAE,+BAA+B;IACnD,kBAAkB,EAAE,8BAA8B;IAClD,aAAa,EAAE,yBAAyB;IACxC,mBAAmB,EAAE,+BAA+B;IACpD,gBAAgB,EAAE,4BAA4B;IAC9C,eAAe,EAAE,0BAA0B;IAC3C,mBAAmB,EAAE,gCAAgC;IACrD,wBAAwB,EAAE,qCAAqC;IAC/D,eAAe,EAAE,2BAA2B;IAC5C,gBAAgB,EAAE,mCAAmC;IACrD,qBAAqB,EAAE,iCAAiC;IACxD,sBAAsB,EAAE,+CAA+C;IACvE,yFAAyF;IACzF,uFAAuF;IACvF,sDAAsD;IACtD,QAAQ,EAAE,kBAAkB;IAC5B,QAAQ,EAAE,kBAAkB;IAC5B,aAAa,EAAE,wBAAwB;IACvC,iBAAiB,EAAE,6BAA6B;IAChD,uFAAuF;IACvF,uFAAuF;IACvF,MAAM,EAAE,gBAAgB;CAChB,CAAC"}
@@ -0,0 +1,354 @@
1
+ /**
2
+ * Which HOST a plugin has earned: the console's own RT thread, or a `mod-host` process.
3
+ *
4
+ * "Thoroughly tested" is not a hand list and never an author's opinion. It is the derived
5
+ * verdict `hosting.rating === "suitable"`, computed on every read from measurements the
6
+ * catalog holds. `path` follows the rating and nothing else: `suitable` earns `in-process`,
7
+ * every rating below it is `isolated` with the reasons that decided it.
8
+ *
9
+ * The shape mirrors {@link import('./destination-suitability.js').classifyForRole} because
10
+ * the same law governs both — a standing is DERIVED, absence is an admission rather than a
11
+ * pass, and an override may only ever demote. What differs is the stake. A destination
12
+ * verdict that is wrong puts a reverb in a wedge; a hosting verdict that is wrong puts an
13
+ * untested plugin on the thread the whole desk's audio runs on, where its crash is not its
14
+ * own. So every dimension here fails CLOSED: no measurement means `unknown`, and `unknown`
15
+ * means isolated.
16
+ */
17
+ import { type SuitabilityKind, type SuitabilityRating } from "./rating.js";
18
+ import type { FeaturesMeasurement, LifecycleReading, RtSafetyMeasurement, SoakReading, ThreadReading } from "./measurement/format.js";
19
+ /**
20
+ * What a reason may interpolate: wire-serialisable scalars only. Structurally the same record as
21
+ * the console's coded-refusal parameters, so a consumer passes it straight through.
22
+ */
23
+ export type CodedRefusalParams = Readonly<Record<string, string | number>>;
24
+ /**
25
+ * The plugin the rating reads — exactly the fields it judges, and nothing a host's own catalog
26
+ * adds. The console's plugin descriptor satisfies it structurally; the standalone CLI
27
+ * builds it from the scan and the interchange document.
28
+ */
29
+ export interface QualifiedPlugin {
30
+ readonly audioInputs: number;
31
+ readonly audioOutputs: number;
32
+ readonly hasMidiIn: boolean;
33
+ /** The benchmark's latency block; only whether it resolved, conforms and moves is read. */
34
+ readonly latency?: {
35
+ readonly unmeasurable?: unknown;
36
+ readonly scalingClass?: string;
37
+ readonly paramSweep?: unknown;
38
+ };
39
+ /** Measured cost per rate (Hz as a string key). */
40
+ readonly cpuCost?: {
41
+ readonly perRate?: Readonly<Record<string, {
42
+ readonly coreFractionP95: number;
43
+ readonly nsPerSampleMedian: number;
44
+ readonly nsPerSampleP95: number;
45
+ }>>;
46
+ };
47
+ readonly stability?: PluginStabilityMeasurement;
48
+ readonly rtSafety?: PluginRtSafetyMeasurement;
49
+ readonly lv2Features?: PluginLv2Features;
50
+ /** The plugin's binary format, the scan's declared fact (CLAP spec §6); absent reads `lv2`. */
51
+ readonly format?: "lv2" | "clap";
52
+ /** What the CLAP prober (`native/clap-qualify`) measured, for a `clap` plugin (CLAP spec §3). */
53
+ readonly clap?: PluginClapMeasurement;
54
+ }
55
+ /** Where a plugin's audio is actually processed. */
56
+ export type HostingPath = "in-process" | "isolated";
57
+ /** The dimensions that decide a host. Six, folded worst-wins. */
58
+ export type HostingDimension = "stability" | "rtSafety" | "features" | "cost" | "latency" | "topology";
59
+ export { OMX_CLAP_HOST_EXTENSIONS, OMX_INPROCESS_FEATURES, OPENMIXER_CONSOLE_PROFILE, type HostProfile } from "./host-profiles.js";
60
+ import { type HostProfile } from "./host-profiles.js";
61
+ /** The catalog key of a CLAP plugin is `urn:clap:<clap_plugin_descriptor.id>` (CLAP spec §6). */
62
+ export declare const CLAP_URI_PREFIX = "urn:clap:";
63
+ /** Whether `uri` is a CLAP catalog key — the key grammar, for a plugin the catalog may not hold. */
64
+ export declare function isClapUri(uri: string): boolean;
65
+ /**
66
+ * Whether the plugin `uri` names is hosted through the CLAP path: the descriptor's DECLARED
67
+ * format where the catalog holds one, else the key grammar (a `urn:clap:` uri the catalog never
68
+ * scanned is still a CLAP the console has no LV2 host for).
69
+ */
70
+ export declare function isClapPlugin(uri: string, descriptor: Pick<QualifiedPlugin, "format"> | undefined): boolean;
71
+ /** The CLAP descriptor feature that makes a plugin an audio effect (`clap/plugin-features.h`). */
72
+ export declare const CLAP_FEATURE_AUDIO_EFFECT = "audio-effect";
73
+ /** The CLAP descriptor feature that makes a plugin an instrument (`clap/plugin-features.h`). */
74
+ export declare const CLAP_FEATURE_INSTRUMENT = "instrument";
75
+ /** The verdict's message codes, declared in their union list file. */
76
+ import { HOSTING_CODE } from "./hosting-codes.js";
77
+ export { HOSTING_CODE };
78
+ export type HostingCode = (typeof HOSTING_CODE)[keyof typeof HOSTING_CODE];
79
+ /**
80
+ * The refusals libomx-clap-core returns from `open`, `activate` and the timed open, each named by the KEY of the
81
+ * `HOSTING_CODE` it answers with — never by the string, so no second spelling of a code exists. The console's
82
+ * generator renders them into omx-clap-host's `clap_host_limits.h` as `CLAP_HOST_CODE_<NAME>`, the left column's
83
+ * name in upper snake case. The facts the qualifier judges from a measurement (latency, thread violations, process
84
+ * errors, RT safety) are the console's and are not here.
85
+ */
86
+ export declare const CLAP_CORE_REFUSALS: {
87
+ readonly headlessFailed: "clapHeadlessFailed";
88
+ readonly notAudioEffect: "clapNotAudioEffect";
89
+ readonly noteInput: "clapNoteInput";
90
+ readonly noAudioInput: "topologyNoAudioInput";
91
+ readonly noAudioOutput: "topologyNoAudioOutput";
92
+ readonly widerThanStrip: "topologyWiderThanStrip";
93
+ readonly extraInputs: "topologyExtraInputs";
94
+ readonly crashedLive: "stabilityCrashedLive";
95
+ readonly outputNonFinite: "outputNonFinite";
96
+ };
97
+ /** One dimension's answer for one plugin. */
98
+ export interface HostingReason {
99
+ readonly dimension: HostingDimension;
100
+ readonly kind: SuitabilityKind;
101
+ readonly rating: SuitabilityRating;
102
+ readonly code: HostingCode;
103
+ readonly params: CodedRefusalParams;
104
+ }
105
+ /**
106
+ * What a reason may contribute. Unlike the destination classifier, every reason here is
107
+ * objective — there is no taste in whether a plugin allocates inside `run()`. The cautions
108
+ * that look advisory (`spawns-threads`, `links-gui-toolkit`) are MEASURED facts, so they are
109
+ * objective too and cap the plugin at `conditional` by their own rating rather than by a kind
110
+ * rule. An advisory reason, if one is ever added, may never do worse than `conditional`.
111
+ */
112
+ export declare function contributedHostingRating(reason: HostingReason): SuitabilityRating;
113
+ /** How a crash was attributed. `unknown` is the default and derives `unknown`, never blame. */
114
+ export type CrashAttribution = "plugin" | "host" | "unknown";
115
+ /** One row of the signature table: a crash shape we have seen and named. */
116
+ export interface CrashSignature {
117
+ readonly id: string;
118
+ readonly attribution: CrashAttribution;
119
+ /** The top plugin-owned stack frame, or a host frame that exonerates the plugin. */
120
+ readonly topFrame: string;
121
+ readonly signal?: "SIGSEGV" | "SIGABRT" | "SIGBUS";
122
+ /** The note that recorded this cluster. Provenance, so a row is never folklore. */
123
+ readonly note: string;
124
+ }
125
+ /**
126
+ * Seeded from the crash notes of 2026-09-03/04. A raw crash matching NO row is
127
+ * `unknown` — the table blames a plugin only where a named investigation did.
128
+ */
129
+ export declare const CRASH_SIGNATURES: readonly CrashSignature[];
130
+ /** A crash as observed, before attribution. */
131
+ export interface ObservedCrash {
132
+ readonly signal?: "SIGSEGV" | "SIGABRT" | "SIGBUS";
133
+ readonly topFrame?: string;
134
+ /** The sanitizer's error class, when a sanitizer build caught it (interchange §3a). */
135
+ readonly sanitizer?: string;
136
+ /** Basename of the object the faulting frame lies in. */
137
+ readonly frameObject?: string;
138
+ }
139
+ /**
140
+ * Map a raw crash to blame. Fails to `unknown`: a signature must match the frame AND, where
141
+ * the row names one, the signal. An unmatched crash never reads as the plugin's fault.
142
+ */
143
+ export declare function attributeCrash(crash: ObservedCrash, table?: readonly CrashSignature[], pluginBinary?: string): CrashAttribution;
144
+ /**
145
+ * What a lifecycle sweep MEASURED about a plugin surviving — the interchange format's stability
146
+ * block as it lands on a descriptor (`measurement-catalog.ts`), RAW: the cycles as run, the
147
+ * threads as counted, the crashes as observed, the soak per rate as rendered. Whether any of that
148
+ * adds up to a pass is {@link stabilityReasons}' to say, and nobody else's.
149
+ */
150
+ export interface PluginStabilityMeasurement {
151
+ readonly lifecycle?: LifecycleReading;
152
+ readonly threads?: ThreadReading;
153
+ readonly crashes?: readonly ObservedCrash[];
154
+ /** A continuous render per rate with the controls swept — judged at EVERY rate it ran. */
155
+ readonly soak?: SoakReading;
156
+ readonly linksGuiToolkit?: boolean;
157
+ /** Recorded when a live console's pre-rack trial or a show killed it. */
158
+ readonly crashedLive?: boolean;
159
+ /** The host the figures were taken on — the run's, set by the door that landed them. */
160
+ readonly runHost?: string;
161
+ }
162
+ /**
163
+ * The interposer's counts inside `run()`, RAW, with the pair it ran at and the flags that say
164
+ * whether the count is a measurement at all ({@link rtSafetyMeasured}) — the interchange's
165
+ * rt-safety block as it lands, minus the reason a block with no reading carries instead.
166
+ */
167
+ export type PluginRtSafetyMeasurement = Omit<RtSafetyMeasurement, "unmeasuredReason" | "unmeasuredDetail">;
168
+ /** What the plugin DEMANDS of a host, read off the instance the sweep loaded. */
169
+ export type PluginLv2Features = Omit<FeaturesMeasurement, "unmeasuredReason" | "unmeasuredDetail">;
170
+ export { DEFAULT_HOSTING_POLICY, type HostingPolicy } from "./qualify-declarations.js";
171
+ import { type HostingPolicy } from "./qualify-declarations.js";
172
+ /** One declared rate's latency reading from the CLAP qualifier (CLAP spec §3, latency). */
173
+ export interface ClapLatencyAtRate {
174
+ /** `latency.get()` after `activate` at this rate; a plugin without the ext declares 0. */
175
+ readonly declaredFrames: number;
176
+ /** Where the qualifier's impulse landed through `process()`, in frames. */
177
+ readonly measuredFrames: number;
178
+ }
179
+ /**
180
+ * What the CLAP prober (`native/clap-qualify`) MEASURED about a CLAP plugin, RAW (CLAP spec §3): the facts the CLAP
181
+ * arm of each dimension reads. Whether any of it earns `suitable` is the `clap*Reasons` arms' to say.
182
+ */
183
+ export interface PluginClapMeasurement {
184
+ /** Main audio ports: channel counts of the ONE main input and ONE main output the qualifier
185
+ * found (0 where none); `sidechainInputs` counts non-main audio inputs. */
186
+ readonly mainInputChannels: number;
187
+ readonly mainOutputChannels: number;
188
+ readonly sidechainInputs: number;
189
+ /** `clap_plugin_descriptor.features`, as declared. */
190
+ readonly features: readonly string[];
191
+ /** `init()` and `activate()` both succeeded HEADLESS, with only OMX_CLAP_HOST_EXTENSIONS offered. */
192
+ readonly headlessOk: boolean;
193
+ /** Note input ports the `note-ports` ext declares (0 where the ext is absent). */
194
+ readonly noteInputs: number;
195
+ /** Declared vs measured latency at every declared rate, keyed by the rate's decimal string. */
196
+ readonly latency: Readonly<Record<string, ClapLatencyAtRate>>;
197
+ /** Strict `thread-check` violations counted across the sweep. */
198
+ readonly threadViolations: number;
199
+ /** `CLAP_PROCESS_ERROR` returns counted across the sweep. */
200
+ readonly processErrors: number;
201
+ /** `process()` calls the sweep made after its warm-up; a sweep of 0 measured nothing. */
202
+ readonly processCalls: number;
203
+ }
204
+ /** What the classifier is told about the console asking. */
205
+ export interface HostingClassifyOptions {
206
+ /** The LIVE rate. Cost is read here and nowhere else — never projected. */
207
+ readonly rate: number;
208
+ /** The LIVE quantum in frames, when the console pins one. */
209
+ readonly quantum?: number;
210
+ /** Basename of the plugin's own binary, when known: a sanitizer error faulting there is the plugin's. */
211
+ readonly pluginBinary?: string;
212
+ readonly policy?: HostingPolicy;
213
+ /** Curator override. `isolated` is the ONLY legal value; see {@link assertHostingOverride}. */
214
+ readonly override?: "isolated";
215
+ /** The host the console is actually running, to compare against the figures' provenance. */
216
+ readonly consoleHost?: string;
217
+ /** False when the sweep's positive control did not fire — poisons the whole verdict. */
218
+ readonly sweepControlFired?: boolean;
219
+ /** The host judged against (plugin-qualify §3a). Absent: `openmixer-console`, the console's own. */
220
+ readonly profile?: HostProfile;
221
+ }
222
+ /** The verdict, and everything that produced it. */
223
+ export interface PluginHosting {
224
+ readonly rating: SuitabilityRating;
225
+ readonly path: HostingPath;
226
+ readonly reasons: readonly HostingReason[];
227
+ /** The ONE reason that decided it: the worst, first-listed. Undefined when suitable. */
228
+ readonly deciding?: HostingReason;
229
+ }
230
+ /**
231
+ * Refuse an illegal override at the door. A curator may take a plugin OFF the RT thread and
232
+ * may never put one on it — no measurement, no in-process, and no "force" exists anywhere.
233
+ * Throws rather than coercing, because silently demoting a bad value would hide the mistake.
234
+ */
235
+ export declare function assertHostingOverride(value: unknown, where: string): "isolated" | undefined;
236
+ /**
237
+ * The level at which a rate is DEAD against the plugin's own best: `policy.soakDeadDropDb` or
238
+ * more below it. A ratio against the plugin itself, never an absolute floor — a quiet plugin is
239
+ * not a dead one. The one application of that threshold; the soak rule below and the report's
240
+ * short-render column both ask it.
241
+ */
242
+ export declare function deadAgainstBest(levelDbfs: number, bestDbfs: number, policy: HostingPolicy): boolean;
243
+ /** A soak's reading: what voided it, if anything, and what it proved when nothing did. */
244
+ export interface SoakFindings {
245
+ /** The SHORTEST rate's seconds — a soak proves no more than its weakest rate ran. */
246
+ readonly seconds: number;
247
+ /** Every rate that voids the soak, as `<why>@<rate>` — `died@`, `non-finite@`, `dead@`, `mostly-silent@`, `startup-blast@`. */
248
+ readonly failures: readonly string[];
249
+ readonly ratesTested: number;
250
+ /** The largest RSS growth across rates, for the record; not judged. */
251
+ readonly rssGrowthKb: number;
252
+ }
253
+ /**
254
+ * Read a soak, rate by rate. A rate voids it if the plugin did not instantiate or died there,
255
+ * emitted a non-finite window, settled dead against its own best rate ({@link deadAgainstBest};
256
+ * settled = the LAST window, never the first — Calf opens at +200 dBFS at 192 k), spent more
257
+ * than half its windows silent, or opened above +6 dBFS. A few silent windows in ten thousand is
258
+ * a gate closing on a swept threshold — the plugin working, not dying.
259
+ *
260
+ * `undefined` when there is no soak, or it covered no rate: nothing was read.
261
+ */
262
+ export declare function soakFindings(soak: SoakReading | undefined, policy: HostingPolicy): SoakFindings | undefined;
263
+ /** Threads the plugin's first instance brought with it. */
264
+ export declare function threadsSpawned(threads: ThreadReading | undefined): number;
265
+ export declare function stabilityReasons(m: PluginStabilityMeasurement | undefined, policy: HostingPolicy, pluginBinary?: string,
266
+ /** Whether the plugin shares its process with others (profile isolation, §3a): threads it
267
+ * spawns and a GUI toolkit it links are cautions only there. */
268
+ sharesProcess?: boolean): readonly HostingReason[];
269
+ /**
270
+ * Whether an interposer count is a MEASUREMENT: the interposer was loaded, all three counts are
271
+ * present, and the controls were swept — or the plugin has none a sweep could move, so its
272
+ * at-rest count is the only state there is. Measured at rest otherwise is not measured.
273
+ */
274
+ export declare function rtSafetyMeasured(m: PluginRtSafetyMeasurement | undefined): boolean;
275
+ /** A measured count above zero — the disqualifier, asked in one place. */
276
+ export declare function rtSafetyViolating(m: PluginRtSafetyMeasurement | undefined): boolean;
277
+ export declare function rtSafetyReasons(m: PluginRtSafetyMeasurement | undefined): readonly HostingReason[];
278
+ export declare function featureReasons(features: PluginLv2Features | undefined, descriptor: Pick<QualifiedPlugin, "hasMidiIn">, provided?: readonly string[], quantumPinned?: boolean,
279
+ /** The host feeds MIDI input (an instrument host, §3a): a MIDI input is no caution there. */
280
+ instruments?: boolean): readonly HostingReason[];
281
+ export declare function hostingCostReasons(descriptor: Pick<QualifiedPlugin, "cpuCost">, options: HostingClassifyOptions, policy: HostingPolicy): readonly HostingReason[];
282
+ export declare function hostingLatencyReasons(descriptor: Pick<QualifiedPlugin, "latency">, handling?: HostProfile["latency"]): readonly HostingReason[];
283
+ export declare function hostingTopologyReasons(descriptor: Pick<QualifiedPlugin, "audioInputs" | "audioOutputs"> & Partial<Pick<QualifiedPlugin, "hasMidiIn">>, instruments?: boolean): readonly HostingReason[];
284
+ /**
285
+ * The CLAP arm of `topology` (CLAP spec §3): the qualifier's main-port facts feed the same
286
+ * dimension the LV2 scan feeds. No measurement → `unknown` (`hosting.clap.unqualified`), never
287
+ * a guess from the descriptor.
288
+ */
289
+ export declare function clapTopologyReasons(clap: PluginClapMeasurement | undefined, instruments?: boolean): readonly HostingReason[];
290
+ /**
291
+ * The CLAP arm of `latency` (CLAP spec §3): declared = measured at EVERY declared rate earns;
292
+ * one rate that lies refuses (`unsuitable` — compensation reads the figure as truth); no rate
293
+ * measured is `unknown`.
294
+ */
295
+ export declare function clapLatencyReasons(clap: PluginClapMeasurement | undefined): readonly HostingReason[];
296
+ /**
297
+ * The CLAP arm of `features` (CLAP spec §3): an audio effect that initialised and activated
298
+ * HEADLESS with only OMX_CLAP_HOST_EXTENSIONS offered, and no note input (the measurement's or the
299
+ * scan's `hasMidiIn`). Every miss is its own code; a note input is an instrument, `unsuitable`.
300
+ */
301
+ export declare function clapFeatureReasons(clap: PluginClapMeasurement | undefined, profile?: HostProfile, scannedNoteInput?: boolean): readonly HostingReason[];
302
+ /**
303
+ * The CLAP additions to `rtSafety` (CLAP spec §3): the interposer's counts are read by
304
+ * `rtSafetyReasons` exactly as for an LV2; these are the two violations only a CLAP can commit
305
+ * — a strict thread-check violation and a `CLAP_PROCESS_ERROR` — each `unsuitable`. A sweep
306
+ * that made no `process()` call measured nothing.
307
+ */
308
+ export declare function clapRtReasons(clap: PluginClapMeasurement | undefined): readonly HostingReason[];
309
+ /**
310
+ * The verdict. Worst-wins across every dimension, exactly as `classifyForRole` folds — with
311
+ * three gates that can poison the whole result before the fold is even read: a curator
312
+ * override, a sweep whose positive control did not fire, and figures taken on a different
313
+ * host than the console is running.
314
+ */
315
+ export declare function classifyPluginHosting(descriptor: QualifiedPlugin, options: HostingClassifyOptions): PluginHosting;
316
+ /** What a racked slot's host actually IS, and the ONE reason it is that. */
317
+ export interface RealisedHosting {
318
+ readonly path: HostingPath;
319
+ /** The plugin's own rating, carried through — `unknown` where nothing could be judged. */
320
+ readonly rating: SuitabilityRating;
321
+ readonly reason: HostingCode;
322
+ }
323
+ /** What the CONSOLE brings to the question, beside the plugin's own verdict. */
324
+ export interface HostingRealisation {
325
+ /** The operator's authored switch (`/console/plugins/hosting`). */
326
+ readonly inProcessEnabled: boolean;
327
+ /** Whether an in-process body exists to run a plugin in at all. */
328
+ readonly inProcessAvailable: boolean;
329
+ /**
330
+ * Whether the plugin is LINKED into the engine (its static-link registry): it needs no verdict
331
+ * to run in-process (`2026-09-26-clap-hosting-path.md` §7). Absent reads as not linked.
332
+ */
333
+ readonly linked?: boolean;
334
+ }
335
+ /**
336
+ * The REALISED host of one racked plugin — a different fact from the verdict, and kept in a
337
+ * different field for that reason.
338
+ *
339
+ * `classifyPluginHosting` answers what a plugin EARNED. This answers where it RUNS, which
340
+ * three things decide in order, each with its own reason so the operator reads the first one
341
+ * that applies rather than a fold:
342
+ *
343
+ * 1. the console's switch is off — nothing runs in-process, whatever it earned;
344
+ * 1a. the plugin is LINKED into the engine — it runs in-process wherever a body exists, with no
345
+ * verdict to read;
346
+ * 2. nothing could be judged (no verdict: the console has observed no rate and quantum) or the
347
+ * verdict is below `suitable` — the plugin's own deciding reason stands;
348
+ * 3. it earned in-process and there is no in-process body to run it in yet.
349
+ *
350
+ * Order matters and is the operator's, not the machine's: a console-wide fact is a better
351
+ * answer to "why is this plugin not on the fast path" than a per-plugin one, because it is the
352
+ * one the operator can act on.
353
+ */
354
+ export declare function realisedHosting(verdict: PluginHosting | undefined, realisation: HostingRealisation): RealisedHosting;