Skip to content

perf(client-runtime): context-window updates stop re-sorting a thread's history - #231

Merged
tusharbhardwaj-bk merged 1 commit into
expbkmainfrom
t3code/perf-18-reducer-activity-append
Sep 27, 2026
Merged

tusharbhardwaj-bk merged 1 commit into
expbkmainfrom
t3code/perf-18-reducer-activity-append

Conversation

@tusharbhardwaj-bk

Copy link
Copy Markdown
Collaborator

Problem

The client applies each thread event with applyThreadDetailEvent (packages/client-runtime/src/state/threadReducer.ts). Plain activities already have an in-order fast path. context-window.updated activities didn't: every one took the slow path, which

  1. filters every activity,
  2. appends,
  3. sorts the whole array with the Effect comparator,
  4. rebuilds the id index.

On the busiest prod thread 1,673 of 8,340 activities (20%) are context-window updates. So replaying a long thread (a resume, or a kept-alive running thread catching up after a wake) paid a full sort for every fifth event. Found by the client-sync analysis subagent.

Fix

A marked block ahead of the slow path handles an in-order context-window update: the array is indexed (so it's known sorted), the id is unseen, and the new row sorts at or after the tail. It drops the same-turn resolvable rows the update supersedes in one linear pass, updates the id index in place, and appends.

That's exactly the slow path's result: removing rows from a sorted array keeps it sorted, and the new row belongs at the end. Out-of-order, redelivered or unindexed cases still take the slow path unchanged.

This is upstream code, so it's a good upstreaming candidate.

Evidence

Micro-benchmark: 1,000 events (25% context-window updates) replayed onto a 5,542-activity thread, 5 runs:

ms
before 359, 405, 351, 359, 353
after 48, 72, 50, 49, 49

Tests:

  • New threadReducer.contextWindow.expbkt3.test.ts: 3 seeded random streams of 600 activities (tool rows, context-window rows, malformed context-window rows, 40 per turn).
    • After every event the reducer's activity order equals a plain filter + append + sort reference.
    • A redelivered row afterwards still matches the reference.
  • Existing threadReducer.test.ts: all pass (58 total).
  • vp run typecheck in packages/client-runtime is clean, and the fork-marker check passes.

Reaches users: browser users on the next bkmain deploy; desktop users with the next desktop build.

Model/harness: Claude Opus 5.5 via Claude Code in T3 Code.

🤖 Generated with Claude Code

…'s history

Each context-window.updated activity (about 20% of a busy thread's
activities) took the reducer's slow path: filter every activity, append,
sort the whole array with the Effect comparator, and rebuild the id index.
Replaying 1,000 events onto a long thread paid that sort for each update.

An in-order update (unseen id, sorts at/after the tail of an indexed array)
now drops the rows it supersedes in one linear pass and appends, which is
the same result without the sort. Replay of 1,000 events onto a
5,542-activity thread: ~355 ms -> ~50 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M labels Sep 27, 2026
@tusharbhardwaj-bk
tusharbhardwaj-bk merged commit 45a429d into expbkmain Sep 27, 2026
9 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants