Repository navigation
perf(web): saving the thread list cache no longer re-encodes every thread - #19915
Merged
juliusmarminge merged 8 commits intoOct 16, 2026
Merged
Conversation
…ead on the UI thread
Contributor
ApprovabilityVerdict: Needs Human Approval Macroscope's review found this PR not approvable — This production-path change replaces web shell-cache encoding with shared memoization and browser task yielding, while relocating the existing mobile encoder and adding focused tests. It also adds a file-level directive suppressing two static-analysis diagnostics, so the change requires human review. You can add or adjust custom eligibility rules. Learn more. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
|
Note Claude Opus 5.5 responding on behalf of Julius. Taking over this one. I will push follow-up commits to this branch on top of yours and merge |
…equality and row reuse The encoder moved from mobile into client-runtime, so its tests move with it. Three cases remain: output matches the schema codec byte for byte across a warm save with changed, added and archived rows; an unchanged row's encoding is reused rather than re-encoded; and the default yield is a host timer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The encoder moved from apps/mobile to packages/client-runtime, but the takeover's first pass replaced main's nine encoder tests with three new ones. Move main's `cooperative shell cache encoding` block verbatim into packages/client-runtime next to the encoder instead, keeping only the fixture it needs, and leave the mobile store tests as they are on main. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… cache Keep the encoder's zero-delay timer as the default yield so mobile keeps its behavior without passing anything, and scope the timer diagnostic suppression to the one call instead of the whole module. Correct the comment: Effect's own yield goes through setImmediate where the host has it, which on React Native is a microtask shim. On web, a MessageChannel message resumes encoding in a fresh task, so input and rendering get a turn between chunks without the 4 ms nested timer clamp or background-tab throttling. Closing the port on interrupt is enough cleanup; a closed port delivers nothing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Saving the thread list cache in the web client now encodes only the threads that changed. With 1,417 real threads (4.6 MB), a save after one thread changes drops from 791 ms to 11 ms of UI-thread work in Chrome, and the saved bytes are identical.
Problem
Every
saveShellin the web client ran a fullSchema.encodeEffectover every thread row, even when one row had changed. The shell cache saves every 10 seconds while threads update, so a large thread list kept the UI thread busy for most of a second each time.encodeShellSnapshotForCachehad kept this cheap before the V2 orchestrator (#2829) removed it.Change
makeStoredShellSnapshotEncoder, perf(mobile): reuse encoded rows in shell cache saves #16129) moves fromapps/mobile/src/connection/shell-cache-encoding.tstopackages/client-runtime/src/platform/shellCacheEncoding.ts, with its tests. Shell rows are immutable and kept by reference, so the encoder remembers each row's encoding and only runs the row codec for new rows.saveShelluses it and yields between chunks with aMessageChanneltask. A zero-delay timer is clamped to 4 ms after a few nested calls and throttled to about once a second in background tabs, and the shell flush also runs on disconnect.setTimeout(0)yield. Its behavior is unchanged.Schema.fromJsonString(StoredOrchestrationShellSnapshot). The web client now keeps one encoded copy of each live row, as mobile already does: about 15 MB of heap for 1,600 rows with 10 pull request links each.Scope and approval
Fixes #18926, which was triaged as an accepted regression. The triage comment asked for exactly this scope: move the encoder into client-runtime, call it from
saveShell, keep byte equality with a test like mobile's, keep mobile's yield, use a browser-friendly yield on web, and leave payload size for #18907.Verification
packages/client-runtime/src/platform/shellCacheEncoding.test.ts: byte equality, row reuse, chunked yields, invalid rows, concurrent saves, interruption and failed encodes. 9/9 pass. The mobile store tests that stay behind pass (8/8), as do webstorage.test.ts(10/10).vp run typecheckin client-runtime, web and mobile, plus targeted lint and format: clean.Original work by @bompus, 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