Skip to content

fix(collab-doc): stream every external file write into open editors, not just edit_content - #6070

Merged
waleedlatif1 merged 2 commits into
realtime-roomsfrom
fix-collab-live-merge-coverage
Jul 30, 2026
Merged

fix(collab-doc): stream every external file write into open editors, not just edit_content#6070
waleedlatif1 merged 2 commits into
realtime-roomsfrom
fix-collab-live-merge-coverage

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

Fixes a real bug found via live testing: a copilot/mothership edit to a markdown file that another user has open did not stream into their editor.

Root-caused from the running server logs + local Redis:

  • The mothership's "Prepend sentence" ran read + file + function_executezero apply-edit calls.
  • Redis confirmed the prepended text never entered the live doc stream.

The live-doc merge bridge (mergeEditIntoLiveFileDoc) was wired into the edit_content tool only. Every other server-side write — the file tool (/api/tools/file/manage), function_execute (/api/function/execute via writeWorkspaceFileByPath), create_file overwrite, and the PUT /content route — went straight to updateWorkspaceFileContent and skipped the merge. The durable file changed (list refreshed) but the open editor never updated.

Fix — centralize at the one chokepoint every external writer shares

  • updateWorkspaceFileContent gains an opt-out { syncLiveDoc } (default on); after the durable write it merges markdown writes into any open collaborative doc (best-effort, no-op when nobody has it open). Any current or future writer is covered automatically.
  • persist opts out — it IS the doc→markdown projection, so merging back would be a persist→merge→persist self-loop.
  • create_file opts its empty shell out (real content arrives via a later write) so an open editor never flickers to empty on overwrite.
  • edit_content drops its now-redundant explicit merge call.
  • Binary writers (image/video/audio/ffmpeg/download) are naturally excluded — the merge is gated to markdown.

Note

Bumps the api-validation route baseline 994 → 996 to match the true route count already on realtime-rooms (pre-existing ratchet drift from an earlier merge — not routes added by this PR; verified against a clean tree).

Type of Change

  • Bug fix

Testing

  • New unit tests: markdown write merges via the chokepoint; syncLiveDoc:false and non-markdown writes do not.
  • Full gates green: sim + realtime tsc, lint, api-validation, boundaries, prune, 35 + 74 targeted tests.
  • Root cause verified live (server logs + Redis stream). End-to-end 2-browser verify still owed on the branch.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

…not just edit_content

A copilot/mothership edit to an open markdown file did not appear live in another
user's editor: the live-doc merge bridge (mergeEditIntoLiveFileDoc) was wired into the
edit_content tool ONLY. Every other server-side write — the file tool
(/api/tools/file/manage), function_execute (/api/function/execute via
writeWorkspaceFileByPath), create_file overwrite, and the PUT /content route — went
straight to updateWorkspaceFileContent and skipped the merge, so the durable file
changed but the open editor never updated. Confirmed from live logs (the mothership
'Prepend sentence' ran read + file + function_execute — zero apply-edit calls) and
Redis (the prepended text was absent from the doc stream).

Centralize the merge at the one chokepoint every external writer shares:
- updateWorkspaceFileContent gains an opt-out "syncLiveDoc" (default on) and, after
  the durable write, merges markdown writes into any open collaborative doc (best-effort;
  no-op when nobody has it open). Any current OR future writer is covered automatically.
- persist.ts opts out (syncLiveDoc:false) — it IS the doc→markdown projection, so merging
  it back would be a persist→merge→persist self-loop.
- create_file opts its empty shell out (real content arrives via a later write) so an open
  editor never flickers to empty on overwrite; threaded through writeWorkspaceFileByPath.
- edit_content drops its now-redundant explicit merge call (the chokepoint handles it).
- binary writers (image/video/audio/ffmpeg/download) are naturally excluded — the merge is
  gated to markdown, the only format the collaborative editor renders.

Also bump the api-validation route baseline 994→996 to match the true route count already
on this branch (pre-existing ratchet drift from an earlier merge; NOT added by this PR).
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jul 30, 2026 12:15am

Request Review

@cursor

cursor Bot commented Jul 30, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches the shared file-write path and live-doc merge behavior for all markdown updates; incorrect opt-in/out could cause editor flicker or persist loops, though defaults and tests target the main failure modes.

