@openmixer/plugin-qualify 0.12.0 → 0.13.1

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.
@@ -109,6 +109,17 @@
109
109
  "clap.params",
110
110
  "clap.state",
111
111
  "clap.thread-check",
112
+ "clap.note-ports",
113
+ "clap.note-name",
114
+ "clap.voice-info",
115
+ "clap.event-registry",
116
+ "clap.timer-support",
117
+ "clap.posix-fd-support",
118
+ "clap.thread-pool",
119
+ "clap.tail",
120
+ "clap.surround/4",
121
+ "clap.ambisonic/3",
122
+ "clap.audio-ports-config",
112
123
  "clap.preset-load/2",
113
124
  "clap.track-info/1",
114
125
  "clap.remote-controls/2"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmixer/plugin-qualify",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "description": "Qualify audio plugins for real-time hosting: scan, offline latency and cost measurement, a mod-host hosting sweep, and a fail-closed rating.",
5
5
  "type": "module",
6
6
  "license": "GPL-3.0-or-later",
@@ -77,12 +77,12 @@
77
77
  "LICENSE"
78
78
  ],
79
79
  "scripts": {
80
- "build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.json",
80
+ "build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && node tools/gen-hosting-codes.mjs --build && tsc -p tsconfig.json",
81
81
  "prepare": "npm run build",
82
82
  "test": "vitest run src",
83
83
  "lint": "tsc -p tsconfig.lint.json",
84
84
  "test:py": "python3 -m pytest tools -q",
85
- "check:generated": "node tools/gen-python-declarations.mjs --check",
85
+ "check:generated": "node tools/gen-python-declarations.mjs --check && node tools/gen-hosting-codes.mjs --check",
86
86
  "check:private-refs": "tools/private-refs-check.sh && tools/private-refs-check.sh --self-test"
87
87
  },
88
88
  "devDependencies": {
@@ -96,6 +96,6 @@
96
96
  "provenance": true
97
97
  },
98
98
  "dependencies": {
99
- "@openmixer/omx-contract": "^2.16.0"
99
+ "@openmixer/omx-contract": "^3.0.0"
100
100
  }
101
101
  }
