Skip to content

fix: seed tracked-revision ids from the document, not the clock#1093

Open
jedrazb wants to merge 2 commits into
mainfrom
fix/revision-id-int32-seed
Open

fix: seed tracked-revision ids from the document, not the clock#1093
jedrazb wants to merge 2 commits into
mainfrom
fix/revision-id-int32-seed

Conversation

@jedrazb

@jedrazb jedrazb commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Suggestion mode seeded its w:ins/w:del revision-id counter from Date.now(), producing 13-digit ids that overflow the signed 32-bit range consumers read w:id into, so exported files were rejected as corrupt. The counter now starts at 1 and is raised above the document's existing revision ids, and every tracked-change parse site folds an out-of-range w:id back into range so seeding and serialization agree.

Fixes #1077

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

jedrazb and others added 2 commits July 17, 2026 14:19
…#1077)

The revision-id counter started at `Date.now()` (~1.8e12), so every
suggestion-mode edit minted a 13-digit `w:id` on `w:ins`/`w:del`.
`ST_DecimalNumber` is an unbounded `xsd:integer` in the schema, but
consumers read it into a signed 32-bit int, so exported documents
overflowed on load and reported corruption.

The clock seed was there to buy uniqueness. Uniqueness now comes from the
document itself: the suggestion-mode plugin's `state.init` raises the
counter above the ids already in the loaded doc. That runs once per
`EditorState.create` in both adapters, so React and Vue stay in lockstep
without either having to remember to call a seeder — Vue never seeded on
load at all, and would otherwise have started reminting ids from 1.

The scan covers ids on marks and on node attrs (`pPrIns`/`pPrDel`,
`trIns`/`trDel`, `cellMarker`); a doc whose only revisions are structural
would otherwise reuse ids it already holds.

Existing ids are file-derived, so they are treated as untrusted: an
out-of-range one is skipped while taking the max rather than poisoning it,
and the serializer now bounds `w:id` at the point every emit path funnels
through (five duplicated normalizers, only one of which was on the main
`w:ins` run-wrapper path). Out-of-range ids fold modulo the range instead
of clamping, so revisions in files written by the overflowing counter stay
distinct across a re-save rather than merging into one group.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… range top

Follow-up to the same #1077 root. Three residuals, all the disagreement
between where the id was sanitized and where it was trusted:

- Parse boundary: `parseTrackedChangeAttrs` and the sibling
  `parseTrackedChangeInfo`/`parsePropertyChangeInfo` parsers (paragraph,
  run, table row/cell) only rejected NaN/negative, so an out-of-range
  `w:id` entered the in-memory model unnormalized — then seeded the mint
  counter and re-serialized as corruption. They now fold through
  `normalizeRevisionId` at parse, so in-memory ids are always in range and
  the serializer fold is idempotent. This is the sanitize-at-the-trust-
  boundary rule; the sink-only fold stays as defense in depth.

- Seed guard: `seedRevisionIdsAbove` accepted `maxId === MAX_REVISION_ID`,
  setting the counter to MAX + 1 and forcing the next mint to wrap to 1 and
  collide with low ids. The top of the range is now reserved (`>=`), so the
  counter always stays within [1, MAX_REVISION_ID].

- Corrected the `mintRevisionId` comment that claimed reaching the wrap
  needs ~2^31 mints; a document seeded near the top reaches it in a few.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 17, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
docx-editor Ready Ready Preview, Comment Jul 17, 2026 3:23pm

Request Review

@eigenpal-release-pal

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅

Posted by the CLA bot.

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.

[Bug] Suggestion mode seeds revision ids from Date.now(), overflowing Word's int32 = exported files corrupt

1 participant