Description
Since #41001 (9113255) the web/desktop app keeps two copies of every submitted prompt in the client message store.
The prompt input adds an optimistic user message stamped with the client's Date.now() and sends the same message id to the server, which persists it with its own timestamp when the prompt lands — so the server copy's time.created is always a bit later. The message.updated handler now resolves rows by messageKey (time.created + id) instead of id, misses the optimistic row because the key differs, and splices the server copy in next to it: same id, two rows.
Knock-on effects until a full session refetch: the fork dialog reads this store without deduping, so the prompt is listed twice; message.removed deletes only the first same-id row, so removing the message leaves a ghost copy; mergeOptimisticPage never confirms the optimistic item because its exact-key search misses too. Same pattern in server-session.ts (message.updated handler and mergeOptimisticPage), sync.tsx, and global-sync/event-reducer.ts.
Steps to reproduce
- In the web app, submit a prompt in any session
- The session's message store now holds two rows with the same message id and different
time.created — the fork dialog lists the prompt twice
- Unit-level repro: sync a
createServerSession store containing a message at time.created: 1, then apply message.updated with the same id at time.created: 2 — the store ends up with 2 rows instead of replacing the one
OpenCode version
dev @ 0bff28d
Operating System
reproduced at the store level (unit test), platform-independent
Description
Since #41001 (9113255) the web/desktop app keeps two copies of every submitted prompt in the client message store.
The prompt input adds an optimistic user message stamped with the client's
Date.now()and sends the same message id to the server, which persists it with its own timestamp when the prompt lands — so the server copy'stime.createdis always a bit later. Themessage.updatedhandler now resolves rows bymessageKey(time.created + id) instead of id, misses the optimistic row because the key differs, and splices the server copy in next to it: same id, two rows.Knock-on effects until a full session refetch: the fork dialog reads this store without deduping, so the prompt is listed twice;
message.removeddeletes only the first same-id row, so removing the message leaves a ghost copy;mergeOptimisticPagenever confirms the optimistic item because its exact-key search misses too. Same pattern inserver-session.ts(message.updatedhandler andmergeOptimisticPage),sync.tsx, andglobal-sync/event-reducer.ts.Steps to reproduce
time.created— the fork dialog lists the prompt twicecreateServerSessionstore containing a message attime.created: 1, then applymessage.updatedwith the same id attime.created: 2— the store ends up with 2 rows instead of replacing the oneOpenCode version
dev @ 0bff28d
Operating System
reproduced at the store level (unit test), platform-independent