A batch flow's input is a run parameter, not a missing value
Docs / docs (push) Successful in 20s
Playwright Tests / test-playwright (1, 2) (push) Failing after 1m59s
Playwright Tests / test-playwright (2, 2) (push) Failing after 1m40s
pre-commit / pre-commit (push) Failing after 2m49s
Test Backend / test-backend (push) Successful in 2m20s
Compose Smoke Test / test-compose (push) Successful in 30s
Playwright Tests / merge-reports (push) Failing after 1m6s

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UytviPMJbXzD8P84nLvXcq
This commit is contained in:
2026-08-25 18:51:54 +02:00
co-authored by Claude Opus 5
parent 3503512d05
commit a3a234756c
4 changed files with 35 additions and 5 deletions
+11 -3
View File
@@ -798,7 +798,7 @@ class FlowController:
nodes: list[Node] = []
loaded: dict[str, LoadedNode] = {}
initial_values: dict[str, Any] = {}
# Declared flow inputs, mapped to whether they start with a value.
# Declared flow inputs, mapped to whether a value will be there.
flow_inputs: dict[str, bool] = {}
for flow, draft in flows:
@@ -1465,14 +1465,22 @@ def with_settings(
def _declared_inputs(flow: FlowDef) -> tuple[dict[str, bool], dict[str, Any]]:
"""A flow's declared inputs, and the ones that start with a value."""
"""A flow's declared inputs, and the ones that start with a value.
An input maps to whether a value will be there when the flow starts, which
for a batch flow is not the same as carrying an initial: it is started
explicitly and its inputs arrive with the run, so one without an initial is
a parameter the caller fills rather than a message nothing ever sets.
Reporting it as the latter had the health summary count an experiment as a
flow that cannot run while the Runs screen showed it running.
"""
declared: dict[str, bool] = {}
initial: dict[str, Any] = {}
for flow_input in flow.inputs:
name = qualify(flow.name, flow_input.spec.name)
if not name:
continue
declared[name] = flow_input.initial is not None
declared[name] = flow_input.initial is not None or flow.mode == "batch"
if flow_input.initial is not None:
initial[name] = flow_input.initial
return declared, initial
+2 -1
View File
@@ -447,7 +447,8 @@ class Pipeline:
"""Report everything that would keep this graph from running.
:param flow_inputs: Messages declared as inputs of a flow rather than
computed by it, mapped to whether they carry an initial value.
computed by it, mapped to whether a value will be there when the
flow starts — from an initial, or from the run that starts it.
"""
declared = flow_inputs or {}
issues: list[ValidationIssue] = []
+21
View File
@@ -1,9 +1,11 @@
"""The wiring fundamentals: name binding, fan-in, namespaces, validation."""
from fluksio.flow.controller import _declared_inputs
from fluksio.flow.events import EventBus
from fluksio.flow.messages import DType, MessageSpec
from fluksio.flow.nodes import Node
from fluksio.flow.pipeline import Pipeline
from fluksio.flow.schemas import FlowDef, FlowInput
def spec(name: str, dtype: DType = DType.FLOAT, port: str = "") -> MessageSpec:
@@ -131,6 +133,25 @@ def test_a_flow_input_without_a_starting_value_is_reported():
assert [issue.code for issue in issues] == ["missing_initial_value"]
def test_a_batch_flows_input_is_a_run_parameter_not_a_missing_value():
"""A flow that only runs when asked gets its inputs from the run.
Calling that a message nothing ever sets had the health summary count a
training flow as one that cannot run while the Runs screen showed it
running. A live flow, which nothing is going to start on its own, still
reports it.
"""
node = make_node(
"n", "f", lambda setpoint, params: None, requires=[spec("setpoint")]
)
batch = FlowDef(name="f", mode="batch", inputs=[FlowInput(spec=spec("setpoint"))])
live = batch.model_copy(update={"mode": "live"})
assert Pipeline(nodes=[node]).validate(_declared_inputs(batch)[0]) == []
issues = Pipeline(nodes=[node]).validate(_declared_inputs(live)[0])
assert [issue.code for issue in issues] == ["missing_initial_value"]
def test_a_failing_node_does_not_stop_its_siblings():
ran = []