From b28a82e1018f21f4ec7f398dfd0690768978ed62 Mon Sep 17 00:00:00 2001 From: Tarcio Date: Fri, 22 May 2026 00:16:30 -0300 Subject: [PATCH] fix(node-status): fire whole flow when play clicked on a trigger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``buildFlow({ stopNodeId })`` runs the upstream subgraph of the clicked node. For a CronTrigger that subgraph is empty — the component has no inputs, and its outputs feed downstream nodes that ``stopNodeId`` deliberately excludes. The result was that clicking play on the trigger ran nothing, contradicting the component's self-description ("Fire this flow on a recurring schedule"). Detect ``data.type === "CronTrigger"`` and drop ``stopNodeId`` for those nodes so the full flow builds, matching the worker's behaviour on a real cron fire. Both the click handler and the hotkey handler share the same branch; the analytics ``track`` payload now carries a ``triggerFire`` flag so downstream funnels can distinguish a manual fire from a regular component run. --- .../components/NodeStatus/index.tsx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/components/NodeStatus/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/NodeStatus/index.tsx index a9a85e04fc..d59d87a063 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/NodeStatus/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/NodeStatus/index.tsx @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from "react"; -import { useTranslation } from "react-i18next"; import { useHotkeys } from "react-hotkeys-hook"; +import { useTranslation } from "react-i18next"; import { getSpecificClassFromBuildStatus } from "@/CustomNodes/helpers/get-class-from-build-status"; import { mutateTemplate } from "@/CustomNodes/helpers/mutate-template"; import useIconStatus from "@/CustomNodes/hooks/use-icons-status"; @@ -206,11 +206,19 @@ export default function NodeStatus({ if (pollingTimeout.current) clearTimeout(pollingTimeout.current); }; + // A trigger node has no inputs and (intentionally) no outputs, so + // running it with the usual upstream-subgraph semantics would + // execute an empty graph. The user clicking play on a trigger is + // really asking "fire this flow as if the schedule just hit", so + // we drop ``stopNodeId`` and let the whole flow build — matching + // what the scheduler worker does on a real fire. + const isTriggerNode = data.type === "CronTrigger"; + function handlePlayWShortcut() { if (buildStatus === BuildStatus.BUILDING || isBuilding || !selected) return; setValidationStatus(null); buildFlow({ - stopNodeId: nodeId, + ...(isTriggerNode ? {} : { stopNodeId: nodeId }), eventDelivery: eventDeliveryConfig, }); } @@ -304,10 +312,13 @@ export default function NodeStatus({ } if (buildStatus === BuildStatus.BUILDING || isBuilding) return; buildFlow({ - stopNodeId: nodeId, + ...(isTriggerNode ? {} : { stopNodeId: nodeId }), eventDelivery: eventDeliveryConfig, }); - track("Flow Build - Clicked", { stopNodeId: nodeId }); + track("Flow Build - Clicked", { + stopNodeId: isTriggerNode ? undefined : nodeId, + triggerFire: isTriggerNode, + }); }; const iconName =