The auto-offline -> online return was dead because setOfflineMode never set the
'auto' flag: I added the state + getter but forgot the assignment, so it stayed
false. So scheduleRecovery bailed immediately (no poll ran) and the online event's
'if (active && auto)' was false. The contour diagnostic confirmed offline.auto
stayed false after an auto-offline. Add 'auto = on && !persist'.
Also remove the temporary network diagnostic panel (netdiag + the lobby strip) --
it did its job of pinpointing this.
A fixed strip in the lobby showing live navigator.onLine / offline.active /
offline.auto / connection.online plus a timestamped event log (offline/online
events, poll ticks, checkReachable results, mode changes) - to pinpoint where the
auto-offline -> online transition breaks on the contour. A 1s heartbeat logs
navigator.onLine flips even if the events never fire. Skipped in the mock; to be
reverted with the fix.
The auto-offline -> online return still did not fire: the retry hung on the
'online' event, which an installed PWA often does not deliver. Replace it with a
lightweight poll while in auto-offline that reads navigator.onLine (a reliable
live flag, unlike the event) and only hits the network for a reachability check
when the interface is actually up - so flight mode ON costs no radio, and flight
mode OFF is detected within ~4s and returns online. The 'online' event, when it
does fire, just kicks an immediate check. Runs only in auto-offline (deliberate
offline is the player's choice); wired for both the mid-session and cold-start
auto-offline paths.
Two mid-session issues found on the contour:
- Auto-offline did not return to online after the network came back: a single
reachability check right after the 'online' event failed (the interface is up
before the gateway is actually reachable again). Retry a few times with backoff
(tryReturnOnline) — that is what actually gets the app back online.
- An online game viewed while offline (flight mode on) still enabled its network
actions, so they hit the kill switch and raised 'something went wrong' toasts.
Gate them: netReady = isLocalGame || (connection.online && !offlineMode.active)
— a local game stays fully usable; an online game's make/exchange/hint/resign
disable while offline and re-enable when back. Also suppress the 'offline' code
in handleError (a blocked call in offline mode is expected, not a toast).
React to the network changing while the app is open (e.g. the player toggling
flight mode), via passive online/offline events - no polling, no battery cost:
- interface lost -> enter offline mode for the session (auto);
- interface back -> if the offline was auto, verify the gateway is really
reachable (an interface being up does not guarantee it) and return online; a
deliberate offline (the toggle or the cold-start dialog) is left as-is.
- offline.svelte: track `auto` (auto-detected vs the player's deliberate choice).
- connection.svelte: checkReachable is now a pure one-shot (the caller decides);
the reachability watcher never probes in offline mode (events drive recovery).
- transport.ts: the reachability probe is exempt from the kill switch - it IS
the mechanism that decides whether to return online, fired only deliberately.
- app.svelte.ts: initNetworkReactivity wires the events (web-only, skipped in
the mock); called from bootstrap.
Online unaffected (skipped in the mock e2e): e2e 196. Mid-session reactivity is
contour-verified.