# Ticket 11.12 — Notification Sounds

**Phase**: 11 — UI/UX Polish
**Priority**: Low
**Depends on**: None

## Description

A `sound.ts` utility module exists and the notification settings UI has a "Sound" toggle, but no component actually plays sounds on events. claude-relay synthesizes notification sounds via the Web Audio API (sine wave). conduit should play a brief audio cue when:
- A processing run completes (done event)
- A permission request arrives (needs attention)
- An error occurs

The sounds should be synthesized (not audio files) to avoid asset loading, respect the user's "Sound" toggle setting, and not play when the tab is focused and visible.

## Acceptance Criteria

### AC1: Sound plays on processing complete when tab not focused
```gherkin
Given the user's "Sound" notification setting is enabled
And the browser tab is not focused (hidden or blurred)
When a processing run completes (done event received)
Then a notification sound plays via the Web Audio API
```

### AC2: Distinct sound for permission requests
```gherkin
Given the user's "Sound" notification setting is enabled
When a permission request arrives
Then a higher-urgency notification sound plays
And the sound is audibly distinct from the completion sound (different frequency/pattern)
```

### AC3: Sounds respect the Sound toggle
```gherkin
Given the user's "Sound" notification setting is disabled
When a processing run completes or a permission request arrives
Then no notification sound plays
```

### AC4: No sounds when tab is focused
```gherkin
Given the user's "Sound" notification setting is enabled
And the browser tab is focused and visible
When a processing run completes
Then no notification sound plays
```

### AC5: Sounds are synthesized via Web Audio API
```gherkin
Given a notification sound needs to play
When the sound is triggered
Then the sound is generated using the Web Audio API (OscillatorNode with sine wave)
And no external audio files are fetched or loaded
```

## Testable Components

| Component | Test Type | What to Assert |
|-----------|-----------|----------------|
| Sound synthesis via Web Audio API | Unit | OscillatorNode is created with correct frequency and duration using mock AudioContext |
| Sound plays on done event when tab hidden | Unit | Done event handler calls the sound function when `document.hidden` is true |
| Sound suppressed when tab visible | Unit | Done event handler does not call the sound function when `document.hidden` is false |
| Sound toggle in settings disables all sounds | E2E | Toggling the Sound setting off in NotifSettings prevents any sounds from playing |
| Different tones for done vs permission | Unit | Completion sound uses one frequency; permission sound uses a different, higher frequency |

## Reference

- Existing stub: `conduit/src/lib/public/utils/sound.ts`
- Sound toggle: `conduit/src/lib/public/components/overlays/NotifSettings.svelte`
- Settings persistence: `conduit/src/lib/public/utils/notif-settings.ts`
- claude-relay sound: `../claude-relay/lib/public/modules/notifications.js` (synthesized sine wave via Web Audio API)
