Documentation / Conception / Guides pratiques / Use the visual editor

Use the visual editor

This recipe walks through building or changing a workflow in the Meridian graph editor (the config plane), from creating or opening a workflow to a saved draft and its publication. It assumes you already know the workflow model — if you do not, start with your first workflow and keep the workflow DSL reference open.

The editor is the React Flow component in apps/config-plane/app/components/WorkflowEditor.tsx, mounted by apps/config-plane/app/page.tsx.

Open the editor

  1. From the workflow list, select a workflow and click Edit. This navigates to /?wf=<file>&edit=1 and mounts the editor on its pending draft, or on the published spec when there is none.
  2. To create one, click New. This navigates to /?new=1 and opens the creation dialog — see Create a workflow.
  3. In edit mode the workflow list pane disappears — the whole width goes to the palette, canvas, and inspector. Return to the list through the Workflows breadcrumb (the unsaved-changes guard will stop you if you have pending edits).

Create a workflow

The creation dialog has three starting points, chosen at its top. All three open the editor on a prefilled spec; nothing is written before the first save, so cancelling leaves nothing behind.

  • Empty — a spec with the chosen trigger and no nodes. When the trigger's event has a default filter (defaultSelector, declared by its plugin), the Filter starts with it; you can change or remove it.
  • YAML file — Choose a file (.yaml or .yml, 1 MiB at most, UTF-8). The API reads it without writing anything (POST /api/workflows/import): syntax errors come back with their line and column, and a file that does not match the schema is refused. An accepted file prefills the name, description, labels, trigger and filter, shows its node and connection counts, and lists its validation errors (a draft can be saved with them; publication refuses them) and warnings (ignored fields). The file keeps its own filter: no default filter is added. Keep the imported file name (ticked by default, offered when the name has the form <name>.workflow.yaml) uses that name as the target file: a workflow.execute node references its child by file name, which deriving the file from the display name would change. A webhook trigger always gets a new URL identifier, and picking another trigger type drops the connections that started from the file's trigger. The file text goes along with the first save, so its comments are kept in the draft.
  • Assistant — an instruction for the AI assistant; the editor generates the workflow when it opens. As in Empty, a trigger picked in the dialog starts with its event's default filter. See Use the AI assistant.

Below the starting point come the Name (the target file is derived from it, or is the imported file name), the description, the Trigger with its Filter when the trigger routes by event, and the labels. Événement reçu has one entry in the trigger list and adds an Event (« Événement ») list: every declared event of the loaded plugins, grouped by the plugin that declares it and sorted by type, each with its description. Create stays unavailable until an event is chosen; the chosen event's default filter prefills the Filter, and the hint names the workflows that already receive it. An imported file on Événement reçu keeps its event. An event the loaded plugins do not declare stays shown, marked (not declared) (« (non déclaré) »): the file keeps its trigger, its filter and its connections, the draft can be created, and publication refuses it until the event is declared or another one chosen — as for a file whose trigger no loaded plugin provides. The dialog's filter has the same guided Field list as the trigger inspector, without the Declared event section and without the Example event panel. Under an event-routed trigger, the dialog names the published workflows that already receive its event (Also received by: …) — once published, the new workflow starts on that event as well, since every workflow that receives an event starts. The target file is checked against the workflow list as you type, then, when you validate the dialog, against every definition of the store — archived workflows and agents included — so the first save cannot be refused for a taken name. When the imported file name is taken, untick Keep the imported file name or rename the file. The target file is fixed when you validate the dialog: renaming the workflow later does not change it.

Know the three panes

  • Left — palette. The trigger selector and the node catalogue grouped by category.
  • Centre — canvas. The toolbar (name, the target file in creation mode, Save the draft, Publish, Simulate, Import), the Build with AI panel, the graph itself, and a status bar.
  • Right — inspector. Context-sensitive: it shows the selected node's controls, or the simulation panel, or the workflow's metadata when nothing is selected.

