瀏覽代碼

fix: a Configurator is a standalone node with no inputs

It had a data input, so using one meant wiring it into the flow - the sdcpp
workflow ended up with the Configurator hanging off the trigger, which says
nothing true about what it does. A Configurator is not a step in the flow. The
node it feeds reads from it when that node runs.

Both input handles are gone: no config input, because it supplies settings
rather than receiving them, and no data input, because it is not part of the
flow. Its output leaves from the right to meet the config input on the left of
its target.

No engine change was needed - it already worked this way. The config edge itself
creates the ordering dependency, so a Configurator with no incoming edge is
still ordered before the node it configures and runs with empty input. The
trigger connection was never doing anything; it just looked like it was.

Fixture added for exactly that: a Configurator with no incoming edge at all,
whose values still reach the target.

Consequence worth knowing: a Configurator can no longer be skipped by a branch,
because nothing feeds it. Selecting between variations at run time by putting
Configurators behind an If or Switch no longer works - every connected one now
always applies, and two of them setting the same key will fail as a conflict.
Variations now mean connecting a different Configurator, not gating one.

Full suite 54/54.
fszontagh 1 月之前
父節點
當前提交
ef08a9646b
共有 2 個文件被更改,包括 25 次插入 和 5 次删除
  1. 18 0
      tests/nodes/configurator-standalone.json
  2. 7 5
      webui/src/components/workflow/WorkflowNode.tsx

+ 18 - 0
tests/nodes/configurator-standalone.json

@@ -0,0 +1,18 @@
+{
+  "name": "diag-configurator-standalone",
+  "nodes": [
+    {"id": "n1", "name": "Trigger", "type": "click-trigger", "position": {"x": 0, "y": 0}, "config": {}},
+    {"id": "cfg", "name": "Standalone", "type": "configurator", "position": {"x": -250, "y": 150},
+     "config": {"settings": [{"name": "message", "value": "from a standalone configurator"}, {"name": "mode", "value": "stop"}]}},
+    {"id": "target", "name": "Target", "type": "stop-and-error", "position": {"x": 0, "y": 200},
+     "config": {"message": "own value", "mode": "error"}}
+  ],
+  "connections": [
+    {"sourceNodeId": "n1", "sourceOutput": "main", "targetNodeId": "target", "targetInput": "data"},
+    {"sourceNodeId": "cfg", "sourceOutput": "main", "targetNodeId": "target", "targetInput": "config"}
+  ],
+  "expectStatus": "completed",
+  "expect": {
+    "target": {"status": "completed", "output": {"stopped": true, "reason": "from a standalone configurator"}}
+  }
+}

+ 7 - 5
webui/src/components/workflow/WorkflowNode.tsx

@@ -62,10 +62,12 @@ export function WorkflowNode({ data, selected }: NodeProps) {
 
   // Get loop info for loop nodes
   const isLoopNode = data.type === 'loop'
-  // A Configurator supplies settings; it does not receive them, so it gets no
-  // config input of its own. Its output goes out the right-hand side to meet
-  // the config input on the left of the node it feeds, rather than dropping out
-  // of the bottom into the data flow it is not part of.
+  // A Configurator stands on its own. It has no inputs at all: not a config
+  // input, because it supplies settings rather than receiving them, and not a
+  // data input, because it is not part of the flow - the node it feeds reads
+  // from it when that node runs. Its output leaves from the right-hand side to
+  // meet the config input on the left of its target, rather than dropping out
+  // of the bottom into a data flow it has nothing to do with.
   const isConfigurator = data.type === 'configurator'
   const loopInfo = isLoopNode && execState?.output ? {
     currentIndex: execState.output.currentIndex,
@@ -125,7 +127,7 @@ export function WorkflowNode({ data, selected }: NodeProps) {
     >
       {/* Input handles at top (not for triggers). A node that names more than
           one input gets a handle each, spread the way outputs are. */}
-      {!isTrigger && inputs.map((input, idx) => (
+      {!isTrigger && !isConfigurator && inputs.map((input, idx) => (
         <Handle
           key={input.name}
           type="target"