Skip to content

Persist mobile composer selectors across drafts#3496

Open
juliusmarminge wants to merge 2 commits into
mainfrom
t3code/fix-mobile-selectors
Open

Persist mobile composer selectors across drafts#3496
juliusmarminge wants to merge 2 commits into
mainfrom
t3code/fix-mobile-selectors

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Jun 21, 2026

Copy link
Copy Markdown
Member

Summary

  • Persisted mobile composer selector state for new-task drafts, including model, runtime, interaction, and workspace selection.
  • Updated the mobile thread outbox to carry selector snapshots so queued sends replay the exact draft configuration.
  • Simplified thread route rendering to prefer draft-backed composer settings and removed legacy per-thread override plumbing.
  • Added schema and test coverage for draft persistence, snapshot reads, selector equality, and backward compatibility with legacy draft records.

Testing

  • vp check
  • vp run typecheck
  • vp run lint:mobile
  • vp test
  • Not run: any additional manual device or simulator verification

Note

Medium Risk
Changes queued message delivery and thread turn configuration (settings sync before startTurn); mistakes could send with wrong model/runtime or mishandle offline queue retries, but behavior is covered by new tests and legacy outbox v1 remains readable.

Overview
Mobile composer model, runtime, interaction, and workspace choices now live in persisted per-draft state (new-task and thread keys) instead of ephemeral React state or immediate thread metadata updates.

New task flow reads/writes selectors via updateComposerDraftSettings; start uses getComposerDraftSnapshot so the created thread matches what the user picked. Thread UI shows draft-backed settings and updates drafts only—thread route no longer issues updateMetadata / setRuntimeMode / setInteractionMode on every selector change.

Queued sends store selector snapshots on outbox messages (schema v2, v1 still decodes). The drain syncs thread settings before startTurn and passes those settings into the turn; settings-sync failures retry instead of discarding the queue entry. After send, only text/attachments are cleared so selectors stay for the next message.

Also: shared attachment schema, branch-load deduping on new-task screen, and expanded tests for drafts/outbox behavior.

Reviewed by Cursor Bugbot for commit d37e228. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Persist mobile composer model, runtime, and workspace selections across drafts

  • Extends ComposerDraft in use-composer-drafts.ts to store model selection, runtime mode, interaction mode, and workspace selection alongside message content, with backward-compatible schema decoding.
  • Rewrites new-task-flow-provider.tsx to source all selector state from per-project composer drafts instead of local React state, so selections survive navigation away and back.
  • Updates NewTaskDraftScreen to read a synchronous draft snapshot at start time and derive all parameters (model, branch, worktree, runtime, interaction) from it.
  • Updates useThreadComposerState so sending a message enqueues the current selector snapshot and clears only content, preserving selections for the next message.
  • Adds settings-sync stage to useThreadOutboxDrain: before starting a turn, the outbox now synchronizes remote thread settings (model, runtime, interaction) to match the queued message, with retry-on-failure and discard logic differentiated by stage.
  • Behavioral Change: thread model/runtime/interaction changes in the detail view no longer persist server-side immediately; they update local draft state and are flushed when a message is sent.

Macroscope summarized d37e228.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 68497774-6ed5-47e0-8572-742b4a520a20

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/fix-mobile-selectors

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

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Jun 21, 2026
- Move model, runtime, interaction, and workspace picks into composer drafts
- Restore draft settings when starting new tasks or opening threads
- Add schema compatibility for legacy outbox and draft records

Co-authored-by: codex <codex@users.noreply.github.com>
@juliusmarminge juliusmarminge force-pushed the t3code/fix-mobile-selectors branch from 66423a1 to dce16f2 Compare June 21, 2026 21:31
@macroscopeapp

macroscopeapp Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a new feature that persists composer selections (model, runtime mode, workspace) across drafts and changes the message sending flow to sync these settings before starting turns. The schema and behavioral changes to the message delivery pipeline warrant human review.

You can customize Macroscope's approvability policy. Learn more.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Queued message dropped on sync
    • Replaced completeDelivery calls in settings sync blocks with isRetryableFailure checks so non-retryable settings failures no longer remove the message from the outbox, allowing startTurn to proceed with settings already embedded in its payload.

Create PR

Or push these changes by commenting:

@cursor push c18bdb10b1
Preview (c18bdb10b1)
diff --git a/apps/mobile/src/state/use-thread-outbox-drain.ts b/apps/mobile/src/state/use-thread-outbox-drain.ts
--- a/apps/mobile/src/state/use-thread-outbox-drain.ts
+++ b/apps/mobile/src/state/use-thread-outbox-drain.ts
@@ -118,6 +118,12 @@
         }
       };
 
+      const isRetryableFailure = (result: AtomCommandResult<unknown, unknown>): boolean => {
+        if (!AsyncResult.isFailure(result)) return false;
+        const error = Cause.squash(result.cause);
+        return Cause.hasInterruptsOnly(result.cause) || shouldRetryThreadOutboxDelivery(error);
+      };
+
       if (!modelSelectionsEqual(settings.modelSelection, thread.modelSelection)) {
         const updateResult = await updateThreadMetadata({
           environmentId: queuedMessage.environmentId,
@@ -127,8 +133,8 @@
             modelSelection: settings.modelSelection,
           },
         });
-        if (AsyncResult.isFailure(updateResult)) {
-          return completeDelivery(updateResult);
+        if (isRetryableFailure(updateResult)) {
+          return false;
         }
       }
 
@@ -142,8 +148,8 @@
             createdAt: queuedMessage.createdAt,
           },
         });
-        if (AsyncResult.isFailure(runtimeResult)) {
-          return completeDelivery(runtimeResult);
+        if (isRetryableFailure(runtimeResult)) {
+          return false;
         }
       }
 
@@ -157,8 +163,8 @@
             createdAt: queuedMessage.createdAt,
           },
         });
-        if (AsyncResult.isFailure(interactionResult)) {
-          return completeDelivery(interactionResult);
+        if (isRetryableFailure(interactionResult)) {
+          return false;
         }
       }

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit dce16f2. Configure here.

Comment thread apps/mobile/src/state/use-thread-outbox-drain.ts
Keep outbox entries retryable until startTurn is attempted, and cover deterministic preparation failures with a regression test.

Co-authored-by: codex <codex@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 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