diff --git a/server/agent-edit-v2.ts b/server/agent-edit-v2.ts index 88626cc5..09c6c5f6 100644 --- a/server/agent-edit-v2.ts +++ b/server/agent-edit-v2.ts @@ -867,7 +867,22 @@ export async function applyAgentEditV2( const usingAuthoritativeFallback = authoritativeBase.base.source === 'live_yjs' || authoritativeMarkdown !== stripEphemeralCollabSpans(doc.markdown ?? ''); if (usingAuthoritativeFallback) { - const persistedBase = parseMarkdownWithHtmlFallback(parser, stripEphemeralCollabSpans(doc.markdown ?? '')); + // The b1..bN refs the agent sent were assigned against the base it declared. For a + // baseRevision-only edit that base is the persisted document row at that revision. + // doc.markdown here is the live-fallback canonical read, so using it would compare the + // live authoritative fragment against itself and never detect that a concurrent edit + // shifted block topology under the agent's refs. Use the persisted row as the drift + // baseline. When a baseToken is supplied the agent opted into the live base directly + // (validated above), so that base is already the correct comparison frame. + const persistedDriftBaseline = baseToken ? null : getDocumentBySlug(slug); + const driftBaselineMarkdown = baseToken + ? stripEphemeralCollabSpans(doc.markdown ?? '') + : stripEphemeralCollabSpans( + persistedDriftBaseline?.revision === baseRevision + ? persistedDriftBaseline.markdown + : doc.markdown ?? '', + ); + const persistedBase = parseMarkdownWithHtmlFallback(parser, driftBaselineMarkdown); if (!persistedBase.doc) { return { status: 500,