Skip to content

perf(mobile): reuse the settled sort when settled rows are unchanged - #16369

Merged
juliusmarminge merged 1 commit into
t3code/mobile-perf/update-native-git-headerfrom
t3code/mobile-perf/reuse-settled-sort
Oct 8, 2026
Merged

juliusmarminge merged 1 commit into
t3code/mobile-perf/update-native-git-headerfrom
t3code/mobile-perf/reuse-settled-sort

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

On mobile Home, five streaming threads cause about 660 shell thread.updated events in 15 s. Each one rebuilds the thread list. Every rebuild re-sorted all settled shells (1204 in the fixture) with sortSettledThreads and re-parsed their timestamps, even though the streams only change unsettled rows. In the Hermes profile this sort accounted for 186 and 224 inclusive samples per capture.

buildThreadListV2Items now keeps one entry: the last settled input array and its sorted output. If the next settled array has the same length and the same elements (===) in the same order, it reuses the output. Otherwise it calls the unchanged sortSettledThreads and replaces the entry. This is safe because shells are immutable (upsertById replaces a row, and the scopedThread WeakMap keeps row identity) and the comparator reads only element fields. now, search, queued keys, and settlement gating all run before the settled array is built, so the identity check covers them. Home and the split-view sidebar share the entry, so a different scope is a cache miss and recomputes, never a stale hit. Retention is bounded to two arrays of references the shell store already holds. Paging previously did pagedSettled.push(selected), which could mutate the sort output. It now builds [...limited, selected], so the cached array is never mutated. Web is unchanged.

Measurement

