# @suaveplan/react-audio-stream — session core (chunk A) - **Target path**: `packages/ui/react/react-audio-stream/` - **Tier**: browser/react - **Date**: 2026-05-23 - **Status**: PROPOSED — splits `enterprise-react-audio-stream` into 4 ship-sized changes; this is chunk A (foundation). ## Why The original `enterprise-react-audio-stream` change folder is ~80-120 files at the project quality bar — too large to ship in a single agent session at 100/100/100/100. Splitting into four disjoint-subtree chunks (session-core, transports, hooks-components, transport-share) lets each land independently while keeping the public surface contract preserved. This chunk ships the session foundation: error codes, FSM lifecycle, source-plugin bridge, interruption classification, OTel instruments, and the package's structural shell (package.json with subpath exports, vitest/vite/tsconfig configs, root barrel). Every downstream chunk imports from here. ## What changes 1. **NEW package shell** `@suaveplan/react-audio-stream` at `packages/ui/react/react-audio-stream/` with `package.json`, `tsconfig.lib.json`, `vite.config.ts`, `vitest.config.ts`, `README.md`, root `src/index.ts`, all required subpath exports per the original spec's Requirement 9 (gated to those whose targets exist after this chunk). 2. **Error codes** — `src/error-codes/` declares `REACT_AUDIO_STREAM.{NO_PROVIDER, INVALID_STATE, SIGNALLING_FAILED, PEER_NOT_FOUND, MEDIA_PERMISSION_DENIED, RECONNECT_EXHAUSTED, INVALID_OPTIONS}` via `defineCodes` plus `ReactAudioStreamError extends SuaveplanError`. 3. **FSM** — `src/session/session-fsm/` builds the 7-state FSM (`idle` → `connecting` → `connected` → `joining` → `joined` → `disconnecting` → `disconnected`) using `@suaveplan/fsm`. Exposes `transition(method)`, emits `'state-changed'` events. 4. **StreamSession** — `src/session/stream-session/` is the public class wrapping the FSM. `connect`, `join`, `leave`, `disconnect`; emits `'state-changed'`, `'interruption'`, `'peer-stream-added'`, `'peer-stream-removed'`. Transport adapter is injected via constructor — concrete adapters land in chunk B. 5. **InterruptionClassifier** — `src/session/interruption-classifier/` deterministic rule mapping to `network | media-permission | signalling | peer | unknown`. 6. **StreamSourcePlugin** — `src/session/stream-source-plugin/` implements `AudioFxPlugin` from `@suaveplan/audio-fx-plugin-base`; `setSourceStream(MediaStream)`; consumed by `StreamSession` to materialise peer streams onto `peer:` tracks. 7. **Telemetry** — `src/telemetry/` registers 6 OTel instruments per the original Requirement 7 (peer connect/disconnect counters, active-sessions gauge, reconnect counters, RTT histogram). 8. **Purity test** — `tests/purity.test.ts` asserts the built `dist/` references no `AudioContext`/`OfflineAudioContext`/`AudioNode`/`AudioWorklet`. ## Requirements landed by this chunk From the original spec at `openspec/changes/enterprise-react-audio-stream/specs/react-audio-stream/spec.md`: - Stream-tier purity (full) - StreamSession FSM lifecycle (full) - Peer streams materialise as source plugins (full) - Interruption classification (full) - OpenTelemetry instrumentation (full) - Structured error codes (full) - Subpath exports — partial: only the subpaths whose targets exist after this chunk (`.`, `./session`, `./error-codes`); transports/hooks/components subpaths come in their chunks. **Deferred to follow-up chunks:** Auto-reconnect (chunk D), per-peer stream mixer (chunk C), transport-share mode (chunk D), WebRTC/WebSocket/Trystero transports (chunk B), 7 hooks + 4 components + Provider (chunk C), bundle-size test (chunk B). ## Impact - New package shell with the FSM foundation. ~25 files (src + tests + docs + configs). - Unblocks chunks B, C, D — each imports `StreamSession`, `ReactAudioStreamError`, `InterruptionClassifier`, `StreamSourcePlugin` from here. - No `dist/` archives until all four chunks merge. Spec fold + change archival happens after chunk D lands.