Skip to content

Order the Docs batch back to front, not merely reversed - #55

Merged
ptheofan merged 1 commit into
mainfrom
54-order-batch-back-to-front
Aug 22, 2026
Merged

Order the Docs batch back to front, not merely reversed#55
ptheofan merged 1 commit into
mainfrom
54-order-batch-back-to-front

Conversation

@ptheofan

Copy link
Copy Markdown
Owner

Pushing a markdown file that differs wholesale from the Doc failed with
Invalid deletion range. Cannot delete the requested range. The batch
began with a ~1,900-character insertText at index 1, followed by three
deletes at descending indices.

Root cause. Google applies a batch sequentially — each request sees
the document as the previous ones left it. diffOpsToRequests ordered
the batch by reversing the generated list, which is not the same as
ordering it back-to-front: operations are generated in document order and
an N:M replacement pushes its deletes before its insert, so the reversal
put the insert first, at the same index those deletes start from. Every
delete after it then landed ~1,900 characters late.

Fix. Sort explicitly — descending by index, deletes before inserts at
the same index, so a position is cleared before anything is written to it.

Verification

The new test applies the emitted batch the way Google does, sequentially,
and refuses an out-of-range delete as the API would. Against the shape of
the failing document — three paragraph runs split by two tables, replaced
wholesale — it failed with:

expected '\nAAAA\nBBBB\nCCCC\nDDDD\n' to be 'ZZZZ\nYYYY\n'

Every original paragraph had survived and the new content was destroyed.
It passes after the sort, with both tables untouched.

The existing "reverse order for index stability" test passed throughout —
it asserts indices are non-increasing, which an insert tied with a delete
satisfies. That is why this survived: the ordering was checked, the
outcome of applying it was not.

Known artefact, not fixed here. A paragraph delete stops one
character short of its run, so each replaced run leaves a trailing
newline behind as an empty paragraph. The short stop is deliberate —
taking the newline before a table is itself invalid — and removing the
leftovers needs the delete to know whether a table follows. Filed in the
issue rather than bundled in.

pnpm test 1034 passed / 62 files · typecheck clean · lint clean.

Closes #54

🤖 Generated with Claude Code

Google applies a batch sequentially: each request sees the document as
the previous ones left it. Operations are generated in document order and
a replacement emits its deletes before its insert, so reversing the list
put the insert first -- at the same index those deletes start from.

The insert then shifted every delete after it by its own length. On a
wholesale rewrite that is thousands of characters: the deletes ate the
text just inserted, missed the text they were meant to remove, and past
the end of the body Google refused the range.

Sorted explicitly instead: descending by index, deletes before inserts at
the same index, so a position is cleared before anything is written to it.

Closes #54

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ptheofan
ptheofan merged commit f2e9fb0 into main Aug 22, 2026
5 checks passed
@ptheofan
ptheofan deleted the 54-order-batch-back-to-front branch August 22, 2026 13:50
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.

Push fails on a wholesale rewrite: the batch is not ordered back-to-front

1 participant