2026-07-03 12:43:55 +02:00
|
|
|
|
# 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)
|
2026-07-08 11:09:26 +02:00
|
|
|
|
**Status:** FIXED 2026-07-07 — option 1 implemented, see
|
|
|
|
|
|
[20-fix-miss09-collab-aware-undo.md](20-fix-miss09-collab-aware-undo.md)
|
2026-07-03 12:43:55 +02:00
|
|
|
|
|
|
|
|
|
|
## 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`.
|
2026-07-07 20:44:33 +02:00
|
|
|
|
|
|
|
|
|
|
**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.
|