Skip to content

Commit a49c118

Browse files
committed
fix(files): idempotent settle apply (update lands client-side; no straggler dup)
Cursor round 6 (Medium): a lone client's `update` never applied client-side — held mid-stream, then skipped by the didApplyStreamRef settle gate — so the rewrite depended entirely on the durable merge (stale if delayed/failed). Root cause was over-correcting round 5. Now that the shadow is opened lazily in the tick (current-seeded), the round-5 base-shadow duplication is already gone, so didApplyStreamRef is unnecessary. Replaced it: settle applies the final body via `agentStreamSessionRef.current ?? beginAgentStream(editor)` — the leader reuses its up-to-date shadow (last throttled frame), while a client that never applied (non-leader, held `update`, pre-seed) opens a FRESH current-seeded shadow. Reconciling current->final is idempotent: a straggler that settles after another wrote the final reconciles to a noop. So a lone `update` applies at settle (no wait on the merge), and there's still no settle-time election or base-shadow dup.
1 parent 3ef00a1 commit a49c118

1 file changed

Lines changed: 10 additions & 25 deletions

File tree

apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.tsx

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -390,15 +390,6 @@ export function LoadedRichMarkdownEditor({
390390
streamOperationRef.current = streamOperation
391391
/** The live agent-stream shadow replica, held for the current stream and freed on settle/unmount. */
392392
const agentStreamSessionRef = useRef<AgentStreamSession | null>(null)
393-
/**
394-
* True once THIS client has applied at least one mid-stream frame for the current stream — i.e. it was
395-
* the elected leader whose shadow is up to date. Gates the settle apply LOCALLY (not on a settle-time
396-
* re-election, which is racy: a straggler that settles after the leader clears its announcement would
397-
* self-elect and re-insert the whole doc via its base-seeded shadow). A client that never applied
398-
* (non-leader, a held `update`, or a pre-seed stream) converges to the final state via Yjs + the
399-
* durable write instead. Reset on settle.
400-
*/
401-
const didApplyStreamRef = useRef(false)
402393
/** True once this client has announced candidacy in the agent-stream election for the current stream. */
403394
const agentAnnouncedRef = useRef(false)
404395
const router = useRouter()
@@ -894,7 +885,6 @@ export function LoadedRichMarkdownEditor({
894885
// from the CURRENT doc, already carrying the prior leader's ops, never a stale base.
895886
if (!agentAnnouncedRef.current) {
896887
agentAnnouncedRef.current = true
897-
didApplyStreamRef.current = false
898888
if (collaboration) announceAgentApplying(collaboration.awareness)
899889
}
900890
const body = splitFrontmatter(content).body
@@ -949,7 +939,6 @@ export function LoadedRichMarkdownEditor({
949939
streamRafRef.current = null
950940
return
951941
}
952-
didApplyStreamRef.current = true
953942
streamRafRef.current = null
954943
lastStreamedBodyRef.current = pending
955944
lastStreamParseAtRef.current = performance.now()
@@ -962,26 +951,23 @@ export function LoadedRichMarkdownEditor({
962951
cancelAnimationFrame(streamRafRef.current)
963952
streamRafRef.current = null
964953
}
965-
// Settle: only a client that actually applied mid-stream (the elected leader, `didApplyStreamRef`)
966-
// applies the FINAL body — its shadow is up to date, so this just catches a throttled last frame, so
967-
// the Y.Doc exactly equals the streamed result. A client that never applied (a non-leader, a held
968-
// `update`, or a pre-seed stream) has no shadow and skips — it converges via Yjs + the durable write.
969-
// This is a LOCAL decision (no settle-time re-election), so a straggler can't self-elect after the
970-
// leader clears its announcement.
954+
// Settle: apply the FINAL body so the Y.Doc exactly equals the streamed result. The mid-stream
955+
// leader REUSES its up-to-date shadow (just catching a throttled last frame); a client that never
956+
// applied mid-stream (a non-leader, a held `update`, or a pre-seed stream) opens a FRESH shadow
957+
// seeded from the CURRENT doc. Reconciling current→final is idempotent — a client that settles after
958+
// another already wrote the final reconciles to a noop — so there is NO settle-time election and no
959+
// base-shadow duplication, and a lone client (incl. an `update`) still applies rather than waiting on
960+
// the durable merge. That durable `edit_content` write then lands as a noop diff too.
971961
if (wasStreamingRef.current && collabReady) {
972962
wasStreamingRef.current = false
973-
const didApply = didApplyStreamRef.current
974-
didApplyStreamRef.current = false
975963
agentAnnouncedRef.current = false
976964
if (collaboration) clearAgentApplying(collaboration.awareness)
977965
lastStreamedBodyRef.current = null
978-
const session = agentStreamSessionRef.current
966+
const finalBody = splitFrontmatter(content).body
967+
const session = agentStreamSessionRef.current ?? beginAgentStream(editor)
979968
agentStreamSessionRef.current = null
980969
if (session) {
981-
if (didApply) {
982-
const finalBody = splitFrontmatter(content).body
983-
runOffRender(() => applyAgentStreamFrame(editor, session, finalBody))
984-
}
970+
runOffRender(() => applyAgentStreamFrame(editor, session, finalBody))
985971
// Free the shadow with an UNGUARDED microtask (not `runOffRender`): a rapid follow-up stream
986972
// can supersede the run token and drop the apply above, but the shadow must always be
987973
// destroyed. Queued after the apply, so it frees the shadow only once that has had its chance.
@@ -1095,7 +1081,6 @@ export function LoadedRichMarkdownEditor({
10951081
agentStreamSessionRef.current = null
10961082
}
10971083
lastStreamedBodyRef.current = null
1098-
didApplyStreamRef.current = false
10991084
agentAnnouncedRef.current = false
11001085
},
11011086
[]

0 commit comments

Comments
 (0)