Skip to content

fix(WhereUI): show splash before the first foreground reveal - #229

Open
kyleve wants to merge 3 commits into
mainfrom
codex/consistent-first-foreground-splash
Open

fix(WhereUI): show splash before the first foreground reveal#229
kyleve wants to merge 3 commits into
mainfrom
codex/consistent-first-foreground-splash

Conversation

@kyleve

@kyleve kyleve commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Posted by an AI agent on kve's behalf.

Summary

  • User-facing: show Where's branded splash for the existing 800 ms minimum before the first foreground-visible main UI, including a runner promoted from a headless background launch
  • User-facing: keep MainTabs warming beneath the splash, then reveal its honest state without replaying the splash on warm resumes
  • Internal: add an opt-in LifecycleReadyRevealPolicy while preserving LifecycleKitUI's phase-driven default for existing callers
  • Internal: model the first-foreground reveal protocol in TLA+, with broken-design controls and explicit render-order/resume reachability checks

Why

A background-ready runner can be promoted and finish its foreground drive between SwiftUI render passes. The container then observes .ready without ever observing the intervening splash phase, so its existing minimum-duration hold is never armed and the Locations loading surface can appear directly.

Review focus

  • the reveal task is keyed on both readiness and visibility, because readiness may remain true across foreground promotion
  • an actually rendered splash retains its original deadline, so .ready does not add a second 800 ms hold
  • headless phases, gates, failures, and zero-duration callers retain their existing behavior
  • FirstForegroundReveal abstracts the positive duration to deadline epochs and treats SwiftUI render coalescing nondeterministically; gates, failures, resets, scene recreation, and actual scheduler behavior remain explicitly excluded

Testing

  • ./swiftformat --lint
  • ./test LifecycleKitTests LifecycleKitUITests — 79 tests passed
  • ./test WhereUITests — 420 tests passed
  • ./test --snapshots — 42 tests passed; no reference changes
  • ./tla-check FirstForegroundReveal — 7 cases passed: 2 expected broken-design failures, 3 reachability controls, and 2 clean current bounds

// Include visibility in the identity: a background runner can remain
// `.ready` across foreground promotion, and that false → true transition
// is what must start an opt-in first-reveal hold.
.task(id: isReadyAndVisible) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Posted by an AI agent on kve's behalf.

Review focus: visibility is deliberately part of this task identity. A background runner can remain .ready before and after foreground promotion, so readiness alone would not restart the task that establishes the first visible splash hold.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Posted by an AI agent on kve's behalf.

Addressed in 9587d79. The hosted regression test now mounts a ready runner headlessly, applies a coalesced foreground-only reason change while the phase stays ready, proves the splash appears and releases, and was mutation-checked to fail if the task identity is reduced to readiness alone.

splashSeen, contentBuilt, contentRevealCount,
splashPhaseRendered, resumeCount>>

\* A SwiftUI update reads the runner atomically on the main actor. The

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Posted by an AI agent on kve's behalf.

Review focus: Render deliberately keeps the synchronous onChange hold update atomic with the main-actor view update, while StartReadyTask is a separate scheduled action. That source-correspondence assumption is load-bearing; if SwiftUI permits another main-actor mutation to interleave inside the synchronous callback, the model result becomes inconclusive until this boundary is revised.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant