fix(WhereUI): show splash before the first foreground reveal - #229
fix(WhereUI): show splash before the first foreground reveal#229kyleve wants to merge 7 commits into
Conversation
| // 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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
…t-foreground-splash # Conflicts: # Where/WhereUI/AGENTS.md
Summary
MainTabswarming beneath the splash, restart an interrupted first-reveal hold on return, and avoid replaying the splash after content has actually appearedLifecycleReadyRevealPolicywhile preserving LifecycleKitUI's phase-driven default for existing callersWhy
A background-ready runner can be promoted and finish its foreground drive between SwiftUI render passes. The container then observes
.readywithout ever observing the intervening splash phase, so its existing minimum-duration hold is never armed and the Locations loading surface can appear directly.Launch reason is also not scene visibility: once promoted, a runner remains
.userForeground. Without a separate active-scene input, the splash deadline can expire while the app is inactive and the next foreground can reveal main UI that was never previously visible.Review focus
releasingstate; only SwiftUI committing the uncovered content marker makes the reveal permanentFirstForegroundRevealmodels one pre-reveal interruption and assumes SwiftUI commits the inactive presentation update before reactivation; scene destruction, multiple windows, and actual scheduler behavior remain explicitly excludedTesting
./swiftformat --lint— 0 files require formatting./test LifecycleKitUITests— 30 tests passed./test WhereUITests— 425 tests passed./test --snapshots— 42 tests passed; no reference changes./tla-check FirstForegroundReveal— 8 cases passed: 2 expected broken-design failures, 4 reachability controls, and 2 clean current bounds