Root cause: graph.getNodes() is a flat list with duplicates — handler compound children appear both nested inside their parent AND as top-level entries. The previous separation tried to filter the flat list but missed the duplicates, leaving handler children in rootNode. New approach: walk from graph.getRoot() following non-ERROR edges to discover main flow nodes. Edges targeting handler compounds (ON_EXCEPTION, ON_COMPLETION) are not followed. This cleanly separates main flow from handler sections using the graph's own structure. Falls back to flat list filtering (old behavior) when graph.getRoot() is null (legacy/test graphs). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>