Skip to content

[lexical-y] Refactor: One ordered lookup for where an item takes its group's fields - #9554

Open
zurfyx wants to merge 1 commit into
mainfrom
zurfyx/y-group-field-source
Open

zurfyx wants to merge 1 commit into
mainfrom
zurfyx/y-group-field-source

Conversation

@zurfyx

@zurfyx zurfyx commented Oct 29, 2026

Copy link
Copy Markdown
Member

Description

Where an item is new to its group (or this write made its marker anew), the writer copies the group's fields it does not own from another item of the group, so that a field a newer client stored is not lost. Finding that source was split over $markerAsStored (the nearest item that kept its stored marker, inline) and $priorGroupItem (the three fallbacks: the line itself as it was before this write, the nearest item as it was, any item the group had), each documented in a different place.

This makes it one method, $groupSource, that tries the four sources in order and documents the order and why it matters: a single nearest-item rule takes another source where a group moves or its items are replaced, and stores other values of the fields (seen with probes: moving a whole group would store [B,A,B] instead of [A,B,C], and replacing all its items would drop them). The code of each step is unchanged.

Changeset

None: internal refactor, no behavior change.

Test plan

Every gate compares this branch with main (0823b74e) on the same machine.

Before

main: golden corpus and Doc-bytes capture recorded (twice for the capture, to find tests whose bytes vary between runs), full unit suite and extended fuzz as listed under After.

After

Stored bytes over fuzz seeds: a scratch recorder (not committed) runs fuzz scripts and hashes, per seed and peer, the Yjs state bytes, the editor JSON, the warnings, the selection and the node map.

bid: 1000 seeds, 0 differ
bidmoves: 600 seeds, 0 differ
gc: 1000 seeds, 0 differ
keys: 600 seeds, 0 differ
long: 400 seeds, 0 differ
moves: 1500 seeds, 0 differ
nomove: 3000 seeds, 0 differ
GOLD IDENTICAL

Stored bytes over the test corpus: every @lexical/y unit test (the saved fuzz scripts in __tests__/fuzz/ included) run with a setup file that gives each Y.Doc a deterministic client id and hashes its state bytes after the test.

tests: 7286; stable across base runs: 7047 (7024 with docs, 67564 docs); unstable: 239
new run: 7286 tests; missing from new: 0; stable tests whose bytes differ: 0
CAP IDENTICAL

pnpm run ci-check:

Found 0 errors

vitest --project unit --project scripts-unit (36 workers), per test against main:

base {'passed': 18179, 'skipped': 6, 'failed': 1}
new  {'passed': 18177, 'skipped': 7, 'failed': 3}
missing 0, added 1, status changed 2
  CHANGED passed -> failed FollowUpCheckCost.test.ts, ServerScale.test.ts (timing thresholds)

(The added skipped test is the scratch recorder.) The failures are timing or heap thresholds under 36 parallel workers (DisposeMemory on both sides, FollowUpCheckCost and ServerScale here); run alone, they pass:

npx vitest run --project unit ServerScale.test.ts DisposeMemory.test.ts FollowUpCheckCost.test.ts
      Tests  5 passed (5)

Extended fuzz, same results as main: Fuzz.test.ts seeds 1 to 3000 (gc 1000) and 1 to 1000 with block ids, ConcurrentShapesFuzz 600 seeds, FollowUpFuzz 1000 seeds, NestedFuzz, SlotsFuzz, BlockIdFuzzSeeds and FuzzReplay pass. Seed 2277 fails with dup on main and on this branch alike (outside the default 300 seeds; not caused by this change).

Browser and e2e tests were not run locally (GitHub CI runs them).

@vercel

vercel Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
lexical Ready Ready Preview Oct 29, 2026 11:54pm UTC
lexical-playground Ready Ready Preview Oct 29, 2026 11:54pm UTC

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 29, 2026
@pkg-pr-new

pkg-pr-new Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
lexical

npm i https://pkg.pr.new/lexical@9554

@lexical/a11y

npm i https://pkg.pr.new/@lexical/a11y@9554

@lexical/clipboard

npm i https://pkg.pr.new/@lexical/clipboard@9554

@lexical/code

npm i https://pkg.pr.new/@lexical/code@9554

@lexical/code-core

npm i https://pkg.pr.new/@lexical/code-core@9554

@lexical/code-prism

npm i https://pkg.pr.new/@lexical/code-prism@9554

@lexical/code-shiki

npm i https://pkg.pr.new/@lexical/code-shiki@9554

@lexical/compiler

npm i https://pkg.pr.new/@lexical/compiler@9554

@lexical/devtools-core

npm i https://pkg.pr.new/@lexical/devtools-core@9554

@lexical/dragon

npm i https://pkg.pr.new/@lexical/dragon@9554

@lexical/eslint-plugin

npm i https://pkg.pr.new/@lexical/eslint-plugin@9554

@lexical/extension

npm i https://pkg.pr.new/@lexical/extension@9554

@lexical/fast-check

npm i https://pkg.pr.new/@lexical/fast-check@9554

@lexical/file

npm i https://pkg.pr.new/@lexical/file@9554

@lexical/hashtag

npm i https://pkg.pr.new/@lexical/hashtag@9554

@lexical/headless

npm i https://pkg.pr.new/@lexical/headless@9554

