* fix(sync): commit first message page before expansion loop (#2084) loadMessages committed the store only after the full expansion sequence (50→100→150), so the hydrating skeleton stayed for 3 sequential HTTP round-trips when a session tail had no user message boundary. Move the store write (materialize + setState) to happen after the first fetch. The expansion loop now commits each expanded page incrementally instead of overwriting a page variable and committing once at the end. The first commit is gated on hasUserMessage(page.session) || page.complete: if the tail is assistant-only, deferring to the expansion loop keeps the skeleton (loading state) instead of rendering an empty chat that looks like a fresh session. Sessions with a user boundary in the first 50 messages get content after a single round-trip. * fix(sync): address review nits for #2084 - deferred init uses page.session instead of [] so limit reflects the real fetched count if the expansion loop is ever a no-op - both stale branches in commitMessagesToStore return messages: [] for consistency - add isStale guard between expansion fetch and commit for defense-in-depth * fix(sync): always commit prepend-mode pages to store (#2084) The deferred init path (assistant-only tail) skipped commitMessagesToStore entirely when options.before was set — prepend mode. The fetched older messages were never written to the store, silently dropping them. Gate the deferral on !options.before: prepend mode always commits because messages are already rendered (no skeleton to protect) and skipping the store write would lose the fetched page. --------- Co-authored-by: bashrusakh <bashrusakh@users.noreply.github.com> Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>