Skip to content

fix(loro.js): root shallow snapshots at the latest critical version - #1144

Merged
zxch3n merged 3 commits into
mainfrom
fix/loro-js-shallow-critical-root
Sep 29, 2026
Merged

zxch3n merged 3 commits into
mainfrom
fix/loro-js-shallow-critical-root

Conversation

@zxch3n

@zxch3n zxch3n commented Sep 27, 2026 •

Copy link
Copy Markdown
Member

Summary

loro.js rooted a shallow snapshot at the requested version, or at the meet of its heads. When a branch merged after that version had forked below it, a retained op was concurrent with the root. loro-crdt roots the snapshot at the latest single-head critical version of the requested frontiers and the latest version (#1095, calc_shallow_doc_start).

Effects on main:

  • The root differs from a Rust export of the same document (e.g. 2@1 instead of 0@1 for the "past version with a later branch below it" case in crates/loro/tests/shallow_root_critical.rs).
  • Checking out retained versions of such a snapshot and returning to the latest version could reorder concurrent Text/List elements. This is the "shallow snapshot of concurrent history differs from the full-history document" failure from random fuzzing. On a branch with the other Text/List fixes from this session, Map/List/Text/Counter fuzzing with the snapshot and shallow oracles went from 175 failing seeds out of 200 to 3 out of 300 with this change. The remaining 3 are the multi-op root commit case that fix(loro.js): keep root-time map values and tree nodes when a shallow doc retreats #1127 fixes (verified on the fix(loro.js): drop containers deleted before the shallow root, keep tree node metadata #1128 head plus this change).

Rationale

Port of Rust's latest_single_head_critical_version (dag.rs) as #latestSingleHeadCriticalVersion. It is a max-heap descent over change spans from both frontier sets, with the same coalescing, lamport-based trimming, implicit same-peer predecessor, and empty-version fallback when a chain reaches a root. #calculateShallowStart now calls it with the latest history frontiers. The StyleStart nudge and the clamp to an existing shallow root are unchanged. The walk only runs on shallow export and is bounded by the history between the requested version and its critical version, as in Rust.

Validation

  • New loro-js/tests/shallow-root-critical.test.ts: ports of independent_heads_have_no_critical_root, shared_root_multi_head_still_trims, and past_version_with_later_branch_below_it; a concurrent-list case; and a randomized check (40 histories × several targets) that the root is critical and that the shallow doc matches the full doc at every retained version and after returning to latest. Three of the five tests fail on main. The expected roots are the ones loro-crdt (WASM build of this main) picks for the same documents.
  • The fuzz harness (test(loro.js): add a multi-peer differential fuzz harness #1141) compares both implementations' shallow roots after every scenario; with this change they agreed in every comparison of the 300-seed run.
  • pnpm --dir loro-js format:check, lint, typecheck, test (252 passed), build.
  • bench:complexity (1k–8k), 7 alternating rounds vs main, loadavg 24–31: 284 items, none both >1.5× and >0.3 ms slower. Total 1219 ms on main, 1172 ms here.

🤖 Generated with Claude Code

loro.js rooted a shallow snapshot at the requested version, or at the
meet of its heads. A branch merged after that version but forked below
it is concurrent with it, so a retained op straddled the root. Checking
out retained versions and returning could then reorder concurrent
Text/List elements, and the root differed from a Rust export.

Port Rust's latest_single_head_critical_version (dag.rs) and use it with
the latest frontiers, as calc_shallow_doc_start does. The StyleStart
nudge and the clamp to an existing shallow root are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

WASM Size Report

  • Original size: 3277.59 KB
  • Gzipped size: 1089.33 KB
  • Brotli size: 762.83 KB

zxch3n commented Sep 29, 2026

Copy link
Copy Markdown
Member Author

Merged the latest main (c0ff1255, includes #1126–#1128, #1136, #1137); no conflicts. pnpm -C loro-js check with LORO_REQUIRE_WASM_REFERENCE=1 passes (26 files, 419 tests).

Still needed on current main: with the differential harness from #1141 (Map/List/Text/Counter, snapshot + shallow + checkout oracles, 200 seeds, shallow-root comparison disabled so data errors are visible), main fails 124 seeds. Seven of them are shallow-latest: the imported shallow doc's latest state is wrong. The rest are wrong values after checking out retained versions. This branch fails 0 of the same 200 seeds.

@zxch3n
zxch3n merged commit c4fd87f into main Sep 29, 2026
2 checks passed
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