Governed Workflows: visual automation where every node answers to the gate
A React-Flow canvas in the Neutron UI over the assay durable engine already shipping in every instance. The node palette is the capability registry — not a hand-built connector library — and every mutating step runs through the same approval gate as an agent turn.
The connector treadmill is already solved
- SaaS-first distribution bars provisioning n8n for tenants — its Sustainable Use License restricts providing it to third parties commercially. Embedding a MIT alternative (Activepieces) would add a second engine, a second credential store, and an ungoverned egress lane. Both fail the product spine.
- Neutron does not need their catalogs: every MCP server, CLI tool, and skill an admin registers is already a node, already gated per agent. The MCP ecosystem is the connector catalog; the registry, OAuth 2.1 stack, and vetting pipeline already consume it.
- What is actually missing is a visual surface: see the automation, edit it, watch a run, and resolve the approval that parks it. Bounded UI plus one executor — not a platform build.
Core, not orbit
Workflows are durable server-side state — triggers, runs, approvals, audit — so they live in core, next to the gate and the registry. Orbit is a device client: it reaches the same workflows through the core API under the same RBAC, and a workflow step that must execute on a machine goes through orbit's existing exec-gate as a capability call. Where the canvas is shown is a UI/RBAC decision, never a second implementation.
What a user sees
One new tab in the workspace: Workflows. Left rail lists the agent's granted capabilities — the palette is read straight from the registry, so anything an admin registers appears here with its gate policy already attached. The canvas shows the graph with live run state. The inspector is where governance becomes visible: selecting a gated node shows the policy chain and the approval that is blocking it.
Palette · granted capabilities
Palette = capability registry. No workflow-specific connectors exist anywhere — register an MCP server, it becomes a node.
Inspector · approval gate
Same durable approval row as a chat turn — the card also lands in the owner's inbox and on WhatsApp / Slack. Approving from any surface resumes the run.
How it plugs into the existing core
Three flows, almost entirely through machinery that already ships. The build is the canvas, one route module, and a step executor — everything below the executor line is untouched.
apps/ui · React Flowroutes/workflows.tsdelta migration NNNNgated workflow_upsert — models write the graphassay cron · webhooks · channel eventsapps/api/src/workflows/guardrails.ts · floor + tool_policycapabilities/ · mcp | tool | skillthe connector catalog — not ours to buildauthz/approval-wait.tsconnectors/inbox.ts · Slack · WhatsAppneutron-authz · requester ≠ approverauthz/audit.ts · providers/cost.ts
Agent-turn nodes reuse the engine seam (engine/index.ts) — a workflow step can be
a headless Claude, Codex, or OpenCode turn with the same event stream and account-pool
rotation as chat. Retries, resume, and schedule durability come from the assay sidecar the
image already bundles: no second engine, no second credential store, no second cron. The
non-escalation invariant holds for graphs — a workflow step has exactly the authority of the
seat that owns it, same as a turn.
The governed path of one execution
Six node kinds, five of them existing machinery
| Node | What it does | Runs on | |
|---|---|---|---|
| Trigger | Cron, inbound webhook, channel event (message, reaction, intake label) | assay cron + existing webhook routes | exists |
| Capability | One gated call: MCP tool, CLI tool, or skill from the registry | capabilities/ + guardrails gate | exists |
| Agent turn | Headless prompt to a named agent; output feeds the next node | engine seam (claude / codex / opencode) | exists |
| Approval | Explicit human checkpoint with role-routed approver | durable approvals + inbox | exists |
| Notify | Post to channel / thread / inbox card | channels + realtime bus | exists |
| Branch | Condition on a prior node's output (expression, no LLM) | executor — plain code | new |
Three tables, one route module
| Addition | Shape | Notes |
|---|---|---|
workflows | id, name, graph JSONB, owner seat, enabled, version | delta migration — self-applies at boot |
workflow_runs | id, workflow_id, trigger, status, started/ended | parked | running | waiting_approval | done | failed |
workflow_step_runs | run_id, node_id, status, input/output refs, approval_id | joins to the existing approvals + audit rows |
routes/workflows.ts | CRUD + run + runs list + SSE run events | guarded by the standard fail-closed API guard |
| authoring tool | workflow_upsert exposed to agents | hard-gated like agent-admin — humans approve the graph an agent proposes |
The loop that makes verticals configuration, not code
Workflows are the last piece of a loop whose other three pieces exist. Developer, marketing, and ops verticals become pack + capability-set + workflow templates.
agent-admin tier · opt-ininstallable org unitsPD004 capability supplyworkflow_upsert · this PDone inboxDepends on PD004 capability supply for curated tool sets and PD005 model routing for headless model/effort choice.
Shippable slices
1 · Viewer
~3–5 sessions- Read-only canvas of existing assay workflows + schedules
- Live run state overlay, run history list
- Zero new execution surface — pure UI over what runs today
2 · Executor
~8–12 sessions- Graph-JSON definition + step executor, every node gated
- Trigger wiring: cron, webhook, channel events
- Durable runs: park on approval, resume across restarts
3 · Editor
~8–12 sessions- Drag-and-drop palette from the capability registry
- Node config panels, validation, versioning
- Agent authoring tool + “paste an n8n workflow” translation flow
What this deliberately is not
What proves it
- A workflow with a gated step parks durably, survives a pod restart while parked, and resumes on approval from web, Slack, and WhatsApp.
- Requester-cannot-self-approve verified on a workflow approval, same as chat.
- A hard-gated capability (outreach.send) cannot be auto-approved by any workflow config.
- An agent-authored graph lands as a proposal requiring human approval before first run.
- Audit shows one decision row + one executed row per mutating step, cost attributed.