Repository navigation
perf(server): fork threads load faster when local turns fill the first page - #20104
juliusmarminge wants to merge 7 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the PR's fork cursor test with one that compares the bounded snapshot of a fork against the full projection. With nine local turns the page still includes inherited parent rows and reads their bodies; with ten it skips them, and positions, the fork marker, cursor and hasMoreHistory still match the full read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t page After #17387 an initial bounded snapshot of a forked thread still read and decoded its parent's retained turn items, including large tool outputs, even when the fork's own user turns filled the returned page. Those inherited rows are dropped during bounding; only their count sets the positions and the history cursor. Share the window selection between the payload read and a count, and when local turns fill the page count the parent's retained rows in SQL instead of reading their bodies. The count query returns NULL for a parent that is itself a run fork, which keeps the full ancestry read for nested forks. Closes #18915 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A fork of a fork inherits the grandparent's rows through its parent, so counting only the parent's own rows would misplace the page. Add the nested case to the filled fork page test; it fails when the count query stops excluding run-forked parents. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ApprovabilityVerdict: Needs Human Approval Macroscope's review found this PR requires human approval — This production optimization changes initial fork snapshot preparation by automatically replacing inherited payload reads with a count when local turns fill the first page. Although focused tests verify output and cursor parity, the automatic decision to skip substantial request-path processing warrants human review. You can add or adjust custom eligibility rules. Learn more. |
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
When a fork's own user turns fill the first page, its initial bounded snapshot no longer reads and decodes the parent's retained turn items. The contributor measured filled-fork snapshot preparation going from 5.51 ms to 3.70 ms, with 31% less CPU. Every other thread shape is unchanged.
Problem
After #17387, an initial bounded snapshot of a forked thread still read and decoded the parent's retained turn items, including large tool outputs. It did this even when ten of the fork's own user turns filled the page. Those inherited payloads are discarded during bounding. Only their count matters, because it sets item positions and the history cursor.
Change
ProjectionStore.tsmoves into one helper,retainedWindowRows(threadId, window). The payload read and a new count read both use it, so they select the same retained rows by construction.>= max(userTurnLimit, 10));buildVisibleTurnItemsoffsets local positions by the parent's retained-row count instead of loading the inherited rows.Scope and approval
Closes #18915. A focused follow-up to #17387. Replaces #18071 (and its earlier copy #17406). The idea and query selection are the contributor's. This version shares one CTE helper instead of duplicating it, and adds a nested-fork guard and a comparison test.
Verification
ProjectionStore.test.tstest compares the bounded first page against the full read: thegetThreadSnapshottail, plus thebuildBoundedThreadProjectioncursor andhasMoreHistory. It covers three shapes:ProjectionStore.test.ts,threadHistoryPaging.test.tsandThreadStream.test.tspass, 77/77, after merging current main.vp run typecheckin apps/server, targeted lint and format: clean.run_id, rolled-back or cancelled parent runs, deleted sources, subagent forks, and recentProjectionStorechanges (fix(server): threads woken by notifications no longer load their whole history #19423, perf(server): commands on busy threads stop decoding thousands of finished tool nodes #19675, fix(server): worktree cleanup no longer runs sweeps back to back #19418).Original work by @bompus in #18071, taken over and finished by maintainers.
Claude Opus 5.5 and Claude Fable 5.1 in Claude Code, with review by GPT-6.1 Sol in Codex.
🤖 Generated with Claude Code