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:
root
2026-08-16 07:37:46 +02:00
co-authored by Claude Fable 5
parent f8693daad6
commit 2cc25970e3
5 changed files with 222 additions and 12 deletions
+11 -2
View File
@@ -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: {