6.2 KiB
Tasks: enterprise-react-audio-foundation
Change ID: enterprise-react-audio-foundation
Package: @suaveplan/react-audio
Target path: packages/react/react-audio
Phase: 1 of 3 (foundation)
Prerequisite packages on disk: @suaveplan/audio-engine-browser, @suaveplan/audio-fx-plugin-base, @suaveplan/audio-controllers, @suaveplan/audio-timeline, @suaveplan/audio-transport, @suaveplan/events, @suaveplan/error, @suaveplan/types, @suaveplan/opentelemetry — all shipped at v0.1.0.
Phase 1 ships provider + hooks + stores + error codes + a11y helpers + hooks-only subpath exports. Components are Phase 2 (
enterprise-react-audio-components). MIDI learn-mode panel is Phase 3 (enterprise-react-audio-midi).
1. Scaffold
- 1.1 Create directory tree per
proposal.md"File layout" —src/{provider,hooks,stores,a11y,error-codes}plustests/. - 1.2 Write
package.jsonat0.1.0,"type": "module","sideEffects": false, foundation-scopedexportsmap (root +./provider,./hooks,./hooks/<name>per hook,./stores,./a11y,./error-codes). NO./componentsentry yet — reserved for Phase 2. React 19 peer dep. - 1.3 Write
tsconfig.json,tsconfig.lib.json,vite.config.ts,vitest.config.ts(happy-dom). Scripts.
2. Error codes
- 2.1
src/error-codes/error-codes.ts—defineCodes("REACT_AUDIO", [...]),ReactAudioErrorextendingSuaveplanErrorfrom@suaveplan/error. ExportReactAudioErrorCode,REACT_AUDIO_ERROR_METADATA,registerReactAudioErrorCodes(). Codes:NO_PROVIDER,PROVIDER_PENDING,UNKNOWN_PARAMETER,PRESET_STORE_FAILED,INVALID_OPTIONS. - 2.2
error-codes.test.ts+error-codes.md— every code reachable from a real call site (asserted).
3. Provider
- 3.1
src/provider/provider-options.ts+.schemas.ts— ZodAudioProviderOptionsSchemacovering adapter context, transport options, plugin auto-registration, preset store, sample rate, slot renderers (just typed — components consume them in Phase 2). - 3.2
src/provider/audio-provider.tsx— strict-mode-safe lifecycle; async init withAbortSignal; double-invoke proof; dispose on unmount; React context exposes{ state: ProviderState | null, version: number }. - 3.3
audio-provider.test.tsx— strict-mode double-invoke produces exactly one engine; unmount disposes adapter + transport + bus registry exactly once; hook-outside-provider throwsREACT_AUDIO.NO_PROVIDER. - 3.4
audio-provider.md.
4. Hooks (per src/hooks/<hook-name>/<hook-name>.{ts,test.tsx,md})
For each hook: implement with useSyncExternalStore, write .test.tsx covering happy + error + SSR-snapshot equality, and .md (≥200 words).
- 4.1
use-engine-state—'pending' | 'ready' | 'error'. - 4.2
use-audio-engine— returnsProviderState. - 4.3
use-timeline—TimelineEngineaccessor + track CRUD. - 4.4
use-transport—Transportaccessor + play/pause/stop/seek/setTempo. - 4.5
use-transport-time— frame-rate-sampled current time. - 4.6
use-track(trackId)— track state + per-track chain accessor. - 4.7
use-plugin-chain(trackId)— chain composition + events. - 4.8
use-chain-plugin(trackId, slotIndex)— slot accessor. - 4.9
use-audio-fx-plugin(pluginId)— plugin by id from registry. - 4.10
use-parameter(plugin, paramId)— value + set + curve + scheduleAt; throwsREACT_AUDIO.UNKNOWN_PARAMETERfor unknown ids. - 4.11
use-preset(plugin)— plugin-level preset save/load. - 4.12
use-chain-snapshot(chain)— chain-level snapshot save/load via preset store. - 4.13
use-bus(name)— bus accessor.
5. Stores
- 5.1
src/stores/audio-store/audio-store.ts— provider-scoped Zustand store with override hook. Slim shape; no playback state (that's on transport). - 5.2
src/stores/ui-store/ui-store.ts— panels, theme, visualization mode, selectedTrackId, dragState. Ports verbatim from main.
6. A11y helpers (non-component-level, foundation-only)
- 6.1
src/a11y/keyboard-bindings/keyboard-bindings.ts— central keymap export consumed by Phase-2 component knobs/sliders. Pure value table. - 6.2
src/a11y/aria-value-text/aria-value-text.ts— unit-awareformatAriaValueText(value, unit, min, max)formatter. Pure function. - 6.3
src/a11y/index.ts— explicit named re-exports.
7. Root barrel + tests
- 7.1
src/index.ts— explicit named re-exports of provider, every hook, both stores, a11y helpers, error-codes surface (rule 10). - 7.2
tests/purity.test.ts— builtdist/references noAudioContext/OfflineAudioContext/AudioNode/node:*/process/Buffer; runtimedependenciesmatch the foundation allowlist. - 7.3
tests/exports.test.ts— every Phase-1 surface has a subpath entry;./componentsis NOT declared.
8. Documentation
- 8.1 Sibling
.mdfor every.ts/.tsx(except test/types/index — per CLAUDE.md), ≥200 words. - 8.2
README.mdat package root with quick-start + hook examples + Phase-2/Phase-3 forward-reference.
9. Pre-archive verification (5-gate triple)
- 9.1
bun run check— Biome 0/0/0/0 monorepo-wide. - 9.2
bunx turbo lint typecheck test --filter=@suaveplan/react-audio— green. - 9.3
bunx turbo test:coverage --filter=@suaveplan/react-audio— 100/100/100/100. - 9.4
bun scripts/openspec-conformance.ts @suaveplan/react-audio— PASS, exit 0. - 9.5
bunx turbo build --filter=@suaveplan/react-audio— green. - 9.6
bun scripts/docs-check.ts packages/react/react-audio— 100%.
10. Archive
- 10.1 Fold the foundation spec delta into NEW
openspec/specs/react-audio/spec.md(Phase 1 shipped requirements). The canonical spec will grow with Phase 2 and Phase 3 — leave room. - 10.2
git mv openspec/changes/enterprise-react-audio-foundation openspec/archive/2026-05-19-enterprise-react-audio-foundation. - 10.3 Refresh
MANIFEST.mdviabun scripts/folder-reorg/generate-manifest.ts.STATUS.mdrow for@suaveplan/react-audiostays at–(Phase 1 alone is incomplete relative to the proposal's full scope) — flip to ✅ only when Phase 3 lands. Note Phase 1 progress in the description column ("Phase 1 of 3 shipped: hooks + provider").