Skip to content

fix(client-runtime): foreground retries every 15 s and phones keep a connection log - #193

Merged
jayleaton merged 1 commit into
mainfrom
fix/mobile-30s-drop-20261104
Nov 4, 2026
Merged

jayleaton merged 1 commit into
mainfrom
fix/mobile-30s-drop-20261104

Conversation

@jayleaton

Copy link
Copy Markdown
Owner

JJ's phone connects after a Tailscale reset, drops about 30 s later, then shows "reconnecting" for minutes. The server logs show the phone's path dying, not the app stalling. This PR keeps a foregrounded app retrying at least every 15 s, and gives the phone a connection log so the next failure explains itself.

Evidence (2026-11-04)

Source Finding
dev-box ws log Phone (T3Agents/10, iOS) connected at 16:42:08 and sent its last frame at 16:42:33.6 (the 25 s activity report). The server reset it as client-silent at 16:44:03.8. Every phone socket that day (11:42, 11:44, 11:46, 15:27, 16:44) ended the same way. 0 send-stalled.
Studio-01 ws log 0 send-stalled, 9 client-silent, all from mobile. The 16:42:10 socket ended after 103 s as Interrupted.
After the drop Only one phone request reached either server in the next 12+ minutes (a descriptor GET at 16:44:16). The phone wasn't hammering a dead socket; it was barely reaching anything.
dev-box tailscaled The iPhone's endpoint flapped every 10–15 s between cellular IPv4 and two IPv6 addresses, with no path from 16:40:48 to 16:43:48. A direct tailscale ping worked again at ~17:03 (46 ms).
Shell fetch timing 10–15 s server-side for the phone over cellular, vs 0.8 s at 11:42 and 0.6 s for the Mac. Network, not CPU.
JS stall bench Decoding and applying a real shell snapshot is far below the 25 s/90 s budgets. Ruled out.

What each hypothesis does now

Diagnostics

  • client-runtime keeps a per-environment log of the last 50 transitions (attempt / connected / failed / lost / waiting), each with route, stage, reason, detail (≤160 chars), network, and fg/bg.
  • Settings → Environments → → Connection log shows the newest 10 and copies all 50.
  • Mobile activity reports carry up to 10 new failed/lost events (connectionTrouble, optional, schema-capped). The server logs them as client reported connection trouble and doesn't store them.

Surfaces

  • Mobile: foreground cap, log UI, trouble reporting.
  • Web/desktop: foreground cap via page visibility. Log UI is mobile only (desktop users can read devtools).
  • Server: one log line. The contract change is an optional field, so old clients and servers interoperate.
  • Providers: n/a.

Checks

vp run check:changed: format, lint, knip, tests, and react compiler pass. Its typecheck step can't spawn in this sandbox (seccomp SUN_LEN), so I ran tsc --noEmit directly in client-runtime, contracts, mobile, web, server, and desktop: clean. lint:mobile only runs native checks, and ktlint/swiftlint aren't installed here; no native code changed.

Shipping: JJ's iOS build has OTA disabled, so this needs a new TestFlight build.

Opus 5.5 via Claude Code in T3 Code.

🤖 Generated with Claude Code

…connection log

A phone that lost its path (server-side client-silent reset) could back off for
minutes while the user was looking at it. Foregrounded clients now cap backoff
at 15 s. Each environment keeps its last 50 connection events, viewable and
copyable in mobile Settings, and failures ride the next activity report so the
server log shows what the phone saw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L labels Nov 4, 2026
@jayleaton
jayleaton merged commit 798be96 into main Nov 4, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant