Each shape updated by a component sync added its reg-objects undo change
with (update :undo-changes concat [...]). That wrapped the whole undo list
in one more lazy concat per shape, with the old list at the head. After a
few thousand shapes, walking the list overflowed the stack, which matches
the nested LazySeq frames reported when a library update is applied.
add-update-attr-changes now conjs the reg-objects before the mod-obj, so
undo restores each shape and then recalculates its parents, as redo does.
The trailing reg-objects also recalculated groups after undoing shapes
that the sync added or removed. To keep that, add-shape-to-instance,
add-shape-to-main and remove-shape now add their own undo reg-objects,
conjed before their other undo changes so it runs after them.
Tests cover many calls to add-update-attr-changes, a sync and undo of
many shapes, and undoing a sync that moves, adds or removes a child of a
group in the copy.
Closes#12576
AI-assisted-by: claude-opus-5-5