#16767: fix: auto-resync webchat on reconnect and prevent message flicker on stream complete
app: web-ui
stale
size: S
## Bug 1: Message flicker on stream complete
When a streamed chat response finishes, the streamed content disappears briefly before `loadChatHistory` completes — the user sees a gap where the message vanishes then reappears.
**Root cause:** `handleChatEvent` clears `chatStream` on "final" state before the history reload finishes.
**Fix:** In `handleChatEvent`, when state is "final" and `payload.message` exists, immediately append the final message to `chatMessages` *before* clearing `chatStream`. The subsequent `loadChatHistory` replaces the array with the authoritative server copy, but the user sees no flicker.
## Bug 2: iOS Safari backgrounding — no automatic resync
When Safari is backgrounded on iOS, the WebSocket dies. On return, the client reconnects but the chat shows stale messages.
**Root cause:** The reconnect path resets stream state but never reloads chat history. The `onGap` handler only sets an error message without recovering.
**Fix:**
- **`gateway.ts`:** Add `onReconnect` callback to `GatewayBrowserClientOptions`, fired after a successful `connect` on reconnections (not the initial connection). Tracked via a `hasConnectedOnce` flag.
- **`app-gateway.ts`:** Wire `onReconnect` to call `refreshChat()`. Update `onGap` to also trigger `refreshChat()` (and auto-clear the gap error). Add a `visibilitychange` listener that refreshes chat when the user returns to the tab (debounced at 300ms).
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Fixes two webchat bugs: message flicker on stream completion and missing chat resync after iOS Safari backgrounding.
The flicker fix appends the final message to `chatMessages` before clearing `chatStream`, preventing the gap where streamed content vanishes while `loadChatHistory` completes.
The resync fix adds an `onReconnect` callback to the gateway client (triggered after successful reconnects, not initial connections) and wires it to refresh chat history. Also adds a `visibilitychange` listener that refreshes chat when users return to the tab, addressing iOS Safari backgrounding.
The `onGap` handler now triggers `refreshChat()` and auto-clears the error message after sync completes.
<h3>Confidence Score: 3/5</h3>
- This PR fixes real bugs but introduces a memory leak that should be addressed before merging
- The fixes for message flicker and chat resync are sound and address real user-facing issues. However, the `visibilitychange` listener is never removed, causing listener accumulation if `connectGateway` is called multiple times. This is already noted in previous review threads but remains unaddressed.
- `ui/src/ui/app-gateway.ts` requires cleanup of the visibilitychange listener to prevent memory leak
<sub>Last reviewed commit: 4af8dac</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#13902: fix: auto-recover on event gap instead of showing error
by nikogamulin · 2026-02-11
84.1%
#9218: Fix Control UI chat resync on gaps and terminal events
by figitaki · 2026-02-05
82.1%
#15564: fix: webchat messages disappear during concurrent session activity
by Automatedcapitalist · 2026-02-13
79.6%
#14966: fix(webchat): preserve user message visibility after chat.send
by BenediktSchackenberg · 2026-02-12
79.6%
#19343: Refactor chat state management: reset chat messages and queue on se...
by saurav470 · 2026-02-17
78.1%
#14309: fix(ui): resolve chat event session key mismatch
by justonlyforyou · 2026-02-11
78.1%
#14993: fix(webchat): add heartbeat detection to prevent zombie WebSocket c...
by BenediktSchackenberg · 2026-02-12
77.2%
#9195: Fix: Control UI fails to render new messages after chat.history Web...
by vishaltandale00 · 2026-02-05
76.9%
#16949: fix(gateway): deliver chat:final even when sessionKey is unresolved (…
by ekleziast · 2026-02-15
76.8%
#18232: fix: webchat rapid messages create orphan sessions
by MisterGuy420 · 2026-02-16
76.1%