Documentation / Développement / Guides pratiques / Validate a workflow

Validate a workflow

meridian-plugin validate statically checks a workflow YAML against your plugin's declared nodes (plus its dependsOn closure) and the engine's core primitives (flow.*, transform.*) — no engine, no infrastructure, nothing is executed (libs/plugin-cli/src/commands/validate.ts). It reports unknown node types, unknown or mistyped ports, missing context, and required inputs left unconnected, and exits non-zero on error so it drops straight into CI.

This is the static half of validation only — it never loads a behavior module and never checks that a port's runtime value actually conforms. For that split, see Two-level validation; for what actually runs your behaviors against sample data, see Simulate a workflow.

It assumes a plugin directory with a plugin.yaml (see Scaffold a plugin) and a workflow YAML wired against its nodes (see Your first workflow and the workflow DSL for the grammar itself). You do not need gen-types or build first: the validator reads contributes straight out of plugin.yaml — types, value-sets, dimensions and node metadata — never the generated/ output or a compiled dist/.

Run the validator

  1. From the plugin's directory, run the CLI directly or the validate npm script the scaffold wires up (libs/plugin-cli/src/commands/scaffold.ts):

    npm run validate workflows/example.workflow.yaml   # → meridian-plugin validate …
    

    With no --plugin flag, the plugin directory defaults to . — the current directory (libs/plugin-cli/src/cli.ts).

  2. Pass --plugin <dir> once per additional plugin directory the workflow's nodes come from. You only need this for plugins that aren't reachable through a dependsOn closure already: dependsOn entries are resolved automatically through node_modules, the same way gen-types resolves them (libs/plugin-cli/src/discover.ts, loadPluginClosure). Repeat the flag for each extra root; results are merged and de-duplicated by plugin name.

    Inside the platform repository, use the root pnpm plugin script (there is no globally installed meridian-plugin binary there) instead of a package-local npm run validate. For example, content/clinical-demo/workflows/bio-result.workflow.yaml wires a trigger and a few patient.*/human.* nodes from the core clinical plugin to compute/sink nodes contributed by the external @posos/demo plugin — neither depends on the other, so both roots must be listed:

    pnpm plugin validate content/clinical-demo/workflows/bio-result.workflow.yaml \
      --plugin external-plugins/clinical \
      --plugin external-plugins/demo
    

    flow.guard, also used in that workflow, needs no --plugin at all — core primitives are baked into the validator's catalog before any plugin is registered (libs/shared/src/engine/catalog-metadata.ts).

Read the result

A valid workflow prints its name and exits 0:

✓ Réception résultat biologie: valid (ports exist, connection types compatible, context, required inputs).

An invalid one lists every error, one per line, and exits 1:

✗ Réception résultat biologie: 2 error(s):
  - Connexion trigger.result → ghost.criteria : nœud cible « ghost » inconnu.
  - Nœud « guard_creatinine » : entrée requise « criteria » non connectée.

The checker's own diagnostic strings are emitted in French today, regardless of your plugin's locale: or an instance's locale — this is internal tooling output from libs/engine-core/src/engine/validate.ts and graph.ts, not the runtime ctx.t translation layer described in the manifest reference's i18n section; don't confuse the two.

Fix common failures

  • Unknown node or trigger type (type inconnu) — a typo in a node's type, or the owning plugin's directory is missing from --plugin and isn't reachable through dependsOn either. Add the directory or fix the id.
  • Unknown source/target node on a connection (nœud … inconnu) — the from/to side of a connection names a node id that isn't declared under nodes: (or isn't trigger). Check for a rename or a typo.
  • Unknown port, or unknown split-pin field (sortie/entrée … inexistante, champ … inconnu) — the port name, or the field addressed after the dot on a split port (node.port.field), doesn't exist on that node or type. Cross-check the node's ports in Plugin manifest or the type's fields in TypeRef.
  • Incompatible connection type (type incompatible) — the output type isn't assignable to the input type under structural subtyping. See TypeRef and The dataflow model.
  • Missing or unestablished context — a node's context entry isn't in the workflow's context: list, or the workflow lists a context key the trigger doesn't establish. Add it to context: or pick a trigger that establishes it.
  • Required input not connected — wire it, or set a fixed value under nodes.<id>.inputs.<port> instead (never both on the same port).
  • Graph structural errors — a cycle outside a loop's own return edge, a nested map, a node claimed by two loop bodies, or map.item feeding a node outside its body. These come from the same loop-body checks the interpreter enforces at runtime; see Loop with map for the model.

For the full mechanics behind every check (context propagation, split-pin resolution, loop-body inference), see Static validation engine.

Limitations

  • Agent nodes aren't projected. The CLI doesn't turn agents: manifest entries into node metadata, so a workflow using an agent.<slug> node reports it as an unknown node type (libs/plugin-cli/src/commands/validate.ts). See Author an agent and Use agents in a workflow.
  • It's static only. A green run confirms nothing about your behaviors, adapters, or live ports at runtime — only that the graph is well-typed and complete. Follow up with Simulate a workflow for a run against sample data, or Run and resolve for the real thing.

Add it to CI

Because the command exits non-zero on any error (libs/plugin-cli/src/cli.ts, main), add it as a plain step in your pipeline — no special integration required:

- run: npm run validate workflows/example.workflow.yaml

Run it once per workflow file you ship, alongside meridian-plugin i18n check (see Translate a plugin) and before building and publishing — a workflow that fails to validate has no reason to reach the registry.

75 documents6 sectionssource : /docs · généré au build