@lexical/history

npm i https://pkg.pr.new/@lexical/history@9554

@lexical/html

npm i https://pkg.pr.new/@lexical/html@9554

@lexical/internal

npm i https://pkg.pr.new/@lexical/internal@9554

@lexical/link

npm i https://pkg.pr.new/@lexical/link@9554

@lexical/list

npm i https://pkg.pr.new/@lexical/list@9554

@lexical/mark

npm i https://pkg.pr.new/@lexical/mark@9554

@lexical/markdown

npm i https://pkg.pr.new/@lexical/markdown@9554

@lexical/mdast

npm i https://pkg.pr.new/@lexical/mdast@9554

@lexical/offset

npm i https://pkg.pr.new/@lexical/offset@9554

@lexical/overflow

npm i https://pkg.pr.new/@lexical/overflow@9554

@lexical/plain-text

npm i https://pkg.pr.new/@lexical/plain-text@9554

@lexical/react

npm i https://pkg.pr.new/@lexical/react@9554

@lexical/rich-text

npm i https://pkg.pr.new/@lexical/rich-text@9554

@lexical/selection

npm i https://pkg.pr.new/@lexical/selection@9554

@lexical/table

npm i https://pkg.pr.new/@lexical/table@9554

@lexical/tailwind

npm i https://pkg.pr.new/@lexical/tailwind@9554

@lexical/text

npm i https://pkg.pr.new/@lexical/text@9554

@lexical/utils

npm i https://pkg.pr.new/@lexical/utils@9554

@lexical/y

npm i https://pkg.pr.new/@lexical/y@9554

@lexical/yjs

npm i https://pkg.pr.new/@lexical/yjs@9554

commit: aac803b

…group's fields

## Description

Where an item is new to its group (or this write made its marker anew), the writer copies the group's fields it does not own from another item of the group, so that a field a newer client stored is not lost. Finding that source was split over `$markerAsStored` (the nearest item that kept its stored marker, inline) and `$priorGroupItem` (the three fallbacks: the line itself as it was before this write, the nearest item as it was, any item the group had), each documented in a different place.

This makes it one method, `$groupSource`, that tries the four sources in order and documents the order and why it matters: a single nearest-item rule takes another source where a group moves or its items are replaced, and stores other values of the fields (seen with probes: moving a whole group would store `[B,A,B]` instead of `[A,B,C]`, and replacing all its items would drop them). The code of each step is unchanged.

## Changeset

None: internal refactor, no behavior change.

## Test plan

Every gate compares this branch with `main` (`0823b74e`) on the same machine.

### Before

`main`: golden corpus and Doc-bytes capture recorded (twice for the capture, to find tests whose bytes vary between runs), full unit suite and extended fuzz as listed under After.

### After

Stored bytes over fuzz seeds: a scratch recorder (not committed) runs fuzz scripts and hashes, per seed and peer, the Yjs state bytes, the editor JSON, the warnings, the selection and the node map.

```
bid: 1000 seeds, 0 differ
bidmoves: 600 seeds, 0 differ
gc: 1000 seeds, 0 differ
keys: 600 seeds, 0 differ
long: 400 seeds, 0 differ
moves: 1500 seeds, 0 differ
nomove: 3000 seeds, 0 differ
GOLD IDENTICAL
```

Stored bytes over the test corpus: every `@lexical/y` unit test (the saved fuzz scripts in `__tests__/fuzz/` included) run with a setup file that gives each `Y.Doc` a deterministic client id and hashes its state bytes after the test.

```
tests: 7286; stable across base runs: 7047 (7024 with docs, 67564 docs); unstable: 239
new run: 7286 tests; missing from new: 0; stable tests whose bytes differ: 0
CAP IDENTICAL
```

`pnpm run ci-check`:

```
Found 0 errors
```

`vitest --project unit --project scripts-unit` (36 workers), per test against `main`:

```
base {'passed': 18179, 'skipped': 6, 'failed': 1}
new  {'passed': 18177, 'skipped': 7, 'failed': 3}
missing 0, added 1, status changed 2
  CHANGED passed -> failed FollowUpCheckCost.test.ts, ServerScale.test.ts (timing thresholds)
```

(The added skipped test is the scratch recorder.) The failures are timing or heap thresholds under 36 parallel workers (`DisposeMemory` on both sides, `FollowUpCheckCost` and `ServerScale` here); run alone, they pass:

```
npx vitest run --project unit ServerScale.test.ts DisposeMemory.test.ts FollowUpCheckCost.test.ts
      Tests  5 passed (5)
```

Extended fuzz, same results as `main`: `Fuzz.test.ts` seeds 1 to 3000 (gc 1000) and 1 to 1000 with block ids, `ConcurrentShapesFuzz` 600 seeds, `FollowUpFuzz` 1000 seeds, `NestedFuzz`, `SlotsFuzz`, `BlockIdFuzzSeeds` and `FuzzReplay` pass. Seed 2277 fails with `dup` on `main` and on this branch alike (outside the default 300 seeds; not caused by this change).

Browser and e2e tests were not run locally (GitHub CI runs them).

This branch was successfully deployed

2 active deployments
Preview – lexical — aac803b8 Deployed Oct 29, 2026 by vercel[bot]
Preview – lexical-playground — aac803b8 Deployed Oct 29, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant