fix(sync): treat part snapshot as a delta coalescing barrier (#1693)

A `message.part.updated` snapshot did not invalidate the pending delta
coalescing key for its message/part. A delta arriving after an intervening
snapshot merged into a delta queued before it, and the snapshot then
overwrote that slot, dropping the later delta's text (e.g. `abc` rendered
as `ab`). Enqueueing a part snapshot now drops that part's pending delta
coalescing keys, while leaving already-queued delta events in place, so
post-snapshot deltas start a fresh entry.

Closes #1647.

Co-authored-by: Ibrahim Khan <ibrakhxn@amazon.com>
This commit is contained in:
Ibrahim Khan
2026-06-18 01:09:24 +03:00
committed by GitHub
co-authored by Ibrahim Khan
parent 08a851f902
commit fbc108f6ba
2 changed files with 65 additions and 0 deletions
+20
View File
@@ -447,6 +447,26 @@ export function createEventPipeline(input: EventPipelineInput): EventPipeline {
const normalizedPayload = normalizeEventType(payload)
const routedDirectory = routeDirectory?.(directory, normalizedPayload) || directory
const d = getOrCreateDir(routedDirectory)
// A full part snapshot is a coalescing barrier for that part's deltas:
// drop its pending delta coalescing keys so a delta arriving after the
// snapshot starts a fresh queue entry instead of merging into a delta
// queued before the snapshot, which the snapshot would then overwrite and
// drop the later delta's text. The already-queued delta event stays.
if (normalizedPayload.type === "message.part.updated") {
const part = (normalizedPayload.properties as { part?: { id?: unknown; messageID?: unknown } }).part
const messageID = typeof part?.messageID === "string" ? part.messageID : undefined
const partID = typeof part?.id === "string" ? part.id : undefined
if (messageID && partID) {
const deltaPrefix = `message.part.delta:${messageID}:${partID}:`
for (const coalesceKey of d.coalesced.keys()) {
if (coalesceKey.startsWith(deltaPrefix)) {
d.coalesced.delete(coalesceKey)
}
}
}
}
const k = key(normalizedPayload)
if (k) {
const i = d.coalesced.get(k)