# Audio Buffer Logic Summary (Loss Prevention During Transcription)

## Goal
The SDK avoids audio loss when websocket transport is unstable by switching from live send to local buffering, then replaying buffered audio in order once the connection is healthy again.

## Where It Is Implemented
- `src/services/transcription/adapters/WhisperTranscriptionAdapter.ts`
- `src/services/transcription/adapters/OfflineAudioBufferStore.ts`
- Audio capture source: `src/services/transcription/worklet/realtime-audio-processor.js` (and inline worklet code in the adapter)

## End-to-End Flow
1. **Capture + normalize audio**
- Audio is captured from `AudioWorkletNode` callbacks.
- Frames are downsampled from `44.1kHz` to `16kHz`, then converted from `Float32` to `Int16`.

2. **Attempt live send first**
- If websocket is open and healthy, chunks are sent immediately.
- Sent chunks are also copied into a short rolling replay window (`preRecoveryReplayMs`, default `500ms`) to protect the edge around outage start.

3. **Switch to buffering when send is risky/unavailable**
- Buffering is used when:
  - reconnecting / socket not open,
  - currently draining old buffered audio,
  - resume gate is active,
  - websocket backpressure exceeds threshold (`maxBufferedAmountBytes`, default `262144` bytes),
  - or send throws.
- On first buffered chunk, buffering start is timestamped and replay window is primed (last ~500ms copied into buffer).

4. **Two-tier buffered storage (RAM -> persistent segments)**
- New chunks first accumulate in memory (`pendingOfflineAudioBuffers`, `pendingOfflineAudioBytes`).
- Once pending bytes reach `64KB` (`OFFLINE_AUDIO_SEGMENT_BYTES = 65536`), data is flushed as one Blob segment into offline storage.
- Offline storage is sequence-ordered per session.
- Storage backend:
  - `IndexedDB` when available,
  - in-memory fallback otherwise.

5. **Drain/replay after reconnection**
- On websocket `open`, adapter starts `drainOfflineAudioBuffer()`.
- Drain loop:
  - forces pending RAM buffers to persist,
  - reads ordered snapshot of buffered segments,
  - sends snapshot payload,
  - waits until transport `bufferedAmount` drains to zero,
  - deletes drained segments (`deleteThrough(maxSequence)`),
  - updates counters and continues until empty.

6. **Stall detection during drain**
- While waiting for transport drain, if `bufferedAmount` does not decrease for `drainStallTimeoutMs` (default `5000ms`), drain is marked stalled and transport reconnect is triggered.

7. **Graceful stop with final drain attempt**
- `stopTranscription()` waits up to `5s` (`STOP_DRAIN_TIMEOUT_MS`) for buffered audio drain before final shutdown.
- Then sends websocket `{ action: "finish" }` and waits for non-partial final response (or times out after `5s`).
- If final response does not arrive, last partial text is emitted as final fallback.

## Why This Prevents Audio Loss
- **Outage edge protection:** recent live audio replay window covers audio right before failure detection.
- **No drop on transient failures:** chunks are persisted locally instead of discarded when send/reconnect fails.
- **Ordered recovery:** sequence-based snapshot + delete-through guarantees replay order and avoids duplicate persistence retention.
- **Backpressure safety:** high websocket buffered amount triggers proactive buffering/reconnect instead of continuing unsafe sends.
- **Shutdown protection:** stop path attempts draining before teardown, reducing tail-loss risk.

## Key Runtime Signals (for diagnostics)
`getResilienceStatus()` exposes:
- `isBufferingAudio`, `isDrainingAudio`
- `pendingBufferedAudioBytes`, `persistedBufferedAudioBytes`, `persistedBufferedAudioSegments`, `totalBufferedAudioBytes`
- `bufferingStartedAt`, `drainStartedAt`, `lastDrainProgressAt`, `drainCycles`, `drainExitReason`
- `transportBufferedAmountBytes`, reconnect progress fields, and `finishDeliveryState`

These metrics make buffering/drain behavior observable in real time and are also consumed by telemetry/audit paths.

## Defaults That Matter Most
- Upstream backpressure threshold: `262144` bytes
- Health check interval: `2000ms`
- Drain poll interval: `100ms`
- Drain stall timeout: `5000ms`
- Replay window before outage: `500ms`
- Segment flush size to offline store: `65536` bytes
- Reconnect max attempts: `96`
