mirror of
https://github.com/wu736139669/hapi.git
synced 2026-11-07 01:21:37 +00:00
* feat(android): global connection and runner version-skew banners
Two Home-screen chrome banners mirroring the web reference banner stack:
- Global connection banner: folds the SSE engine's attributed failure
notice (reconnectNoticeFailure, 4s grace) with the resync lifecycle
into one notice - Syncing (300ms debounce, 10s auto-clear cap,
depth-counted episodes) suppresses Reconnecting, which labels the
outage reason (heartbeat timeout / visibility recovery / connect
timeout / transport error with HTTP code / closed). The engine now
attributes every attempt end into ConnectionState.lastFailure,
cleared on the next successful handshake.
- Runner version-skew banner: capability-set comparison
(RunnerSkew.REQUIRED_MACHINE_CAPABILITIES, not semver) flags runners
missing agent-availability/cursor-chat-store-status; expanded card +
minimized chip, snooze 1h, manual restart via new
POST /api/machines/:id/restart-runner (restart gated on
newer-binary-on-disk mtime drift AND supervised restart).
StoreSyncTargets grows an onResyncEvent lifecycle hook (default no-op)
feeding the syncing half; HomeScreen grows optional chrome params
(null hides each banner).
Declared divergences from web/src:
- Banners mount on Home only, not above every route: an open chat
already surfaces its own pipe state ("Reconnecting... Tap to retry"),
so the two never double up on one screen (web mounts App-wide).
- The web OfflineBanner is not ported: a native app has no browser
online/offline event pair; hub reachability is expressed through the
reconnecting banner.
- Minimize/snooze state is process-lifetime memory (web: sessionStorage
- same session-scoped lifetime per platform); the snooze timer does
not survive process death.
- Runner rows filter only on capability skew; the active filter lives
upstream in MachineStore (web filters machine.active on its side).
Co-Authored-By: Claude Code <noreply@anthropic.com>
via [HAPI](https://hapi.run)
Co-Authored-By: HAPI <noreply@hapi.run>
* Fix codeless transport attribution and banner polish from review
- SseEngine: a codeless Failure (DNS/TLS/socket errors — the dominant
mobile outage shape) now attributes TransportError like the web's
unconditional onerror → transport-error mapping, instead of degrading
to a "connection closed" label.
- ConnectionChrome: a resync restart while the syncing banner is
already shown keeps it shown (web doStartSync reshuffles timers with
isSyncing staying true — no blink); the web's first-connect/30s-from-
background display gating is now a declared divergence.
- RunnerSkewBanner: the expanded card scrolls past a 320dp cap (web
40vh) so many skewed hosts cannot push the session list off-screen;
restart failures surface the hub body's parsed error field instead
of raw embedded JSON; handoff-hint/tooltip omissions declared.
Co-Authored-By: Claude Code <noreply@anthropic.com>
via [HAPI](https://hapi.run)
Co-Authored-By: HAPI <noreply@hapi.run>
* Show the reconnecting banner for auth-side outages with no failure reason
Review finding (ConnectionState.kt): a paired hub starting with a stale
JWT whose token refresh keeps failing cycles SseEngine Backoff without
any transport attempt running, so lastFailure stays null — after the 4s
grace the notice flow emitted null and ConnectionChrome read "no
banner". Home could sit disconnected indefinitely with no notice.
Outage visibility now lives on its own marker, independent of the
optional reason: reconnectOutageNotice (was reconnectNoticeFailure)
emits OutageNotice(lastFailure) once the grace elapses — non-null even
when no attempt attributed a reason — and ConnectionChrome maps any
OutageNotice to Reconnecting(failure), so a reason-less outage renders
the plain "Reconnecting…" strip (web shows the banner on sseDisconnected
regardless of reason; the parenthetical label simply drops).
Coverage added per review:
- repeated null-token retries keep the notice visible (engine level);
- null-reason outage still emits a notice after the grace (state level);
- outage without a reason still shows the banner (chrome level);
- the notice hides while backgrounded and returns with the foreground.
Resync debounce/cap and runner-restart error paths were already covered
(a sync shorter than the debounce…, a stuck sync auto-clears…, restart
failure surfaces the message…).
Co-Authored-By: Claude Code <noreply@anthropic.com>
---------
Co-authored-by: HAPI <noreply@hapi.run>
Co-authored-by: Claude Code <noreply@anthropic.com>