Skip to content

Instantly share code, notes, and snippets.

@JavaGT
Created June 11, 2026 08:59
Show Gist options
  • Select an option

  • Save JavaGT/e80e4c344847f910e7b5a24188126a9a to your computer and use it in GitHub Desktop.

Select an option

Save JavaGT/e80e4c344847f910e7b5a24188126a9a to your computer and use it in GitHub Desktop.
Google Docs Multi-User Undo Stack — 15 Test Results

Google Docs Undo Stack — Test Results

Setup

  • Document: Untitled Google Doc, shared "Anyone with the link can edit"
  • Tab 0 (User A): javagrant@gmail.com
  • Tab 1 (User B): Same account (javagrant@gmail.com), second browser tab
  • Key finding: Undo stacks are per-session/tab, not per-account

Test 1: Basic Undo

Action: Type "Hello" → Press Undo Result: ✅ Text removed. Document returns to empty.

Test 2: Operation Grouping (Typing Burst)

Action: Type "HelloWorld" (contiguous burst) → Press Undo once Result: ✅ Entire "HelloWorld" removed in one undo. Consecutive typing actions are grouped as one logical undo unit.

Test 3: Undo After Deletion of Your Text

Action: Type "Hello" → Select All + Delete → Press Undo Result: ✅

  • Undo 1: Restored "Hello" (undid the deletion action, which has inverse "Insert Hello")
  • Undo 2: Removed "Hello" (undid the original typing action)

Test 4: Formatting vs Deletion

Action: Type "Hello" → Select All + Bold → Select All + Delete → Press Undo repeatedly Result: ✅ Three separate undo steps:

  • Undo 1: Restored bold "Hello" (undid deletion)
  • Undo 2: Removed bold formatting → "Hello" in normal text
  • Undo 3: Removed "Hello" → empty

Test 5: Undo After Formatting Applied to Your Text

Action: Type "Hello" → Bold → Press Undo Result: ✅ (same pattern as Test 4 — formatting action is separate from typing on the undo stack)

Test 6: Mixed Actions (Type, Bold, Type More, Undo)

Action: Type "Hello" → Bold → Type " World" → Press Undo Result: ✅ Only the last typing burst (" World") removed. Left bold "Hello".

  • Confirms each action type (typing group, formatting change) creates a separate undoable entry.

Test 7: Multi-User — Per-Session Undo

Action: Tab 0 types "AAA" → Tab 1 types "BBB" → Tab 0 presses Undo Result: ✅ Only "AAA" removed. "BBB" preserved.

  • Confirms each tab has its own undo stack scoped to that session.