Two serialized A/B pairs (before 1, after 1, before 2, after 2) on an iOS simulator release build (Hermes, no Metro). Native binary is identical, only the JS bundle is swapped. Before: bundle 068d9912… (#16330 head). After: bundle 92c76eea… built from 176294b18d. Its composed source map differs from the before map only in threadListV2.ts, and that file is byte-equal to the committed blob. Each run: Home visible, five synthetic 20 Hz streams for 15 s on unsettled threads, a 1210-shell fixture (Home shows "1204 settled threads"), and the same restored fixture and client DB. All 5 streams completed 300/300 in every run.

Capture App CPU (s) sortSettledThreads incl. buildThreadListV2Items incl. self dateParse self arrayPrototypeSort GC Active / total samples Active %, 0–16 s
before 1 4.55 186 248 46 75 253 2259 / 14539 18.45
after 1 4.34 0 52 0 2 229 1904 / 12549 17.30
before 2 4.45 224 274 57 86 275 2424 / 15052 18.70
after 2 4.28 0 71 3 0 274 2290 / 15007 17.72

App process CPU fell by 4.6% and 3.8% in the two pairs (0.01 s ps resolution). The settled sort is gone from both after captures. Sample counts are inclusive and overlap, so they must not be summed. After 1 has fewer total samples because it had 57 sampler gaps over 20 ms (about 2.0 s in total), all after 16.4 s, when the streams had ended. That makes its trace span 23.83 s, against a profiler window of about 22.59 s. Its raw active count is therefore not directly comparable. The last column normalizes to the gap-free 0–16 s window, where every run's largest gap is ≤14.4 ms.

This is a narrow workload: many settled threads with streams on unsettled ones. Nothing here measures latency, frames, or battery, and nothing ran on a physical device. Aggregate evidence (no traces or client data): settled-sort-evidence.json.

Tests

threadListV2.test.ts adds 5 tests under "buildThreadListV2Items settled sort reuse", for 102/102 passing. They cover the baseline order; a re-sort after a settled row is replaced, added, or removed; a re-sort when tied rows are reordered; a skipped sort when only an unsettled row changes (a Date.parse spy); and alternating environment scopes. Disabling the identity check fails 3 of them. Disabling reuse fails 1.

Opus 5.5 via Claude Code in T3 Code.

🤖 Generated with Claude Code


Devin Review

@juliusmarminge
juliusmarminge added this pull request to stack #15249 October 6, 2026 05:07
@github-actions github-actions Bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Oct 6, 2026
@juliusmarminge juliusmarminge added the macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews label Oct 6, 2026
@juliusmarminge
juliusmarminge marked this pull request as ready for review October 6, 2026 05:07
@github-actions github-actions Bot added the size:M 30-99 changed lines (additions + deletions). label Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ℹ️ No successful main baseline artifact is available yet. This run establishes the initial measurement.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire — 5.0 KiB — 6.8 KiB ✅
Codex Thread snapshot wire — 3.8 KiB — 4.9 KiB ✅
Codex Live turn WebSocket wire — 1.2 KiB — 2.0 KiB ✅
Codex Live turn WebSocket decoded — 20.9 KiB — 29.3 KiB ✅
Codex Live turn messages — 2 — 8 ✅
Claude Total thread wire — 5.0 KiB — 6.8 KiB ✅
Claude Thread snapshot wire — 3.8 KiB — 4.9 KiB ✅
Claude Live turn WebSocket wire — 1.2 KiB — 2.0 KiB ✅
Claude Live turn WebSocket decoded — 21.2 KiB — 29.3 KiB ✅
Claude Live turn messages — 2 — 8 ✅

Baseline: unavailable · PR result: 35ab4d9 · Source CI: success

Scenario and decoded snapshot size

10 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.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@macroscopeapp

macroscopeapp Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR adds production-path memoization that automatically skips sorting and timestamp parsing for unchanged settled-thread rows, with behavior guarded by identity checks and focused tests. Because it changes when significant list-processing work executes on the mobile request path, an additional human review is warranted despite the localized diff.

No code changes detected at 35ab4d9. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Important

Review skipped

We couldn't safely recover the incremental review. No full review was started, and the last reviewed checkpoint was preserved. Retry later, or explicitly request a full review by commenting @coderabbitai full review.

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: pingdotgg/t3code/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Team
  • Run ID: e689f482-359a-4c5c-be0f-0c174ec080ba
📥 Commits

Reviewing files that changed from the base of the PR and between 7bff9d5 and 176294b.

📒 Files selected for processing (2)
  • apps/mobile/src/features/threads/threadListV2.test.ts
  • apps/mobile/src/features/threads/threadListV2.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.


📝 Walkthrough

Walkthrough

The thread list now reuses settled-row sorting when its input references are unchanged. It also keeps a selected settled thread visible when that thread falls beyond the page limit.

Changes

Settled Thread List

Layer / File(s) Summary
Settled sorting and cache validation
apps/mobile/src/features/threads/threadListV2.ts, apps/mobile/src/features/threads/threadListV2.test.ts
A one-entry cache reuses the sorted settled rows when the input references match. Tests cover newest-first ordering, input changes, stable ordering for equal timestamps, parse counts, and alternating environment scopes.
Selected thread paging
apps/mobile/src/features/threads/threadListV2.ts
A selected thread beyond the limit-sized settled prefix remains visible. The prefix continues to determine the displayed settled page.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Refactor

Suggested reviewers: t3dotgg

Merge Risk: ⚪ Minimal · up to 17629

This change skips re-sorting settled threads when their rows are unchanged, which reduces CPU use on Home while threads stream. No concrete defect was found, so the merge risk is low.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main change: reusing the settled-thread sort when settled rows are unchanged.
Description check ✅ Passed The description explains the problem, implementation, and focused verification in detail. It does not include the required Scope and approval section or explain why prior approval is not needed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@juliusmarminge
juliusmarminge force-pushed the t3code/mobile-perf/reuse-settled-sort branch from 176294b to e1017c4 Compare October 7, 2026 02:34
@juliusmarminge
juliusmarminge force-pushed the t3code/mobile-perf/reuse-settled-sort branch 2 times, most recently from 8559aca to c2c19ab Compare October 8, 2026 05:31
@juliusmarminge
juliusmarminge force-pushed the t3code/mobile-perf/reuse-settled-sort branch from c2c19ab to 2076011 Compare October 8, 2026 05:39
Streams update only unsettled threads, yet every Home list rebuild re-sorted
all settled shells and re-parsed their timestamps. Keep the last settled
input and output and reuse it when the new settled array is element-identical.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@juliusmarminge
juliusmarminge force-pushed the t3code/mobile-perf/reuse-settled-sort branch from 2076011 to 35ab4d9 Compare October 8, 2026 05:41
@juliusmarminge
juliusmarminge merged commit b402f5f into main Oct 8, 2026
34 of 36 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/mobile-perf/reuse-settled-sort branch October 8, 2026 05:49
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Oct 8, 2026
## What's Changed
* chore(review): remove the custom Approvability check by @esthor in pingdotgg/t3code#17018
* perf(mobile): show the cached thread list sooner and stop the freeze after it by @juliusmarminge in pingdotgg/t3code#16713
* fix(server): replaying a command no longer freezes the server by @SunkenInTime in pingdotgg/t3code#17041
* fix(server): opening a long thread no longer blocks the server for a second by @SunkenInTime in pingdotgg/t3code#17029
* fix(server,web,mobile): thread links reference the thread id, not a baked title by @juliusmarminge in pingdotgg/t3code#17017
* fix(web): messages with quotes or links no longer collapse when short by @flamboh in pingdotgg/t3code#16624
* refactor(web): PR loading skeleton shares the detail panel's layout by @flamboh in pingdotgg/t3code#15583
* fix(web): PR file headers keep the file name in narrow panels by @flamboh in pingdotgg/t3code#15562
* fix(web): PR timeline no longer shifts when its scrollbar appears by @flamboh in pingdotgg/t3code#15588
* fix(server): held queued wakes no longer keep delegated tasks running by @juliusmarminge in pingdotgg/t3code#17028
* fix(server): a message sent during a rollback no longer undoes it by @t3dotgg in pingdotgg/t3code#17079
* fix(web): PR file stats ignore the hide-whitespace toggle by @flamboh in pingdotgg/t3code#16162
* perf(mobile): omit duplicated turn items from bounded thread snapshots by @juliusmarminge in pingdotgg/t3code#15385
* perf(mobile): pause elapsed-time timers on hidden thread screens by @juliusmarminge in pingdotgg/t3code#15397
* fix(mobile): pause hidden home thread list updates by @juliusmarminge in pingdotgg/t3code#15705
* fix(mobile): restore file viewer insets and glass header by @juliusmarminge in pingdotgg/t3code#17073
* fix(web): PR code toolbar no longer overlaps in narrow panels by @flamboh in pingdotgg/t3code#15561
* fix(web): PR commit menu no longer stretches across the window by @flamboh in pingdotgg/t3code#15560
* fix(web): command palette scrollbar no longer clipped at the top by @flamboh in pingdotgg/t3code#17035
* fix(web): Usage breadcrumb stays centered on small viewports by @flamboh in pingdotgg/t3code#15552
* fix(mobile): stop refreshing Git status on streamed thread updates by @juliusmarminge in pingdotgg/t3code#15893
* fix(mobile): skip move indexes for empty and single-thread sections by @juliusmarminge in pingdotgg/t3code#16115
* perf(mobile): reuse encoded rows in shell cache saves by @juliusmarminge in pingdotgg/t3code#16129
* perf(mobile): skip showcase subscriptions in normal builds by @juliusmarminge in pingdotgg/t3code#16131
* perf(mobile): remove unused Home project sorting by @juliusmarminge in pingdotgg/t3code#16177
* perf(mobile): reduce move-menu index allocations by @juliusmarminge in pingdotgg/t3code#16256
* perf(mobile): skip impossible thread-key lookups by @juliusmarminge in pingdotgg/t3code#16263
* fix(mobile): collect UI runtime garbage on iOS memory warnings by @juliusmarminge in pingdotgg/t3code#16296
* fix(mobile): stop Git sheet refresh loop by @juliusmarminge in pingdotgg/t3code#16305
* fix(mobile): refresh Git status after reconnect by @juliusmarminge in pingdotgg/t3code#16329
* fix(mobile): update the iOS Git header menu when status changes by @juliusmarminge in pingdotgg/t3code#16330
* perf(mobile): reuse the settled sort when settled rows are unchanged by @juliusmarminge in pingdotgg/t3code#16369
* perf(mobile): highlight source files in small batches that keep grammar state by @juliusmarminge in pingdotgg/t3code#16729
* feat(web): quote chips show what you said about the quote by @flamboh in pingdotgg/t3code#15703
* feat(web): projectless threads show their machine in the sidebar by @flamboh in pingdotgg/t3code#17022
* fix(lineage): keep agent effort and speed after completion by @Bil0000 in pingdotgg/t3code#16925
* fix(mobile): align diff scrolling with glass headers by @juliusmarminge in pingdotgg/t3code#17085
* fix(web): workspace page headers can no longer grow past the top-bar height by @maria-rcks in pingdotgg/t3code#17086
* feat(mobile): redesign the Add environment sheet by @juliusmarminge in pingdotgg/t3code#17092


**Full Changelog**: pingdotgg/t3code@v0.0.46-nightly.20261008.2801...v0.0.46-nightly.20261008.2813

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.46-nightly.20261008.2813
github-actions Bot added a commit to davidvanderklay/t3code-flake that referenced this pull request Oct 8, 2026
## What's Changed
* chore(review): remove the custom Approvability check by @esthor in pingdotgg/t3code#17018
* perf(mobile): show the cached thread list sooner and stop the freeze after it by @juliusmarminge in pingdotgg/t3code#16713
* fix(server): replaying a command no longer freezes the server by @SunkenInTime in pingdotgg/t3code#17041
* fix(server): opening a long thread no longer blocks the server for a second by @SunkenInTime in pingdotgg/t3code#17029
* fix(server,web,mobile): thread links reference the thread id, not a baked title by @juliusmarminge in pingdotgg/t3code#17017
* fix(web): messages with quotes or links no longer collapse when short by @flamboh in pingdotgg/t3code#16624
* refactor(web): PR loading skeleton shares the detail panel's layout by @flamboh in pingdotgg/t3code#15583
* fix(web): PR file headers keep the file name in narrow panels by @flamboh in pingdotgg/t3code#15562
* fix(web): PR timeline no longer shifts when its scrollbar appears by @flamboh in pingdotgg/t3code#15588
* fix(server): held queued wakes no longer keep delegated tasks running by @juliusmarminge in pingdotgg/t3code#17028
* fix(server): a message sent during a rollback no longer undoes it by @t3dotgg in pingdotgg/t3code#17079
* fix(web): PR file stats ignore the hide-whitespace toggle by @flamboh in pingdotgg/t3code#16162
* perf(mobile): omit duplicated turn items from bounded thread snapshots by @juliusmarminge in pingdotgg/t3code#15385
* perf(mobile): pause elapsed-time timers on hidden thread screens by @juliusmarminge in pingdotgg/t3code#15397
* fix(mobile): pause hidden home thread list updates by @juliusmarminge in pingdotgg/t3code#15705
* fix(mobile): restore file viewer insets and glass header by @juliusmarminge in pingdotgg/t3code#17073
* fix(web): PR code toolbar no longer overlaps in narrow panels by @flamboh in pingdotgg/t3code#15561
* fix(web): PR commit menu no longer stretches across the window by @flamboh in pingdotgg/t3code#15560
* fix(web): command palette scrollbar no longer clipped at the top by @flamboh in pingdotgg/t3code#17035
* fix(web): Usage breadcrumb stays centered on small viewports by @flamboh in pingdotgg/t3code#15552
* fix(mobile): stop refreshing Git status on streamed thread updates by @juliusmarminge in pingdotgg/t3code#15893
* fix(mobile): skip move indexes for empty and single-thread sections by @juliusmarminge in pingdotgg/t3code#16115
* perf(mobile): reuse encoded rows in shell cache saves by @juliusmarminge in pingdotgg/t3code#16129
* perf(mobile): skip showcase subscriptions in normal builds by @juliusmarminge in pingdotgg/t3code#16131
* perf(mobile): remove unused Home project sorting by @juliusmarminge in pingdotgg/t3code#16177
* perf(mobile): reduce move-menu index allocations by @juliusmarminge in pingdotgg/t3code#16256
* perf(mobile): skip impossible thread-key lookups by @juliusmarminge in pingdotgg/t3code#16263
* fix(mobile): collect UI runtime garbage on iOS memory warnings by @juliusmarminge in pingdotgg/t3code#16296
* fix(mobile): stop Git sheet refresh loop by @juliusmarminge in pingdotgg/t3code#16305
* fix(mobile): refresh Git status after reconnect by @juliusmarminge in pingdotgg/t3code#16329
* fix(mobile): update the iOS Git header menu when status changes by @juliusmarminge in pingdotgg/t3code#16330
* perf(mobile): reuse the settled sort when settled rows are unchanged by @juliusmarminge in pingdotgg/t3code#16369
* perf(mobile): highlight source files in small batches that keep grammar state by @juliusmarminge in pingdotgg/t3code#16729
* feat(web): quote chips show what you said about the quote by @flamboh in pingdotgg/t3code#15703
* feat(web): projectless threads show their machine in the sidebar by @flamboh in pingdotgg/t3code#17022
* fix(lineage): keep agent effort and speed after completion by @Bil0000 in pingdotgg/t3code#16925
* fix(mobile): align diff scrolling with glass headers by @juliusmarminge in pingdotgg/t3code#17085
* fix(web): workspace page headers can no longer grow past the top-bar height by @maria-rcks in pingdotgg/t3code#17086
* feat(mobile): redesign the Add environment sheet by @juliusmarminge in pingdotgg/t3code#17092


**Full Changelog**: pingdotgg/t3code@v0.0.46-nightly.20261008.2801...v0.0.46-nightly.20261008.2813

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.46-nightly.20261008.2813
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews size:M 30-99 changed lines (additions + deletions). 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.

1 participant