Repository navigation
Conversation
After a send, the desktop composer kept its stored model selection for that thread. That selection wins over the server thread's model for both the picker and the next send, so when another client switched the thread to a different model, desktop kept showing the old one and silently wrote it back on the next message. Every explicit model pick now gets its own id. A send captures that id and releases the selection after the turn starts, only while the draft still holds that id, so a pick made in flight survives. A draft session is only marked sent and promotion drops the selection, since its local placeholder thread would otherwise show the project default until the server thread arrives. Draft storage moves to version 10 and the migration strips never-picked seeds from server-thread drafts. Fixes pingdotgg#10684. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
|
|
||
| export const COMPOSER_DRAFT_STORAGE_KEY = "t3code:composer-drafts:v1"; | ||
| const COMPOSER_DRAFT_STORAGE_VERSION = 9; | ||
| const COMPOSER_DRAFT_STORAGE_VERSION = 10; |
There was a problem hiding this comment.
🟠 High src/composerDraftStore.ts:69
Existing v9 drafts containing previewAnnotations lose those annotations during the v10 migration, and the next persistence write permanently discards them. normalizePersistedDraftsByThreadId treats previewAnnotations as user content but does not copy it into the normalized draft; preserve that field alongside the other draft content fields.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/composerDraftStore.ts around line 69:
Existing v9 drafts containing `previewAnnotations` lose those annotations during the v10 migration, and the next persistence write permanently discards them. `normalizePersistedDraftsByThreadId` treats `previewAnnotations` as user content but does not copy it into the normalized draft; preserve that field alongside the other draft content fields.
ApprovabilityVerdict: Would Approve Macroscope's review found this PR approvable — This is a focused web bug fix that restores server-thread model authority after successful sends, with targeted coverage for concurrent picks, draft promotion, and reloads. The v10 migration has an unresolved high-severity risk of dropping existing preview annotations, which must be addressed independently. Not approved because:
Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more. |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe composer now assigns identities to explicit model picks, retains picks through asynchronous sends, and releases them after successful turn startup. Draft persistence and migration preserve sent state, while regular sends and plan follow-ups invoke the release operation. ChangesComposer model selection lifecycle
Estimated code review effort: 3 (Moderate) | ~25 minutes Severity of issue fixed: Medium Sequence Diagram(s)sequenceDiagram
participant ChatView
participant ComposerDraftStore
participant TurnStartup
ChatView->>ComposerDraftStore: Capture modelSelectionId
ChatView->>TurnStartup: Start turn or plan follow-up
TurnStartup-->>ChatView: Confirm successful startup
ChatView->>ComposerDraftStore: releaseModelSelection
Suggested reviewers: ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
Superseded by #10780 after renaming the head branch; the diff is unchanged. |
Fixes #10684.
After a send, the web composer kept its stored model selection for that thread. That selection wins over the server thread's model for both the picker and the next send, so when another client switched the thread to a different model, desktop kept showing the old one and silently wrote it back on the next message.
Every explicit model pick on a thread draft now gets its own id. A send captures that id, and once the turn starts the composer releases the selection only while the draft still holds that id. A pick made while the send was in flight, including a same-value re-pick, has a different id and stays. With the override gone, the composer follows
thread.modelSelection, so a change from another client shows through and the next send no longer reverts it.A draft session is only marked sent, because its local placeholder thread shows the project default until the server thread arrives. The marker is persisted, and promotion drops the selection instead of moving it onto the server thread. Draft storage moves to version 10; the migration also strips never-picked seeds from server-thread drafts, which fixes threads created before this change without one more silent reversal.
Known limitation: explicit picks persisted before this change carry no marker, so a stale one is released only after the next send from this client. Seeded ones are covered by the migration. Mobile has its own fix in #10205; no wire, server, or mobile changes here.
Verification
Two paired browser tabs against one isolated dev server, Claude provider, same thread. The thread's
model_selection_jsonwas read from the projection table after each step.Recordings of tab 1; the second tab acts off-screen.
Before, on
main: tab 1 keeps showing Sonnet 5 after the remote switch to Opus 5, and its next send reverts the thread.After, on this branch: tab 1 switches to Opus 5 when the remote change lands, and its next send leaves the thread on Opus 5.
Reloading both tabs between the runs migrated their v9 storage to v10 in place.
Tests: composer draft store suite (115 tests, 9 new) covering the pick id rules, release on server threads, a newer pick surviving the in-flight window, the draft-session hold and promotion drop, and the persisted marker across a reload. Model selection, thread action, and ChatView logic suites pass (169 tests). Web typecheck, lint on the changed files, and formatting pass.
Prepared with Claude Fable 5.1 in Claude Code.
🤖 Generated with Claude Code
Summary by CodeRabbit