# Audio startup and recovery Fenrir handles speech and sound connections independently. The recovery code uses Linux processes, sockets and deadlines; it does not require systemd or a particular service manager. Explicit server, speech driver and sound driver choices remain authoritative. Failures never select a different audio driver. ## Speech Before opening console keyboards or an X11 terminal session, Fenrir retries temporary speech connection failures for up to 20 seconds. Permission and configuration errors fail without waiting through that retry window. Speech Dispatcher's own configuration decides whether to allow daemon autostart. When a running Speech Dispatcher connection fails, the speech worker attempts to reconnect for up to 20 seconds. Keyboard forwarding, interruption, mute and quit commands remain responsive. Pending speech is discarded during recovery; it is not replayed when the connection returns. Numeric speech settings and mute state survive recovery. Later output uses the restored connection. If recovery fails, Fenrir reports the error and exits. Keyboard grabs are released before waiting for UI tasks, speech or sound shutdown. Native hardware speech drivers retain their existing failure handling; they do not start a Speech Dispatcher daemon or select a different synthesizer. ## Root at boot and sudo sessions A boot service running as root without `SUDO_UID` uses root's Speech Dispatcher environment and configuration. It does not choose a logged-in user's daemon. Root's speech and sound connections receive the same retry/recovery handling. The audio server must be configured to start independently of graphical login and permit the required socket and output-device access. For `sudo fenrir`, `SUDO_UID` identifies the invoking user. If the selected local Speech Dispatcher is missing, Fenrir looks up that account and starts only the daemon child with that account's UID, primary group and supplementary groups. The child receives the user's home and an owned runtime directory when present; it does not inherit root's session bus or unrelated environment variables. An owned `XDG_RUNTIME_DIR` is preferred for the default speech socket, followed by the user's owned `/run/user/UID` directory, or the user's cache directory when no runtime directory exists. Explicit speech socket/address settings still take precedence. Fenrir retains its own console/device privileges. Logging out may stop user-owned Speech Dispatcher or audio services. Recovery can reconnect when the same services return; it does not keep a login session alive or migrate to another user's audio. No session-owned daemon is killed or restarted to force recovery. ## Sound effects Sound effects retry failed initialization and disconnected playback in a background worker for up to 20 seconds per outage. While unavailable, sound cues are rejected so Fenrir can use their speech equivalents. Cues and progress tones from an outage are discarded. The latest sound volume is applied to a restored connection. Each sound-driver open attempt has a three-second deadline. Some native/library calls cannot be interrupted: if an attempt remains stuck, Fenrir stops opening new connections for that player. At most one abandoned opener remains, and it closes its result if it eventually returns. Working speech and keyboard input continue. Exhausted sound recovery disables sound effects for that run. ## Limits Retries cannot repair missing services, incorrect permissions, a wrong server, or a missing playback device. A successful Speech Dispatcher protocol exchange also does not prove audible output: an output module can accept a request and fail inside its own audio backend. PipeWire, PulseAudio and Speech Dispatcher still need appropriate configuration. In particular, PulseAudio's libpulse autospawn behavior does not imply that a PipeWire service will start itself. ## Verification Automated checks use private sockets and fake audio backends, without taking over the active reader: ```sh go test -race ./internal/audio ./internal/speech ./internal/sound ./internal/app go test ./... ``` For optional isolated Speech Dispatcher startup checks using its dummy module: ```sh FENRIR_NATIVE_SPEECH_STARTUP=1 go test ./internal/speech -run TestNativeSpeechDispatcherColdStart -count=1 ``` Before submission, review the diff and test on an affected machine with a recovery path, such as SSH or another accessible console. Keep only one reader on a console. Source tests do not prove installed behavior or audible output. 1. Boot without a GUI and before user login, with Fenrir started by the normal boot service. Verify speech, startup sounds, typing, review and quit. Repeat with the audio server delayed briefly: speech should recover if the required service becomes available inside the retry window. 2. Check ordinary `sudo fenrir` startup with the user's Speech Dispatcher both already running and initially absent. Check daemon ownership and confirm speech and sound. No root-owned daemon should be created in the user's runtime directory. Test a console session without systemd as well. 3. With a recovery connection available, interrupt the configured speech/audio service using that system's normal service controls. While it is unavailable, check ordinary typing, Control, mute and Fenrir's quit shortcut. Restore the service and check fresh review output, volume/rate/pitch and absence of old queued announcements. A permanent speech failure must release the keyboard; a sound-only failure must leave speech working. 4. Repeat ordinary X11 terminal startup, review, interruption and exit, plus explicit ALSA sound output where available. Check settings changes during recovery and verify that saved settings are not silently rewritten. 5. For user-owned audio, log out and back in with the recovery connection still available. Verify behavior against that machine's session-lifetime policy; this is separate from root-owned pre-login audio.