Automattic / Automattic/gutenberg-sync-engines
Typing right after opening a post can duplicate or delete another person's text
- Dominant language
- JavaScript
- Stars
- 2
- Forks
- 0
- Avg merge
- 8h 34m
- Merged PRs (30d)
- 44
Description
## What happens now
When an editor window opens a post that others are editing under the yjs-server method, any edits made before the live session finishes connecting are saved up and applied to the shared document afterwards. The editor's very first automatic step — reading the saved file into blocks — can be one of those held-back edits. When it is, an old copy of the whole post (with brand-new internal block ids) gets merged over the live shared document right after connecting: recent changes by other people can be overwritten with older text, and every block's identity changes, which also strips the server's per-block bookkeeping (the wrapper records it keeps from the post's first snapshot are keyed by the old ids).
This is a timing race: usually the live document arrives first and nothing happens. No damage from it has been observed in a test yet — this issue is filed from code analysis during #38 — so it first needs a reproduction before a fix can be judged.
## Example
There are no reliable steps yet. The expected shape of a reproduction:
1. Two windows editing one post under the yjs-server method; window A keeps typing.
2. Window B reloads on a slow machine (the fuzzer's `RTC_FUZZ_CPU_THROTTLE` knob, or the e2e fixtures' `RTC_E2E_CPU_THROTTLE`, slows a page deliberately).
3. Watch window A after B finishes connecting.
**What you would see:** text A typed since the last save disappears or reverts, without any conflict message.
**What you expected:** opening or reloading a window never changes the post's content by itself.
## What should happen instead
A window that is still connecting should never push its own automatic reading of the saved file into the shared session. Only deliberate edits made by the person should survive the wait and be applied.
## How we will know it is done
Still being shaped. Missing: (a) a reproduction — seeded, ideally via the fuzzer with the CPU-slowdown knob; (b) a decision on where the guard belongs (see notes). Whoever shapes this should turn the reproduction into the done-check.
## Notes for whoever picks this up
The buffering lives in this plugin's yjs-server client adapter: `src/engines/yjs-server/engine.ts`, `pendingLocalChanges` — every pre-bootstrap `applyLocalChanges` call is queued and replayed verbatim once the server's first snapshot arrives. The framework forwards ALL local record edits there, including `useEntityBlockEditor`'s initial parse dispatch (blocks parsed from saved content, fresh uuid clientIds, `originalContent`/`isValid` keys attached). Replaying that parse runs `mergeCrdtBlocks` over the whole document: stale content overwrites newer Y state positionally, incoming clientIds are adopted (the framework's merge accepts new ids), and `META_WRAPPERS` entries in `includes/engines/yjs-server/class-wp-yjs-server-engine.php` become unreachable (keyed by the old ids; note the `_save` fallback that should cover this is itself broken — see the saved-HTML-note issue). The intent-log engine already solved the same problem differently: it discards pre-init trees except for a guarded empty-document recovery (see the AGENTS.md notes on pre-init edits). Candidate directions: filter the buffered queue to drop block-list edits that predate bootstrap unless the document is empty (mirroring intent-log), or mark the initial parse dispatch so engines can tell it from a human edit (that marking would be framework territory). Related: #38 (where this lane was analyzed; its fix removed the most common trigger for divergence between parsed and adopted blocks, which may make this race even rarer and harder to reproduce).
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in src/engines/yjs-server/engine.ts at pendingLocalChanges and compare its pre-bootstrap buffering with the intent-log engine and AGENTS.md notes. Use the fuzzer's RTC_FUZZ_CPU_THROTTLE or the e2e fixtures' RTC_E2E_CPU_THROTTLE to seek a seeded two-window reproduction, then inspect META_WRAPPERS in includes/engines/yjs-server/class-wp-yjs-server-engine.php. Done begins with a reliable reproduction and an agreed guard location/check.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php, typescript
- Domain
- distributed-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 38/100