Skip to content

fix(solo): preserve and resume unsent chat drafts - #3525

Merged
tellaho merged 3 commits into
mainfrom
tho/solo-draft-recipient-restore
Nov 14, 2026
Merged

tellaho merged 3 commits into
mainfrom
tho/solo-draft-recipient-restore

Conversation

@tellaho

@tellaho tellaho commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Overview

Category: fix

User Impact: Unsent Solo chats keep their selected agents and remain available in the workspace list so users can return to the same draft.

Problem: Leaving an unsent chat hid its list entry and could replace its chosen agent when reopened. Using New on an open draft also recreated its editor.

Solution: Keep explicit recipient choices with the existing draft, retain started draft slots in the Solo list, and return New to the current editor. A pencil identifies unsent entries while retaining the visible “New chat” wording. An explicitly addressed new-chat route takes precedence over an earlier saved toolbar pick.

Changes

File changes

src/features/messages/MessageComposer.tsx
Persist explicit recipient picks for new-chat submissions using the existing draft key.

src/features/solo/draft-projects.ts
Derive workspace and unfiled draft membership from existing text and attachment owners, ignoring unrelated view writes.

src/features/channel-navigation/SoloInventory.tsx
Keep saved drafts listed, preserve their workspace settings, and refocus an open editor. Use the existing pencil icon and an accessible unsent-draft description.

src/bundled/channels/SoloWorkspaces.tsx
Render multiple draft slots outside the chat cap; only a selected draft expands its workspace.

src/features/channel-navigation/routes.ts
Reuse an ordinary draft's exact route only within the same account/community and requested workspace.

src/features/solo/SoloDraft.tsx
Expose the existing draft editor as a focus destination, including while it is busy. Reconcile saved toolbar recipients before restoring a draft opened for a named agent; ordinary picks and authored inline mentions retain their behavior.

src/app/shell/NewMenu.tsx
Hand focus to the existing editor through the menu's final-focus contract instead of reopening its route.

src/app/shell/sidebar-testing.tsx
Supply the navigation entry used by the real New action.

src/features/messages/MessageComposer.test.tsx
Update the existing empty-draft picker lifecycle case to require retention after remount.

src/features/solo/SoloDraft.test.tsx
Extend the existing refused-start/retry case with text and recipient restoration before the first send, exact send routing, and consumed-pick checks. Extend the existing named-agent case with an earlier empty-draft pick, then verify the named route and actual send recipient.

src/features/channel-navigation/SoloInventory.test.tsx
Exercise workspace settings, independent draft slots, sidebar remount, attachment-only retention, clearing, and accessibility in both sidebar hosts.

src/features/channel-navigation/routes.test.ts
Cover same-workspace reuse and cross-community, other-workspace and source-region boundaries.

tests/browser/solo-chats.spec.mjs
Add one journey for native editor identity and focus after menu dismissal, retained-row navigation, and clearing a draft.

docs/buzz-help.md
Document retained recipients, persistent draft rows, the pencil icon, and New's refocus behavior.

Validation and test footprint

  • Documentation-only head 2f9b6ecda70886520277896c994e568968ec8928 reflows the help paragraph with an identical word sequence. Source and tests match a4b73b2f41. Mandatory hooks passed: TypeScript/related units (252.41 s), design types/guards (5.72 s), and publication gates. No additional app run; new-head CI is queued.
  • Follow-up head a4b73b2f417e4a98d9f2cbeaf834620521813105: all mandatory push hooks passed, including TypeScript/related unit tests (321.63 s), design types/guards (9.56 s), and publication policy gates. Local and remote heads match; the PR remains ready for review. Hosted checks are running; DCO passed.
  • Initial publication: mandatory pre-push checks passed at c1fbf8115f937aaf0079284964fda95a4ef8ef21: TypeScript and related unit tests (259.94 s), design types/guards (6.58 s), and publication policy gates.
  • Before main integration, the two consolidated full component files passed 140 tests. The complete Solo chat browser file passed 22 Chromium/WebKit executions on identical feature production code. A fresh headless Chromium run of the new journey passed while capturing the images below.
  • Human testing covered the initial draft-retention flow before the named-agent follow-up. No fresh human acceptance or native app run was performed for that follow-up.
  • Diff against the merge base: tests and fixtures +174/−9; non-test files, including help: +268/−67. Before the named-agent follow-up these were +160/−8 and +260/−58; consolidation reduced tests from +190/−5 without changing production code.
  • The named-agent regression failed at c1fbf8115f with the test extension: saved agent A remained selected instead of route agent B. After the fix, the complete SoloDraft file passed 100 tests (17.29 s), including the actual send to B, ordinary pick remount/recovery/retry, and authored-inline-mention preservation. This extends an existing case; no new cases or browser journeys were added.
  • Added mounted cases: 5 → 2; one new route-logic case remains. Browser journeys: 1 added, 0 removed, executed in both engines. Native editor identity, menu-dismissal focus and app routing justify the browser case; the state matrix stays in mounted tests.
