Files
suaveplan/openspec/changes/enterprise-audio-codecs/tasks.md
2026-05-27 23:29:53 +01:00

8.0 KiB
Raw Blame History

Tasks: enterprise-audio-codecs

Change ID: enterprise-audio-codecs Package: @suaveplan/audio-codecs Target path: packages/media/audio/audio-codecs/ Prerequisite packages on Verdaccio: @suaveplan/audio-core, @suaveplan/audio-metadata, @suaveplan/streams, @suaveplan/error, @suaveplan/types


0. Pre-flight (non-negotiable)

  • 0.1 Confirm clean working tree (git status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in each)
  • 0.2 If any worktrees exist under .claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
  • 0.3 Confirm the package set in MANIFEST.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Scaffold

  • Create directory packages/media/audio/audio-codecs/ with src/, tests/, src/__benchmarks__/, and all feature subdirectories per the layout in proposal.md.
  • Write package.json at version 0.1.0 with "type": "module", "sideEffects": false, full exports map, deps: @suaveplan/audio-core workspace:^, @suaveplan/audio-metadata workspace:^, @suaveplan/streams workspace:^, @suaveplan/error workspace:^, @suaveplan/types workspace:^, zod catalog:forms, @opentelemetry/api catalog:observability.
  • Declare optional peer deps / optionalDependencies for WASM codec packages (@audio/decode-mp3, @audio/decode-flac, etc.).
  • Write tsconfig.json extending @suaveplan/typescript-config/library.json.
  • Write vitest.config.ts using createTestConfig() from @suaveplan/testing/config.
  • Add lint, typecheck, test, test:coverage, build, docs:check scripts.

2. Types + errors

  • Write src/registry/registry.types.ts — AudioCodec, DecodeResult, StreamingDecoder, StreamingEncoder, CodecRegistryOptions.
  • Write src/registry/registry.md.
  • Write src/decode/decode-source.types.ts — Source, DecodeSourceOpts, DecodeOpts.
  • Write src/decode/decode-source.md.
  • Write src/encode/encode-to.types.ts — Destination, EncodeOpts.
  • Write src/encode/encode-to.md.
  • Write src/stream/stream-decoder.types.ts — streaming decoder config types.
  • Write src/stream/stream-decoder.md.
  • Write src/stream/stream-encoder.types.ts — streaming encoder config types.
  • Write src/stream/stream-encoder.md.
  • Write src/error-codes.ts — AudioCodecErrorCode, AUDIO_CODEC_ERROR_METADATA, registerAudioCodecErrorCodes().
  • Write Zod schemas for every public options object (DecodeOptsSchema, EncodeOptsSchema, DecodeSourceOptsSchema, AudioCodecSchema, StreamingDecoderSchema, StreamingEncoderSchema).

3. Format detection

  • Implement detectSource(source) — read first HEADER_SIZE (256KB) bytes, resolve string sources to fetch() or fs/promises.readFile() based on runtime, return format + bytes/reader.
  • Integrate @suaveplan/audio-metadata magic-byte detection.
  • Write src/detect/detect-format.test.ts.
  • Write src/detect/detect-format.md.

4. CodecRegistry

  • Implement CodecRegistry.register() / unregister() / detect().
  • Implement CodecRegistry.decode(source, opts) — dispatch to codec, return AudioDocument.
  • Implement CodecRegistry.encode(doc, format, opts) — dispatch to codec, return Uint8Array.
  • Implement CodecRegistry.decodeStream() — ReadableStream<Uint8Array> → ReadableStream<AudioPage> via StreamingDecoder.
  • Implement CodecRegistry.encodeStream() — AudioDocument → ReadableStream<Uint8Array> via StreamingEncoder.
  • Wire OTel span on codec.decode / codec.encode + counters + histograms.
  • Write src/registry/registry.test.ts.
  • Write src/registry/registry.md.

