Files
2026-05-27 23:29:53 +01:00

7.1 KiB

Capability Spec: audio-plugins-fx (split)

Package: @suaveplan/audio-fx-plugin-base + 24 @suaveplan/audio-fx-plugin-<name> packages Status: SHIPPED — bundled package retired 2026-05-19 via split-audio-plugins-fx

Every requirement carries a **Verified by:** line.

Predecessor history: this capability previously lived inside the single @suaveplan/audio-plugins-fx package (folded from enterprise-audio-plugins-fx and extend-audio-plugins-fx-enterprise-extras). On 2026-05-19 the package was split into 25 per-plugin packages. The split itself is documented in openspec/archive/2026-05-19-split-audio-plugins-fx/. The retired bundled spec lives at that archive's specs/audio-plugins-fx/spec.md.

Family layout

The audio FX capability is distributed across 25 packages: one foundation (@suaveplan/audio-fx-plugin-base) plus one per concrete FX plugin (@suaveplan/audio-fx-plugin-<name>). The previously-bundled @suaveplan/audio-plugins-fx was deleted on 2026-05-19.

Requirements

Requirement: Per-plugin package layout

The audio FX capability SHALL be distributed as one foundation package (@suaveplan/audio-fx-plugin-base) plus one package per concrete plugin (@suaveplan/audio-fx-plugin-<name>). No package SHALL bundle more than one plugin's DSP, parameter surface, or registry entry.

Scenario: each plugin is independently installable

  • WHEN a consumer adds only @suaveplan/audio-fx-plugin-eq to its package.json
  • THEN bun install resolves audio-fx-plugin-eq + audio-fx-plugin-base + their transitive deps only, and no other audio-fx-plugin-* package appears in the lockfile

Scenario: each plugin advances independently

  • WHEN audio-fx-plugin-eq graduates to 1.0.0 while audio-fx-plugin-reverb remains at 0.1.0
  • THEN both packages publish from the same monorepo commit without forcing the other to bump

Verified by: packages/audio-fx/audio-fx-plugin-base/tests/dep-graph.test.ts::"declares no @suaveplan/audio-fx-plugin-* deps" AND each plugin's own test suite green at 100/100/100/100


Requirement: audio-fx-plugin-base owns the shared contract surface

@suaveplan/audio-fx-plugin-base SHALL export the AudioFxPlugin contract, AbstractAudioFxPlugin, PluginChain, ChainContext, AmbientState, BusRegistry, PluginRegistry, ChainSnapshot, ChainEventBus, ParameterSchedule, PresetSnapshot, the biquad DSP helpers (computeBiquad, BiquadCoeffs, BiquadState, BiquadBand, BiquadBandType, identityCoeffs), and the shared AUDIO_FX.* error-codes registry. No concrete plugin SHALL re-implement these primitives.

Scenario: contract is the single source of truth

  • WHEN a new plugin package is authored
  • THEN it imports AbstractAudioFxPlugin from @suaveplan/audio-fx-plugin-base/base and never declares its own variant

Scenario: chain composition stays in base

  • WHEN a consumer composes audio-fx-plugin-eq, audio-fx-plugin-compressor, and audio-fx-plugin-reverb into a chain
  • THEN it imports PluginChain from @suaveplan/audio-fx-plugin-base/chain and the chain accepts every plugin without per-plugin glue

Scenario: biquad is shared, not duplicated

  • WHEN audio-fx-plugin-eq, audio-fx-plugin-filter, and any other biquad-consuming plugin compute filter coefficients
  • THEN each imports computeBiquad from @suaveplan/audio-fx-plugin-base/biquad (not from a sibling plugin)

Verified by: packages/audio-fx/audio-fx-plugin-base/src/chain/chain.test.ts::"PluginChain accepts any AudioFxPlugin implementation" AND packages/audio-fx/audio-fx-plugin-base/src/registry/registry.spec-conformance.test.ts::"PluginRegistry register/has/create/list invariants" AND packages/audio-fx/audio-fx-plugin-base/src/biquad/biquad.test.ts::"emits finite coefficients for low-shelf, high-shelf, low-pass, high-pass, peaking"


Requirement: audio-fx-plugin-base is dep-graph-isolated from concrete plugins

@suaveplan/audio-fx-plugin-base SHALL declare zero dependencies on any @suaveplan/audio-fx-plugin-* package. The dependency arrow MUST point only from concrete plugins to base, never base to plugins.

Scenario: no upward dep

  • WHEN CI runs tests/dep-graph.test.ts in audio-fx-plugin-base
  • THEN the test fails if package.json contains any @suaveplan/audio-fx-plugin-* entry other than audio-fx-plugin-base itself (which would be a self-dep and is also rejected)

Verified by: packages/audio-fx/audio-fx-plugin-base/tests/dep-graph.test.ts::"declares no @suaveplan/audio-fx-plugin-* deps"


Requirement: Error-code namespace preserved across the split

Every error code that existed in @suaveplan/audio-plugins-fx SHALL remain reachable under the same AUDIO_FX.* namespace from @suaveplan/audio-fx-plugin-base/error-codes. Concrete plugins SHALL throw the existing codes via AudioFxError constructed from the same registry. No code SHALL be renamed, removed, or re-numbered by the split.

Scenario: shared codes resolve from base

  • WHEN a caller imports AUDIO_FX_CODES.CHAIN_SLOT_NOT_FOUND from @suaveplan/audio-fx-plugin-base/error-codes
  • THEN the value is byte-identical to the value previously exported from @suaveplan/audio-plugins-fx/error-codes

Verified by: packages/audio-fx/audio-fx-plugin-base/src/error-codes/error-codes.test.ts — every AUDIO_FX_CODES entry preserved and thrown by at least one production path


Requirement: Migration leaves zero references to the old package

After this change ships, no file under packages/, apps/, scripts/ (excluding scripts/split-audio-fx-plugin/), docs/, openspec/changes/, or openspec/specs/ SHALL import from @suaveplan/audio-plugins-fx. The old package directory SHALL be deleted. References inside openspec/archive/ are immutable history and are exempt; the scripts/split-audio-fx-plugin/ scaffold script may reference the old name as part of its rewrite logic.

Scenario: no stale imports

  • WHEN CI runs rg "@suaveplan/audio-plugins-fx" packages/ apps/ scripts/ docs/ openspec/changes/ openspec/specs/ | grep -v scripts/split-audio-fx-plugin
  • THEN the command exits with status 1 (no matches found)

Scenario: old package directory removed

  • WHEN the post-archive tree is inspected
  • THEN packages/audio/audio-plugins-fx/ does not exist

Verified by: bunx turbo build whole-monorepo green (catches missed import rewrites) AND test ! -d packages/audio/audio-plugins-fx

Historical: bundled package

The bundled @suaveplan/audio-plugins-fx@0.1.0 (~503 tests, 100% coverage, 24 plugins under src/plugins/) was retired on 2026-05-19 in favour of this per-plugin layout. The bundled error-code registry, plugin contract, chain primitive, and registry now live in @suaveplan/audio-fx-plugin-base; every concrete plugin moved verbatim into its own package, with the shared biquad DSP math promoted to @suaveplan/audio-fx-plugin-base/biquad. No DSP, parameter, or error-code changes accompanied the split. See openspec/archive/2026-05-19-split-audio-plugins-fx/ for the full proposal + tasks.