fix(mothership): persist queued messages, edit-in-place preserves order#4769
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Edit-in-place keeps queue order: editing binds a row to the composer, submit replaces the slot via 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. |
Greptile SummaryThis PR lifts the mothership message queue from component-local
Confidence Score: 5/5Safe 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 Important Files Changed
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
Reviews (3): Last reviewed commit: "fix(mothership): honor user removal duri..." | Re-trigger Greptile |
…leanup on edit, merge on migrate
|
@greptile |
|
@cursor review |
|
@cursor review |
|
@greptile |
|
@greptile |
|
@cursor review |
There was a problem hiding this comment.
✅ 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.
Summary
useStateinto a newuseMothershipQueueStore(Zustand + sessionStorage), keyed by chatId — queue now persists across nav and reload, and chat-delete clears its bucketreplaceAt) instead of removing + re-appending at the tailpending::<shortId>) migrates to the real chatId onadoptResolvedChatId, so first-send queues follow their resolved chatchatHistory.activeStreamIdso it doesn't race the reconnect pathqueuedSendHandoffon edit + on persist so a fresh handoff is minted at send timeType of Change
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