@@ -42,8 +42,7 @@ export const OMX_INPROCESS_FEATURES: readonly string[] = [
42
42
  * than on the desk. The list's home is the CLAP library — libomx-clap-core's own
43
43
  * `clap_host_extensions.h`, what its host object offers — and this is its mirror, held equal to the
44
44
  * header at the pinned omx-clap-host (by the console's conformance test), so the verdict never
45
- * promises more or less than the host provides. Nothing else: no `gui`, no `timer-support`, no
46
- * `posix-fd-support`, no `thread-pool`.
45
+ * promises more or less than the host provides. Nothing else: no `gui`.
47
46
  */
48
47
  export const OMX_CLAP_HOST_EXTENSIONS: readonly string[] = [
49
48
  "clap.log",
@@ -52,6 +51,17 @@ export const OMX_CLAP_HOST_EXTENSIONS: readonly string[] = [
52
51
  "clap.params",
53
52
  "clap.audio-ports",
54
53
  "clap.state",
54
+ "clap.note-ports",
55
+ "clap.note-name",
56
+ "clap.voice-info",
57
+ "clap.event-registry",
58
+ "clap.timer-support",
59
+ "clap.posix-fd-support",
60
+ "clap.thread-pool",
61
+ "clap.tail",
62
+ "clap.surround/4",
63
+ "clap.ambisonic/3",
64
+ "clap.audio-ports-config",
55
65
  ];
56
66
 
57
67
  /**
@@ -0,0 +1,84 @@
1
+ // SPDX-License-Identifier: GPL-3.0-or-later
2
+ // Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
3
+ //
4
+ // GENERATED by tools/gen-hosting-codes.mjs from omx-clap-core's hosting tables — do not hand-edit.
5
+ //
6
+ // Regenerate: node tools/gen-hosting-codes.mjs --write
7
+ // Verify: node tools/gen-hosting-codes.mjs --check
8
+
9
+ /** omx-clap-core's hosting codes, by key: every `HOSTING_<DIMENSION>_CODES` table, flattened. */
10
+ export const HOSTING_CODE = Object.freeze({
11
+ "clapExtensionsNotCovered": "hosting.clap.extensions-not-covered",
12
+ "clapFormatNotHosted": "hosting.clap.format-not-hosted",
13
+ "clapHeadlessFailed": "hosting.clap.headless-failed",
14
+ "clapLatencyMismatch": "hosting.clap.latency-mismatch",
15
+ "clapNoteInput": "hosting.clap.note-input",
16
+ "clapProcessError": "hosting.clap.process-error",
17
+ "clapThreadViolation": "hosting.clap.thread-violation",
18
+ "clapUnqualified": "hosting.clap.unqualified",
19
+ "costAboveCeiling": "hosting.cost.above-ceiling",
20
+ "costFieldsMissing": "hosting.cost.fields-missing",
21
+ "costSpiky": "hosting.cost.spiky-tail",
22
+ "costUnmeasured": "hosting.cost.unmeasured-at-rate-and-quantum",
23
+ "costWithinCeiling": "hosting.cost.within-ceiling",
24
+ "featuresAtomInUnfed": "hosting.features.atom-in-unfed",
25
+ "featuresDisqualifying": "hosting.features.host-not-provided",
26
+ "featuresMidiIn": "hosting.features.midi-in-fed-empty",
27
+ "featuresMissing": "hosting.features.missing",
28
+ "featuresPortsUnreadable": "hosting.features.ports-unreadable",
29
+ "featuresProvided": "hosting.features.all-provided",
30
+ "featuresUnscanned": "hosting.features.scan-predates-field",
31
+ "latencyNonconforming": "hosting.latency.nonconforming",
32
+ "latencyParameterDependent": "hosting.latency.parameter-dependent",
33
+ "latencyResolved": "hosting.latency.resolved",
34
+ "latencyUnmeasured": "hosting.latency.unmeasured",
35
+ "outputNonFinite": "hosting.output.non-finite",
36
+ "overrideIsolated": "hosting.override.curator-isolated",
37
+ "patchBusy": "hosting.patch.busy",
38
+ "patchNotWritable": "hosting.patch.not-writable",
39
+ "patchTooLarge": "hosting.patch.too-large",
40
+ "presetNotFound": "hosting.preset.not-found",
41
+ "hostProvenanceDiffers": "hosting.provenance.host-differs",
42
+ "sweepControlDidNotFire": "hosting.provenance.sweep-control-did-not-fire",
43
+ "disabled": "hosting.disabled",
44
+ "linked": "hosting.linked",
45
+ "noRealisation": "hosting.no-realisation",
46
+ "realisedInProcess": "hosting.realised.in-process",
47
+ "unjudged": "hosting.unjudged",
48
+ "rtSafetyClean": "hosting.rt-safety.clean-under-sweep",
49
+ "rtSafetyNotReproducible": "hosting.rt-safety.not-reproducible",
50
+ "rtSafetyNotesClean": "hosting.rt-safety.clean-under-notes",
51
+ "rtSafetyNotesUnmeasured": "hosting.rt-safety.notes-unmeasured",
52
+ "rtSafetyNotesViolations": "hosting.rt-safety.violations-under-notes",
53
+ "rtSafetyUnmeasured": "hosting.rt-safety.unmeasured",
54
+ "rtSafetyViolations": "hosting.rt-safety.violations-in-run",
55
+ "stabilityBelowFloor": "hosting.stability.cycles-below-floor",
56
+ "stabilityCrashUnattributed": "hosting.stability.crash-unattributed",
57
+ "stabilityCrashedAttributed": "hosting.stability.crashed-attributed-plugin",
58
+ "stabilityCrashedLive": "hosting.stability.crashed-live-on-this-rig",
59
+ "stabilityLinksGuiToolkit": "hosting.stability.links-gui-toolkit",
60
+ "stabilitySoakFailedAtRate": "hosting.stability.soak-failed-at-rate",
61
+ "stabilitySoaked": "hosting.stability.soaked",
62
+ "stabilitySpawnsThreads": "hosting.stability.spawns-threads",
63
+ "stabilityUnmeasured": "hosting.stability.unmeasured",
64
+ "stabilityUnsoaked": "hosting.stability.unsoaked",
65
+ "stateNotACopy": "hosting.state.not-a-copy",
66
+ "statePathOnAudio": "hosting.state.path-on-audio",
67
+ "statePathOutside": "hosting.state.path-outside",
68
+ "statePathSymlink": "hosting.state.path-symlink",
69
+ "statePathTooLong": "hosting.state.path-too-long",
70
+ "statePathUnmakeable": "hosting.state.path-unmakeable",
71
+ "stateUnreadable": "hosting.state.unreadable",
72
+ "topologyAmbisonicConfig": "hosting.topology.ambisonic-config",
73
+ "topologyChannelMapUnknown": "hosting.topology.channel-map-unknown",
74
+ "topologyInstrument": "hosting.topology.instrument",
75
+ "topologyMatched": "hosting.topology.matched",
76
+ "topologyNoAudioInput": "hosting.topology.no-audio-input",
77
+ "topologyNoAudioOutput": "hosting.topology.no-audio-output",
78
+ "topologyPortTooWide": "hosting.topology.port-too-wide",
79
+ "topologyPortWithoutChannels": "hosting.topology.port-without-channels",
80
+ "topologyTooManyPorts": "hosting.topology.too-many-ports",
81
+ "topologyWiderThanStrip": "hosting.topology.wider-than-strip",
82
+ "workerNonconforming": "hosting.worker.nonconforming",
83
+ "idleHoldRefused": "hosting.idle.hold-refused",
84
+ } as const);
@@ -1,82 +1,21 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-or-later
2
2
  // Copyright (C) 2026 Pau Aliagas <linuxnow@gmail.com>
3
3
  /**
4
- * The hosting verdict's message codes. Codes, never prose: the
5
- * surface renders the operator's locale.
4
+ * The hosting verdict's message codes. Codes, never prose: the surface renders the operator's
5
+ * locale.
6
6
  *
7
- * A UNION list file: `.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.
7
+ * omx-clap-core declares every hosting code: one `HOSTING_<DIMENSION>_CODES` table per dimension,
8
+ * in the TypeScript modules its package installs under `pkg-config --variable=tsdir omx-clap-core`
9
+ * (BUILDING.md). {@link HOSTING_CODE} is those tables flattened into one `key: code` map, written
10
+ * as plain data into `hosting-codes.generated.ts` by `tools/gen-hosting-codes.mjs` at build time:
11
+ * a code omx-clap-core does not declare cannot be named here, and this module reads no file and
12
+ * imports no `node:` module, so every public subpath that reaches it bundles for a browser.
13
+ * `src/contract-vocabulary-conformance.test.ts` holds the generated map to omx-clap-core's real
14
+ * modules, either way.
11
15
  */
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
- stabilitySoakFailedAtRate: 'hosting.stability.soak-failed-at-rate',
19
- stabilityUnmeasured: 'hosting.stability.unmeasured',
20
- stabilityCrashUnattributed: 'hosting.stability.crash-unattributed',
21
- stabilitySpawnsThreads: 'hosting.stability.spawns-threads',
22
- stabilityLinksGuiToolkit: 'hosting.stability.links-gui-toolkit',
23
- rtSafetyClean: 'hosting.rt-safety.clean-under-sweep',
24
- rtSafetyViolations: 'hosting.rt-safety.violations-in-run',
25
- rtSafetyUnmeasured: 'hosting.rt-safety.unmeasured',
26
- rtSafetyNotReproducible: 'hosting.rt-safety.not-reproducible',
27
- // The note sweep: an instrument played under the interposer, a count of its own.
28
- rtSafetyNotesClean: 'hosting.rt-safety.clean-under-notes',
29
- rtSafetyNotesViolations: 'hosting.rt-safety.violations-under-notes',
30
- rtSafetyNotesUnmeasured: 'hosting.rt-safety.notes-unmeasured',
31
- featuresProvided: 'hosting.features.all-provided',
32
- featuresMissing: 'hosting.features.required-not-provided',
33
- featuresDisqualifying: 'hosting.features.host-not-provided',
34
- featuresCv: 'hosting.features.cv-ports',
35
- featuresMidiIn: 'hosting.features.midi-in-fed-empty',
36
- featuresUnscanned: 'hosting.features.scan-predates-field',
37
- costWithinCeiling: 'hosting.cost.within-ceiling',
38
- costAboveCeiling: 'hosting.cost.above-ceiling',
39
- costSpiky: 'hosting.cost.spiky-tail',
40
- costUnmeasured: 'hosting.cost.unmeasured-at-rate-and-quantum',
41
- costFieldsMissing: 'hosting.cost.fields-missing',
42
- latencyResolved: 'hosting.latency.resolved',
43
- latencyUnmeasured: 'hosting.latency.unmeasured',
44
- latencyNonconforming: 'hosting.latency.nonconforming',
45
- latencyParameterDependent: 'hosting.latency.parameter-dependent',
46
- topologyMatched: 'hosting.topology.matched',
47
- topologyNoAudioInput: 'hosting.topology.no-audio-input',
48
- topologyNoAudioOutput: 'hosting.topology.no-audio-output',
49
- topologyExtraInputs: 'hosting.topology.extra-inputs-fed-silence',
50
- topologyInstrument: 'hosting.topology.instrument',
51
- // The CLAP arm: the SAME dimensions fed from CLAP facts,
52
- // minted here where the LV2 table has no code for the fact.
53
- topologyWiderThanStrip: 'hosting.topology.wider-than-strip',
54
- clapLatencyMismatch: 'hosting.clap.latency-mismatch',
55
- clapNotAudioEffect: 'hosting.clap.not-audio-effect',
56
- clapHeadlessFailed: 'hosting.clap.headless-failed',
57
- clapNoteInput: 'hosting.clap.note-input',
58
- clapThreadViolation: 'hosting.clap.thread-violation',
59
- clapProcessError: 'hosting.clap.process-error',
60
- clapUnqualified: 'hosting.clap.unqualified',
61
- clapFormatNotHosted: 'hosting.clap.format-not-hosted',
62
- clapExtensionsNotCovered: 'hosting.clap.extensions-not-covered',
63
- outputNonFinite: 'hosting.output.non-finite',
64
- workerNonconforming: 'hosting.worker.nonconforming',
65
- // The pin (spec §11; one-contract §10.3): a host's refusal at add, by the daemon's code.
66
- integrityUnpinned: 'hosting.integrity.unpinned',
67
- integrityBinaryMismatch: 'hosting.integrity.binary-mismatch',
68
- integrityLayoutMismatch: 'hosting.integrity.layout-mismatch',
69
- overrideIsolated: 'hosting.override.curator-isolated',
70
- hostProvenanceDiffers: 'hosting.provenance.host-differs',
71
- sweepControlDidNotFire: 'hosting.provenance.sweep-control-did-not-fire',
72
- // The five REALISED reasons. Everything above answers what a plugin EARNED; these answer
73
- // what the console DID with that, and the difference between the two is the operator's
74
- // whole question when a slot is not on the fast path.
75
- disabled: 'hosting.disabled',
76
- unjudged: 'hosting.unjudged',
77
- noRealisation: 'hosting.no-realisation',
78
- realisedInProcess: 'hosting.realised.in-process',
79
- // A plugin LINKED into the engine (the static-link registry) runs in-process without a
80
- // verdict: the library's oracles are its proof.
81
- linked: 'hosting.linked',
82
- } as const;
16
+ import { HOSTING_CODE } from "./hosting-codes.generated.js";
17
+
18
+ export { HOSTING_CODE };
19
+
20
+ /** {@link HOSTING_CODE}'s type: each key's literal code. */
21
+ export type HostingCodeTable = typeof HOSTING_CODE;
@@ -148,12 +148,11 @@ export type HostingCode = (typeof HOSTING_CODE)[keyof typeof HOSTING_CODE];
148
148
  */
149
149
  export const CLAP_CORE_REFUSALS = {
150
150
  headlessFailed: "clapHeadlessFailed",
151
- notAudioEffect: "clapNotAudioEffect",
152
151
  noteInput: "clapNoteInput",
153
152
  noAudioInput: "topologyNoAudioInput",
154
153
  noAudioOutput: "topologyNoAudioOutput",
155
154
  widerThanStrip: "topologyWiderThanStrip",
156
- extraInputs: "topologyExtraInputs",
155
+ tooManyPorts: "topologyTooManyPorts",
157
156
  crashedLive: "stabilityCrashedLive",
158
157
  outputNonFinite: "outputNonFinite",
159
158
  } as const satisfies Record<string, keyof typeof HOSTING_CODE>;
@@ -164,12 +163,11 @@ export const CLAP_CORE_REFUSALS = {
164
163
  * refusal so the renderer holds no prose. A refusal without a line renders bare.
165
164
  */
166
165
  export const CLAP_CORE_REFUSAL_DESCRIPTIONS: { readonly [K in keyof typeof CLAP_CORE_REFUSALS]?: string } = {
167
- notAudioEffect: "the plugin is not an audio effect",
168
166
  noteInput: "the plugin declares a note input the configuration refuses",
169
167
  noAudioInput: "the plugin has no audio input",
170
168
  noAudioOutput: "the plugin has no audio output",
171
169
  widerThanStrip: "the plugin is wider than the strip",
172
- extraInputs: "the plugin has extra inputs, fed silence",
170
+ tooManyPorts: "the plugin has more ports than the auxiliary limit allows",
173
171
  crashedLive: "the plugin crashed live on this rig",
174
172
  outputNonFinite: "the plugin produced non-finite output",
175
173
  };
@@ -696,9 +694,6 @@ export function featureReasons(
696
694
  }),
697
695
  );
698
696
  }
