Skip to content

Share chat option writes across desktop and phone - #36624

Merged
brennanb2025 merged 3 commits into
mainfrom
brennanb2025/selection-write-owner
Oct 31, 2026
Merged

brennanb2025 merged 3 commits into
mainfrom
brennanb2025/selection-write-owner

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 6 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​375 $\color{#cf222e}{\Huge{\mathbf{−}}}$​13 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​362
Prod 10 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​262 $\color{#cf222e}{\Huge{\mathbf{−}}}$​210 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​52

Problem. When you change a chat's model, effort, permissions or Fast setting, desktop and phone each ran their own copy of the write: checking the pick, sending it to the host, waiting for the answer, showing a failure, and refreshing. Each copy also kept its own "a pick is pending" marker beside the shared record of in-flight picks, so the two could disagree. On the phone, that separate marker was cleared when the latest pick finished even if an earlier pick was still in flight. #36582 already shared the reading side of these options; this shares the writing side, so both apps run one implementation.

What changes for users. Nothing should look or behave differently. The same picks are accepted, the same pickers lock and unlock at the same moments, and the same failure toast (desktop) or banner (phone) appears. This removes the duplicated logic behind those behaviors, so future fixes land once for both apps.

How it works. One shared writer, used by both apps, checks a pick, records it as in flight, sends it, and handles the host's answer. Nothing stores a separate "pending" marker any more. The phone works out whether its options sheet is locked from the latest pick it sent, whether that pick was made while the agent was starting or was a permission change, and whether that pick is still in flight. That keeps today's phone behavior: picks made during startup and permission picks never lock the sheet, and resetting or reconnecting clears the lock. Desktop keeps its per-row busy state, sends picks made while a new chat is still launching straight to the launch path, keeps its original connection and its extra setup steps for older paired servers, and still refreshes after every answer. Failure notices keep the same scope as before: desktop shows a toast while you are still on that chat (and always for a refused pick made during launch), the phone shows its banner for a refused pick, and an unconfirmed delivery stays silent on the phone. Model and effort values are still sent exactly as picked, whether or not they appear in the model list and whatever name the agent reports back. No saved data, wire fields, capabilities or RPCs change.

Differences from the common pattern:

  • Intended: host-owned accepted values and finite overlays remain necessary for desktop and phone to converge on one chat; only a causally later published read retires accepted/unknown overlays.
  • Intended: desktop requests retain original pairing authority, while phone uses its logical client and captured record to avoid retargeting a replacement runtime.
  • Intended: desktop retains immutable create input and legacy prerequisite writes for older paired servers.
  • Intended: failures keep the existing desktop toast and phone banner surfaces, because each pick is written to the host immediately and can be refused there, which the common pattern's local picks never are. Unconfirmed phone delivery stays silent because every phone mutation treats unconfirmed delivery that way and the follow-up reread shows the host's actual value, so no second notice is shown.
  • Intended: the common pattern disables its picker while its own write is unacknowledged. Here each pick is a host write, and older hosts take options from saved host state at send, so the pick must land first. The phone therefore keeps its sheet and Send lock, and desktop its same-row lock. Picks made during startup and permission picks never lock, as before.

Tests. The shared writer tests run picks for Claude and Codex chats against an empty model list. They cover a pick sent as typed, an older host that leaves the options out of its reply, refused and unconfirmed replies, startup ending or beginning while a pick is in flight, two quick picks finishing in either order, reset, replacement, detach, and failure-notice scope. Desktop hook tests, using the production presentation check, cover a refusal shown during launch, a refusal shown on the same chat after its runtime changes, and a refresh after a superseded answer. A phone hook test covers the latest pick finishing while an earlier one is still in flight. Phone suites run in CI.

Held launch refusals toast again while the transport is still closed, desktop
notices follow only the chat's presentation scope so a fence or status change
on the same chat still reports, and the phone banner no longer depends on the
read record.
@brennanb2025
brennanb2025 marked this pull request as ready for review October 31, 2026 07:47
@coderabbitai

coderabbitai Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a746505a-8ff6-487c-9da8-092791335880
Commits

Reviewing files that changed from the base of the PR and between 33cf289 and 7d88dcd.

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

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e33deef2-80a7-4366-a76a-cfcf0ba3dbb0
Commits

Reviewing files that changed from the base of the PR and between 20a356e and 33cf289.

Files selected for processing: 16
  • mobile/src/session/use-mobile-structured-agent-options.test.ts
  • mobile/src/session/use-mobile-structured-agent-options.ts
  • src/renderer/src/components/native-chat/use-structured-agent-session-options.permission-mode.test.tsx
  • src/renderer/src/components/native-chat/use-structured-agent-session-options.test-fixture.tsx
  • src/renderer/src/components/native-chat/use-structured-agent-session-options.ts
  • src/renderer/src/components/native-chat/use-structured-agent-session-options.write-owner.test.tsx
  • src/renderer/src/lib/structured-agent-session-launch-options.ts
  • src/shared/structured-agent-session-option-snapshot.ts
  • src/shared/structured-agent-session-option-view.test.ts
  • src/shared/structured-agent-session-option-view.ts
  • src/shared/structured-agent-session-options-read-owner.ts
  • src/shared/structured-agent-session-options-write-owner.test.ts
  • src/shared/structured-agent-session-options-write-owner.ts
  • src/shared/structured-agent-session-options.ts
  • src/shared/structured-agent-session-permission-option.test.ts
  • src/shared/structured-agent-session-selection-operations.ts
Files with no reviewable changes: 1
  • src/shared/structured-agent-session-options-read-owner.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.

Walkthrough

The change moves structured option-write validation, operation tracking, refusal presentation, and revalidation into a shared write owner. The mobile and renderer hooks use that owner. Pending-option reporting now uses the latest controller receipt instead of option state, which no longer stores pendingId. Tests cover overlapping writes, startup and reply ordering, resets, and refusal handling.

Merge Risk: Minimal · previous review 33cf2

This change moves chat option writes (model, effort, permissions, Fast) into one shared implementation used by desktop and phone. No concrete defect was found, and the tests cover the main ordering and failure cases, so the merge risk is minimal.

Pre-merge checks: Passed 3 · Failed 2

Failed Failed checks: 2 warnings

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check Warning The description is thorough and largely follows the repository template. It has a clear Problem and What Changes section, a How It Works section that explains the mechanism, and a Differences section … Add an ELI5 section with a plain-language summary. Replace the blank "Fixes #" with a linked issue. Outside contributors need an actual issue, and maintainers should attach a pre-existing one. Add a Visual Proof section. Because the change …
Passed Passed checks: 3 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: sharing chat option writes between desktop and phone.
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: Docstring Coverage

Explanation

Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 11 functions across 15 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Full details: Description check

Explanation

The description is thorough and largely follows the repository template. It has a clear Problem and What Changes section, a How It Works section that explains the mechanism, and a Differences section that explains deviations from the common pattern. It also lists the tests that were added. Several template sections are missing or incomplete: the ELI5 section is absent, the Linked Issue section has no issue reference ("Fixes #" is blank), the Visual Proof section is absent (it is required for UI or behavior changes, and the description says behavior is unchanged but gives no N/A statement), the Testing checklist boxes are not checked and the description does not name the platforms tested, the AI Disclosure section is absent, the Review and Agent skill upstream boundary sections are absent, and the Notes and Checklist sections are absent. The description also does not state whether pnpm lint, typecheck, test, and build pass.

Resolution

Add an ELI5 section with a plain-language summary. Replace the blank "Fixes #" with a linked issue. Outside contributors need an actual issue, and maintainers should attach a pre-existing one. Add a Visual Proof section. Because the change is described as behavior-preserving but touches the desktop toast and phone banner paths, either attach before and after evidence or write N/A with a short reason. Mark the Testing checkboxes that apply and list the platforms actually tested (macOS, Linux, Windows, SSH). Add the AI Disclosure section, or leave it blank only if the author is a StablyAI team member. Complete the Agent skill upstream boundary checkbox. Add the Checklist items that apply and state the result of pnpm lint, typecheck, test, and build, or note that CI covers them. Remove the template comments once each section is filled in.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

Share

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review loop status: done. Two review rounds, all findings fixed, CI green.

The problem. When you changed a chat's model, effort, permissions or Fast setting, desktop and phone each ran their own copy of the write. Each copy also kept its own "a pick is pending" marker beside the shared record of in-flight picks, so the two could drift apart; on the phone the marker cleared when the latest pick finished even if an earlier pick was still in flight. This PR moves the write into one shared writer for both apps and works the phone's lock out from the shared in-flight record instead of storing it.

What users get. Nothing should look or behave differently. Picks are accepted the same way, pickers lock and unlock at the same moments, and the same failure toast (desktop) or banner (phone) appears. The duplicated logic is gone, so future fixes land once for both apps.

Fixed during review (33cf289):

  • The first version of the shared writer dropped some failure notices main shows. A refused pick made while a new desktop chat was still launching showed no toast, and on both apps a refused or unconfirmed pick lost its notice if the chat's connection or runtime changed while the pick was in flight, even though the user was still on that chat. Notices now follow main's rules exactly, and two desktop tests (using the real presentation check) cover both cases. Both tests fail on the first version and pass now.
  • The PR description now discloses this and gives the reason behind each difference from the common pattern.

Deferred (follow-ups, not user-visible):

  • The selection controller's "latest pending key" API is now unused in production; only tests read it. It's cheap to remove later.
  • The shared writer rebuilds the option view just to compare two identities on the phone's send check. It runs once per pick.
  • Phone behavior already on main, left unchanged on purpose: after the sheet unlocks, re-picking a key whose earlier write is still in flight is refused silently. Changing the phone to per-row locks would be a product choice.

What was verified.

  • I traced each changed entry point on main and the branch, desktop and phone: ordinary picks, picks made during startup, permission repicks, two quick picks finishing in either order, reset/reconnect, chat switch, held launch, and older hosts that leave options out of their reply. All match main, and no pick is refused or held because of the model list or what the agent reports back.
  • Named test files pass locally (6 files, 89 tests). CI ran the phone suites ("Mobile verify") and passed.
  • Whole-mechanism comparison with the references: every difference is labelled with its reason.
  • Readiness checklist: no P0/P1/P2. No added work that grows with chats or rows.
  • QA type: tests only. The pickers' layout and interaction are unchanged, so the QA table calls for no screenshot run.
  • Not verified: no running-app or screenshot run, since none is called for.

Main was merged once before final CI (7d88dcd, no conflicts, no overlap with this PR's files); final CI on that head is green.

For Brennan's review:

@brennanb2025
brennanb2025 merged commit 4a81143 into main Oct 31, 2026
51 checks passed
@brennanb2025
brennanb2025 deleted the brennanb2025/selection-write-owner branch October 31, 2026 08:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant