Skip to content

fix(plugin-sdk-value): drop remote normalize-merge mutation echoes#3000

Closed
ryanbonial wants to merge 1 commit into
mainfrom
fix-plugin-skip-remote-normalize-echo
Closed

fix(plugin-sdk-value): drop remote normalize-merge mutation echoes#3000
ryanbonial wants to merge 1 commit into
mainfrom
fix-plugin-skip-remote-normalize-echo

Conversation

@ryanbonial

Copy link
Copy Markdown
Contributor

Description

Receiving editors were pushing their own normalize fallout after applying a collaborator's formatting (adjacent same-mark span merges). Those competing merges truncated or duplicated text on the originator's block under concurrent edit (Axios format-while-peer-types).

Studio FormBuilder does not show this class for the same gesture; the SDK plugin path does because it eagerly pushes every mutation flush. This change arms a short gate after remote apply and drops a flush only when its patches look like a normalize merge (unset of a keyed children member plus text diffMatchPatch/set). Ordinary typing flushes are unaffected.

Supersedes the approach in #2999 (core withoutPatching nesting), which broke self-solving tests. Prefer fixing at the plugin push boundary.

Not in scope: paste-count loss, same-block typing scramble, overlapping-format SILENT-LOSS. Those remain a separate roadmap; Phase 0 research will decide whether follow-ups branch from this PR or from main.

What to review

  • packages/plugin-sdk-value/src/plugin.sdk-value.tsx: remoteApplyEchoGate / isNormalizeMergeEcho / drop in listenToEditor
  • packages/plugin-sdk-value/src/plugin.value-sync.browser.test.tsx: echo not pushed; unpushed typing still pushed after remote normalize

Testing

  • Plugin browser suite: 24/24 including new regression tests
  • External harness (pte-sdk-concurrent-repro on local plugin build):
    • duo-typing.mjs format: block A text integrity restored vs released (released still mangled)
    • duo-typing.mjs blocks / solo-typing.mjs: green after shape-based gate (value-equality gate had caused blocks scramble; fixed before this PR)
    • Full matrix still has known reds (paste counts, same-block, overlapping SILENT-LOSS); not claimed fixed here

Notes for release

Customer-facing: concurrent bold/format while another user types in another block should no longer scramble the formatted block's text via competing normalize merges when using @portabletext/plugin-sdk-value.

@vercel

vercel Bot commented Jul 22, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
portable-text-example-basic Ready Ready Preview, Comment Jul 22, 2026 3:17am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
portable-text-playground Skipped Skipped Jul 22, 2026 3:17am

Request Review

@changeset-bot

changeset-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 16925b9

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Stats — @portabletext/editor

Compared against main (11d72d02)

@portabletext/editor

Metric Value vs main (11d72d0)
Internal (raw) 806.6 KB -
Internal (gzip) 154.9 KB -
Bundled (raw) 1.42 MB -
Bundled (gzip) 319.2 KB -
Import time 90ms +0ms, +0.5%

@portabletext/editor/behaviors

Metric Value vs main (11d72d0)
Internal (raw) 467 B -
Internal (gzip) 207 B -
Bundled (raw) 424 B -
Bundled (gzip) 171 B -
Import time 2ms +0ms, +3.1%

@portabletext/editor/plugins

Metric Value vs main (11d72d0)
Internal (raw) 2.7 KB -
Internal (gzip) 894 B -
Bundled (raw) 2.5 KB -
Bundled (gzip) 827 B -
Import time 7ms +0ms, +1.1%

@portabletext/editor/selectors

Metric Value vs main (11d72d0)
Internal (raw) 82.7 KB -
Internal (gzip) 15.4 KB -
Bundled (raw) 78.4 KB -
Bundled (gzip) 14.3 KB -
Import time 7ms -0ms, -1.0%

@portabletext/editor/traversal

Metric Value vs main (11d72d0)
Internal (raw) 28.1 KB -
Internal (gzip) 5.6 KB -
Bundled (raw) 28.1 KB -
Bundled (gzip) 5.5 KB -
Import time 6ms +0ms, +0.6%

@portabletext/editor/utils

Metric Value vs main (11d72d0)
Internal (raw) 30.6 KB -
Internal (gzip) 6.4 KB -
Bundled (raw) 28.2 KB -
Bundled (gzip) 6.1 KB -
Import time 6ms -0ms, -0.6%

🗺️ . · ./behaviors · ./plugins · ./selectors · ./traversal · ./utils · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — @portabletext/markdown

Compared against main (11d72d02)

Metric Value vs main (11d72d0)
Internal (raw) 53.8 KB -
Internal (gzip) 9.8 KB -
Bundled (raw) 348.9 KB -
Bundled (gzip) 96.3 KB -
Import time 35ms -2ms, -5.5%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

@ryanbonial

Copy link
Copy Markdown
Contributor Author

Confirmation vs #3001 / @portabletext/editor@7.10.11

Harness rebaseline on released @portabletext/editor@7.10.11 + @portabletext/plugin-sdk-value@7.1.3 (no push-boundary echo gate from this PR):

Cell Result
duo-typing.mjs format 4/4 SAFE (first run); confirming second run
Completed trials MANGLED 0
Plugin contains remoteApplyEchoGate No (7.1.3)

Agreed with the #3001 rationale: skipping cosmetic normalize on remote apply removes the echo at the source, so this plugin push gate has nothing left to drop. Keeping a local-only merge would still fork span structure from the store.

Recommendation: close this draft as superseded by #3001. Residual roadmap work (paste counts, same-block scramble, overlapping-format SILENT-LOSS) is separate and does not need this branch.

@ryanbonial ryanbonial closed this Jul 22, 2026
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