Skip to content

perf(swift-ios): stop redoing work on every keystroke and stream event - #17343

Merged
t3dotgg merged 38 commits into
t3code/rebuild-mobile-app-swiftfrom
t3/swiftui-cleanup-performance
Oct 10, 2026
Merged

t3dotgg merged 38 commits into
t3code/rebuild-mobile-app-swiftfrom
t3/swiftui-cleanup-performance

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

The SwiftUI app did a lot of repeated work on hot paths. Each keystroke in a thread re-ran the whole thread view. Each stream event re-parsed tool output, re-folded the full transcript, and decoded JSON twice. Home re-sorted every thread for a pull request cache that nothing read. The app also carried a lot of dead code.

This PR removes that work without changing what users see. Net change is about 380 lines removed (+806 / -1189).

Thread view

  • Typing in the composer no longer re-runs the thread view body. A small child view now watches the draft.
  • Tool failure and embedded-content flags are computed once per work item. The saved format does not change.
  • The turn-folding result is reused until the thread's detail revision, fold state, or feedback changes.
  • The transcript skips its workflow scan when nothing it reads has changed.
  • Tool icons use a small in-memory cache instead of AsyncImage, so reused cells do not fetch and decode them again.

Home and workspace

  • Removed the unused model-level pull request cache and its 30 s poll. It also forced full Home re-sorts.
  • Project badge hashing, New Task project grouping, and lastVisitedAt parsing no longer repeat on every render or keystroke.
  • The arrange sheet no longer re-diffs on every stream event.

Core and state

  • JSONValue decoding no longer runs a re-encode round trip or throws through a chain of DecodingErrors for each node.
  • V2 HTTP snapshots are decoded once. V2 control state decodes only the keys it reads.
  • Removed a quadratic checkpoint lookup. Lifecycle timestamps are parsed once. Preview text no longer splits and joins the full message.
  • Queue commands use the existing tested OrchestrationV2Commands builders.

Other features

  • After 512 KB of output, the terminal no longer resets and replays its whole buffer for each chunk.
  • Typing a review comment no longer re-diffs the file.

Cleanup: removed unused T3Client RPCs, the dead image preview in Files, production helpers that only tests used, and duplicate provider label and suffix-merge code.

One narrow difference: after a clone reset clears the New Task project and opens the picker, a later snapshot update no longer picks a project behind the picker. This matches the existing rule that an explicit selection never jumps (NewThreadView creationProjectIDs handler). All other New Task selection paths behave the same.

Skipped on purpose: file-backed draft images (the risk is larger than the gain) and lazy diff lines in the PR Files tab (it changes scrolling behavior).

How I checked it

  • Full xcodebuild test suite passes after the rebase onto the current SwiftUI branch: 1267 Swift Testing and 510 XCTest. New focused tests cover the work item format after the flag change, JSONValue token decoding, preview text, and terminal buffer trimming. Removed tests that only covered deleted dead code.
  • Smoke test on an iPhone simulator, with a signed Debug build paired to an isolated dev server running current main with copied real data. I checked pairing, the Home list and badges, opening and scrolling a long V2 thread with tables, typing, a draft that survives navigation and an app relaunch, and the New Task sheet with its project picker.

Reviewed with sol-loop: 2 rounds with GPT-6.1 Sol on high.

Created with Claude Opus 5.5 in Claude Code. A parallel audit and implementation workflow used Claude Opus 5.5 subagents.

🤖 Generated with Claude Code

t3dotgg and others added 30 commits October 8, 2026 15:23
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…itself

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…atch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ce per detail flush

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ences

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…changed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The model-level pull request cache had no reader, but each update still
forced a full Home re-sort, and the thread detail polled the server every
30 seconds only to fill it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nder

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HomeCollectionCell hides the row from accessibility and exposes its own
label and value, so the row built a string on every render for nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop needsAttention, homeProviderLabel, reasoningSummary, the model
isEffectivelySettled wrapper, and DailyUXSidebarIndex search, which every
production caller ran with an empty query. Search tests now use
HomePresentation. Rename ProjectAndArchiveViews.swift to AddProjectView.swift
to match its only type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…date

The sheet rebuilt and reconfigured every row on each snapshot change.
It now applies only when the order, shared cell inputs, or a row thread
changed, reconfigures only changed rows, and looks rows up by id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
t3dotgg and others added 8 commits October 8, 2026 15:23
…nk at the cap

Trim terminal history to 3/4 of the limit so later output arrives as an
append, and take the snapshot out of the cache before appending so the
buffer is not copied on every output event.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…view

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eystroke

Move the review comment field into its own view so only it reads the
draft text. Typing no longer re-runs the diff body over every line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop FeatureSourceControlOperation, homeProviderLabel, reasoningSummary,
submissionText, markdownLink, the mailbox peek, the QR-only pairing path,
and saveAppearance. Point the recovery tests at the live
FeatureGitOperation and keep the pairing and appearance tests on the
live paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…comments

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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:XXL 1,000+ changed lines (additions + deletions). labels Oct 8, 2026
@juliusmarminge juliusmarminge added the macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews label Oct 8, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This is a broad Swift iOS production refactor spanning streaming state, protocol decoding, caches, workspace behavior, thread rendering, and public client APIs. It also changes the New Task project-selection default and pairing-related behavior, so the runtime and user-facing effects require human review.

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

@github-actions

github-actions Bot commented Oct 8, 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 — 4.9 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.8 KiB — 29.3 KiB ✅
Codex Live turn messages — 1 — 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: 49766f5 · 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.

@t3dotgg

t3dotgg commented Oct 8, 2026

Copy link
Copy Markdown
Member Author

Note

🤖 Claude Opus 5.5 responding on behalf of Theo

I checked both behavior claims against the diff.

  • Pairing: no behavior change. The diff removes PairingURL.pairingURL(fromQRCode:), its two error cases, and PlatformRouteMailbox.peek(). Only tests called them. The app target builds without them. The QR scanner hands its value to ConnectionOnboardingView.applyConnectionString, which never called the removed helper, and PairingService still uses the unchanged PairingURL.resolve.
  • New Task: one narrow difference, kept on purpose. requestedInitialProjectID now computes its fallback only while no project is selected and the selection is not explicit. Every path that reads it already requires projectID.isEmpty, so the only case that changes is after a clone reset (explicit selection, empty project, picker open): a later snapshot update no longer picks a project behind the picker. This matches the existing rule in the creationProjectIDs handler (if projectSelectionIsExplicit { return }). The PR description now states this difference.

Agreed that the scope needs human review. Theo will review it before any merge.

@t3dotgg
t3dotgg merged commit 9e23767 into t3code/rebuild-mobile-app-swift Oct 10, 2026
34 checks passed
@t3dotgg
t3dotgg deleted the t3/swiftui-cleanup-performance branch October 10, 2026 22:58
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:XXL 1,000+ 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.

2 participants