Let a flow keep state in the messages it already has
Logic nodes are pure functions with no state handle, but real automations count things and remember the last reading. The shape for that is a message a node both reads and writes: the graph already declines to make a node depend on itself, so this worked by accident. It is now defined, tested, and checked — a node that is the only writer of a message it reads is told at edit time that it needs a starting value, rather than silently never running. Feeding a value back between two nodes was still a cycle, and rejected. An input can now be marked non-triggering: read when the node runs, never the reason it runs, and no dependency either way. That is what a back edge actually means. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011LF61rxW1FG5YCD2J9YqjY
This commit is contained in:
@@ -507,6 +507,11 @@ export const MessageSpecSchema = {
|
||||
minimum: 0,
|
||||
title: 'Interval',
|
||||
default: 0
|
||||
},
|
||||
trigger: {
|
||||
type: 'boolean',
|
||||
title: 'Trigger',
|
||||
default: true
|
||||
}
|
||||
},
|
||||
type: 'object',
|
||||
@@ -521,7 +526,11 @@ export const MessageSpecSchema = {
|
||||
:param interval: Deliver at most every this many seconds; 0 is every time.
|
||||
On an output it holds back publishing, on an input it holds back waking
|
||||
the node. The value is never lost — state keeps the latest — only the
|
||||
delivery is skipped.`
|
||||
delivery is skipped.
|
||||
:param trigger: Whether arriving values wake the node. An input with this
|
||||
off is read when the node runs for some other reason, but never causes
|
||||
a run and never makes the node wait — which is how a node reads a
|
||||
message it also produces without depending on itself.`
|
||||
} as const;
|
||||
|
||||
export const MessageValueSchema = {
|
||||
@@ -1377,7 +1386,7 @@ export const ValidationIssueSchema = {
|
||||
properties: {
|
||||
code: {
|
||||
type: 'string',
|
||||
enum: ['cycle', 'unconnected_input', 'missing_initial_value', 'node_error', 'unauthenticated_hook'],
|
||||
enum: ['cycle', 'unconnected_input', 'missing_initial_value', 'node_error', 'unauthenticated_hook', 'self_loop_needs_initial'],
|
||||
title: 'Code'
|
||||
},
|
||||
message: {
|
||||
|
||||
@@ -152,12 +152,17 @@ export type MessageHistory = {
|
||||
* On an output it holds back publishing, on an input it holds back waking
|
||||
* the node. The value is never lost — state keeps the latest — only the
|
||||
* delivery is skipped.
|
||||
* :param trigger: Whether arriving values wake the node. An input with this
|
||||
* off is read when the node runs for some other reason, but never causes
|
||||
* a run and never makes the node wait — which is how a node reads a
|
||||
* message it also produces without depending on itself.
|
||||
*/
|
||||
export type MessageSpec = {
|
||||
name?: string;
|
||||
port?: string;
|
||||
dtype?: DType;
|
||||
interval?: number;
|
||||
trigger?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -382,7 +387,7 @@ export type ValidationError = {
|
||||
* A problem that keeps a flow from running correctly.
|
||||
*/
|
||||
export type ValidationIssue = {
|
||||
code: 'cycle' | 'unconnected_input' | 'missing_initial_value' | 'node_error' | 'unauthenticated_hook';
|
||||
code: 'cycle' | 'unconnected_input' | 'missing_initial_value' | 'node_error' | 'unauthenticated_hook' | 'self_loop_needs_initial';
|
||||
message: string;
|
||||
flow?: string;
|
||||
nodes?: Array<(string)>;
|
||||
@@ -391,7 +396,7 @@ export type ValidationIssue = {
|
||||
message_name?: (string | null);
|
||||
};
|
||||
|
||||
export type code = 'cycle' | 'unconnected_input' | 'missing_initial_value' | 'node_error' | 'unauthenticated_hook';
|
||||
export type code = 'cycle' | 'unconnected_input' | 'missing_initial_value' | 'node_error' | 'unauthenticated_hook' | 'self_loop_needs_initial';
|
||||
|
||||
export type ValidationResult = {
|
||||
issues?: Array<ValidationIssue>;
|
||||
|
||||
Reference in New Issue
Block a user