From acb7cade90f9dda2c83ed447ed961dc3bce05952 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Fri, 27 Mar 2026 22:33:40 +0100 Subject: [PATCH] fix: exclude handler compound children from main flow ELK graph MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause found: RouteGraph.getNodes() is a FLAT list that includes handler compound children (log8, setBody1, etc.) as top-level entries alongside the main flow nodes. The handler separation only identified the compound PARENTS (ON_EXCEPTION) but not their children, so 7 handler children leaked into rootNode as main flow nodes, causing ELK to place the real main flow at wrong Y positions. Fix: two-pass separation — first identify handler compounds and collect ALL descendant IDs, then build mainNodes excluding both handler compounds AND their descendants. Debug logging left in temporarily for verification. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../app/diagram/ElkDiagramRenderer.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/diagram/ElkDiagramRenderer.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/diagram/ElkDiagramRenderer.java index 44292dbd..1d58e754 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/diagram/ElkDiagramRenderer.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/diagram/ElkDiagramRenderer.java @@ -206,15 +206,24 @@ public class ElkDiagramRenderer implements DiagramRenderer { Map nodeColors = new HashMap<>(); Set compoundNodeIds = new HashSet<>(); - // Separate handler sections from main flow nodes + // Separate handler sections from main flow nodes. + // graph.getNodes() is a FLAT list that includes handler compound children + // as top-level entries. We must identify and exclude them. List mainNodes = new ArrayList<>(); List handlerNodes = new ArrayList<>(); + Set handlerDescendantIds = new HashSet<>(); if (graph.getNodes() != null) { + // First pass: identify handler compounds and collect their descendant IDs for (RouteNode rn : graph.getNodes()) { if (rn.getType() != null && HANDLER_SECTION_TYPES.contains(rn.getType()) && rn.getChildren() != null && !rn.getChildren().isEmpty()) { handlerNodes.add(rn); - } else { + collectDescendantIds(rn.getChildren(), handlerDescendantIds); + } + } + // Second pass: main flow = everything that isn't a handler or handler descendant + for (RouteNode rn : graph.getNodes()) { + if (!handlerNodes.contains(rn) && !handlerDescendantIds.contains(rn.getId())) { mainNodes.add(rn); } } @@ -643,6 +652,16 @@ public class ElkDiagramRenderer implements DiagramRenderer { } /** Recursively collect all node IDs from a tree. */ + /** Collect IDs of all RouteNode descendants (for handler separation). */ + private void collectDescendantIds(List nodes, Set ids) { + for (RouteNode n : nodes) { + ids.add(n.getId()); + if (n.getChildren() != null) { + collectDescendantIds(n.getChildren(), ids); + } + } + } + private void collectAllIds(PositionedNode node, Set ids) { ids.add(node.id()); if (node.children() != null) {