699
- if ((features.cvPorts ?? 0) > 0) {
700
- out.push(reason("features", "objective", "unsuitable", HOSTING_CODE.featuresCv));
701
- }
702
697
  }
703
698
  if (descriptor.hasMidiIn && !instruments) {
704
699
  out.push(reason("features", "objective", "conditional", HOSTING_CODE.featuresMidiIn));
@@ -821,7 +816,7 @@ export function hostingTopologyReasons(
821
816
  ];
822
817
  }
823
818
  return [
824
- reason("topology", "objective", "conditional", HOSTING_CODE.topologyExtraInputs, { ins, outs }),
819
+ reason("topology", "objective", "conditional", HOSTING_CODE.topologyTooManyPorts, { ins, outs }),
825
820
  ];
826
821
  }
827
822
 
@@ -845,7 +840,7 @@ export function clapTopologyReasons(clap: PluginClapMeasurement | undefined, ins
845
840
  return [reason("topology", "objective", "unsuitable", HOSTING_CODE.topologyWiderThanStrip, { ins, outs })];
846
841
  }
847
842
  if (clap.sidechainInputs > 0) {
848
- return [reason("topology", "objective", "unsuitable", HOSTING_CODE.topologyExtraInputs, { ins, outs, sidechains: clap.sidechainInputs })];
843
+ return [reason("topology", "objective", "unsuitable", HOSTING_CODE.topologyTooManyPorts, { ins, outs, sidechains: clap.sidechainInputs })];
849
844
  }