Set the trigger and its filter

  1. Pick the trigger type in the palette's Trigger dropdown, or select the trigger node and use Type at the top of its inspector. Every workflow has exactly one trigger; changing it replaces the trigger node. The card of a trigger that receives a declared event shows the event under its label.
  2. With Événement reçu, choose the event in Event (« Événement »): the declared events of the loaded plugins, grouped by the plugin that declares them. The trigger's outputs are the event's fields, in declaration order; an event the loaded plugins no longer declare stays shown, marked (not declared) (« (non déclaré) »), and the trigger then has only done. The Declared event section then reads « Événement « X » non déclaré par les plugins chargés. Choisir un autre événement ou charger le plugin qui le déclare. », and the Filter states that the workflow receives no event, with the same action.
  3. Changing the trigger — its type, or the event of Événement reçu — removes the edges that started from the old trigger (the inspector states how many) and resets the configuration and the filter; a trigger whose event has a default filter starts with it. Going back to a type or an event you left restores its configuration (a webhook keeps its URL), its filter and those edges, when their target input is still free; going back to Événement reçu with Type restores the last event you left on it. Otherwise a removed filter is shown with Restore. An import or an assistant proposal starts from its own trigger: only the trigger it replaced can be restored, without edges.
  4. A trigger that receives a declared event — Événement reçu once its event is chosen, or a dedicated trigger (event: in its plugin's manifest) — shows the Declared event section: the event type, its context kinds with their identifier field, required or optional, and its fields with their type, a required mark and their description. These are the event.<field> a filter can read, and the guided Field list of the filter below is built from the same contract. Fields the contract does not declare are accepted and passed on as received (see Events and triggers).
  5. The trigger inspector ends with the Filter section (trigger.selector): a guided mode over the fields of the trigger's event and an expression mode, checked as you type. The filter only narrows what this workflow receives. When other published workflows receive the same event, the panel says so: {event} events also start: followed by each of them (marked (filter) when it has a filter, (filter in error) when its filter is empty) and its effects, the effect types this workflow shares with them, and the number of events of that type received over the last 7 days. The filter is then tested on the example event (Kept by this filter. or Not kept by this filter.), followed by the workflows that would receive that event, this one included; the collapsible Example event panel shows the event itself. See Edit the filter in the console.

Add nodes

  1. Filter the catalogue with the search box (matches label, description, and type id) if the list is long.
  2. Click a palette entry to drop the node on the canvas; it is selected automatically so you can configure it right away. The coloured dot marks the node's role (source, compute, gateway, human-task, agent, transform…).
  3. To place a node already wired to something, drag from a port into empty space instead — see Wire ports.
  4. Rename a node by selecting it and editing Identifier in the inspector; the id is slugified to [A-Za-z0-9_] and must be unique. Delete a node with the Delete button in the inspector or the Delete/Backspace key. Edges attached to it are pruned automatically.

Configure a node

Select a node; the inspector renders controls that depend on the node type:

  • Config fields for static nodes (for example a human-task SLA), typed as text / number / boolean.

  • Dynamic editors for the core transform and gateway primitives — a From→Into picker for transform.convert (filtered to conversions compatible with what is already wired), an object-type picker for transform.make / transform.break, a condition field for flow.guard, a case list for flow.switch, and the number of inputs (2 to 8) and the value type for flow.merge (set by the first input wired when its source has a type). Condition fields autocomplete field paths and expect JEXL; the API checks them as you type and lists the problems under the field (syntax with its column, decimal comma, unknown variable or transform). map infers its item/result types from the wiring. See branch with guards and switch and loop with map.

  • Connection for a node whose plugin requires one (a notification node, a call to an external API): a list of the outbound connections that fit the node. The empty choice keeps the default connection of the type, or the port bound by the instance. Gérer les connexions opens the connections page in a new tab; the list refreshes when you come back.

Config edits recompute the node's ports and drop any edge whose socket no longer exists, so wire types after you finish configuring.

Wire ports

Grounding for the concepts here lives in wire ports and the dataflow model; the mechanics in the editor are:

Draw a connection

  1. Drag from an output handle to an input handle.
  2. The editor validates the connection against the resolved port types. An incompatible pair is rejected with an error message and no edge is created; a compatible one flashes a confirmation.
  3. An input accepts only one cable — a new connection replaces the previous one. The exception is after, a multi-branch join, which accumulates cables (see sequence with after and done).

Create a compatible node by dragging into space

  1. Drag from a port and release on empty canvas.
  2. A menu lists only nodes that fit: from an output, nodes that can consume the type; from an input, nodes that can produce it. Flow ports (after) are excluded from these suggestions.
  3. Pick one — the editor creates the node, wires it, and infers dynamic types from the anchor port.
  1. Hover an edge and click the ≡ button at its midpoint.
  2. Choose an action — insert a compatible convert, break, or make node, or delete the link. The original edge is replaced by wiring through the inserted node.

Expose object ports (double-click)

An object-typed port shows a single socket. To wire its fields individually:

  1. Double-click the object port to expand it into one sub-port per field.
  2. Wire the sub-ports you need.
  3. Double-click any sub-port to collapse the group back into the single object socket.

The same expand/compose toggles are available as checkboxes under Split ports in the inspector. (Ports are re-registered on the fly when they change, so a freshly exposed sub-port is immediately connectable.)

Set fixed values and exposed parameters

Two inspector sections let you supply data without a cable:

  • Fixed inputs — type a constant directly onto an unwired primitive/coded input port. Wiring a port hides its fixed-value control.
  • Parameters as ports — tick a config field to expose it as an input port so it can be driven by the graph instead of a constant. Un-ticking it removes the port and any edge into it.

Import a YAML file into an open workflow

Import in the toolbar replaces the graph, the trigger and its filter with those of a YAML file. The file is read by the API as in the creation dialog; a refused file shows its errors. An accepted file opens a confirmation with its node and connection counts, its validation errors and warnings, and a reminder that unsaved changes will be lost. Replace loads it into the editor; the target file stays the same, a webhook keeps its current URL (a webhook replacing another trigger type gets a new one, never the file's), and the imported file's comments are not kept. Nothing is written until you save the draft.

Read live validation

  • Wiring is checked as you connect (type match, port existence).
  • Guard and switch conditions and the trigger filter are checked by the API as you type.
  • The status bar reflects overall state: saved, unsaved (edits pending), or invalid (last action produced an error). It also shows node and connection counts, and flags a brand-new workflow.
  • This is only the editor's in-canvas feedback. The authoritative static checks run on save and in simulation; for the two layers of validation see two-level validation and validate a workflow.

Save the draft and publish

  1. Set the workflow name in the toolbar. In creation mode the target file is shown next to it, read-only: it was fixed by the creation dialog.
  2. Click Save the draft. The editor builds a spec from the current graph and writes it as the workflow's draft (PUT /api/workflows/:slug/draft); only the schema is checked, so a draft may still have errors. The draft receives no event.
  3. Click Publish to save the draft and publish it. The confirmation names the other published workflows that start on the same event, those sharing an effect type with this one first (Same effect type: …), each with its effects, and the number of events of that type received over 7 days. A second prompt then asks for an optional publication note, stored with the version (leave it empty for none). The server then revalidates everything (dataflow, expressions, codes, terminology, webhook address) and refuses the publication with the errors otherwise.
  4. On success the current state becomes the new "saved" baseline (the guard disarms). The publication warnings — other workflows that also start on the event, the event's default filter missing from the filter — stay in a block under the toolbar until you close it. A 409 means the workflow changed since it was loaded; Reload fetches it again.

A new workflow is created as a draft by its first save — Save the draft or Publish — and the page then reopens it in edit mode (/?wf=<file>&edit=1); the warnings of a publication made at creation are shown there, once. The creation dialog already refuses a taken target file; if another workflow or agent takes it in the meantime, the save is refused: create the workflow again under another name from the list.

The saved YAML round-trips: reopening the file rebuilds the same graph, including node positions, split/exposed ports, and inline values.

The unsaved-changes guard

The editor snapshots the spec after the catalogue loads and compares it to the current graph. While they differ — and, for a new workflow, until its first save:

  • Closing or reloading the tab triggers the browser's leave-confirmation.
  • Clicking an internal link (breadcrumb, "View", a workflow in the rail) prompts a confirmation before navigating away.

Saving clears the pending state. If you want to discard changes instead, confirm the navigation away without saving.

Next steps

97 documents17 sectionssource : /docs · généré au build