5. Built-in codecs (native)

  • Implement WavCodec — native WAV decode/encode with streaming support, no external deps.
  • Write src/codecs/wav.test.ts — round-trip, header validation, streaming chunks.
  • Write src/codecs/wav.md.
  • Implement AiffCodec — native AIFF decode (no encode), no external deps.
  • Implement CafCodec — native CAF decode (no encode), no external deps.

6. Built-in codecs (WASM wrappers)

  • Implement Mp3Codec — dynamic import('@audio/decode-mp3') wrapper, decode + encode + streaming.
  • Write src/codecs/mp3.test.ts using deterministic fixtures (no network mocks).
  • Write src/codecs/mp3.md.
  • Implement FlacCodec — dynamic import('@audio/decode-flac') wrapper, decode + encode + streaming.
  • Write src/codecs/flac.test.ts.
  • Write src/codecs/flac.md.
  • Implement OggVorbisCodec, OpusCodec, AacCodec, WebMCodec, QoaCodec — decode wrappers with dynamic imports, optional streaming where supported.

7. Universal decode entry point

  • Implement decodeSource(source, opts) — resolve source, detect format, dispatch to CodecRegistry.decode, support notify / ondata callbacks, page pre-allocation, memory budget.
  • Implement source resolution rules: http/https/data/blob → fetch(); file: → fs/promises.readFile(); other string → runtime-determined.
  • Write src/decode/decode-source.test.ts.
  • Write src/decode/decode-source.md.

8. Universal encode entry point

  • Implement encodeTo(doc, destination, format, opts) — encode then write to file path, WritableStream, or callback.
  • Write src/encode/encode-to.test.ts.
  • Write src/encode/encode-to.md.

9. Progressive streaming decode

  • Implement progressiveDecode() — fill AudioDocument.pages as chunks arrive via StreamingDecoder or fallback full-decode.
  • Implement pageAccumulator() — push decoded PCM into pages, call notify on first page, call ondata per block.
  • Return usable AudioDocument immediately (before decode completes) with edits extending as pages arrive.
  • Write src/stream/stream-decoder.test.ts.
  • Write src/stream/stream-decoder.md.

10. Stream encoder

  • Implement createEncodeTransform() — TransformStream<Float32Array[], Uint8Array> using StreamingEncoder.
  • Write src/stream/stream-encoder.test.ts.
  • Write src/stream/stream-encoder.md.

11. Memory budget

  • Implement memory budget enforcement in decodeSource and progressiveDecode — project bytes from sampleRate × channels × duration × 4 and throw AudioCodecError(AUDIO_CODECS.BUDGET_EXCEEDED) if over budget.
  • Write budget tests in decode-source.test.ts.

12. Benchmarks

  • Write src/__benchmarks__/decode.bench.ts — decode 5-minute stereo WAV (native) and MP3 (WASM).
  • Write src/__benchmarks__/encode.bench.ts — encode 5-minute stereo WAV and FLAC.
  • Write src/__benchmarks__/stream-decode.bench.ts — progressive stream decode 5-minute MP3.

13. Integration + property tests

  • Write tests/integration.test.ts — full round-trip: decodeSource → run edits → encodeTo → file written.
  • Write tests/property.test.ts (fast-check) — decode-then-encode round-trip for WAV/FLAC yields identical PCM; detect returns correct format for known magic bytes; streaming decode total samples equals non-streaming decode total samples.

14. Docs + verify + publish

  • All co-located .md files (≥200 words each).
  • Write README.md at package root.
  • bunx turbo lint typecheck test --filter=@suaveplan/audio-codecs — green.
  • bunx turbo test:coverage --filter=@suaveplan/audio-codecs — 100%.
  • bun scripts/publish.ts --package @suaveplan/audio-codecs.
  • Fold specs/audio-codecs/spec.md into openspec/specs/audio-codecs/spec.md.
  • Archive change folder to openspec/archive/2026-05-14-enterprise-audio-codecs/.