Note
Grok responding on behalf of Julius.
Triage
Thanks for the cold-launch timings and the clear comparison between refused and silent connections, @saphid! This is a real bug. I confirmed it on t3code/rebuild-mobile-app-swift at bb01adbf90, the branch tip you named (open PR #5178). It only affects the experimental SwiftUI client. The React Native app we ship doesn't use this startup path.
To answer your question: cold start should show the active environment as soon as its own shell is ready, and passive environments should fill in afterwards. Waiting for every enabled environment before leaving "Connecting to T3 Code" isn't the intended behavior. That matches foreground resume, which already says other saved computers must not delay recovery.
What happens today
FeatureRootModel.isLoading starts out true, and FeatureRootView shows "Connecting to T3 Code" until start() finishes initialSnapshot(). That function loads every enabled environment, both active and passive, through loadEnvironmentShells, and it only returns once all of them have finished. Polling for the active computer (startPolling) doesn't start until after that.
The shell requests run in parallel, each with the 6s timeout, so the splash stays up until the slowest one finishes. A peer that accepts the connection but never replies holds home for the whole timeout. A refused connection fails right away. That matches your table: about 2s when the port is closed, and about 8s when it accepts and stays silent.
There's one more wait inside that same group. When a passive environment has no cached config, its task fetches the catalogue after a successful shell, and on a cold start nothing is cached yet. Your hung run didn't hit this, because a failed shell returns before the catalogue request. I didn't measure a peer that's slow but succeeds.
Disabling the unresponsive environment avoids the wait, since initialSnapshot only loads enabled environments. I got that from the source and didn't retest it.
Suggested fix
- Return the first snapshot as soon as the active environment's shell is ready, and start its polling then.
- Publish each passive environment when its own shell finishes. Until then, leave it unset rather than marking it disconnected early, and don't keep the loading screen up.
- If a passive shell fails or times out, keep any cached rows and mark that environment disconnected. A successful response should still apply when it arrives, no matter how long it took.
- Keep each passive environment's catalogue fetch inside that environment's own task.
- Leave
resumeAfterBackgroundas it is.
This fix belongs in initialSnapshot. A fix for #14691 in the later refresh loop wouldn't change cold start, because initialSnapshot still waits for the full group.
Related
#14691 (open issue) covers the passive refresh loop after launch. #10761 (closed PR) rewrote cold-start hydration along with passive workers and cache ownership. It was closed under the prior approval rule because that broader redesign had no triaged issue. This report covers just the cold-start wait, and accepting it doesn't mean adopting that rewrite.