Skip to content

test: pin the diff-value round-trip property for the revised patch pipeline#3011

Draft
christianhg wants to merge 1 commit into
mainfrom
diff-value-round-trip-property
Draft

test: pin the diff-value round-trip property for the revised patch pipeline#3011
christianhg wants to merge 1 commit into
mainfrom
diff-value-round-trip-property

Conversation

@christianhg

Copy link
Copy Markdown
Member

First brick of the revised patch pipeline (the "one transport" unification): property tests that pin the round-trip fidelity of diffValue-into-editor-patches, the transport update value will eventually ride.

Store-side, 500 seeded value pairs: applyAll(convert(diffValue(a, b)), a) reproduces b exactly, across a 15-mutation generator (text edits, mark toggles, annotation add/remove, span splits, rekeys, block reorders/inserts/removals, inline and block objects, list/style changes). This is the reference application every patch consumer shares, and it is byte-faithful across the space.

Engine-side, 40 seeds through the editor's real patches event. The fuzz made its first discovery immediately, and it is architectural rather than an applier bug: 7 seeds fail because mount-time canonicalization merges adjacent same-mark spans in the initial value, so the engine's base diverges from the diff's base before any patch arrives, and the diff's keyed targets are gone. Correlation is exact, every failing seed has same-mark adjacency in fromValue, zero counterexamples. This empirically promotes "cosmetic normalization runs only as local-edit fallout" from desirable cleanup to a hard prerequisite of the one-transport architecture: diff(engineValue, incoming) requires the engine to byte-track the document.

The 7 seeds are committed as test.fails, green today, and the moment adoption-time cosmetics stop, vitest reports them as unexpectedly passing and they must be moved to the green set: the suite is its own acceptance test for that future change.

Deliberately minimal surface: diffValueToPatches duplicates the plugin's convertPatches/arrayifyPath into test-utils with keep-in-sync pointers (house rule: duplicate over export), and @sanity/diff-patch/@sanity/json-match enter as devDependencies only. No runtime code, no public API, no changeset.

@changeset-bot

changeset-bot Bot commented Jul 23, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: cea8ecf

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

@vercel

vercel Bot commented Jul 23, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
portable-text-editor-documentation Ready Ready Preview, Comment Jul 23, 2026 9:40am
portable-text-example-basic Ready Ready Preview, Comment Jul 23, 2026 9:40am
portable-text-playground Ready Ready Preview, Comment Jul 23, 2026 9:40am

Request Review

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Stats — @portabletext/editor

Compared against main (68b5d174)

@portabletext/editor

Metric Value vs main (68b5d17)
Internal (raw) 807.5 KB -
Internal (gzip) 155.1 KB -
Bundled (raw) 1.42 MB -
Bundled (gzip) 319.4 KB -
Import time 105ms +5ms, +5.0%

@portabletext/editor/behaviors

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

@portabletext/editor/plugins

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

@portabletext/editor/selectors

Metric Value vs main (68b5d17)
Internal (raw) 82.7 KB -
Internal (gzip) 15.4 KB -
Bundled (raw) 78.4 KB -
Bundled (gzip) 14.3 KB -
Import time 8ms +0ms, +1.1%

@portabletext/editor/traversal

Metric Value vs main (68b5d17)
Internal (raw) 28.1 KB -
Internal (gzip) 5.6 KB -
Bundled (raw) 28.1 KB -
Bundled (gzip) 5.5 KB -
Import time 6ms +0ms, +4.6%

@portabletext/editor/utils

Metric Value vs main (68b5d17)
Internal (raw) 30.6 KB -
Internal (gzip) 6.4 KB -
Bundled (raw) 28.2 KB -
Bundled (gzip) 6.1 KB -
Import time 6ms +0ms, +5.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 (68b5d174)

Metric Value vs main (68b5d17)
Internal (raw) 53.8 KB -
Internal (gzip) 9.8 KB -
Bundled (raw) 348.9 KB -
Bundled (gzip) 96.3 KB -
Import time 42ms +3ms, +6.8%

🗺️ 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.

…peline

Two property tests over deterministic generated value pairs (seeded
generator, 15 mutation kinds: text edits, mark toggles, annotations,
splits, rekeys, reorders, block objects, inline objects, list/style
changes). Store-side: `applyAll(convert(diffValue(a, b)), a)` equals
`b` for 500 seeds, pinning the diff-into-patches transport that
`update value` will ride. Engine-side: the same patches through the
editor's `patches` event for 40 seeds.

The engine-side fuzz made its first discovery before merging: 7 seeds
fail because mount-time canonicalization merges the initial value's
adjacent same-mark spans, so the engine's base diverges from the
diff's base before any patch arrives and keyed targets are gone.
Verified by correlation: every failing seed has same-mark adjacency in
`fromValue`, zero counterexamples. Those seeds are pinned as
`test.fails`, so the day cosmetic normalization stops running on
value adoption they flip to unexpectedly-passing and must move to the
green set.

`diffValueToPatches` duplicates `convertPatches`/`arrayifyPath`
from `plugin-sdk-value` (keep-in-sync pointers both ways) and lives
in `test-utils`; `@sanity/diff-patch` and `@sanity/json-match`
enter as devDependencies only. No runtime code, no API surface.
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