From c36d15db20961897c711e0bc3ace57357d3e4891 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Sat, 24 Oct 2026 10:13:30 +0200 Subject: [PATCH] :bug: Compare pushed swaps against the updated parent frame Use the parent's pushed geometry when deciding whether a promoted swap needs an orientation override. Its original container still has the old rotation, which can create a phantom geometry group during update-main of a rotated parent (F172). The portable regression fails its touched assertion before the fix. All 54 swap tests pass 214 assertions, the library-308 shrink passes, and the pure golden master matches 1149 assertions without recording. Common and frontend lint and format pass. Benchmark ratios against Phase 1e: main 10/100/1000 copies 0.49/0.58/0.59, three levels 0.54, chain 0.52, library 0.56, many shapes 0.48, variant switch 0.63. No regression flags. AI-assisted-by: GPT-6 --- .serena/memories/common/component-data-model.md | 3 +++ common/src/app/common/logic/libraries.cljc | 12 +++++++++++- .../test/common_tests/logic/swap_group_test.cljc | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/.serena/memories/common/component-data-model.md b/.serena/memories/common/component-data-model.md index 326965701d..9b9c4cbbc3 100644 --- a/.serena/memories/common/component-data-model.md +++ b/.serena/memories/common/component-data-model.md @@ -78,3 +78,6 @@ component. If the old position supplied rotation or flips that differ from that component's expected parent placement, the new nested copy needs a geometry-group override. Otherwise a later sync discards the orientation that update-main just pushed (F168). The source copy clears its overrides. +Compare against the parent's pushed frame: the inverse sync has already +written its new rotation, while the original container still has the old +one. Using that old frame creates phantom geometry overrides (F172). diff --git a/common/src/app/common/logic/libraries.cljc b/common/src/app/common/logic/libraries.cljc index 4816bf7b0d..6f066313fd 100644 --- a/common/src/app/common/logic/libraries.cljc +++ b/common/src/app/common/logic/libraries.cljc @@ -777,13 +777,23 @@ (ctf/find-content-ref-shape nil container libraries child-inst :include-deleted? true :with-context? true)) + ;; The parent's inverse sync already pushed its geometry. Compare + ;; against that frame, rather than its original container value. + placement-container + (cond-> main-container + content-main + (assoc-in [:objects (:id parent-main)] + (-> (ctn/get-shape container (:parent-id child-inst)) + (reposition-shape root-inst root-main) + (assoc :id (:id parent-main) + :shape-ref (:shape-ref parent-main))))) update-new-shape (fn [new-shape original-shape] (cond-> (reposition-shape new-shape root-inst root-main) (and (= (:id original-shape) (:id child-inst)) (ctk/swapped? child-inst) (not swap-in-copy?)) - (rehead-pushed-swap content-main main-container))) + (rehead-pushed-swap content-main placement-container))) ;; the copy now follows the main, swap included update-original-shape (fn [original-shape new-shape] diff --git a/common/test/common_tests/logic/swap_group_test.cljc b/common/test/common_tests/logic/swap_group_test.cljc index 8682181ae0..7a5b46a607 100644 --- a/common/test/common_tests/logic/swap_group_test.cljc +++ b/common/test/common_tests/logic/swap_group_test.cljc @@ -917,3 +917,19 @@ (:points (ths/get-shape-by-id full (:id head))) 0.001)) (t/is (= (thn/normalize-file (tm/file swapped)) (thn/normalize-file (tm/file (r/step updated (n/undo))))))))) + +(t/deftest pushing-a-swap-uses-the-parents-updated-orientation + (let [swapped (-> (tm/make-situation (setup-sized)) + (r/step (n/swap-shape :copy-star :heart)) r/settle + (r/step (n/rotate :copy 45)) r/settle) + updated (-> swapped (r/step (n/update-main :copy)) r/settle) + file (tm/file updated) + head (tm/resolve-shape updated :copy-star) + main (tm/shape-by-id updated (:shape-ref head)) + full (:file (r/sync-cascade file {} (keys (get-in file [:data :components]))))] + (t/is (empty? (:touched main))) + (t/is (mth/close? 45 (:rotation main) 0.001)) + (t/is (gsh/close-attrs? :points (:points head) + (:points (ths/get-shape-by-id full (:id head))) 0.001)) + (t/is (= (thn/normalize-file (tm/file swapped)) + (thn/normalize-file (tm/file (r/step updated (n/undo))))))))