perf(mobile): thread list resumes from the saved cache, and large gaps reload over gzip HTTP#32287
On a cold start every client downloaded the full thread list, even when its saved copy was a few events behind. When a client did resume from a cursor beyond the server's replay cap (1,000 events, 8 MB), the server answered with a full snapshot over the socket. iOS does not compress that, so it arrived at 1.37 MB instead of the 88 KB the gzip HTTP route costs.
Fix
- Resume from the saved cache. The shell cache now records the snapshot its rows came from as
origin: server version, database migration, omission flags, andsnapshotAt. A client whose cache matches its subscription and is under 12 hours old subscribes withafterSequenceinstead of loading a fresh snapshot. Replay never renewssnapshotAt, so drift stays bounded. snapshot-required. A resuming client sendshttpSnapshotFallback: true. If the server cannot replay that cursor (over the gap cap or pruned), it answers with onesnapshot-requireditem and doesn't send the snapshot. The client then loads the gzip HTTP snapshot and subscribes again from it.- A cursor from a snapshot that was just loaded never sends the flag, so a refusal cannot loop.
- Old servers ignore the flag, and old clients never send it.
- Items buffered from a superseded session are dropped, and a resume that catches up bumps
generation.
Scope and approval
Maintainer-authored. It replaces #30732 at the maintainers' direction, after measuring replay against a fresh snapshot. Layer 1 of 2; #32288 builds on it.
Measurements
Measured on a real database with 2,279 threads:
| path | wire | server CPU | client CPU (JIT / interpreter) |
|---|---|---|---|
| HTTP snapshot (gzip) | 88.5 KB | ~210 ms | 11.6 / 40 ms |
| replay ≤ 1,000 events (deflate) | 7 KB | ~125 ms | 0.6 / 3.2 ms |
| over-cap snapshot over the iOS socket (before) | 1.37 MB | ~160 ms | — |
Replay beats a snapshot on every metric up to the cap. Past roughly 1,200 dense events it costs about as much as a snapshot, which is why the cap stays at 1,000.
Real history decides how often the cap is hit: replay covers 96% of returns after 5 minutes away, 50% after 30 minutes, 25% after 2 hours, and 4% after 8 hours. Large gaps were the common case for heavy users, and those now reload over gzip HTTP.
Verification
- Tests:
shell-sync.test.tsgains a resume table and asnapshot-requiredcase, covering cold and warm. Reverting the loop guard fails 6 cases.ws.test.tsgains a pruned-boundary row.- The cache-store test covers the
originround-trip.
- Web, against a copy of a 2.1 GB real database:
- Reloading made no shell snapshot request.
- On a warm reconnect against pruned history, the trace showed a refused subscribe (13 ms), then one HTTP snapshot (112 ms), then a resubscribe without the flag.
Web client after a warm reconnect over pruned history: thread list intact
Credit
This replaces #30732 by @Bil0000 and builds on #30317 by @emmems. Both found the cold-start cost and the uncompressed socket fallback.
Built by Claude Fable 5.1 and Claude Opus 5.5 in Claude Code.
🤖 Generated with Claude Code
Checks
Added 1 commit
- perf(mobile): thread list resumes from the saved cache, and large gaps reload over gzip HTTP460a65b
Approvability
Verdict: Approved at 460a65b
Macroscope's review found this PR approvable — Thread lists resume from recent, compatible cached snapshots and reload unreplayable gaps through HTTP. Provenance checks, a 12-hour age limit and authoritative snapshot fallback keep the synchronization change bounded.
You can add or adjust custom eligibility rules. Learn more.
Thread transfer impact
✅ Thread transfer remains within every enforced ceiling.
| Provider | Metric | Main baseline | This PR | Impact | PR ceiling | |
|---|---|---|---|---|---|---|
| Codex | Total thread wire | 5.0 KiB | 5.0 KiB | 0 B (0.0%) | 6.8 KiB | ✅ |
| Codex | Thread snapshot wire | 3.8 KiB | 3.8 KiB | 0 B (0.0%) | 4.9 KiB | ✅ |
| Codex | Live turn WebSocket wire | 1.2 KiB | 1.2 KiB | 0 B (0.0%) | 2.0 KiB | ✅ |
| Codex | Live turn WebSocket decoded | 21.5 KiB | 21.5 KiB | 0 B (0.0%) | 24.4 KiB | ✅ |
| Codex | Live turn messages | 1 | 1 | 0 (0.0%) | 8 | ✅ |
| Claude | Total thread wire | 5.0 KiB | 5.0 KiB | 0 B (0.0%) | 6.8 KiB | ✅ |
| Claude | Thread snapshot wire | 3.8 KiB | 3.8 KiB | 0 B (0.0%) | 4.9 KiB | ✅ |
| Claude | Live turn WebSocket wire | 1.2 KiB | 1.2 KiB | 0 B (0.0%) | 2.0 KiB | ✅ |
| Claude | Live turn WebSocket decoded | 21.9 KiB | 21.9 KiB | 0 B (0.0%) | 24.4 KiB | ✅ |
| Claude | Live turn messages | 1 | 1 | 0 (0.0%) | 8 | ✅ |
Baseline: 23bdb9e · PR result: 460a65b · 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.
@coderabbitai review
No actionable comments were generated in the recent review.
Walkthrough
-
Shell cache records now include origin metadata. The client uses this metadata to decide whether to resume a cached snapshot. When a resume requires a snapshot, the server can signal the client to load one over HTTP and resubscribe.
Changes: Shell resume and snapshot fallback
Layer / File(s) Summary Resume contracts and cache metadata
packages/contracts/src/orchestrationV2.ts,packages/client-runtime/src/platform/orchestrationCache.ts,packages/client-runtime/src/platform/persistence.ts,apps/mobile/src/connection/environment-cache-store.ts,apps/mobile/src/connection/environment-cache-store.test.ts,apps/web/src/connection/storage.tsThe shell stream contract adds snapshot-requiredand the optionalhttpSnapshotFallbackinput. Cache types and mobile and web stores support optional origin metadata. The mobile test checks that the metadata survives a save and load.Server snapshot fallback
apps/server/src/ws.ts,apps/server/src/ws.test.tsWhen a shell resume requests HTTP snapshot fallback, the server emits snapshot-requiredinstead of loading and streaming a shell snapshot. Tests cover both fallback and the existing snapshot behavior.Client cache validation and reload
packages/client-runtime/src/state/shell.ts,packages/client-runtime/src/state/shellReducer.ts,packages/client-runtime/src/state/shell-sync.test.tsThe client resumes cached rows only when origin identity matches and the snapshot is less than 12 hours old. It handles snapshot-requiredby reloading over HTTP, updates cache origin metadata, and filters stream items by active subscription. Tests cover eligible and ineligible cached origins and cold and warm fallback paths.Estimated code review effort: 4 (Complex) | ~45 minutes
Change: Feature
Sequence Diagram(s)
mermaid1 sequenceDiagram 2 participant ShellSync 3 participant subscribeOrchestrationV2Shell 4 participant HTTPSnapshotLoader 5 participant EnvironmentCacheStore 6 ShellSync->>subscribeOrchestrationV2Shell: Subscribe with cursor and HTTP fallback 7 subscribeOrchestrationV2Shell-->>ShellSync: Emit snapshot-required 8 ShellSync->>HTTPSnapshotLoader: Load authoritative snapshot 9 HTTPSnapshotLoader-->>ShellSync: Return snapshot 10 ShellSync->>EnvironmentCacheStore: Save snapshot with origin 11 ShellSync->>subscribeOrchestrationV2Shell: Resubscribe from snapshot sequence Possibly related PRs
- pingdotgg/t3code#30732: Implements an earlier cursor-resume design with cache metadata and a 12-hour age check.
- pingdotgg/t3code#24704: Implements a parallel cursor-resume design with a different cache basis and age limit.
- pingdotgg/t3code#26556: Adds server-version validation for cached shell resume.
Suggested reviewers:
bil0000,saphid,t3dotgg
Merge Risk: External image Minimal · up to 460a6
-
The change lets cold starts resume from the saved cache and falls back to the gzip HTTP snapshot when replay is not possible. No concrete merge-blocking defect was established.
Pre-merge checks: Passed 4
-
Check name Status Explanation Title check Passed The title names the two main changes: the thread list resumes from the saved cache on cold start, and large replay gaps reload over gzip HTTP. The raw summary confirms both: cache origin with afterSeq… Description check Passed The description covers the problem, the fix, scope and approval, measurements, and verification, which matches the template's sections. It explains the cold-start cost, the cache origin and snapshot-r… 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. Full details: Title check
-
Explanation
The title names the two main changes: the thread list resumes from the saved cache on cold start, and large replay gaps reload over gzip HTTP. The raw summary confirms both: cache origin with afterSequence resume, and the snapshot-required fallback that loads an HTTP snapshot. The title follows the conventional commit format and is specific to the changeset.
Full details: Description check
-
Explanation
The description covers the problem, the fix, scope and approval, measurements, and verification, which matches the template's sections. It explains the cold-start cost, the cache origin and snapshot-required mechanism, and the loop guard. It links the superseded PRs and gives concrete test and manual verification results. The template asks for a conventional commit title and a maintainer approval link. The author states the work is maintainer-authored and gives a reason for scope, but the Scope section does not link a triaged issue or a maintainer approval comment, so it is only partly complete. The Problem section is also folded into the opening paragraph rather than headed separately. The Verification section includes a screenshot and a trace, so the UI-change requirement is met. Overall the description is mostly complete, so the check passes.
-
Finishing Touches
-
Generate docstrings
-
- Commit to this branch
- Create a new PR
Generate unit tests (beta)
-
- Commit to this branch
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
External image Recent review info
-
External image Run configuration
-
- Source mode: Auto
- Repository: pingdotgg/t3code
- Branch: t3/shell-cold-resume
- File: .coderabbit.config.ts
- Commit: 460a65b
- Layers: Repository TS config
- Review profile: CHILL
- Plan: Team
- Run ID:
caeefcd6-67e2-4896-b41a-b326de87c03d
External image Commits
-
Reviewing files that changed from the base of the PR and between 23bdb9ef1cc39ddb21a3e077f97f580d50b8fc22 and 460a65be963cf7a0d9b400c7cc15b7fc77e55aed.
External image Files selected for processing: 11
-
apps/mobile/src/connection/environment-cache-store.test.tsapps/mobile/src/connection/environment-cache-store.tsapps/server/src/ws.test.tsapps/server/src/ws.tsapps/web/src/connection/storage.tspackages/client-runtime/src/platform/orchestrationCache.tspackages/client-runtime/src/platform/persistence.tspackages/client-runtime/src/state/shell-sync.test.tspackages/client-runtime/src/state/shell.tspackages/client-runtime/src/state/shellReducer.tspackages/contracts/src/orchestrationV2.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.
External image Recent review info
-
External image Run configuration
-
- Source mode: Auto
- Repository: pingdotgg/t3code
- Branch: t3/shell-cold-resume
- File: .coderabbit.config.ts
- Commit: 460a65b
- Layers: Repository TS config
- Review profile: CHILL
- Plan: Team
- Run ID:
caeefcd6-67e2-4896-b41a-b326de87c03d
External image Commits
-
Reviewing files that changed from the base of the PR and between 23bdb9ef1cc39ddb21a3e077f97f580d50b8fc22 and 460a65be963cf7a0d9b400c7cc15b7fc77e55aed.
External image Files selected for processing: 11
-
apps/mobile/src/connection/environment-cache-store.test.tsapps/mobile/src/connection/environment-cache-store.tsapps/server/src/ws.test.tsapps/server/src/ws.tsapps/web/src/connection/storage.tspackages/client-runtime/src/platform/orchestrationCache.tspackages/client-runtime/src/platform/persistence.tspackages/client-runtime/src/state/shell-sync.test.tspackages/client-runtime/src/state/shell.tspackages/client-runtime/src/state/shellReducer.tspackages/contracts/src/orchestrationV2.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.
-
Action not completed
-
Already reviewed the last commit. Use
@coderabbitai full reviewto rerun a review of the entire changeset.Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.