Repository navigation
fix(drafts): keep annotation reply visible through concurrent WS update - #226
Merged
Merged
Conversation
Posting a reply in an annotation thread made the message vanish on Send, then reappear. When the reply @-mentions Aye it triggers a turn, so a concurrent draft_annotation:updated event lands mid-flight; the WS updater replaced the whole annotation with the server payload (whose messages lack the in-flight optimistic reply), dropping it until the next refetch echoed it back. - useAddDraftAnnotationMessage: add onSuccess that swaps the optimistic temp message for the real server row, so the real id is in the cache promptly. onSettled invalidate stays as a (now-non-flickering) safety net. - annotation-ws-updaters: preserve in-flight temp-id messages when a WS event replaces an annotation wholesale (onDraftAnnotationUpdated and the re-match branch of onDraftAnnotationCreated). Add isTempMessageId. - onDraftAnnotationMessageCreated: keep id dedupe, and also replace an un-swapped temp message with the same author+body instead of appending a duplicate echo. - Tests: optimistic reply survives a mid-flight rewrite, the echo reconciles to exactly one row, and onSuccess swaps temp->real. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Posting a reply in an annotation thread made the message vanish on Send, then reappear — a flicker, not a data loss.
Root cause
useAddDraftAnnotationMessageoptimistically appends a message with a temp id (temp-message-…), thenonSettledinvalidates the list. But when the reply @-mentions Aye it triggers a turn, so a concurrentdraft_annotation:updatedWS event arrives mid-flight.onDraftAnnotationUpdatedreplaced the whole annotation with the WS payload (whosemessagesare the server's, without the in-flight optimistic reply) → the optimistic message was dropped. The later refetch/echo brought the committed copy back, producing the flicker.A latent second bug:
onDraftAnnotationMessageCreateddeduped only byid, but the optimistic temp id ≠ the server id, so the user's own message-created echo would have appended a duplicate.Fix (frontend-only,
packages/core/drafts/)useAddDraftAnnotationMessage): newonSuccessreplaces the optimistic temp message (matched by the temp id carried in theonMutatecontext) with the realapi.addDraftAnnotationMessageresponse, so the real id is in the cache promptly.onErrorrollback unchanged;onSettledinvalidate kept as a now-non-flickering safety net.annotation-ws-updaters.ts): a sharedwithPreservedTempMessageshelper keeps any temp-id messages the incoming WS payload omits, applied inonDraftAnnotationUpdatedand the re-match branch ofonDraftAnnotationCreated. Adds/exportsisTempMessageId(mirrorsisTempAnnotationId).onDraftAnnotationMessageCreated): keeps the id dedupe and, for the window before the temp→real swap, replaces an un-swapped temp message with the sameauthor_user_id+bodyinstead of appending a duplicate.Tests
packages/core/drafts/annotation-ws-updaters.test.tsandannotation-mutations.test.tsx:onDraftAnnotationUpdatedwith a payload that lacks it → assert the optimistic reply survives (this is where it used to disappear).onDraftAnnotationMessageCreatedwith the real row → assert exactly one copy.onSuccessswaps temp→real (real id lands in cache);onErrorrolls back.Verification
pnpm --filter @multica/core typecheck✓ ·pnpm --filter @multica/views typecheck✓ · fullpnpm typecheck✓ (6/6)pnpm --filter @multica/core exec vitest run✓ (42 files, 378 tests) · fullpnpm test✓ (857 views tests, all packages)pnpm --filter @multica/core lint✓ (no new findings)No backend/schema/daemon change.
🤖 Generated with Claude Code