# Tasks: enterprise-gaming-r3f-audio **Change ID**: enterprise-gaming-r3f-audio **Package**: `@suaveplan/r3f-audio` **Target path**: `packages/gaming/r3f-audio` **Prerequisite packages on Verdaccio**: `@suaveplan/audio-plugins-fx`, `@suaveplan/react-audio`, `@suaveplan/audio-transport`, `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/opentelemetry` --- ## 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 - [ ] 1.1 Create directory tree per `proposal.md` file layout. - [ ] 1.2 Write `package.json` at `0.1.0`, `"sideEffects": false`, full `exports` map (`.`, `./components`, `./components/`, `./hooks`, `./hooks/`, `./util`). React + R3F + three as peer deps. - [ ] 1.3 Write `tsconfig.json`, `vitest.config.ts` (`happy-dom`). ## 2. Error codes - [ ] 2.1 `src/error-codes.ts` — `defineCodes("R3F_AUDIO", [...])`, `R3fAudioError extends AudioError`. - [ ] 2.2 `error-codes.test.ts`. ## 3. Util - [ ] 3.1 `src/util/pose-to-ambient/` — `Vec3`/`Quat` conversions, scratch-vector helpers; no allocation per call. - [ ] 3.2 `src/util/raycaster-pool/` — shared raycaster instance + per-source scoreboard. ## 4. Hooks - [ ] 4.1 `use-audio-listener` — binds an `Object3D` ref to `engine.updateAmbient({ listener })` per frame. - [ ] 4.2 `use-positional-audio` — acquires `SpatialPositionalPlugin`, writes pose per frame, releases on unmount. - [ ] 4.3 `use-music-layer-weights` — writes `gameState` into `AmbientState.extension.gameState`. - [ ] 4.4 `use-proximity-voice` — acquires `ProximityVoicePlugin`, attaches stream, writes position per frame. - [ ] 4.5 `use-occlusion` — runs the raycaster pool against occluders, writes smoothed `occlusionDb`. ## 5. Components (each: `.tsx`, `.md`, `.types.ts`, `.test.tsx`, `.stories.tsx`) - [ ] 5.1 `audio-listener/` — default target = active camera. - [ ] 5.2 `positional-audio/`. - [ ] 5.3 `proximity-voice-chat/`. - [ ] 5.4 `music-layer-mixer/`. - [ ] 5.5 `voice-transform/`. - [ ] 5.6 `occlusion-provider/`. ## 6. OTel - [ ] 6.1 Register `r3f_audio_active_sources` gauge; wire mount/unmount to its `add`/`subtract`. ## 7. Benchmarks - [ ] 7.1 `src/__benchmarks__/pose-sync.bench.ts` — 60 fps pose write loop over 1024 sources. - [ ] 7.2 `src/__benchmarks__/occlusion.bench.ts` — raycast pool over 256 sources against a 5 k-tri scene. ## 8. Integration + purity + bundle-size - [ ] 8.1 `tests/integration.test.tsx` — full R3F scene + AudioProvider + listener + 4 positional sources + occlusion + music layer mixer; verify ambient state writes land on chain. - [ ] 8.2 `tests/purity.test.ts` — built `dist/` references no audio context. - [ ] 8.3 `tests/exports.test.ts` — every surface has subpath export; no `export *` in feature barrels. - [ ] 8.4 `tests/bundle-size/listener-only.size.test.ts`. ## 9. Docs + verify - [ ] 9.1 All co-located `.md` files (≥200 words). - [ ] 9.2 `README.md` at package root. - [ ] 9.3 `bunx turbo lint typecheck test --filter=@suaveplan/r3f-audio` green. - [ ] 9.4 `bunx turbo test:coverage --filter=@suaveplan/r3f-audio` 100/100/100/100. - [ ] 9.5 Banned-pattern + runner-import + file-size + missing-`.md` sweeps empty. ## 10. Spec reconciliation + archive - [ ] 10.1 Walk every `### Requirement` and `**Verified by:**`. - [ ] 10.2 Fold spec delta into `openspec/specs/r3f-audio/spec.md`. - [ ] 10.3 Move change folder to archive.