fix(clients): sync thread modes across devices - #5519
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
4d7d5b8 to
86fbe33
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 86fbe3313e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ApprovabilityVerdict: Needs human review This PR introduces cross-device thread mode synchronization - a new feature with significant new logic across mobile and web clients. The scope of changes (new files, new state management, retry logic) combined with unresolved review comments identifying potential bugs (mode overrides not clearing properly on draft routes) warrants human review. You can customize Macroscope's approvability policy. Learn more. |
86fbe33 to
c4d5af5
Compare
c4d5af5 to
1298ae5
Compare
1298ae5 to
702984a
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 03787b3. Configure here.
| setComposerDraftInteractionMode, | ||
| setComposerDraftRuntimeMode, | ||
| updatePendingInteractionModeSync, | ||
| updatePendingRuntimeModeSync, |
There was a problem hiding this comment.
Draft route never clears mode overrides
Medium Severity
Mode sync still runs on draft routes once a server thread exists (serverModeThreadId), but acknowledgment only runs when routeKind === "server". Pending sync state and composer overrides on the draft target therefore never clear while the draft route stays mounted, so remote mode updates stay hidden until navigation.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 03787b3. Configure here.
| if (next !== currentPending) { | ||
| input.writePending(next); | ||
| } | ||
| clearRetry(commandId); |
There was a problem hiding this comment.
Mode sync errors never clear
Low Severity
Terminal mode-sync failures call setPendingConnectionError, but a later successful dispatch only records the sequence and never clears that pending error. The prior in-composer flow cleared the error by id on success, so failed-then-succeeded mode changes can leave a stale pairing/connection error around.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 03787b3. Configure here.


Closes #5278
Problem
Runtime and interaction mode changes on an existing thread were stored as device-local composer overrides until the next message was sent. Other clients could therefore keep showing and using a different mode for the same server-backed thread. The optimistic override was also cleared by value equality, which could mis-handle rapid A → B → A changes or hide a later remote update.
Fix
commandIdand the returnedDispatchResult.sequence; clear it only after the environment shell has applied that sequence or a later one.commandIdfrom a root sync worker, so retries survive reconnects, navigation, and app restarts.Impact
Mode changes persist as thread state and propagate across local, remote, and multi-device clients without requiring a prompt. Rapid local changes and remote overwrites no longer depend on matching values to acknowledge an update. Web and desktop share the web implementation; mobile uses the same server commands and a persistent retry path. Provider adapters and wire contracts are unchanged.
There are no visual changes, so before/after screenshots would be identical.
Validation
vp test run packages/client-runtime/src/state/threadModeSync.test.ts apps/mobile/src/state/use-composer-drafts.test.ts apps/web/src/composerDraftStore.test.ts(92 tests)@t3tools/client-runtime,@t3tools/mobile, and@t3tools/webModel: OpenAI Codex (GPT-5)
Harness: Codex desktop app
Note
Medium Risk
Touches cross-client thread state, optimistic UI, persistence hydration, and background dispatch/retry paths; server commands are existing but client coordination bugs could show wrong modes or duplicate sends.
Overview
Thread mode changes on existing server-backed threads now sync immediately instead of staying as device-local composer overrides until the next message.
A shared
thread-mode-synchelper inclient-runtimetracks optimistic mode updates (command id, dispatch sequence, ack/fail/retry rules). Web/desktop dispatchessetRuntimeMode/setInteractionModeon change, keeps a short-lived composer override, then clears it when the environment shell sequence confirms or on terminal failure (error toast).Mobile stores the same pending sync on composer drafts, runs a new
useThreadModeSyncDrainalongside the outbox drain inBackgroundSyncWorker, and retries like the outbox. Hydration drops staleruntimeMode/interactionModeon existing-thread draft keys unless a pending sync is present (new-task and pending-task drafts unchanged). Mode toggles no-op when already active; connection errors use id-scoped pending error clearing to avoid races.Reviewed by Cursor Bugbot for commit 03787b3. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Sync thread runtime and interaction mode changes across devices with pending state and retry
PendingThreadModeSyncabstraction inthreadModeSync.tswith helpers to begin, dispatch, acknowledge, and fail pending mode syncs based on environment snapshot sequences.useThreadComposerStaterecords pendingruntimeModeSync/interactionModeSyncin composer drafts when mode changes are initiated;useThreadModeSyncDraindispatches these in the background with exponential backoff and acknowledgment.ChatViewContentdispatches mode changes immediately, tracks pending sync state, acknowledges on snapshot sequence catchup, and shows a toast on terminal failure while rolling back draft overrides.ComposerDraftis extended to store pending mode sync fields; existing thread drafts no longer hydrate server-owned modes directly.Macroscope summarized 03787b3.