Round 2: the drag must track, the ask must be one box
Second owner walk of the live workflows surface, same day. Eight things bit — every one a surface behaviour, none a governance defect. This page records what was hit, what actually caused it, and the fix that was decided; every fix is now specified against a studied reference rather than invented.
Eight findings, eight causes — and two additions
Each row is what you said, what the code turned out to be doing, and what was decided. The figures below draw them, at the fidelity a builder can work from.
Reference-backed as of this revision. n8n, Dify, Langflow and Flowise were cloned under
forks/ and read; each mechanism below is lifted from one of them and carries its
decision id (D1–D8, D-icons). The approved wireframes v2 are the visual ground truth — where
this page and a wireframe disagree, the wireframe wins. Two additions joined the original
eight: node kind icons (§02) and contextual run actions (§07).
| You said | Verified cause | Decided fix | ||
|---|---|---|---|---|
| 1 | “The drag doesn't track — the box only lands when I let go.” | GovernedCanvas is a controlled React Flow: it passes nodes down and handles only onNodeDragStop. With no onNodesChange, React Flow's intermediate positions are dropped, so nothing re-renders until the drop. |
Apply position changes live via applyNodeChanges — the library's own idiom for controlled graphs (§02) |
bug |
| 2 | “Why is there already a trigger box when I make a new one?” | starterGraph() seeds a trigger_1 cron card into every new workflow, so the canvas opens with a decision already made for you. |
Empty canvas + n8n's two dashed squares — + Add first step or ✦ Build with AI — nothing placed until you choose (§03) | gap |
| 3 | “The wiring feels complex.” | Port-drag exists, but ports are 10px and give no hover feedback — undiscoverable. Beneath the canvas sits a second, redundant from/to/label dropdown form, so wiring looks like paperwork. | Port-drag becomes the only mechanism: kind-coloured handles that grow on hover, green/red target feedback mid-drag, a kind picker when the wire lands on empty canvas, fat edge hit paths, click-select + Delete. The dropdown form is deleted (§03) | gap |
| 4 | “‘Build with agent' opens this big chat panel — I just want a box you click where you can type what you want.” | Round 1 specified an in-section assistant slide-over; shipped, it reads as a whole second app opening over the work. | A one-line inline AI box (“Describe the workflow you want…”). What opens is a ~400px docked panel that squeezes the canvas, never an overlay (§04) | redesign |
| 5 | The agent answered as the whole platform, not as a workflow builder. | Builder turns go through plain /api/chat with zero workflow framing — no role, no NEP context, no graph. The generic assistant persona is exactly what you get. |
A workflow-builder turn lane that injects scoped context server-side: role, NEP-0006 summary, the open workflow's graph, propose via workflow_upsert; proposals preview on the canvas with N/M badges (§04) |
bug |
| 6 | “Why is the whole list of workflows at the bottom of the page I'm already inside?” | Detail views render the all-workflows list below their own content — a leftover from the single-panel layout, now redundant with the sidebar. | Removed from every detail view. The list lives in the sidebar and the list state only (§05) | gap |
| 7 | “Where do I see the runs?” | GET /api/workflows/:id/runs already returns run history; the governed detail view only ever renders the latest run, so history has no door. |
A Runs section per governed workflow — status · when · duration · the failure's own error text — whose rows load into the run view that already exists (§05) | small |
| 8 | “The runs should light up.” | Run status is a subtle border tint; the only motion on the page is the parked-approval pulse. A running workflow looks identical to a finished one at a glance. | Grey-out then light-up: spinner + accent border on the running node, marching dash on the active edge, green on completed, slow amber ring on a parked gate, auto-pan, and Run now auto-follows its run (§06) | small |
| + | Addition, from the study: a graph of grey boxes is unreadable at a glance. | Node kind is small-caps text only; nothing distinguishes a Slack post from a cron trigger except reading it. | A coloured kind icon chip on every node, brand icons on mapped capability tools, status as spinner / ✓ / ✕ in the head (§02) | addition |
| + | Addition, from the study: watching a run and acting on it are the same moment. | A run in flight cannot be stopped from where you watch it, and a finished run cannot be repeated. | Stop welded to the running pill, Re-run on terminal runs, decide-in-place for parked ones (§07) | addition |
Every node says what it is, in colour
Addition, from the study. Today a node is a grey box whose kind you read in small caps. Every node now carries a kind icon chip — one saturated hue per kind, white glyph, Dify's chip anatomy — so a graph is legible at a glance. A capability node whose tool maps to a known service shows that service's icon instead; unknown tools keep the kind chip, so something always renders. D-icons
Status rides in two channels that never collide: an icon in the head (spinner · ✓ · ✕) and the border tint. Selection outranks status — a selected node always shows its selection ring.
It tracks, it starts empty, it wires by hand
Findings 1–3 are one story: the canvas answers late, decides for you, and offers two ways to do the same thing. The fix is the opposite of each.
Palette · drag onto canvas
Nothing is pre-placed. The publish rule is unchanged — the server still requires exactly one trigger; it just stops being pre-decided for you.
Wiring · one mechanism
Ports take their node's kind colour. Cycles and self-connections are refused as you drag, not at publish. There is no second way to connect two nodes.
approval · notify · branch 1 every frame renders — applyNodeChanges 3 fat hit path — click selects, ⌫ removes 3 green = valid drop · red = invalid 3 drop on empty canvas → pick a kind, wired at the drop point
A second wiring path that duplicated the ports and made the graph feel like a form. With handles that announce themselves, it has no job left.
One box, and an agent that knows it builds workflows
Findings 4 and 5 are the same request from two sides: make asking small, and make the answer be about workflows. The box is one line on the surface you're already on. What opens afterwards is a docked panel, ~400px, that squeezes the canvas rather than covering it — n8n's grid column, never an overlay. Close it and the thread survives. D4
No panel opens on click — it is a text input. On submit, the builder turn starts and the
thread panel slides in to show that turn, then the proposal arrives as the same
gated workflow_upsert approval card as always.
Governed workflows · 3
Steps
The panel took its width from the canvas — the graph shrank, nothing is hidden behind it.
Workflow builder · docked
Parked at the gate. Approving lands it disabled — arming stays a separate human action.
The proposal is a preview, not a mutation: the live graph is untouched until the card is approved, so you judge the picture instead of a text summary. D5
That is the correct answer to a question nobody asked. The turn carried no workflow framing at all, so it introduced the platform. The builder lane fixes the input, not the model:
workflow_upsert → approval cardNothing about the gate moves. The lane is context, not authority: an agent that knows it is building a workflow still cannot publish one without the card.
Lose the list at the bottom, gain the runs
Findings 6 and 7 trade places. The all-workflows list leaves the detail view — you are already inside one, and the sidebar has the rest. In its place goes the thing you actually asked for: the history the API has been returning all along, one row per run — status dot, when, duration, and a failure's own error text in the row rather than one click away. D6 · D7
Sidebar · the list lives here
Exactly one place to change which workflow you are looking at. The detail view no longer repeats it below the fold.
Runs · 6
list_issues
Open run
Rows come from GET /api/workflows/:id/runs — already shipped. Clicking one loads the existing run view; no new run surface is invented.
A running workflow should look like one
Finding 8, now specified against the references. At run start the whole graph greys to about 70% and then lights up node by node (Dify's model), so “what is happening right now” is the only thing at full strength. The executing node carries a spinner in its head plus an accent border; the edge feeding it runs a marching dash; finished nodes and their edges settle green with a ✓; a parked approval wears the slow amber ring from §02. The view auto-pans to the active node, and Run now follows the run it started. D8
One correction from the study worth recording: n8n's visible “spinner” is a rotating ring, and its actual spinner component is dead code. The spinner drawn here is Dify's — the clearer cue, and the one we are copying.
Steps
Run now navigated here by itself, and the canvas panned to the running node — the run you started is the run you are watching.
Motion is status, not decoration: only the executing node and the edge feeding it move.
Everything honours prefers-reduced-motion — with motion off, the same states read
as solid colour, a static ring, and a still dash.
The action lives where the state is
Addition, from the study. Watching a run and acting on it are the same moment, so the controls ride the status rather than sitting in a toolbar: Stop is welded to the running pill (Dify), Re-run appears on a run that has finished or failed, and a parked run is decided in place — the inspector approve/deny already specified as round 1 acceptance item 5, not a second mechanism. Each control exists only in the state it applies to, so there are no dead buttons; if the engine has no cancel path for Stop, that path gets built rather than faked.
What a live walk has to observe
Each line is one observation a spec-verifier can make on the deployed surface, in order, in a single session. Items 1–8 are the findings; 9 and 10 are the additions.
workflow_upsert; opened from a workflow its reply reflects that workflow's actual graph; a pending proposal renders on the canvas with N badges on new nodes and M on modified ones while the live graph stays unchanged until the card is approved.prefers-reduced-motion set, every one of those states is still distinguishable without animation.