Automattic / Automattic/gutenberg-sync-engines

The saved-HTML note kept for blocks added during a session is deleted as soon as the block changes

Open
#55 0 comments 0 reactions 0 assignees View on GitHub
agent:parked
Dominant language
JavaScript
Stars
2
Forks
0
Avg merge
11h 22m
Merged PRs (30d)
49

Description

## What happens now

For every block someone adds while editing together, the browser stores a small note in the shared document: the block's saved HTML, so the server can later rebuild the post's content with the right surrounding HTML element. That note is deleted the moment any edit passes over the block — the merge routine writes a fresh copy of the note and then its own cleanup step immediately deletes it. The server then rebuilds such blocks with no surrounding element at all: a Group added during a session comes out as bare inner blocks with no `

` around them.

Nothing a user sees reads that rebuilt content today, which is why this has gone unnoticed, but it quietly undoes what PR #35 shipped: the rebuilt content is part of the engine's contract, the diagnostics and benchmarks trust it, and anything built on it later will inherit broken output.

## Example

1. Start the tests site (`npm run env:tests start`) and run one automated editing session that adds Groups mid-session: `npm run fuzz -- --combos=yjs-server/http-polling --seed-list=3 --steps=12` (pass `WP_BASE_URL` if the site is not on :8889).
2. Find the newest entry in `npm run env:tests -- run cli wp collaboration rooms list`.
3. Dump it: `npm run env:tests -- run cli wp eval-file wp-content/plugins//tests/tools/dump-yjs-room-blocks.php "postType/post:"`.

**What you see:** the Groups added during the session print `wrapper:no _save=(none)`, and the rebuilt content at the bottom shows them as `…` — inner blocks directly inside the comment markers, no `

` element.

**What you expected:** every block added in-session keeps its saved-HTML note, and the rebuilt content wraps the Group's children in its `

` exactly as the editor would save it.

## What should happen instead

The note should survive edits (and keep being refreshed as the block's attributes change, which is what the code visibly intends), so the server can always rebuild a faithful copy of the post.

## How we will know it is done

Repeat the example: the dump shows `_save` present on in-session container blocks after later edits, and the rebuilt content contains their wrapper elements. Needs a test — no unit test today covers the note surviving a merge (the existing PHP test writes the note server-side and never exercises the browser's merge routine).

## Notes for whoever picks this up

The defect is in the bundled framework copy, `gutenberg/packages/core-data/src/utils/crdt-blocks.ts`: the update lane refreshes the `_save` mirror (lines 556–567, added by PR #35), then the trailing cleanup loop (lines 710–714) deletes every key the incoming editor block does not carry — and editor blocks never carry `_save` (it is stripped on read at line 229). Both happen in one transaction, so peers and the server only ever see the deleted state. The fix is one line: skip `CRDT_BLOCK_SAVE_KEY` in that cleanup loop, the same exclusion `areBlocksEqual` already makes at line 261. Because it is a `gutenberg/` subtree change, the loop's rules put it with a human (plus the subtree round-trip that carries it upstream) — hence filed as parked rather than ready. Evidence and the full investigation trail: #38 (2026-08-26 cycle comment; confirmed on real session data from passing and failing runs alike). The diagnosis tools referenced above landed in #54.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in gutenberg/packages/core-data/src/utils/crdt-blocks.ts, reading the _save refresh around lines 556–567 and cleanup around 710–714, then compare the exclusion in areBlocksEqual at line 261. Reproduce with the listed npm test-site and fuzz commands, and inspect the room dump. Done means an in-session container retains _save after edits and rebuilt content includes its wrapper; add coverage for the merge path.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
distributed-systems
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.