Files
suaveplan/openspec/archive/2026-05-19-split-audio-plugins-fx/proposal.md
2026-05-27 23:29:53 +01:00

114 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Split `audio-plugins-fx` into `audio-fx-plugin-*` per-plugin packages
**Package**: `@suaveplan/audio-fx-plugin-*` (new), `@suaveplan/audio-fx-plugin-base` (new), `@suaveplan/audio-plugins-fx` (removed)
**Status**: PROPOSED
**Type**: refactor
## Why
`audio-plugins-fx` currently bundles the contract, `PluginChain`, `PluginRegistry`, `ChainSnapshot`, and 24 concrete plugins into a single package. Subpath exports give bundlers tree-shaking, but consumers still pay the full peer-dep surface, every plugin advances on the same version, and the dep graph in `MANIFEST.md` cannot distinguish a chain consumer from a reverb consumer.
Every other plugin family in the repo is split per-plugin: `network-plugin-*`, `drive-plugin-chunks`, `i18n-formatter-*`, `i18n-backend-*`, the recently-shipped `audio-ir-{family}-plugin-<name>`. Audio FX is the outlier. Splitting now — while still at `0.1.0` and before downstream consumers (`audio-engine-browser`, `react-audio`) wire against it — avoids a much larger rename later and matches the established convention.
### Naming
The new family uses the `audio-fx-plugin-*` prefix (`fx` is the family marker, distinguishing this from future `audio-ir-*`, `audio-synth-*`, etc. plugin families):
- `@suaveplan/audio-fx-plugin-base` — foundation (contract + chain + registry + error codes).
- `@suaveplan/audio-fx-plugin-<name>` — one package per concrete FX plugin.
## What changes
### Foundation (1 package)
- ADDED: `@suaveplan/audio-fx-plugin-base` — `AudioFxPlugin` contract, `AbstractAudioFxPlugin`, `PluginChain`, `ChainContext`, `AmbientState`, `BusRegistry`, `PluginRegistry`, `ChainSnapshot`, `ChainEventBus`, `ParameterSchedule`, `PresetSnapshot`, shared `AUDIO_PLUGIN.*` error-code registry. Universal-tier; consumes `@suaveplan/{audio-core, audio-transport, error, events, opentelemetry, schema-registry, types}` but has zero `@suaveplan/audio-fx-plugin-*` deps. Owns every file in the current `audio-plugins-fx/src/{base,chain,registry}/` trees plus the root `error-codes.ts`.
### Per-plugin packages (24 packages)
Each new package owns one folder from `audio-plugins-fx/src/plugins/<source-dir>/` verbatim — no DSP, parameter, or error-code changes.
| New package | Source dir | One-line description |
|---|---|---|
| `audio-fx-plugin-audio-analyzer` | `audio-analyzer/` | tap-point FFT analyser |
| `audio-fx-plugin-audio-player` | `audio-player/` | playback source plugin |
| `audio-fx-plugin-auto-filter` | `auto-filter/` | LFO-driven filter |
| `audio-fx-plugin-chorus` | `chorus/` | modulated comb / multi-voice chorus |
| `audio-fx-plugin-compressor` | `compressor/` | feed-forward compressor with sidechain input |
| `audio-fx-plugin-delay` | `delay/` | stereo delay with ping-pong + BPM sync |
| `audio-fx-plugin-distortion` | `distortion/` | waveshaping + soft/hard clip |
| `audio-fx-plugin-eq` | `eq/` | parametric / shelving / high-pass / low-pass EQ |
| `audio-fx-plugin-filter` | `filter/` | biquad / IIR / state-variable |
| `audio-fx-plugin-flanger` | `flanger/` | short-delay modulated comb |
| `audio-fx-plugin-game-audio` | `game/` | game-audio orchestration plugin (impl filename is `game-audio.ts`) |
| `audio-fx-plugin-gate` | `gate/` | noise gate with hold + linked envelope |
| `audio-fx-plugin-glados` | `glados/` | pitch-tracked vocal-style effect |
| `audio-fx-plugin-limiter` | `limiter/` | look-ahead brick-wall limiter |
| `audio-fx-plugin-microphone` | `microphone/` | source plugin simulating mic colour |
| `audio-fx-plugin-mixer` | `mixer/` | bus mixer with per-channel sends |
| `audio-fx-plugin-phaser` | `phaser/` | all-pass-bank phaser |
| `audio-fx-plugin-pitch-shifter` | `pitch-shifter/` | phase-vocoder pitch shift |
| `audio-fx-plugin-reverb` | `reverb/` | engine-pluggable reverb (FDN, Schroeder, convolution) |
| `audio-fx-plugin-sampler` | `sampler/` | sample-playback source |
| `audio-fx-plugin-spatial` | `spatial/` | HRTF-based spatial panner |
| `audio-fx-plugin-synthesizer` | `synthesizer/` | polyphonic subtractive synth |
| `audio-fx-plugin-tremolo` | `tremolo/` | amplitude-modulated tremolo |
| `audio-fx-plugin-widener` | `widener/` | stereo width / mid-side processor |
### Scaffold script (one new tooling file)
- ADDED: `scripts/split-audio-fx-plugin/scaffold.ts` — reusable helper that takes a plugin slug (`eq`, `reverb`, …) plus the source dir name, copies a per-plugin template (config files), `git mv`s the source folder, and rewrites imports. The script is committed alongside the split so the same scaffold can be reused for future `audio-fx-plugin-*` additions.
### Removals + repo plumbing
- REMOVED: `@suaveplan/audio-plugins-fx` — superseded. Package is at `0.1.0`, never published to Verdaccio. Folder `packages/audio/audio-plugins-fx/` is deleted after the moves.
- MODIFIED: `MANIFEST.md` — refreshed via `bun scripts/folder-reorg/generate-manifest.ts`.
- MODIFIED: `STATUS.md` — replace the single `audio-plugins-fx` ✅ row with 25 new ✅ rows (1 base + 24 per-plugin). Net `Migrated` count delta: +24.
- MODIFIED: `CLAUDE.md` § Packages (audio family subsection) — replace the bundled listing with the per-plugin enumeration.
- MODIFIED: `scripts/folder-reorg/assignment.yml` — every new `audio-fx-plugin-*` package maps to the `audio` plugin slot; remove the `audio-plugins-fx` entry.
## Out of scope
- New plugins beyond what `audio-plugins-fx` already ships — additions land as their own packages directly under the new layout.
- React / R3F bindings for plugins — remain in `react-audio-*` packages, untouched.
- Engine-pluggable backends (e.g. a future `audio-fx-plugin-reverb-convolution`) — possible future split, not in this change.
- Changing any plugin's runtime behavior, DSP, parameter surface, or error codes. This is a pure relocation + dep-graph refactor.
## Source mapping
> Genesis-original — no `enterprise/audio-plugins-fx.md` exists. Section retained as a pointer to the in-tree source.
| Requirement | Source path | Notes |
|---|---|---|
| base contract + chain + registry surface | `packages/audio/audio-plugins-fx/src/{base,chain,registry}/` | Moves verbatim into `audio-fx-plugin-base` |
| each concrete plugin | `packages/audio/audio-plugins-fx/src/plugins/<source-dir>/` | Moves verbatim into `audio-fx-plugin-<slug>` per the table above |
| shared error codes | `packages/audio/audio-plugins-fx/src/error-codes.ts` | Moves into `audio-fx-plugin-base/src/error-codes/`; concrete plugins import codes from `@suaveplan/audio-fx-plugin-base/error-codes` |
### Requirements with no source line
| Requirement | Justification |
|---|---|
| per-package independent versioning | New constraint introduced by the split; each plugin can graduate to `1.0.0` on its own cadence |
| `audio-fx-plugin-base` cannot depend on any `audio-fx-plugin-<name>` | Prevents cycles in the dep graph that the bundled package masked |
| scaffold script committed to `scripts/split-audio-fx-plugin/` | Future additions to the FX family use the same template — keeps shape consistent across plugins |
## Verification approach
- Per-plugin: each new package keeps its existing test suite intact under the new path. `bunx turbo test --filter=@suaveplan/audio-fx-plugin-<name>` green at 100/100/100/100 for every plugin.
- `audio-fx-plugin-base/tests/dep-graph.test.ts` — asserts the package has zero `@suaveplan/audio-fx-plugin-*` deps (other than its own name, which would be a self-dep and is also rejected).
- `scripts/folder-reorg/generate-manifest.ts` after the split — `MANIFEST.md` diff shows 25 new packages and no orphaned `audio-plugins-fx` entry.
- `rg "@suaveplan/audio-plugins-fx" packages/ apps/ scripts/ docs/ openspec/changes/ openspec/specs/` — zero hits after the migration commit (archive is allowed).
- `bunx turbo build` whole monorepo — green; catches any missed import rewrite.
## Migration impact
- **In-monorepo consumers**: `rg "@suaveplan/audio-plugins-fx" packages/ apps/` returns hits only inside the package itself (no downstream package has wired it up yet — `audio-engine-browser` and `react-audio` are still `–` in STATUS.md). The split lands before first consumer.
- **External consumers**: none — package is `0.1.0` and was never published to Verdaccio.
## Impact
### Affected dependents
Run `bunx turbo ls --filter=...@suaveplan/audio-plugins-fx` to enumerate. At proposal time, the only dependent is `audio-plugins-fx`'s own test suite — no other live package imports it. The placeholder `audio-engine-browser` is still status `–` (not yet ported) and will compose against `audio-fx-plugin-*` directly when it lands.
- No live dependents (verified by `bunx turbo ls --filter=...@suaveplan/audio-plugins-fx`).