Skip to content

fix(mothership): persist queued messages, edit-in-place preserves order#4769

Merged
waleedlatif1 merged 5 commits into
stagingfrom
waleedlatif1/mship-queue-fix
May 28, 2026
Merged

fix(mothership): persist queued messages, edit-in-place preserves order#4769
waleedlatif1 merged 5 commits into
stagingfrom
waleedlatif1/mship-queue-fix

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

Summary

  • Mothership message queue moved from component-local useState into a new useMothershipQueueStore (Zustand + sessionStorage), keyed by chatId — queue now persists across nav and reload, and chat-delete clears its bucket
  • Editing a queued message now replaces in place at the original index (via replaceAt) instead of removing + re-appending at the tail
  • Edit button + "send now" disabled on the head while it's being dispatched; new "cancel edit" affordance on the in-edit row
  • Pending-chat sentinel (pending::<shortId>) migrates to the real chatId on adoptResolvedChatId, so first-send queues follow their resolved chat
  • Auto-kick effect drains the queue on rehydrate, gated on chatHistory.activeStreamId so it doesn't race the reconnect path
  • Stripped volatile queuedSendHandoff on edit + on persist so a fresh handoff is minted at send time
  • Wired through to the workflow panel copilot for parity

Type of Change

  • Bug fix

Testing

Tested manually — queue survives chat-switch, home reset, browser reload; edit on a non-head queued message preserves position; edit on the dispatching head is disabled; cancel-edit leaves the slot intact. Added 16 store unit tests covering enqueue / insertAt / replaceAt (incl. handoff strip) / remove (incl. editing cleanup) / migrate / clearChat / setEditing. Council audit caught and fixed a stale-closure send-after-edit race and a stale-handoff issue.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link
Copy Markdown

vercel Bot commented May 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped May 28, 2026 5:52pm

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 28, 2026

PR Summary

Medium Risk
Touches send/dispatch, stream handoff, and rehydrate auto-send paths in useChat; mistakes could duplicate sends or drop queue edits, though behavior is covered by new store tests.

Overview
Mothership’s queued messages move from hook-local state into a sessionStorage-backed useMothershipQueueStore, keyed per chat (with a pending::<id> bucket that migrates when a new chat gets a real id). Queues survive navigation/reload within the tab; deleting a task clears that chat’s bucket.

Edit-in-place keeps queue order: editing binds a row to the composer, submit replaces the slot via replaceAt (and strips stale queuedSendHandoff), and cancel edit resumes auto-drain. The dispatcher pauses while the head is being edited, tracks dispatchingHeadId (disables edit/send-now on that row), and honors explicit removes mid-dispatch so failed sends don’t resurrect removed items. A rehydrate kick drains when history shows no active stream.

UI adds in-edit highlighting, cancel edit, and the same queue props on home and workflow copilot. 16 unit tests cover the new store.

Reviewed by Cursor Bugbot for commit 036bbb3. Configure here.

Comment thread apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts Outdated
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 28, 2026

Greptile Summary

This PR lifts the mothership message queue from component-local useState into a new useMothershipQueueStore (Zustand + sessionStorage), keyed by chatId, so the queue survives navigation and page reloads. It also changes edit behaviour so an edited queued message replaces in-place at its original index rather than being removed and re-appended at the tail.

  • New useMothershipQueueStore (stores/mothership-queue/) provides enqueue, insertAt, replaceAt, remove, migrate, clearChat, and setEditing, all with unit tests. editing is intentionally excluded from the persisted snapshot so a reloaded tab cannot stall the drain loop.
  • use-chat.ts adopts a chatKey/pendingChatKey sentinel pattern, adds dispatchingHeadId React state to gate UI affordances on the in-flight head, and introduces an auto-drain useEffect that kicks the dispatch loop on rehydrate once chatHistory confirms no active stream.
  • UI and parity (queued-messages.tsx, mothership-chat.tsx, panel.tsx) wire through editingQueuedId, dispatchingHeadId, and onCancelQueueEdit for both the home chat and the workflow copilot panel.

Confidence Score: 5/5

Safe to merge; the queue persistence and edit-in-place mechanics are well-guarded and the previous-review issues are all resolved.

