# Design miss 09 — Undo is not collab-aware: Ctrl+Z reverts peers' work and re-broadcasts it **Severity:** design gap (converges, but with surprising and destructive UX) **Status:** FIXED 2026-07-07 — option 1 implemented, see [20-fix-miss09-collab-aware-undo.md](20-fix-miss09-collab-aware-undo.md) ## Where - `wasm/bindings/eeschema_embind.cpp:722-724` / `:843-845` and `wasm/bindings/pcbnew_embind.cpp:806-809` / `:886-888` — remote applies go through `SCH_COMMIT` / `BOARD_COMMIT` with `Push( "Collaborative edit" )`, i.e. they create ordinary **undoable** entries on the receiving editor's stack - `web/standalone/src/wasm/collab/kicad-binding.ts:176-188` — the adopt path applies ALL doc roots as **one** wire batch → one giant commit - `web/standalone/src/wasm/collab/sheet-manager.ts:188-191` — parked-dirty rebind runs that adopt on every sheet revisit that saw remote traffic ## What happens Going through a real commit is the right call for model consistency (connectivity, ratsnest, ERC recompute like a UI edit) — but it has an unhandled consequence: 1. **Ctrl+Z reverts remote work.** A remote apply is on the local undo stack. The user pressing undo after a peer's edit reverts *the peer's* change; the reversion is a normal local commit → it flushes → it propagates to everyone, including the original author. From the author's perspective their edit "randomly disappears". 2. **Adopt commits are undo bombs.** The seed/adopt and the parked-dirty sheet rebind apply the *entire sheet* as one commit ("Collaborative edit (items)"). One Ctrl+Z after revisiting a sheet reverts the whole remote catch-up — potentially dozens of peers' edits — and broadcasts the stale sheet state to the room. 3. Convergence is preserved (the system happily syncs the reverted state), which is exactly the problem: the damage replicates perfectly. There is no loop risk (the reversion is applied on peers as a remote change and suppressed from re-emit), and redo behaves symmetrically. This is purely a policy/UX gap, not an algorithmic one — but it can destroy significant work with one keystroke, so it deserves an explicit decision rather than the current default. ## Options 1. **Exclude remote applies from the undo stack.** Both commit classes support pushing without undo (or the undo entry can be dropped after Push). Standard collaborative-editor semantics: undo is local-ops-only. This is the direction most products (Figma, Google Docs) take. Cost: KiCad's undo machinery assumes the stack mirrors model history; entries referencing items later replaced by remote applies must be invalidated or made resilient (item-by-uuid re-resolution at undo time — KiCad's PICKED_ITEMS_LIST holds pointers, so this needs care). 2. **Keep remote applies undoable but split adopt into per-item diffs** (see [13-opt-parked-dirty-full-sheet-replace.md](13-opt-parked-dirty-full-sheet-replace.md)) so at least the bomb shrinks to the real delta. Doesn't fix (1), halves the blast radius. 3. **Minimum bar:** name the undo entries distinctly (already done) and clear the undo stack on adopt (a full-sheet replace is a reasonable "history barrier"). Cheap, removes the worst case, keeps normal-sized remote entries undoable. Recommendation: 3 now (one call at adopt time), 1 as the eventual model, evaluated against how invasive pointer-invalidation is in `PICKED_ITEMS_LIST`. **Update 2026-07-07:** option 1 feasibility researched in [19-undo-option1-feasibility.md](19-undo-option1-feasibility.md) — verdict: ~3–5 days; pcbnew undo already UUID-guards stale pointers, the core work is porting that guard to eeschema plus two small `SKIP_UNDO` fork fixes (SCH connectivity gate, removed-item leak). Option 3 becomes largely moot under it.