Consolidated scenario Retained coverage
Separate successful recipient-remount/send case The existing refused-start case now first unmounts/remounts the unsent draft, checks text and recipient restoration, verifies exactly one send to that agent, and checks pick consumption. Default reseeding, refused-start recovery and successful retry remain in that case.
Separate retention and workspace-settings cases for each sidebar host One scenario per host retains all settings/route assertions, independent workspace/unfiled slots, inactive-row state and description, sidebar unmount/remount, file-only retention, and final-content removal. Only repeated fixture setup was removed.

Hosted CI for the follow-up head and reviewer confirmation remain pending. No native run was performed by the agent. The captures cover dark/wide headless presentation; the complete theme, narrow-width, enlarged-text and performance matrices were not evaluated.

Reproduction steps

  1. Open Solo's New chat, choose an agent other than the main agent, and enter a message.
  2. Open another chat. The unsent New chat row should remain, marked with a pencil.
  3. Return through that row. The text and chosen agent should remain.
  4. Choose New while that draft is open. Typing should return to the same editor.
  5. Clear the draft and leave it; its retained row should disappear. Repeat inside a workspace and confirm New preserves its draft settings.
  6. In an empty draft, choose agent A, leave, and use search’s “Chat with” action for agent B. The draft should select B and send to B.

Screenshots / headless demo

Headless Chromium, production build with disposable modeled relay data. Both images show the implemented UI, cropped to the changed list.

Draft open After switching chats
Open unsent draft with the pencil icon Unsent draft retained after selecting Alpha

Keep explicitly chosen recipients with unsent drafts, retain started drafts in the workspace list, and focus an existing editor from New. Mark unsent draft rows with a pencil icon.

Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@feedback.communities.buzz.xyz>
Signed-off-by: Taylor Ho <[email protected]>
@tellaho
tellaho marked this pull request as ready for review November 14, 2026 00:16
@tellaho
tellaho requested a review from a team as a code owner November 14, 2026 00:16

@kalvinnchau kalvinnchau 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.

🤖 Found a recipient-selection regression; see inline comment.

Reconcile a previous toolbar pick before restoring a draft opened for a named agent. Ordinary draft restoration and authored inline mentions keep their existing behavior.

Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@feedback.communities.buzz.xyz>
Signed-off-by: Taylor Ho <[email protected]>

@wpfleger96 wpfleger96 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤖 LGTM. I traced how the saved agent pick is written and cleared: it is cleared on a successful start, written back on a refused start, and cleared by a named-agent route (the fix for the earlier finding about the named agent). Removing the !submission guard only matters for Solo routing, so the people-DM NewMessage composer is unaffected. The draft rows and the New refocus path look right.

