Let a node's failure outlive the run that followed it

A node's error cleared the moment it ran again, so a failure that genuinely
fired an alert could leave no trace on the canvas by the time anyone looked.
The engine records it now — on the node's status, so it survives a reload and
every client agrees — and reading the traceback is what clears it. The seam is
the event bus, which is where every failing path already meets: a queued live
run, an explicit run, a preview, and a single triggered node all publish
`node_error`, while the controller's own observer would have seen only one of
them.

That was half the confusion. The other half: clicking a failed neuron on Home
often landed on a flow where everything looked fine. Nodes merge into one
neuron by instance key — every InfluxDB node pointing at the same bucket is one
neuron — and the click went to whichever flow contributed a member first, not
the one that failed. It now goes to the failing member and selects it, and the
canvas marks a failing node rather than leaving it to the dot alone.

The inject node emitted one payload to every port it declared, whatever their
types, so an inject on a bool port carrying the text "true" raised at publish
time. Each port gets its own field now, typed and parsed by that port's dtype,
and remembers what it last sent. A port that is renamed carries its value with
it; one that is removed takes its value with it. An inject written before this
keeps emitting exactly what it did.

The derived-cron chip also appeared on the delay node, where `interval` is a
rate limit and a schedule derived from it means nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uq8mtNb97A7praJLyeEYgs
This commit is contained in:
2026-08-21 14:33:41 +02:00
co-authored by Claude Opus 5
parent 06f84e18ae
commit b0efb4b0f1
19 changed files with 738 additions and 86 deletions
+4 -4
View File
@@ -17,11 +17,11 @@ import { Switch } from "@/components/ui/switch"
import useCustomToast from "@/hooks/useCustomToast"
export const Route = createFileRoute("/_layout/")({
component: Dashboard,
component: Home,
head: () => ({
meta: [
{
title: "Dashboard - Fluksio",
title: "Home - Fluksio",
},
],
}),
@@ -50,7 +50,7 @@ function FlowRow({ flow }: { flow: FlowSummary }) {
return (
<div
className="flex items-center gap-3 border-b border-border px-5 py-3 last:border-b-0"
data-testid="dashboard-flow-row"
data-testid="home-flow-row"
>
<Link
to="/flows/$flowName"
@@ -100,7 +100,7 @@ function FlowRow({ flow }: { flow: FlowSummary }) {
* it has been doing. The brain and the health screens compose in here rather
* than living at routes of their own.
*/
function Dashboard() {
function Home() {
// The health block's window: one choice, read by the tiles, the flow table,
// the charts and the lists under them.
const [range, setRange] = useState(DEFAULT_RANGE)