#20475: fix(macos): resolve 120%+ CPU regression and gateway stability
channel: nextcloud-talk
app: macos
gateway
size: S
Cluster:
Text-to-Speech Provider Enhancements
## Summary
- **GatewayChannel CPU fix**: Replace `JSONDecoder` + `AnyCodable` decode with native `JSONSerialization`, eliminating the recursive try/catch cascade that was burning 120%+ CPU on every gateway message. Includes `wrapJSONValue()` helper using `CFBooleanGetTypeID()` to preserve JSON bool/int distinction through NSNumber bridging.
- **keepaliveLoop busy-spin fix**: `try? await Task.sleep(...)` silently swallowed `CancellationError`, causing cancelled tasks to spin indefinitely. Now exits cleanly on cancellation.
- **TalkOverlayView performance**: Use static `Circle()` when idle/paused instead of 60fps `TimelineView`; throttle active animation to 15fps.
- **Duplicate WebSocket prevention**: `DevicePairingApprovalPrompter`, `NodePairingApprovalPrompter`, and `VoiceWakeGlobalSettingsSync` now subscribe to the existing gateway connection instead of calling `refresh()` which spawned duplicate WebSocket connections.
- **talk.config systemVoice**: `normalizeTalkConfigSection()` was missing `systemVoice` from its field whitelist, silently dropping it from the response.
- **TalkModeRuntime**: Default to Edge TTS (free, no API key) when no ElevenLabs key is configured; system voice only when explicitly requested. Add error logging to `fetchTalkConfig()` catch block.
- **Gateway auth**: Allow `health` method for any authenticated role.
- **Nextcloud Talk**: Keep channel task alive to prevent framework auto-restart collision.
## Test plan
- [ ] Launch macOS app, verify CPU settles below 10% after startup (was 120%+)
- [ ] Enter talk mode, verify TTS playback works (Edge TTS default or configured voice)
- [ ] Verify gateway WebSocket shows single connection (not duplicates)
- [ ] Verify device/node pairing prompts still appear on `device.pair.requested` events
- [ ] Verify `talk.config` response includes `systemVoice` when set in config
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR addresses a critical 120%+ CPU regression caused by expensive JSON decoding and implements several gateway stability improvements:
- **GatewayChannel CPU fix**: Replaces `JSONDecoder` + `AnyCodable` with native `JSONSerialization` to eliminate recursive try/catch overhead. Includes `wrapJSONValue()` helper using `CFBooleanGetTypeID()` to preserve JSON bool/int distinctions.
- **keepaliveLoop busy-spin fix**: Properly handles `CancellationError` instead of silently swallowing it with `try?`, preventing infinite busy-spinning on cancelled tasks.
- **TalkOverlayView performance**: Uses static `Circle()` when idle/paused instead of 60fps `TimelineView`, and throttles active animation to 15fps.
- **Duplicate WebSocket prevention**: Three components (`DevicePairingApprovalPrompter`, `NodePairingApprovalPrompter`, `VoiceWakeGlobalSettingsSync`) now subscribe to existing gateway connection instead of calling `refresh()`.
- **talk.config systemVoice**: Adds missing `systemVoice` field to `normalizeTalkConfigSection()` whitelist.
- **Gateway auth**: Allows `health` method for any authenticated role.
- **Nextcloud Talk**: Keeps channel task alive to prevent framework auto-restart collision.
**Critical Issue**: The PR introduces references to `TalkGatewayTTSSynthesizer` and `TalkSayCommandSynthesizer` classes that don't exist in the codebase, which will cause compilation failures.
<h3>Confidence Score: 0/5</h3>
- This PR cannot be merged due to missing class definitions that will cause compilation failures
- The PR references `TalkGatewayTTSSynthesizer` and `TalkSayCommandSynthesizer` on lines 672-673 and 734-735 of `TalkModeRuntime.swift`, but these classes are not defined anywhere in the codebase. This will cause Swift compilation to fail. While the other changes (CPU fix, keepalive loop, WebSocket deduplication) appear well-implemented and address important issues, the missing synthesizer implementations are blocking issues that must be resolved before merge.
- apps/macos/Sources/OpenClaw/TalkModeRuntime.swift requires missing synthesizer implementations
<sub>Last reviewed commit: dde0ce5</sub>
<!-- greptile_other_comments_section -->
<sub>(4/5) You can add custom instructions or style guidelines for the agent [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#16274: feat(voice): Fix persistent speech errors, silent playback, and feedb…
by ryotsukuda333 · 2026-02-14
77.7%
#9456: feat(mac): add enhanced Siri neural voice support for Talk mode
by teknomage8 · 2026-02-05
77.5%
#8260: fix(macOS): gateway readiness detection + reversible Configure later
by xksteven · 2026-02-03
76.1%
#17007: fixed stacy voice
by tashen247 · 2026-02-15
75.4%
#6302: fix: Add timeouts to prevent indefinite hangs (issues #4954, #4956,...
by batumilove · 2026-02-01
74.6%
#8713: feat: gateway memory monitor, install linger, docs and failover
by quratus · 2026-02-04
74.4%
#22480: fix: memory leak, silent WS failures, and connection error handling
by Chase-Xuu · 2026-02-21
74.0%
#20272: fix: LaunchAgent KeepAlive causes restart loop (fixes #20257)
by MisterGuy420 · 2026-02-18
73.7%
#23778: feat: chat UI facelift — speech, themes, config categories, and polish
by BunsDev · 2026-02-22
73.7%
#22469: fix(gateway): avoid stale whatsapp labels on direct sessions
by loganprit · 2026-02-21
73.5%