Skip to content

perf(bootstrap): lazy thread hydration with listing snapshot#38

Merged
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration
Apr 10, 2026
Merged

perf(bootstrap): lazy thread hydration with listing snapshot#38
tyulyukov merged 2 commits into
mainfrom
feature/perf/lazy-hydration

Conversation

@tyulyukov
Copy link
Copy Markdown
Owner

Summary

Implements a two-phase bootstrap strategy to unblock sidebar rendering while background-loading thread data:

  • Phase 1 (Listing Snapshot): Fast initial load fetches projects + lightweight OrchestrationThreadSummary (metadata, session, latest turn, pre-computed latestUserMessageAt). Skips messages, activities, checkpoints, and proposed plans. Sets bootstrapComplete = true to render sidebar immediately.
  • Phase 2 (Lazy Hydration): When user navigates to a thread, full data is fetched on-demand via getThread() and merged into the store. The hollow thread structure persists until hydrated.

Additional improvements:

  • Added database indexes on projection_thread_messages and projection_turns for listing snapshot query performance
  • Sidebar shows skeleton loaders during bootstrap instead of blank state
  • Text reveal animation now triggers scroll-to-reveal for large messages during hydration
  • Sidebar summary fields (hasPendingApprovals, hasPendingUserInput, hasActionableProposedPlan) default to false and are corrected by real-time domain events within seconds

Architecture:

  • New server-side getListingSnapshot() and getThread(threadId) methods in ProjectionSnapshotQuery
  • WebSocket endpoints: orchestration.getListingSnapshot and orchestration.getThread
  • Frontend isThreadHydrated() check in _chat.$threadId.tsx triggers hydration on navigation
  • Structural sharing preserved in store so unhydrated threads don't trigger unnecessary re-renders

Testing

  • bun fmt passes
  • bun lint passes
  • bun typecheck passes
  • Manual: Sidebar renders immediately with skeleton loaders on cold start
  • Manual: Clicking a thread fetches and merges full data without UI jank
  • Manual: Real-time domain events update sidebar summary fields after hydration
  • Manual: Text reveal animation scrolls large messages into viewport during hydration
  • Manual: Switching between threads preserves hydrated data; hollow threads are skipped
  • Manual: Reconnect/snapshot recovery still uses full getSnapshot() as fallback

- Fetch lightweight listing snapshot (thread summaries only) on initial load
- Sidebar renders immediately with hollow threads and skeleton indicators
- Full thread data fetches on-demand via getThread(threadId) on navigation
- Reduce initial JSON parsing and unblock UI during app startup
- Add text reveal animation on message completion with smooth scroll anchoring
- Add ResizeObserver for content height tracking and scroll synchronization
- Threads hydrate incrementally from domain events or full snapshot fallback
- Suppress notifications for 5s after user stops/interrupts to prevent spurious completion notifications
- Improve scroll behavior during agent execution: respect user scroll intent, use multiple timeout attempts
- Optimize paint performance by disabling content-visibility auto for messages near bottom
- Enhance text reveal animation to support checkboxes and horizontal rules
@tyulyukov tyulyukov merged commit 0802522 into main Apr 10, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant