Skip to content

Conversation

@chriszarate
Copy link
Contributor

What?

Improve the format and type of persisted CRDT document meta.

Why?

The unknown record value type allowed consuming code to incorrectly nest document meta in a meta property. As a result, persisted CRDT documents could not be correctly validated, resulting in incorrect merge behavior and CRDT document invalidation.

How?

Provide the CRDT document meta correctly, improve the type to prevent nested meta in the future, and add tests.

@chriszarate chriszarate added [Feature] Real-time Collaboration Phase 3 of the Gutenberg roadmap around real-time collaboration [Type] Experimental Experimental feature or API. labels Dec 12, 2025

export function createYjsDoc( documentMeta: Record< string, unknown > ): Y.Doc {
interface DocumentMeta {
[ key: string ]: boolean | number | string;
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm kind of hesitant here, are these the only meta attributes that third-party blocks can have, I wonder if it's possible to have arrays and nested objects too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@youknowriad CRDT document metadata are in-memory metadata of the CRDT document instance (example: a boolean indicating whether the document was loaded from persistence / post meta). The sync package fully controls this metadata and it is not persisted or synced. I've added a comment in 2beb2f4 that hopefully makes this more clear.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah I thought this was about post meta

@github-actions
Copy link

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: chriszarate <[email protected]>
Co-authored-by: youknowriad <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions
Copy link

Flaky tests detected in 2beb2f4.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/20241061840
📝 Reported issues:

type DocumentMeta = Record< string, DocumentMetaValue >;
type DocumentMetaValue = boolean | number | string;

export function createYjsDoc( documentMeta: DocumentMeta = {} ): Y.Doc {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we messing with these "meta"? Is it because we need to persist the doc in the database or something, so we need some serialized information.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The main use case for CRDT document metadata is local state that is colocated with the document. "Local" because we don't want this metadata to be synced to peers or persisted—it applies only to this document instance, in-memory.

We have one use case currently: We use CRDT document metadata to store a marker that the CRDT document was loaded from persistence. This allows us to have code in core-data that runs only when we are validating persisted CRDT documents:

if (
ydoc.meta?.get( CRDT_DOC_META_PERSISTENCE_KEY ) &&
editedRecord.content
) {
const blocks = ymap.get( 'blocks' ) as YBlocks;
return (
__unstableSerializeAndClean(
blocks.toJSON()
).trim() !== editedRecord.content.raw.trim()
);
}

tl;dr: They are in-memory descriptors for a CRDT document.

@chriszarate chriszarate merged commit 254d63a into trunk Dec 16, 2025
40 of 44 checks passed
@chriszarate chriszarate deleted the fix/persisted-crdt-doc-meta-type branch December 16, 2025 23:40
@github-actions github-actions bot added this to the Gutenberg 22.4 milestone Dec 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Real-time Collaboration Phase 3 of the Gutenberg roadmap around real-time collaboration [Type] Experimental Experimental feature or API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants