Warn when a node that runs no code names a device
A device on a node without source was accepted and silently ignored. The build now reports it as ignored_device, an advisory issue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WgBfkm4rCmtd1T4vHWjEPx
This commit is contained in:
@@ -174,6 +174,8 @@ class LoadedNode:
|
||||
#: Published, and with no body of its own — so what runs is the new-node
|
||||
#: template, which returns nothing at all.
|
||||
missing_source: bool = False
|
||||
#: The device this node names although its type never runs on a worker.
|
||||
ignored_device: str = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -1041,6 +1043,8 @@ class FlowController:
|
||||
node_type = NODE_TYPES.get(node_def.type)
|
||||
if node_type is None:
|
||||
raise ValueError(f"Unknown node type '{node_def.type}'")
|
||||
if node_def.device and not node_type.has_source:
|
||||
entry.ignored_device = node_def.device
|
||||
|
||||
params = resolve_params(node_def.params)
|
||||
|
||||
@@ -1923,6 +1927,22 @@ def _collect_issues(
|
||||
for entry in loaded.values()
|
||||
if entry.missing_source
|
||||
]
|
||||
# Only code travels to a worker, so a device on any other node is a field
|
||||
# nothing reads. Saying so beats a setting that looks like it took.
|
||||
issues += [
|
||||
ValidationIssue(
|
||||
code="ignored_device",
|
||||
message=(
|
||||
f"Node '{entry.id.rpartition('.')[2]}' names device "
|
||||
f"'{entry.ignored_device}', but only a Function node runs on a "
|
||||
"worker. The setting is ignored."
|
||||
),
|
||||
flow=entry.flow,
|
||||
node=entry.id,
|
||||
)
|
||||
for entry in loaded.values()
|
||||
if entry.ignored_device
|
||||
]
|
||||
return issues
|
||||
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ MANUAL_RUN_PREFIX = "manual-"
|
||||
# neither the brain graph nor the health summary treats them as a fault.
|
||||
# Lives here rather than beside the controller so `ValidationIssue` can carry
|
||||
# the distinction itself, and every reader gets it for free.
|
||||
ADVISORY_ISSUES = frozenset({"unauthenticated_hook"})
|
||||
ADVISORY_ISSUES = frozenset({"unauthenticated_hook", "ignored_device"})
|
||||
|
||||
|
||||
class ValidationIssue(BaseModel):
|
||||
@@ -61,6 +61,7 @@ class ValidationIssue(BaseModel):
|
||||
"self_loop_needs_initial",
|
||||
"missing_source",
|
||||
"node_unhealthy",
|
||||
"ignored_device",
|
||||
]
|
||||
message: str
|
||||
flow: str = ""
|
||||
|
||||
@@ -125,6 +125,29 @@ def test_a_published_node_with_no_body_is_reported(store: FlowStore):
|
||||
assert [i for i in controller.issues if i.code == "missing_source"] == []
|
||||
|
||||
|
||||
def test_a_device_on_a_node_without_code_is_reported(store: FlowStore):
|
||||
"""Only code travels to a worker, so the device would be ignored silently."""
|
||||
store.write_flow(
|
||||
FlowDef(
|
||||
name="heating",
|
||||
nodes=[
|
||||
NodeDef(
|
||||
id="relay",
|
||||
type="change",
|
||||
device="gpu",
|
||||
provides=[MessageSpec(name="temp")],
|
||||
)
|
||||
],
|
||||
)
|
||||
)
|
||||
controller = FlowController(store)
|
||||
|
||||
asyncio.run(controller.reload())
|
||||
(issue,) = [i for i in controller.issues if i.code == "ignored_device"]
|
||||
assert issue.node == "heating.relay"
|
||||
assert issue.advisory
|
||||
|
||||
|
||||
def test_a_node_being_written_in_the_editor_is_not_reported(store: FlowStore):
|
||||
"""A draft legitimately has no published body yet — that is what a draft is."""
|
||||
store.write_draft(a_flow(), 0)
|
||||
|
||||
@@ -132,9 +132,10 @@ The canvas reports issues on the affected nodes:
|
||||
| `missing_source` | a Function node has no saved code |
|
||||
| `node_error` | code or configuration could not load |
|
||||
| `unauthenticated_hook` | a webhook or served websocket has no shared secret; advisory |
|
||||
| `ignored_device` | a node other than a Function node names a device, which nothing reads; advisory |
|
||||
| `node_unhealthy` | a loaded node reports a runtime problem, such as a lost connection |
|
||||
|
||||
Build issues prevent the affected flow from running. The last two are runtime
|
||||
Build issues prevent the affected flow from running. The last three are runtime
|
||||
or advisory issues: other work continues, and node health clears when the
|
||||
connection recovers.
|
||||
|
||||
|
||||
@@ -4325,7 +4325,7 @@ export const ValidationIssueSchema = {
|
||||
properties: {
|
||||
code: {
|
||||
type: 'string',
|
||||
enum: ['cycle', 'unconnected_input', 'missing_initial_value', 'node_error', 'unauthenticated_hook', 'self_loop_needs_initial', 'missing_source', 'node_unhealthy'],
|
||||
enum: ['cycle', 'unconnected_input', 'missing_initial_value', 'node_error', 'unauthenticated_hook', 'self_loop_needs_initial', 'missing_source', 'node_unhealthy', 'ignored_device'],
|
||||
title: 'Code'
|
||||
},
|
||||
message: {
|
||||
|
||||
@@ -1406,7 +1406,7 @@ export type ValidationError = {
|
||||
* Something wrong with a flow — a fault, or merely advisory.
|
||||
*/
|
||||
export type ValidationIssue = {
|
||||
code: 'cycle' | 'unconnected_input' | 'missing_initial_value' | 'node_error' | 'unauthenticated_hook' | 'self_loop_needs_initial' | 'missing_source' | 'node_unhealthy';
|
||||
code: 'cycle' | 'unconnected_input' | 'missing_initial_value' | 'node_error' | 'unauthenticated_hook' | 'self_loop_needs_initial' | 'missing_source' | 'node_unhealthy' | 'ignored_device';
|
||||
message: string;
|
||||
flow?: string;
|
||||
nodes?: Array<(string)>;
|
||||
@@ -1419,7 +1419,7 @@ export type ValidationIssue = {
|
||||
readonly advisory: boolean;
|
||||
};
|
||||
|
||||
export type code = 'cycle' | 'unconnected_input' | 'missing_initial_value' | 'node_error' | 'unauthenticated_hook' | 'self_loop_needs_initial' | 'missing_source' | 'node_unhealthy';
|
||||
export type code = 'cycle' | 'unconnected_input' | 'missing_initial_value' | 'node_error' | 'unauthenticated_hook' | 'self_loop_needs_initial' | 'missing_source' | 'node_unhealthy' | 'ignored_device';
|
||||
|
||||
export type ValidationResult = {
|
||||
issues?: Array<ValidationIssue>;
|
||||
|
||||
Reference in New Issue
Block a user