diff --git a/admin_core_service/src/main/java/vacademy/io/admin_core_service/features/workflow/service/WorkflowEngineService.java b/admin_core_service/src/main/java/vacademy/io/admin_core_service/features/workflow/service/WorkflowEngineService.java index 126d267061..2eed13b69a 100644 --- a/admin_core_service/src/main/java/vacademy/io/admin_core_service/features/workflow/service/WorkflowEngineService.java +++ b/admin_core_service/src/main/java/vacademy/io/admin_core_service/features/workflow/service/WorkflowEngineService.java @@ -245,6 +245,19 @@ public Map run(String workflowId, Map seedContex "node.type", nodeType, "layer", "2-workflow-engine")); } + // is_end_node terminates ONLY this branch — not the entire workflow. + // Other paths already on the execution stack (from multi-goto fan-out + // or diamond DAGs) will still run. The handler above has already + // executed for this node, so any final side-effects (e.g. a closing + // SEND_EMAIL or UPDATE_RECORD) are honored before the path ends. + // To stop a whole workflow run, every terminal leaf must be marked + // is_end_node = true. + if (Boolean.TRUE.equals(current.getIsEndNode())) { + log.info("Node {} has is_end_node=true — terminating this path (other branches, if any, continue)", + current.getId()); + continue; + } + // Evaluate routing to find next nodes and push them to stack List nextNodeIds = evaluateRoutingNextNodeIds(effectiveConfig, ctx); log.info("Routing evaluation for node {} returned: {}", current.getId(), nextNodeIds);