A sticky-online cold start with no network hung the splash on adoptSession's
retrying profile fetch. Now, for an offline-capable web install with a cached
profile:
- No network interface (navigator.onLine === false) -> enter offline mode for
the session (no dialog; the next launch re-evaluates).
- Interface up but the gateway is unreachable within 3s (a single-attempt
reachability probe, not the 6-retry loop) -> a 'No connection. Enable offline
mode?' dialog: Enable -> sticky offline; Keep trying -> the normal online
adopt (retries, 'Connecting...').
- connection.svelte: checkReachable(timeout) - a bounded single probe.
- offline.svelte: setOfflineMode(on, persist) - auto-offline is session-only, a
deliberate choice (dialog/toggle) is sticky.
- app.svelte.ts: the cold-start auto-detect in bootstrap + the dialog resolver;
App.svelte renders the boot dialog. i18n en/ru.
- App-entry bundle budget 113->114 (the boot path cannot be lazy-loaded).
Online cold-start unaffected (auto-detect gated to isStandalone, off in the mock
e2e): e2e 196. The offline paths are contour-verified.
Next: PR2 - mid-session flight-mode reactivity (online/offline events).
Connectivity failures become state, not a toast on every attempt. A global online
signal (lib/connection.svelte.ts) flips on a transport unavailable / rate_limited and
on the live stream's drop, driving a pure-CSS header spinner + 'Connecting…' in place
of the title and softly disabling the in-game server actions (commit / exchange / pass
/ hint; local board/rack/reset stay live).
- transport: exec auto-retries with capped exponential backoff — every op on a
rate-limit (rejected before processing, safe), reads only on unavailable (a mutation
is never blindly re-sent, to avoid double-applying one whose response was lost; its
button is disabled while offline so the player re-issues on reconnect). A reachability
watcher (profile.get probe) and any successful traffic clear the signal.
- the old red error.unavailable toast is gone (handleError suppresses connection codes;
the indicator replaces it). A server-data screen still opens with the spinner and
fills on reconnect (global indicator + read auto-retry), so navigation is never dead.
- pure retry policy unit-tested (retry.ts); a mock-only window.__conn hook drives a
Chromium+WebKit e2e (indicator shows offline, the action disables, both clear on
reconnect). Full suite + build green.
- docs: ARCHITECTURE transport note, FUNCTIONAL (+ _ru), PLAN tracker (incl. #1 — the
bot already drains all updates, no change).
Also records #1 as investigated/no-change in PLAN. Other server-action buttons (chat
send, profile save, …) still degrade to a safe no-op offline; visual disable is easy to
extend.