The core state machine — enqueue, dispatch, restore, migrate, and drain — is carefully gated with epoch checks, synchronous ref guards, and explicit kicks on every edit-resolution path. The one remaining edge case (a concurrent chat-delete racing a failing dispatch) produces a stale sessionStorage bucket that clears on tab close and cannot be re-triggered without navigating back to the deleted chat URL, so it does not affect normal usage or data integrity.

The interaction between restoreQueuedMessage in use-chat.ts and the clearChat calls in tasks.ts is worth a follow-up, but only because the queue is now persistent — the old in-memory queue would have been garbage-collected on navigation.

Important Files Changed

Filename Overview
apps/sim/stores/mothership-queue/store.ts New Zustand + sessionStorage store with well-guarded mutators (enqueue, insertAt, replaceAt, remove, migrate, clearChat). Previous-review fixes are in place: editing excluded from partialize, migrate merges rather than overwrites, stripVolatile drops stream refs on persist.
apps/sim/stores/mothership-queue/store.test.ts 16 unit tests covering enqueue, remove, insertAt, replaceAt (including handoff strip), migrate (including merge-not-overwrite), clearChat, and setEditing. No test for the clearChat + concurrent dispatch race.
apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts Major refactor: queue moved from useState to Zustand store, chatKey/pendingChatKey sentinel pattern added, dispatchingHeadId React state, userRemovedDuringDispatchRef, and auto-drain useEffect. The clearChat/restoreQueuedMessage race is the one remaining concern.
apps/sim/hooks/queries/tasks.ts Adds clearChat(chatId) calls in onSettled for both single and bulk delete mutations — correct place to clean up queue state on deletion.
apps/sim/app/workspace/[workspaceId]/home/components/queued-messages/queued-messages.tsx Wires editingQueuedId and dispatchingHeadId: shows Cancel-edit button on editing row, disables edit+send-now on dispatching head. Clean conditional rendering.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A([User submits message]) --> B{sendingRef?}
    B -- No --> C{pendingStop?}
    C -- No --> D[startSendMessage directly]
    B -- Yes --> E{editingId set?}
    C -- Yes --> E
    E -- Yes, in queue --> F[replaceAt + setEditing null\nenqueueQueueDispatch]
    E -- No / stale --> G[enqueue to store]
    G --> H{pendingStop?}
    H -- Yes --> I[enqueueQueueDispatch send_head]
    H -- No --> J[Wait for notifyTurnEnded kick]
    D --> K([Turn ends / notifyTurnEnded])
    K --> L{queue non-empty?}
    L -- Yes --> M[enqueueQueueDispatch]
    M --> N[dispatchQueuedMessage]
    N --> O{editing head?}
    O -- Yes --> P[pause / continue loop]
    O -- No --> Q[setDispatchingHeadId\nstartSendMessage liveMsg]
    Q --> R{success?}
    R -- Yes --> S[removeQueuedMessage\nclear dispatchingHeadId]
    R -- No --> T{userRemoved?}
    T -- Yes --> U[drop / clear dispatchingHeadId]
    T -- No --> V[restoreQueuedMessage insertAt\nclear dispatchingHeadId]
    W([User cancels edit]) --> X[setEditing null\nenqueueQueueDispatch]
    Y([Page reload]) --> Z[sessionStorage rehydrate\nauto-drain useEffect]
    Z --> M
    AA([Chat deleted]) --> AB[clearChat chatId]
    AB -.->|race if dispatch in flight| V
Loading

Reviews (3): Last reviewed commit: "fix(mothership): honor user removal duri..." | Re-trigger Greptile

Comment thread apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts
Comment thread apps/sim/stores/mothership-queue/store.ts
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/stores/mothership-queue/store.ts
Comment thread apps/sim/stores/mothership-queue/store.ts Outdated
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 036bbb3. Configure here.

@waleedlatif1 waleedlatif1 merged commit 811ecbd into staging May 28, 2026
14 checks passed
@waleedlatif1 waleedlatif1 deleted the waleedlatif1/mship-queue-fix branch May 28, 2026 18:26
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