docs/platforms/mac/voice-overlay.md
Audience: macOS app contributors. Goal: keep the voice overlay predictable when wake-word and push-to-talk overlap.
info in categories voicewake.overlay, voicewake.ptt, and voicewake.chime (session start, partial, final, send, dismiss, chime reason).VoiceSession at a time.beginWakeCapture, beginPushToTalk, updatePartial, endCapture, cancel, applyCooldown.token, source (wakeWord|pushToTalk), committed/volatile text, chime flags, timers (auto-send, idle), overlayMode (display|editing|sending), cooldown deadline.VoiceSessionPublisher (ObservableObject) mirrors the active session into SwiftUI.VoiceWakeOverlayView renders only via the publisher; it never mutates global singletons directly.sendNow, dismiss, edit) call back into the coordinator with the session token.endCapture: if trimmed text is empty → dismiss; else performSend(session:) (plays send chime once, forwards, dismisses)..info logs in subsystem ai.openclaw, categories voicewake.overlay and voicewake.chime.session_started, adopted_by_push_to_talk, partial, finalized, send, dismiss, cancel, cooldown.Stream logs while reproducing a sticky overlay:
sudo log stream --predicate 'subsystem == "ai.openclaw" AND category CONTAINS "voicewake"' --level info --style compact
Verify only one active session token; stale callbacks should be dropped by the coordinator.
Ensure push-to-talk release always calls endCapture with the active token; if text is empty, expect dismiss without chime or send.
VoiceSessionCoordinator, VoiceSession, and VoiceSessionPublisher.VoiceWakeRuntime to create/update/end sessions instead of touching VoiceWakeOverlayController directly.VoicePushToTalk to adopt existing sessions and call endCapture on release; apply runtime cooldown.VoiceWakeOverlayController to the publisher; remove direct calls from runtime/PTT.