Skip to content

Persist mobile composer selectors across drafts#3496

Merged
juliusmarminge merged 3 commits into
mainfrom
t3code/fix-mobile-selectors
Jun 22, 2026
Merged

Persist mobile composer selectors across drafts#3496
juliusmarminge merged 3 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 when thread metadata is updated on the server and adds a settings-sync step to offline/queued delivery; mistakes could send turns with wrong model/runtime or leave stale queued messages.

Overview
Mobile composer model, runtime, interaction, and workspace choices now live in persisted composer drafts (per thread and per new-task project), not ephemeral screen state or immediate server updates.

New task and thread flows read/write selectors through updateComposerDraftSettings / getComposerDraftSnapshot. Starting a task or sending a message uses a synchronous draft snapshot for text, attachments, and all selector fields; send clears only message content so the next message keeps the same picks.

Thread detail no longer fires updateMetadata / runtime / interaction commands on every picker change. The UI merges draft overrides into the displayed thread, and useThreadOutboxDrain syncs remote thread settings to match the queued snapshot before startTurn. Queued messages store optional selector fields (outbox schema v2); settings-sync failures retry, while deterministic start-turn failures can discard the queue entry.

Includes schema decoding for legacy drafts/messages, shared image attachment schema, and tests for persistence, equality, and failure staging.

Reviewed by Cursor Bugbot for commit 0a60926. 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 0a60926.

@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: 922fd9e1-4644-4398-90ba-059110024e6f

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.

No code changes detected at 0a60926. Prior analysis still applies.

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
juliusmarminge and others added 2 commits June 21, 2026 14:41
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>
@juliusmarminge juliusmarminge merged commit 37ac970 into main Jun 22, 2026
16 checks passed
@juliusmarminge juliusmarminge deleted the t3code/fix-mobile-selectors branch June 22, 2026 19:37
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