850
845
  return [reason("topology", "objective", "suitable", HOSTING_CODE.topologyMatched, { ins, outs })];
851
846
  }
@@ -899,11 +894,6 @@ export function clapFeatureReasons(
899
894
  }
900
895
  const out: HostingReason[] = [];
901
896
  const offered = profile.clapExtensions;
902
- const kindHosted =
903
- clap.features.includes(CLAP_FEATURE_AUDIO_EFFECT) || (profile.instruments && clap.features.includes(CLAP_FEATURE_INSTRUMENT));
904
- if (!kindHosted) {
905
- out.push(reason("features", "objective", "unsuitable", HOSTING_CODE.clapNotAudioEffect, { features: clap.features.join(" ") }));
906
- }
907
897
  if (!clap.headlessOk) {
908
898
  out.push(reason("features", "objective", "unsuitable", HOSTING_CODE.clapHeadlessFailed));
909
899
  } else {
@@ -99,7 +99,7 @@ export interface MeasureOptions {
99
99
  * them: the provider's kinds (`--features`), its buf-size declarations (`--block-declares`) and its log
100
100
  * modes (`--log`). A conformance test reads both.
101
101
  */
102
- export const LV2_FEATURE_KINDS = ["map", "unmap", "options", "bounded", "worker", "default-state", "log"] as const;
102
+ export const LV2_FEATURE_KINDS = ["map", "unmap", "options", "bounded", "worker", "default-state", "log", "rtmempool"] as const;
103
103
  export const LV2_BLOCK_DECLARATIONS = ["bounded", "fixed", "pow2"] as const;
104
104
  export const LV2_LOG_MODES = ["silent", "route", "capture"] as const;
105
105
 
@@ -147,7 +147,7 @@ export interface MeasurementHost {
147
147
 
148
148
  /** How the latency figures in this document were taken. */
149
149
  export interface LatencyMethod {
150
- /** A {@link ../vocabulary.METHOD_TERMS} code — `impulse-onset` or `declared-port`. */
150
+ /** A {@link ../vocabulary.METHOD_TERMS} code — `impulse-onset`, `declared-port` or `note-onset`. */
151
151
  readonly method: string;
152
152
  readonly blockFrames: number;
153
153
  /** What was fed in, in words. An onset measured through an opened gate is not the same