Nonblocking:

  • docs/buzz-help.md: the rewrap leaves a short open draft. Selected line; reflowing the paragraph would read cleaner.
  • soloDraftInput() queries #new-chat-prompt across the whole document. That is fine while only one Solo draft editor can mount at a time, but scoping it to the navigator's pane would hold up if that changes.

Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@feedback.communities.buzz.xyz>
Signed-off-by: Taylor Ho <[email protected]>
@tellaho
tellaho merged commit 7398f6d into main Nov 14, 2026
68 checks passed
@tellaho
tellaho deleted the tho/solo-draft-recipient-restore branch November 14, 2026 01:23
wpfleger96 added a commit that referenced this pull request Nov 14, 2026
* origin/main: (27 commits)
  [2/2] feat(agents): offer Automatic (Recommended) in Create agent and Identity (#3345)
  fix(solo): preserve and resume unsent chat drafts (#3525)
  fix(builderlab): give refused app sign-ins and Identity links a route to Organization sign-in (#3078)
  fix(agents): say why Import an agent can't import here, and link Manage plugins (#3073)
  [1/2] feat(agents): add a recommended agent setup (core) (#3344)
  test(browser): survive the live-stream restart a page-clock jump causes (#3442)
  chore(renovate): hold rusqlite below 0.41 for mesh-llm (#3438)
  test(browser): serve the diffs and CLI reload fixtures outside the navigation budget (#3445)
  test(browser): play the draft clip at rate 0 instead of looping it before the Play click (#3446)
  fix(status): reopen a status whose preset deadlines coincide as Today (#3449)
  fix(messages): tolerate a cancelled reaction-name chunk load (#3450)
  fix(pairing): tolerate /proc/net/tcp rows printed twice or missed (#3451)
  test(browser): wait on the signal each test asserts in local-agent-activity and message-navigation (#3444)
  Expose native channel headings to Solo sources (#3523)
  test(browser-host): run the 100,000-channel broker scale check alone in the measurements lane (#3447)
  fix(solo): keep every contributed destination action (#3516)
  fix(identity): report an issuer that keeps rate-limiting an agent as a limit (#2365)
  fix(identity): let a stuck at-launch sign-in copy its link (#2453)
  fix(profiles): open avatar previews on hover in compact density (#3510)
  feat(solo): smooth chat recency reordering (#3502)
  ...

# Conflicts:
#	src-tauri/src/federated_identity/agent/tests.rs
#	src-tauri/src/federated_identity/mod.rs
#	src-tauri/src/federated_identity/protocol.rs
joahg added a commit to joahg/buzz-app that referenced this pull request Nov 14, 2026
* origin/main: (133 commits)
  Expose ordinary chats without workspace headings (block#3530)
  [2/2] feat(agents): offer Automatic (Recommended) in Create agent and Identity (block#3345)
  fix(solo): preserve and resume unsent chat drafts (block#3525)
  fix(builderlab): give refused app sign-ins and Identity links a route to Organization sign-in (block#3078)
  fix(agents): say why Import an agent can't import here, and link Manage plugins (block#3073)
  [1/2] feat(agents): add a recommended agent setup (core) (block#3344)
  test(browser): survive the live-stream restart a page-clock jump causes (block#3442)
  chore(renovate): hold rusqlite below 0.41 for mesh-llm (block#3438)
  test(browser): serve the diffs and CLI reload fixtures outside the navigation budget (block#3445)
  test(browser): play the draft clip at rate 0 instead of looping it before the Play click (block#3446)
  fix(status): reopen a status whose preset deadlines coincide as Today (block#3449)
  fix(messages): tolerate a cancelled reaction-name chunk load (block#3450)
  fix(pairing): tolerate /proc/net/tcp rows printed twice or missed (block#3451)
  test(browser): wait on the signal each test asserts in local-agent-activity and message-navigation (block#3444)
  Expose native channel headings to Solo sources (block#3523)
  test(browser-host): run the 100,000-channel broker scale check alone in the measurements lane (block#3447)
  fix(solo): keep every contributed destination action (block#3516)
  fix(identity): report an issuer that keeps rate-limiting an agent as a limit (block#2365)
  fix(identity): let a stuck at-launch sign-in copy its link (block#2453)
  fix(profiles): open avatar previews on hover in compact density (block#3510)
  ...

Signed-off-by: Joah Gerstenberg <[email protected]>

# Conflicts:
#	src/bundled/codex/prompt.test.ts
#	src/bundled/goose/runtime.ts
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.

3 participants