# Tasks: enterprise-audio-codec-ffmpeg-native Implementation checklist for `@suaveplan/audio-codec-ffmpeg-native`. Tasks are ordered; each section must be complete before the next begins. Every `### Requirement` and `#### Scenario` from `specs/audio-codec-ffmpeg-native/spec.md` is enumerated in §0c working notes and checked off as implementation lands. --- ## 1. Scaffold - [ ] Create `packages/media/audio/audio-codec-ffmpeg-native/` with `package.json`, `tsconfig.json`, `vitest.config.ts` (use `createTestConfig()` from `@suaveplan/testing/config`; `pool: 'forks'`). - [ ] `package.json` declares `"engines": { "node": ">=20" }`, no `"browser"` field, `"sideEffects": false`, `"type": "module"`, and `version: "0.1.0"`. - [ ] `dependencies`: `@suaveplan/audio-codecs workspace:^`, `@suaveplan/audio-core workspace:^`, `@suaveplan/streams workspace:^`, `@suaveplan/error workspace:^`, `@suaveplan/types workspace:^`, `fluent-ffmpeg catalog:audio`, `zod catalog:forms`, `@opentelemetry/api catalog:observability`, `tslib catalog:build`. - [ ] `optionalDependencies`: `@ffmpeg-installer/ffmpeg catalog:audio`. - [ ] `devDependencies`: `@suaveplan/testing workspace:^`, `vitest catalog:testing`, `@vitest/coverage-istanbul catalog:testing`, `fast-check catalog:testing`, `mitata catalog:testing`, `@types/fluent-ffmpeg catalog:types`. - [ ] If `fluent-ffmpeg`, `@ffmpeg-installer/ffmpeg`, or `@types/fluent-ffmpeg` are not yet in the root `package.json` catalog block, add them under `catalog.audio` / `catalog.types` and re-run `bun install`. - [ ] Declare subpath `exports` for every feature module listed in `proposal.md` "Module layout" plus the root barrel — every entry resolves to a typed `import` condition under `dist/`. - [ ] Add per-package `tsconfig.json` extending the shared preset. ## 2. Types + error codes - [ ] `src/error-codes.ts` — `defineCodes` with the eight `AUDIO_CODEC_FFMPEG_NATIVE.*` codes; export `AudioCodecFfmpegNativeError extends SuaveplanError`, `AUDIO_CODEC_FFMPEG_NATIVE_ERROR_METADATA`, `registerAudioCodecFfmpegNativeErrorCodes()`. - [ ] `src/error-codes.test.ts` — assert each code is thrown by at least one call site (this test cross-references the per-feature tests once they exist). - [ ] `src/error-codes.md` — Purpose / Features / Basic / Advanced / API / Notes. - [ ] `src/constants/index.ts` — supported format lists, MIME types, default timeouts, default concurrency. ## 3. Binary resolver - [ ] `src/binary/binary.ts` — `resolveBinary(opts): Promise` implementing the 4-step discovery order; cache result on success. - [ ] `src/binary/binary.types.ts` — `BinaryResolverOptions`, `ResolvedBinary` shapes. - [ ] `src/binary/binary.test.ts` — covers `binaryProvider` precedence, explicit `binaryPath`, dynamic-import fallback, and `BINARY_MISSING` throw path. - [ ] `src/binary/binary.md`. - [ ] `src/binary/index.ts` — explicit named exports. ## 4. Semaphore - [ ] `src/semaphore/semaphore.ts` — ~30-line FIFO semaphore with `acquire` / `release`; no external dep. - [ ] `src/semaphore/semaphore.types.ts`. - [ ] `src/semaphore/semaphore.test.ts` — concurrency cap, FIFO ordering, slot-release on error. - [ ] `src/semaphore/semaphore.md`, `index.ts`. ## 5. Subprocess runner (the only place `node:child_process` / `fluent-ffmpeg` is imported) - [ ] `src/runner/runner.ts` — `runDecode(opts)` and `runEncode(opts)`. Owns spawn, stdin pipe, stdout drain (chunk-by-chunk, no full-output `Buffer.concat`), stderr ring buffer, exit handling, timeout enforcement, semaphore release. - [ ] `src/runner/runner.types.ts` — `RunDecodeOptions`, `RunEncodeOptions`, `RunnerCallbacks`, `RunnerHandles`. - [ ] `src/runner/runner.test.ts` — happy path, non-zero exit → `_DECODE_FAILED` with stderr context, spawn failure → `_SPAWN_FAILED`, timeout → `_TIMEOUT` with SIGTERM/SIGKILL escalation observed. - [ ] `src/runner/runner.md`, `index.ts`. ## 6. Decode pipeline - [ ] `src/decode/decode.ts` — `decodeOneShot(bytes, opts)` orchestrating `runDecode` + per-channel `Float32Array` accumulators + interleave splitter. - [ ] `src/decode/decode.types.ts`. - [ ] `src/decode/decode.test.ts` — well-formed `DecodeResult`, chunk-by-chunk assertion (instrument runner to count `Buffer.concat`), `_DECODE_FAILED` with stderr in context, `canDecode` delegation. - [ ] `src/decode/decode.md`, `index.ts`. ## 7. Encode pipeline - [ ] `src/encode/encode.ts` — `encodeOneShot(pcm, opts)` with PCM-shape validation, interleaver writing into a 64 KiB working buffer, runner invocation. - [ ] `src/encode/encode.types.ts`. - [ ] `src/encode/encode.test.ts` — round-trip through decode, `_INVALID_PCM` for ragged channels, `_UNSUPPORTED_CODEC` for unknown format and for WMA encode. - [ ] `src/encode/encode.md`, `index.ts`. ## 8. Streaming adapters - [ ] `src/stream/stream-decoder.ts` — `createDecoder(opts)` returning `StreamingDecoder`; `createDecodeTransform(opts)` returning a `@suaveplan/streams` `TransformStream`. - [ ] `src/stream/stream-encoder.ts` — `createEncoder(opts)` returning `StreamingEncoder`; `createEncodeTransform(opts)` returning a `TransformStream`. - [ ] Sibling `.types.ts`, `.test.ts`, `.md` for each; tests cover chunked-write equivalence, TransformStream composition, encoder yielding before flush. - [ ] `src/stream/index.ts` — explicit named exports. ## 9. AbortSignal wiring - [ ] `src/abort/abort.ts` — `attachAbort(child, signal, semaphoreRelease)`: sends `SIGTERM`, starts 1 s timer, escalates to `SIGKILL`, releases slot, rejects with `_ABORTED`. Short-circuit path for pre-aborted signals. - [ ] `src/abort/abort.types.ts`. - [ ] `src/abort/abort.test.ts` — pre-aborted short-circuit (no spawn observed), mid-decode SIGTERM → SIGKILL escalation under a child that ignores SIGTERM. - [ ] `src/abort/abort.md`, `index.ts`. ## 10. OpenTelemetry instrumentation - [ ] `src/otel/otel.ts` — span / counter / histogram / inflight-gauge helpers; inert without exporter; no import-time side effects. - [ ] `src/otel/otel.types.ts`, `otel.test.ts` (in-memory `MeterProvider` + `TracerProvider`), `otel.md`, `index.ts`. ## 11. Codec class + register helper - [ ] `src/codec/codec.ts` — `FfmpegNativeCodec` implementing `AudioCodec`. Owns the per-instance semaphore, the cached binary path, the OTel handles, and delegates to `decode/`, `encode/`, `stream/` modules. - [ ] `src/codec/codec.schemas.ts` — `FfmpegNativeCodecOptionsSchema`, `EncodeOptsSchema` (Zod). - [ ] `src/codec/codec.types.ts`, `codec.test.ts` (constructor Zod validation, `canDecode` rejection without spawn, WMA encode short-circuit, full `DecodeResult` shape), `codec.md`, `index.ts`. - [ ] `src/register/register.ts` — `register(registry, options?)` idempotent installer. - [ ] `src/register/register.test.ts`, `register.types.ts`, `register.md`, `index.ts`. ## 12. Root barrel - [ ] `src/index.ts` — explicit named re-exports of every public symbol from each feature module. No `export *` (rule 10 / §6a). ## 13. Tests — purity, integration, exports - [ ] `tests/purity.test.ts` — built dist has no browser globals; `child_process` imported only from `src/runner/runner.ts`; package import mutates no global OTel state. - [ ] `tests/integration.test.ts` — full round-trip across every declared encodable format using the bundled `@ffmpeg-installer/ffmpeg` binary. - [ ] `tests/exports.test.ts` — every feature module has a working subpath export entry and is independently importable. - [ ] Every test file imports runner primitives from `@suaveplan/testing/runner` (rule 11 / §7). ## 14. Benchmarks - [ ] `src/__benchmarks__/codec.bench.ts` — mitata suite: decode 10 s WAV, decode 10 s MP3, encode 10 s WAV→MP3, concurrent-decode at `concurrencyLimit` = 4. Use `--cpu-prof-md` / `--heap-prof-md` for LLM-assisted analysis. - [ ] Publish baseline numbers in `README.md`. ## 15. Documentation - [ ] Every `.ts` (excluding `.test.ts`, `.types.ts`, `.schemas.ts`, barrel `index.ts`) has a sibling `.md` meeting §4: ≥200 words, Purpose / Features / Basic / Advanced / API Reference / Implementation Notes. - [ ] `README.md` at package root: name, one-paragraph description, install, quick-start, Modules section linking every `src//.md`, format support matrix from `design.md`, baseline benchmark numbers. ## 16. Pre-publish gates (§13) - [ ] Step 0 — Spec reconciliation: walk every requirement and `Verified by` from the spec delta; confirm every symbol, span, counter, error code, and test name exists verbatim in `src/`. STOP if any item is unmatched. - [ ] `bunx turbo lint typecheck test --filter=@suaveplan/audio-codec-ffmpeg-native`. - [ ] `bun run check` (biome). - [ ] `bunx turbo test:coverage --filter=@suaveplan/audio-codec-ffmpeg-native` reports 100 / 100 / 100 / 100 (istanbul). - [ ] `bunx turbo build --filter=@suaveplan/audio-codec-ffmpeg-native`. - [ ] Banned-pattern scan: zero hits for `@deprecated`, `TODO`, `FIXME`, `ts-ignore`, `ts-expect-error`, `.skip`, `.only`, bare `any`, `console.log`. - [ ] Barrel scan (§13 step 4a): zero `export *` in feature barrels. - [ ] Test-runner import scan (§13 step 4b): zero direct `from "vitest"` / `from "bun:test"` imports. - [ ] Missing-`.md` scan returns zero output. - [ ] `README.md` present. - [ ] Bare-version scan returns zero output. - [ ] Subpath-export count matches feature-module count. - [ ] Security scan for hardcoded secrets returns zero output. - [ ] (Not a foundation package — dependent sweep optional; run it anyway since this is a fresh public surface.) ## 17. Root-doc refresh + markdownlint (§13b) - [ ] `bun scripts/folder-reorg/generate-manifest.ts` → refresh `MANIFEST.md`. - [ ] `bun scripts/generate-missing.ts` → refresh `MISSING.yml`. - [ ] Move the package row from Pending → Completed in `STATUS.md`. - [ ] Add the per-package paragraph in `SUMMARY.md`. - [ ] `bunx markdownlint-cli2 --fix`; resolve any remaining violations by hand. ## 18. Fold spec into canonical location - [ ] Fold `openspec/changes/enterprise-audio-codec-ffmpeg-native/specs/audio-codec-ffmpeg-native/spec.md` into `openspec/specs/audio-codec-ffmpeg-native/spec.md` per §14. ## 19. Archive - [ ] Move `openspec/changes/enterprise-audio-codec-ffmpeg-native/` to `openspec/archive/2026-05-15-enterprise-audio-codec-ffmpeg-native/`.