| Poll returns exact same messages as cached |
Silent no-op, no redraw |
| Poll returns 1 new message while user is at bottom |
Append, auto-scroll, update selection |
| Poll returns 1 new message while user is scrolled up |
Append, show “New messages” indicator, preserve scroll |
| Poll returns N new messages in a batch |
Append all, auto-scroll once if at bottom, show count if scrolled
up |
| Poll returns a message that was already sent optimistically |
Replace optimistic entry with confirmed one, preserving scroll
position |
| Poll fails (timeout) |
Log, retry next interval, no UI change |
| Poll fails 3 times in a row |
Show subtle offline indicator (gray dot in nav bar) |
| User sends message while poll is in-flight |
Optimistic append immediately, poll response merges without
conflict |
| User opens OSK while “New messages” indicator is visible |
Indicator stays visible, OSK opens on top |
| User navigates back to chat list while “New messages” is
visible |
Indicator is dismissed, scroll position is NOT preserved for next
chat entry |
| App receives a reaction message (associatedMessageGuid set) |
Treated as a new message, appended, indicator shown |
| Message list grows beyond 200 messages |
Truncate oldest messages from self.messages, but NOT
from BlueBubbles history (user can scroll up to load more later) |