From ae127f185bf6cfbce4a5328f87fb59142f974bf2 Mon Sep 17 00:00:00 2001 From: Rudy Celekli Date: Tue, 29 Sep 2026 20:55:03 -0400 Subject: [PATCH] Keep collaboration replay cursors contiguous and ignore duplicate delivery --- ...ngineering-realtime-collaboration-engineer.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/engineering/engineering-realtime-collaboration-engineer.md b/engineering/engineering-realtime-collaboration-engineer.md index e2ca05c4..256f1987 100644 --- a/engineering/engineering-realtime-collaboration-engineer.md +++ b/engineering/engineering-realtime-collaboration-engineer.md @@ -41,7 +41,8 @@ You are **Realtime Collaboration Engineer**, an expert in the systems behind liv ```typescript // The contract: server assigns seq to every op; client acks what it has applied; -// resume replays the gap. Duplicates are impossible by construction (opId dedupe). +// resume replays the gap. Server opId dedupe prevents duplicate log entries; +// clients must separately ignore replayed deliveries and reject sequence gaps. class SyncConnection { private lastServerSeq = 0; // highest seq applied locally private pending = new Map(); // sent, not yet acked @@ -66,9 +67,16 @@ class SyncConnection { private receive(msg: ServerMsg) { if (msg.type === 'op') { - this.lastServerSeq = msg.seq; // server ordering is truth - this.pending.delete(msg.opId); // ack of our own op, or... - this.applyRemote(msg); // ...someone else's, transformed + if (msg.seq <= this.lastServerSeq) return; // replay: already applied + if (msg.seq !== this.lastServerSeq + 1) { + // Keep the contiguous cursor: reconnect/replay from the last applied op. + // Closing triggers the existing onclose reconnect path. + this.ws.close(); + return; + } + this.applyRemote(msg); // may throw; do not advance yet + this.lastServerSeq = msg.seq; + this.pending.delete(msg.opId); // ack only after successful apply } }