The gateway replays the full transcript over a new socket on session.resume unless omit_messages is set; mobile hydrates history via REST in parallel, so the replay is redundant. When a reconnect lands mid-stream, the replayed message gets a fresh UUID and dedup can't collapse the 90% orphan with the 100% replay (different ids + content), leaving a duplicate bubble. Desktop already sends omit_messages: true; mirror that in both resume paths (ChatViewModel + NotificationReplyReceiver) and pin it in the exact-map test verifies. Co-authored-by: M57 <hy4ri@users.noreply.github.com>