# OpenSpec Change: enterprise-audio-engine-server - **Change ID**: enterprise-audio-engine-server - **Package**: `@suaveplan/audio-engine-server` - **Category**: server - **Target path**: `packages/media/audio/audio-engine-server` - **Status**: PROPOSED - **Date**: 2026-05-14 - **Design**: `./design.md` - **Spec delta**: `./specs/audio-engine-server/spec.md` --- ## Summary Deliver `@suaveplan/audio-engine-server` on the genesis branch from scratch at enterprise quality. Server-side file I/O adapters for the genesis audio ecosystem. This package is **intentionally thin** — all DSP, planning, streaming, and timeline logic lives in universal packages (`audio-core`, `audio-timeline`, `audio-transport`). This package only provides file source adapters, file sink adapters, and a server timeline adapter that renders to file output instead of speakers. Batch processing is universal (uses `@suaveplan/queue` + `@suaveplan/streams`) and is documented in the master spec as a cross-cutting pattern, not a server-specific feature. --- ## Current-state audit (genesis) The package does not exist on genesis yet — this is a fresh build. No prior source, no test results, no coverage data. The old `enterprise-browser-audio-engine` was browser-only and had no server equivalent. --- ## Available workspace deps on genesis | Package | Role | |---|---| | `@suaveplan/audio-core` (`workspace:^`) | `AudioDocument`, `PlanEngine`, `ProcessPipeline` | | `@suaveplan/audio-timeline` (`workspace:^`) | `TimelineEngine`, `Track`, `Clip` | | `@suaveplan/audio-transport` (`workspace:^`) | `Transport` | | `@suaveplan/audio-codecs` (`workspace:^`) | `CodecRegistry`, `encodeTo`, `decodeSource` | | `@suaveplan/streams` (`workspace:^`) | `TransformStream`, `ReadableStream`, `WritableStream` primitives | | `@suaveplan/error` (`workspace:^`) | `SuaveplanError` + `defineCodes`; the package exports `AudioEngineServerError` (extending `AudioError` from `@suaveplan/audio-core`) carrying `AUDIO_ENGINE_SERVER.*` codes. | | `@suaveplan/types` (`workspace:^`) | `LoggerLike` | | `@suaveplan/opentelemetry` (`workspace:^`) | OTel spans, counters, histograms | | `zod` (`catalog:forms`) | Zod schemas for option types | Dev deps: `vitest`, `@vitest/coverage-istanbul`, `fast-check`, `mitata` (all `catalog:testing`). --- ## Public API ### Core types ```ts interface FileSourceOptions { fs?: FsLike // injectable fs adapter for testing fetch?: typeof fetch // injectable fetch for URLs } class FileSourceAdapter { constructor(options?: FileSourceOptions) async load(path: string, opts?: LoadOptions): Promise createReadStream(path: string): ReadableStream async loadMany(paths: string[], opts?: LoadOptions): Promise } interface FileSinkOptions { fs?: FsLike } class FileSinkAdapter { constructor(options?: FileSinkOptions) async save(doc: AudioDocument, path: string, format: string, opts?: SaveOptions): Promise createWriteStream(path: string): WritableStream async saveMany(docs: AudioDocument[], pathFormat: string): Promise } interface ServerAdapterOptions { outputPath?: string format?: string sampleRate?: number ppqn?: number } class ServerTimelineAdapter implements TimelineAdapter { constructor(options?: ServerAdapterOptions) init(options?: { signal?: AbortSignal }): Promise setTracks(tracks: Track[]): void addTrack(track: Track): void removeTrack(trackId: string): void updateTrack(trackId: string, track: Track): void play(time?: number): void pause(time?: number): void stop(time?: number): void seek(time: number): void getCurrentTime(): number setLoop(start: number, end: number, enabled: boolean): void setMasterVolume(volume: number): void setTrackVolume(trackId: string, volume: number): void setTrackMute(trackId: string, muted: boolean): void setTrackSolo(trackId: string, soloed: boolean): void setTrackPan(trackId: string, pan: number): void setTempo(bpm: number, atTick?: number): void ticksToSeconds(ticks: number): number secondsToTicks(seconds: number): number readonly lookAhead: 0 readonly ppqn: number dispose(): void /** Extra non-interface method: render the timeline to a finite output, useful for non-realtime export. */ render(options?: { startTime?: number; endTime?: number; signal?: AbortSignal }): Promise } ``` --- ## New additions for genesis 1. **Injectable fs adapter** — `FileSourceAdapter` and `FileSinkAdapter` accept an `FsLike` interface for testing without touching the real filesystem. 2. **Streaming load/save** — `createReadStream` / `createWriteStream` return WHATWG streams compatible with `@suaveplan/streams`. 3. **Batch helpers** — `loadMany` / `saveMany` for bulk operations. 4. **Server playback = render** — `ServerTimelineAdapter.play()` renders as fast as possible via `audio-core.streamPlan()` piped to codec encoder and file sink. 5. **OTel instrumentation** — spans for `file.load`, `file.save`, `server.render`, counters for bytes read/written. 6. **Property-based tests** — load then save → file exists, round-trip preserves sample count. 7. **Benchmark suite** — load 100MB WAV, save 100MB FLAC. --- ## File layout ```text packages/media/audio/audio-engine-server/ package.json tsconfig.json vitest.config.ts src/ index.ts error-codes.ts source/ file-source.ts file-source.md file-source.types.ts file-source.test.ts index.ts sink/ file-sink.ts file-sink.md file-sink.types.ts file-sink.test.ts index.ts adapter/ server-adapter.ts server-adapter.md server-adapter.types.ts server-adapter.test.ts index.ts __benchmarks__/ io.bench.ts tests/ integration.test.ts property.test.ts ``` --- ## Impact **Breaking changes**: none — fresh build, package does not yet exist on genesis. **Prerequisites**: `@suaveplan/audio-core`, `@suaveplan/audio-timeline`, `@suaveplan/audio-transport`, `@suaveplan/audio-codecs`, `@suaveplan/streams`, `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/opentelemetry` published to Verdaccio. **No universal package may depend on this.** It is a leaf node in the dependency graph. ### Affected dependents `@suaveplan/audio-engine-server` is a leaf node — its only downstream consumer is the audio CLI, which does not exist on genesis yet (it lands in a later change in this same batch). It is created against this package's spec delta, not retrofitted. - `@suaveplan/audio-cli` — [ ] tests run / [ ] docs reviewed / [x] no action needed (reason: does not exist yet; created in `enterprise-audio-cli`) Once landed, any subsequent change to this package's public surface MUST run `bunx turbo test --filter=...@suaveplan/audio-engine-server` per `openspec/AGENTS.md` §13 step 10.