From a4cb627442fbb1bd7c221bceab7efb073cf5aeda Mon Sep 17 00:00:00 2001 From: Yash Raj Pandey Date: Thu, 9 Jul 2026 10:01:10 -0400 Subject: [PATCH] fix(collab): stop /documents/:slug/ops replaying stale DB markdown into live rooms After a non-rewrite.apply op succeeds, the route reapplied the persisted DB row into the live collab room with source 'rest-ops'. Unlike other server-side write sources, 'rest-ops' is not covered by the legacy live-apply guard (shouldBlockLegacyLiveApplySource), so a point-in-time DB snapshot could replay stale markdown over concurrent live edits that were not yet persisted. Engine-backed (non-rewrite.apply) ops now only refresh the narrow presence/cursor coupling and no longer push a broad DB snapshot into the live room; rewrite.apply continues to commit through the canonical Yjs path. This restores the collab-security invariant that /documents/:slug/ops avoids a broad DB reapply after engine-backed mutations. Fixes #63 --- server/routes.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/server/routes.ts b/server/routes.ts index 80971cb3..8987661f 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -1728,20 +1728,18 @@ apiRoutes.post('/documents/:slug/ops', opsRateLimiter, async (req: Request, res: if (result.status >= 200 && result.status < 300) { // Collab mutations for rewrite.apply are committed through the canonical Yjs path. - // Other ops still need explicit projection sync into the live room. + // Other (engine-backed) ops only refresh presence/cursor coupling below; they do not + // push a DB snapshot into the live room. if (op !== 'rewrite.apply') { try { const collabRuntime = getCollabRuntime(); if (collabRuntime.enabled) { const updatedDoc = getDocumentBySlug(slug); if (updatedDoc) { - const applyOptions = { - markdown: typeof updatedDoc.markdown === 'string' ? updatedDoc.markdown : undefined, - marks: parseJson(updatedDoc.marks), - source: 'rest-ops', - }; - await applyCanonicalDocumentToCollab(slug, applyOptions); - + // Route-level reapplication from the DB can replay stale markdown over concurrent + // live edits that are not yet persisted, so engine-backed (non-rewrite.apply) ops + // no longer push a broad DB snapshot into the live room. Only the narrow, + // non-content presence/cursor coupling below is synced. if (participation) { try { applyAgentPresenceToLoadedCollab(slug, participation.presenceEntry, {