Skip to content

fix(web): paginate large thread history for remote clients#4018

Open
sercada wants to merge 7 commits into
pingdotgg:mainfrom
sercada:fix/paginate-thread-activity-history
Open

fix(web): paginate large thread history for remote clients#4018
sercada wants to merge 7 commits into
pingdotgg:mainfrom
sercada:fix/paginate-thread-activity-history

Conversation

@sercada

@sercada sercada commented Jul 15, 2026

Copy link
Copy Markdown

Summary

Fixes #2761.

This rebases the server and web implementation from #3510 onto current main, where that PR is now conflicting after the repository history changes. Original implementation commits retain @olafura as their author. It deliberately leaves native mobile out of scope.

  • bound initial thread-detail activity reads to the newest 500 rows
  • expose cursor-based older-activity pages over RPC
  • let web users load older history while preserving scroll position and live updates
  • reset and deduplicate paged history when the live window reshapes
  • avoid sidebar thread-detail prewarming in browser/headless clients; Electron keeps its current warm-up behavior

Why

A live remote-web reproduction showed that the server and Tailscale path returned a 12.6 MB snapshot in about 0.2 seconds, so transport was not the bottleneck. The browser was eagerly hydrating eight visible thread details totaling roughly 38 MB. The largest thread contained 1,758 activities and serialized to 16.4 MB.

With this change, a browser initially hydrates only the selected thread and only its newest 500 activities. In the same dataset that window is about 6.7 MB, while older history remains reachable on demand.

Verification

  • pnpm exec vp run typecheck
  • pnpm exec vp check — 0 errors; 9 existing nested-component warnings
  • pnpm exec vp test apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts — 11 passed
  • pnpm exec vp test packages/client-runtime/src/state/threadReducer.test.ts — 26 passed
  • pnpm exec vp test apps/web/src/components/chat/MessagesTimeline.test.tsx — 15 passed
  • full workspace build — web, server, marketing, and desktop completed

Related: #3817 addresses heartbeat tolerance, but does not reduce the large history payload or browser processing cost.


Note

Medium Risk
Touches projection read paths, a new RPC surface, and chat timeline state (cursors, dedup, reshape resets); behavior is heavily tested but wrong pagination could hide or duplicate activity rows on long threads.

Overview
Caps thread-detail activity loads to the 500 newest rows and sets hasMoreActivities when older rows exist, with a new orchestration.getThreadActivities RPC (sequenced beforeSequence or legacy beforeCreatedAt / beforeActivityId cursors) to page older history.

Web merges lazy-loaded pages into the live window in ChatView, resets on thread/window reshape, dedupes by id, and MessagesTimeline scroll-up auto-load (edge-triggered to avoid retry loops) plus a manual “Load older history” control. Browser sidebar no longer prewarms thread details; Electron still does. snapshot HTTP and CLI offline resolution switch from getSnapshot to getCommandReadModel so project-only consumers avoid loading full per-thread tables.

Reviewed by Cursor Bugbot for commit de8fd65. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Paginate large thread history for remote web clients

  • Caps thread-detail snapshots at the 500 most recent activities and sets hasMoreActivities on threads that have older history beyond this window.
  • Adds a new orchestration.getThreadActivities WebSocket RPC with cursor-based pagination, supporting both sequenced (beforeSequence) and legacy unsequenced (beforeCreatedAt/beforeActivityId) cursors.
  • ChatView now lazy-loads and prepends older activities on demand, merging them with the live window for display and the composer context meter.
  • MessagesTimeline renders a 'Load older history' header control or a loading indicator, and auto-triggers a single load when the viewport reaches the top, rearming only when the cursor advances or the user scrolls away.
  • Sidebar thread prewarming is disabled for browser clients and kept only for Electron, and the snapshot/offline-snapshot endpoints now return the lightweight command read model instead of the full projection snapshot.
  • Behavioral Change: remote clients no longer receive full thread activity history in the initial snapshot; older history must be fetched via the new paginated RPC.

Macroscope summarized de8fd65.

olafura and others added 3 commits July 15, 2026 16:45
A busy SQLite database materialised hundreds of MB of activity payloads into the
Node heap and crashed the server. Bound the reads and add an on-demand
pagination path so deep history is still recoverable.

- /api/orchestration/snapshot and the `t3` CLI offline path called getSnapshot(),
  loading every thread's full activity/message/checkpoint history even though
  they only read `.projects`. Point both at getCommandReadModel() (same shape,
  without the heavy per-thread tables).
- getThreadDetailById windows activities to the most recent 500 (it fetches
  WINDOW+1 to detect truncation and sets `hasMoreActivities` so clients can
  lazy-load). Live activities still stream in via the event subscription.
- New orchestration.getThreadActivities RPC pages older activities on demand:
  cursor is {beforeSequence} for sequenced rows or {beforeCreatedAt,
  beforeActivityId} for legacy unsequenced (NULL) rows, output {activities,
  hasMore}. The sequenced query orders by `sequence DESC` with
  `(sequence < ? OR sequence IS NULL)` so the (thread_id, sequence, created_at,
  activity_id) index satisfies the ORDER BY instead of a filesort. Unsequenced
  rows page by a (created_at, activity_id) cursor consistent with the window's
  ordering.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The server windows thread detail to the most recent 500 activities and reports
`hasMoreActivities`. The web timeline now fetches older pages on demand via the
getThreadActivities RPC:

- client-runtime: loadThreadActivities command on orchestrationEnvironment.
- ChatView keeps per-thread older pages in local state, prepends them ahead of
  the live window, and derives hasMore from the server flag (no client-side
  window-size constant). A thread-keyed in-flight ref coalesces the duplicate
  dispatches a fast scroll-to-top would otherwise fire, and a request-key guard
  discards results after a thread switch.
- MessagesTimeline triggers a load on reaching the top
  (maintainVisibleContentPosition anchors the viewport on prepend) with a
  "Load older history" header and loading indicator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0354f988-d9a3-4df0-ac72-7c2a2bf226c0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Jul 15, 2026
olafura and others added 2 commits July 15, 2026 16:55
Detect a live-window reshape from an order-independent oldest boundary instead of activities[0], so the first unsequenced live append does not incorrectly clear paged history.

Run the older-pages reset in useLayoutEffect to prevent one stale frame after a thread switch. Keep the shared chronology helper in client-runtime with unit coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sercada sercada force-pushed the fix/paginate-thread-activity-history branch from 6e8cf9b to 2c17c8b Compare July 15, 2026 21:55
Comment thread apps/web/src/components/chat/MessagesTimeline.tsx Outdated
@macroscopeapp

macroscopeapp Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a new pagination feature for thread activities with significant runtime behavior changes (windowing to 500 activities, new RPC endpoint, complex client-side lazy-loading state management). The scope and complexity warrant human review.

You can customize Macroscope's approvability policy. Learn more.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c9783d1. Configure here.

Comment thread apps/web/src/components/chat/MessagesTimeline.tsx
@olafura

olafura commented Jul 16, 2026

Copy link
Copy Markdown

I'm mostly using this branch which contains all the fixes and a tui https://github.com/olafura/t3code/tree/olafura/simple-tui been pretty busy I have some updates hit another limit 😁 I'm pushing some updates that you can cherry pick if you want

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

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Degraded performance of the remote webapp on large threads.

2 participants