Overview
Fixes copilot and other server-side markdown writes not appearing in open collaborative editors — only edit_content used to call mergeEditIntoLiveFileDoc; durable storage updated but the live Yjs doc did not.

updateWorkspaceFileContent is now the single chokepoint: after a successful blob/DB update it best-effort merges markdown into any open file doc via mergeEditIntoLiveFileDoc (default syncLiveDoc: true). edit_content drops its duplicate merge. writeWorkspaceFileByPath forwards syncLiveDoc on overwrites.

Opt-outs: relay persistFileDoc and create_file empty shells pass syncLiveDoc: false to avoid persist→merge loops and briefly blanking an open editor.

Editor: collaborative setEditable runs in a queueMicrotask so Yjs-driven readiness updates do not trigger flushSync during React render.

Adds unit tests for merge vs opt-out vs non-markdown; bumps API route validation baseline 994 → 996 (pre-existing drift).

Reviewed by Cursor Bugbot for commit 299842e. Configure here.

@greptile-apps

greptile-apps Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Centralizes live collaborative-doc merge on every durable workspace file write so open markdown editors receive external updates.

  • updateWorkspaceFileContent merges markdown writes into open collab docs by default (syncLiveDoc).
  • persist and empty create_file shells opt out; edit_content drops its redundant explicit merge.
  • Defers TipTap setEditable via microtask to avoid flushSync-during-render; bumps api-validation route baseline 994→996.

Confidence Score: 5/5

This PR appears safe to merge; no blocking failures remain from prior or current review.

No blocking failure remains.

Important Files Changed

Filename Overview
apps/sim/lib/uploads/contexts/workspace/workspace-file-manager.ts Adds default-on markdown live-doc merge after successful durable update; opt-out via syncLiveDoc false.
apps/sim/lib/collab-doc/persist.ts Opts persist out of live merge to avoid persist→merge→persist loops.
apps/sim/lib/copilot/tools/server/files/edit-content.ts Removes redundant per-tool merge; relies on shared chokepoint.
apps/sim/lib/copilot/tools/server/files/create-file.ts Opts empty-shell create out of merge to avoid blanking open editors.
apps/sim/lib/copilot/vfs/resource-writer.ts Forwards optional syncLiveDoc on overwrite path.
apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/rich-markdown-editor.tsx Defers setEditable to a cancellable microtask to avoid flushSync during React render.
apps/sim/lib/uploads/contexts/workspace/workspace-file-storage-accounting.test.ts Covers default merge, syncLiveDoc false, and non-markdown skip.
scripts/check-api-validation-contracts.ts Ratchets route baseline to 996 for pre-existing drift on the base branch.

Sequence Diagram

sequenceDiagram
  participant Writer as External writer<br/>(file tool / function_execute / PUT content / edit_content)
  participant UWC as updateWorkspaceFileContent
  participant Storage as Durable storage + DB
  participant Merge as mergeEditIntoLiveFileDoc
  participant Editor as Open collab markdown editor

  Writer->>UWC: content Buffer (+ optional syncLiveDoc)
  UWC->>Storage: upload + finalize metadata
  alt "syncLiveDoc !== false and markdown"
    UWC->>Merge: fileId + utf-8 markdown
    Merge->>Editor: CRDT merge (best-effort)
  else syncLiveDoc false or non-markdown
    Note over UWC: skip live merge
  end
Loading

Reviews (2): Last reviewed commit: "fix(collab-doc): defer setEditable out o..." | Re-trigger Greptile

… warning)

The collab editability-reapply effect called editor.setEditable synchronously. In collab
mode isEditable flips from readiness (synced + seeded), which is driven by a Yjs
config.observe firing synchronously inside Y.applyUpdate — so the effect can run while React
is mid-render. TipTap's React binding commits setEditable's transaction with flushSync, which
throws "flushSync was called from inside a lifecycle method. React cannot flush when React is
already rendering." Defer the setEditable to a microtask (runs right after the current commit,
before paint), guarding against a destroyed editor or a stale value before it fires.

Only the collab path (this effect) hit the warning; the streaming/settle effect's setEditable
calls run on the non-collab path where isEditable isn't driven by a mid-render Yjs observer.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 299842e. Configure here.

@waleedlatif1
waleedlatif1 merged commit fd34853 into realtime-rooms Jul 30, 2026
5 checks passed
@waleedlatif1
waleedlatif1 deleted the fix-collab-live-merge-coverage branch July 30, 2026 00:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant