1.9 KiB
Android Realtime Noise Reduction Design
Goal
Reduce Android message receive stutter and reconnect-visible reloads by preventing heartbeat-only SSE traffic from triggering full conversation list refreshes.
Evidence
The production boss-web service was active and had no recent service errors. A direct authenticated SSE probe showed the stream stays open with keepalive comments, but every local-agent heartbeat emitted devices.updated followed by conversation.updated with only deviceId. Android MainActivity treated that device-only conversation.updated as a full conversations tab reload. The stream also sends an initial conversation.context_indicator.updated snapshot on connection; Android was not consuming its payload directly and instead used it as another full reload trigger.
Design
Keep devices.updated for every heartbeat so device status remains fresh. Stop publishing conversation.updated for unchanged device heartbeats. Publish a single conversation.updated with note: "device_import.updated" only when heartbeat candidate/import state actually changes the conversation drawer structure.
On Android, do not use the initial conversation.context_indicator.updated snapshot as a full list reload trigger. Actual conversation and project message events still refresh the relevant views, while the existing root-page auto-refresh remains the low-frequency fallback for aggregate context status.
Testing
Add event-contract tests proving unchanged heartbeats only emit devices.updated, while changed import candidates emit one conversation.updated marked as device_import.updated. Add an Android static test proving MainActivity no longer returns true for the initial context snapshot refresh path.
Out Of Scope
This change does not replace SSE with a different transport, does not remove project-scoped message events, and does not implement payload-level in-place updates for Android conversation rows.