Test 8: Multi-User — OT-Transformed Undo (B Edits Inside A's Text)

Action: Tab 0 types "The cat sat." → Tab 1 inserts "Black " inside → Tab 0 presses Undo Result: ✅ A's undo removed only A's contribution ("the cat sat"). B's "Black " preserved.

  • Undo operation was OT-transformed against B's concurrent edit.
  • Result: "Black."

Test 9: Multi-User — No-Op Undo (B Deletes A's Text)

Action: Tab 0 types "Hello" → Tab 1 Select All + Delete → Tab 0 presses Undo Result: ✅ No-op. Document stays empty.

  • Inverse operation (Delete Hello) targets text that no longer exists. OT transforms it to Delete(0,0) — inert.

Test 10: Multi-User — Interleaved Typing

Action: Tab 0 and Tab 1 alternate typing characters (H, W, e, orld, llo) Result: "WorldHello" in document. Tab 0 presses Undo.

  • Undo 1: Removed "llo"
  • Undo 2: Removed "e"
  • Undo 3: Removed "H"
  • Result: "World" (selected)
  • Key finding: Remote operations that arrive DURING a typing burst BREAK the typing group. Each character becomes a separate undoable action.

Test 10b: Completed Burst — Group Intact Despite Remote Insert

Action: Tab 0 types "AAAAA" as one completed burst → Tab 1 inserts "X" at position 2 → Tab 0 presses Undo once Result: ✅ All 5 A's removed in one undo. The burst was committed as one group before the remote op arrived. "X" remains.

  • Distinction from Test 10: if the burst finishes BEFORE the remote edit arrives, the group stays intact.
  • Remote edits in the middle of existing text do not retroactively break completed typing groups.

Test 11: Redo After Remote Edit (Corrected)

Action: Tab 0 types "Hello" → Tab 0 undoes → Tab 1 types "World" → Tab 0 redoes (⌘Y) Result: ✅ Redo survives, OT-transformed. "WorldHello" produced.

  • Tab 0's redo (Insert "Hello" at 0) was OT-transformed against Tab 1's Insert "World" at 0 → Insert "Hello" at position 5.
  • Redo button aria-disabled="false" throughout — the redo stack is NOT cleared by remote edits.
  • Initial test was a false negative due to keyboard focus not being on the text entry iframe.

Test 12: Local New Edit Clears Redo Stack

Action: Tab 0 types "Hello" → Tab 0 undoes → Tab 0 types "World" → Tab 0 redoes (⌘Y) Result: ✅ Redo was a no-op. New local edit ("World") after undo cleared the redo stack.

  • Standard single-user redo semantics hold: any new edit (local or remote) clears the redo stack.

Test 13: Undo of Pasted Content

Action: Tab 0 pastes "Hello World" (via ⌘V) → Tab 0 presses Undo Result: ✅ Entire paste removed in one undo. Paste is treated as a single undoable action, not broken into per-character groups.

  • Google Docs coalesces paste into one logical undo entry (unlike interleaved typing which breaks groups).

Test 14: Three-Way Chain (A Types → B Edits → C Deletes → A Undoes)

Action: Tab 0 types "AAA" → Tab 1 types "BBB" → Tab 0 deletes "AAA" → Tab 0 presses Undo Result: ✅ Undo correctly scoped to A's actions across the chain.

  • Undo 1: Restored "AAABBB" (undid the deletion of "AAA")
  • Undo 2: Removed "AAA" (undid original typing by A)
  • B's "BBB" remained intact throughout.
  • OT correctly transforms undo operations across a three-way interleaved chain of operations (insert → insert → delete → undo)

Test 15: Undo While B Is Actively Typing

Action: Tab 0 types "AAA" → Tab 1 types "b" → Tab 0 presses Undo (while B's typing is in-flight) → Tab 1 types "BB" Result: ✅ "BBB" remains, "AAA" gone. Undo was correctly OT-transformed against B's concurrent "b" insertion.

  • Concurrent typing by another user does not break or corrupt A's undo.
  • OT transformation correctly handles the race condition between B's ops and A's undo.

Test 15b: Undo Mid-Burst (Partial Typing Before, Full Burst After)

Action: Tab 0 types "AAAAA" → Tab 1 types "BB" (partial burst) → Tab 0 undoes → Tab 1 types "BBB" (completes burst) Result: ✅ "BBBBB" remains, "AAAAA" gone. Undo correctly removed A's text while preserving B's partial burst ("BB") that arrived before the undo, and B's continuation ("BBB") that arrived after.

  • Typing group boundaries are respected across undo: one side's partial burst isn't corrupted by the other's undo.
  • No data loss or duplication.


Verification Round (v2)

All high-risk tests re-run with explicit doc.body.focus() before keyboard input to eliminate potential focus false negatives:

Test Original v2 Result Status
7 (per-session undo) ✅ "AAA" removed, "BBB" intact Confirmed
8 (OT-transformed undo) ✅ "Black." remains Confirmed
9 (no-op undo) ✅ Doc stays empty, undo consumed Confirmed
10 (interleaved typing) ✅ 3 undos: "llo"→"e"→"H", "World" remains Confirmed
10b (completed burst intact) ✅ All 5 A's removed in one undo Confirmed
11 (redo after remote edit) ❌ false negative ✅ Redo survives, OT-transformed to "WorldHello" Corrected
15b (undo mid-burst) ✅ "BBBBB" intact, no corruption Confirmed

Key lesson: Always explicitly focus the text entry iframe (docs-texteventtarget-iframe) before issuing keyboard commands. The tab switch alone does not transfer keyboard focus to the editor.


Key Insights Confirmed

  1. Per-session/tab undo stacks, not per-account
  2. OT transforms undo operations — positions shift as other users edit
  3. User-scoped undo — A's undo never removes B's content
  4. Remote ops arriving DURING a burst break typing groups — each character becomes a separate undo entry (Test 10)
  5. Completed bursts stay intact — if the burst finishes before the remote op arrives, the group is NOT retroactively broken (Test 10b)
  6. Deletion is its own undoable action — format/delete/type are all separate entries
  7. No-op on phantom text — undo of already-deleted content is inert
  8. Undo = apply inverse op through OT — not "restore previous state"
  9. Redo survives remote edits — redo stack is NOT cleared by remote edits; redo actions are OT-transformed (Test 11, corrected)
  10. Local new edit clears redo stack — standard single-user redo semantics (Test 12)
  11. Paste is one undo group — pasted content coalesces into a single undoable action (Test 13)
  12. Undo survives concurrent editing — pressing undo while another user is actively typing does not corrupt the result (Test 15)
  13. Undo mid-burst preserves context — undo correctly handles the case where a collaborator has partial typing before the undo and continues after (Test 15b)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment