CodeNomad/.github/workflows
Dark 67cb394e8f
fix(ui): resolve stuck/duplicated messages after mobile background (#639)
## Problem

On mobile, backgrounding CodeNomad while the AI is working suspends JS
execution. The SSE connection eventually times out (~45s), and any
events that arrived while suspended (a tool call finishing, a message
completing) are lost. Returning to the app could leave the UI
permanently stuck showing "running" even though the work had actually
finished on the server — the only way out was aborting the request.

## Fix

**1. `useForegroundRefresh` hook**
(`packages/ui/src/lib/hooks/use-foreground-refresh.ts`)

Subscribes to the SSE transport's `disconnected -> connected` transition
(not `visibilitychange`) and re-fetches session status + force-reloads
messages only when a reconnect follows a real disconnect. An earlier
attempt triggered on `visibilitychange` after a fixed delay, but that
force-reloaded even when the connection had stayed alive the whole time,
clearing in-flight "sending" state and making it look like the AI never
responded to a message the user had just sent.

Also fixes a latent bug in `server-events.ts`: `onPing` could fire for a
stale SSE generation after a reconnect, sending a pong tied to the wrong
connection. Guarded with the existing `connectGeneration` counter.

**2. Two `hydrateMessages` bugs** found while testing the hook against
real mobile background/foreground cycles
(`packages/ui/src/stores/message-v2/instance-store.ts`):

- **Unnecessary full re-render on every reload.** The force-reload path
always bumped every message's revision regardless of whether the server
returned identical content, invalidating render caches and re-rendering
the entire visible session on every reconnect. Several reconnects in a
row produced perceptible lag. Fixed by only bumping revision when a
message's parts or status actually changed — reload time dropped from
~280-320ms to ~10ms for unchanged content.
- **Duplicated message bubble on a race.** If the user sends a message
right as a reconnect happens, the optimistic "sending" bubble
(client-side temp id) isn't in the REST snapshot yet and was silently
dropped from the visible list — without being deleted from the store.
When the real SSE echo for it later arrived, the code could no longer
find it to swap cleanly, so it created a new record instead, and a
subsequent reload could leave both the orphaned bubble and the new one
visible at once. Fixed by preserving pending "sending" messages across a
reload until they're actually confirmed.

## Validation

- `npm run typecheck` (workspace `@codenomad/ui`): clean
- `node --test src/stores/message-v2/instance-store.test.ts`: 4/4 pass
(2 new tests covering the duplicate-message race)
- Full UI test suite run file-by-file: 57/66 pass; the 9 that don't fail
identically on a pristine `upstream/dev` checkout with none of this PR's
changes applied (pre-existing solid-toast SSR issue in the test
environment, unrelated to this change)
- `npm run build` (workspace `@codenomad/ui`): succeeds
- Manually verified over ~1.7 hours of real mobile background/foreground
cycles (14s glances up to two 40+ minute backgrounds) — refresh fires
only on genuine reconnects, completes in ~200-450ms, no stuck "running"
state and no duplicated messages on return

---------

Co-authored-by: Pascal André <pascalandr@gmail.com>
2026-08-07 15:53:25 +02:00
..
build-and-upload.yml build: publish deb and portable tar.gz Linux artifacts (#492) 2026-07-12 19:29:33 +02:00
comment-pr-artifacts.yml fix(restore): simplify persistence and harden cross-platform cleanup (#602) 2026-07-17 22:17:15 +01:00
dev-release.yml ci: run dev prerelease nightly 2026-02-20 13:58:32 +00:00
manual-npm-publish.yml fix(ci): invoke pinned npm cli directly 2026-04-21 11:18:38 +01:00
pr-build.yml fix(ui): resolve stuck/duplicated messages after mobile background (#639) 2026-08-07 15:53:25 +02:00
release-ui.yml fix(desktop): restore managed Node server startup (#348) 2026-04-26 13:20:47 +01:00
release.yml chore(release): publish dev builds as codenomad-dev 2026-02-13 00:39:14 +00:00
restrict-non-dev-prs.yml fix(workflows): recheck non-dev PR authorization by author 2026-04-01 23:11:25 +01:00
reusable-release.yml fix: wire Winget automation into release pipeline (#551) 2026-06-13 17:51:24 +01:00
update-winget.yml fix: wire Winget automation into release pipeline (#551) 2026-06-13 17:51:24 +01:00