Edits autosave to flow.draft.json and nodes.draft/ instead of the files the
engine reads, so the pipeline keeps running the published version until
someone publishes. Every save carries the version it was based on: a second
client editing the same flow is refused with 409 and offered the choice
between their version and its own, rather than silently overwriting.
Draft saves no longer rebuild the pipeline; validation and node status for a
draft come from a throwaway build that never touches live state.
Also fixes a latent bug where an empty state backend is falsy, so Pipeline
quietly built itself a second, private state and left message history empty.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- One dot per node now carries the whole story: primary while running, sage
after a good run, red when anything is wrong, with the explanation on hover.
The corner badge is gone, along with the second way of saying the same thing.
- A node that publishes something flashes a ring, so a running flow is legible
without reading the edge values. Nodes that consume but publish nothing stay
quiet, which is why the event carries an output count.
- Flow settings open in the same panel its nodes use, from a pencil in the
dock: the title, the name, and deleting the flow. NodePanel and FlowPanel
share the panel chrome rather than each drawing their own.
- Renaming is a server operation, because a flow's name is the namespace of its
messages: the directory moves and every other flow reading `old.message` is
repointed, instead of being left pointing at a flow that no longer exists.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WzrvW7rjQbynnhF6pxh6i
Makes the flow engine reachable from the API, which is what M3 needs before
any of it can reach the browser.
- app/flow is a package now; the prototype's watch-dir scripts and the
matplotlib/networkx visualiser are gone with their dependencies.
- Messages carry a serializable dtype instead of a live Python type, and a
port name, so the graph can speak qualified names while node functions keep
local arguments. Redis state is JSON, not pickle.
- Message names are namespaced per flow ("heating.temp"); a bare name resolves
to its own flow, a dotted one crosses flows.
- Several nodes may provide the same message: producers are a list, so fan-in
is a real edge instead of a silently dropped one.
- Flows are stored as flow.json plus node sources in a git repository, one
commit per save, with identical saves skipped so autosave stays quiet.
- Node failures are isolated and reported per node; validate() returns cycles
and unconnected inputs instead of raising deep in a run.
- Credentials live in an encrypted store and are referenced as {"$secret": …}.
- Engine events reach websocket clients through a bus, so values, node status
and execution show up live.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WzrvW7rjQbynnhF6pxh6i