Documentation / Conception / Guides pratiques / Simulate a workflow

Simulate a workflow

Simulation runs the current graph — even unsaved — against a forged event, through the same interpreter as production, but with no durability and no side effects. Use it to check a workflow's logic before you save or run it.

In the editor

Press ▶ Simulate in the workflow editor. Choose the event and the scenario, then read the result overlaid on the graph.

The event is prefilled from the trigger: its type, the example payload (the declared event's example for a trigger that references one), and a placeholder identifier <kind>-exemple for each context kind the example does not carry: the required kinds of a declared event, otherwise every kind the trigger establishes.

The Subject field names a real subject: its kind, among the kinds the trigger establishes (every registered kind for a trigger whose context comes with the call), then its identifier. That subject's memory seeds the simulated memory, and its live record seeds the record port of its kind when the kind has one. The field is absent when the trigger establishes no kind. A kind that no loaded plugin registers is refused (options.subject : sorte de contexte « … » inconnue); a record that cannot be read answers 502.

When the subject changes, the event's identifier field of that kind follows it, provided the event is valid JSON and the field is absent, still holds the value the prefill wrote (the <kind>-exemple placeholder or the value of the event's example), or holds the previous subject's identifier. A value you typed in the event stays. Clearing the subject puts the prefilled value back.

The simulation runs this workflow only, and it does not evaluate the trigger filter: the graph runs on the event whatever the filter says. Test the filter in the trigger inspector's Filter section instead, which evaluates it on the example event (Kept by this filter. or Not kept by this filter.). The other workflows that start on the same event are listed there too ({event} events also start:); see Choose which events start a workflow.

What the sandboxes do (each port's sandbox is contributed by the plugin that owns the port):

  • Seeded reads — each port reads from its seed: an inline snapshot you supply per port, or, for a record port, the live record of a real subject (via the record service, in-memory or FHIR depending on the instance).
  • Captured writes — addObservation / addCondition / addNotification … are listed as "what would have been written" but never applied. The sandbox does read back its own writes, so read-after-write within a run stays consistent.
  • Sandboxed memory — memory nodes read from a seed and their writes and forgets are captured as effects (port: memory). The seed holds the workflow's real workflow-global items and, with a subject filled in, that subject's real memory, read-only, versions included; a conditional write is checked against the seeded version.
  • Scenario-driven validations — each human-validation node is decided per the chosen scenario (accepted / rejected / timed-out). This is how you exercise the escalation branch without waiting for an SLA.
  • Simulated agents — a deterministic example output of the declared type, with no LLM call, unless you tick "real agents".

The result appears as an overlay on the graph (✓ ran / ⤼ skipped, with the reason and the inputs/outputs on hover). The side panel lists the captured writes, the validations presented, and the trace.

When the trigger claims a declared event type, the forged event is checked against that type's contract (gate EVENT_CONTRACTS), whatever the event's type: under enforce a non-conforming event is refused, under observe the simulation runs; in both cases the panel lists the issues. The rules are those of the ingestion check.

Via the API

POST /api/simulate
{
  "spec": { … } ,            // or "file": "my-workflow.workflow.yaml"
  "workflow": "my-workflow.workflow.yaml",  // optional: identity of an inline spec,
                                            // addresses its memory of scopes workflow
                                            // and workflow-global
  "event": { … },
  "options": {
    "seeds": {                // one opaque seed per port name (shape defined by
      "patient": { … },       // the owning plugin's sandbox — e.g. a partial
      "prescription": [ … ]   // PatientProfile, a prescription list…)
    },
    "subject": { "kind": "patient", "id": "…" },  // a REAL subject of a registered kind:
                                                  // its memory seeds the simulated memory,
                                                  // its record the record port of its kind
                                                  // (when the kind has one)
    "memory": [               // memory seed, wins over the real memory
      { "scope": "workflow", "workflow": "my-workflow", "subject": { "kind": "patient", "id": "…" },
        "key": "…", "value": …, "type": "Decimal", "version": 12 },  // type and version optional
      { "scope": "workflow-global", "key": "…", "value": … }          // no subject for this scope
    ],
    "validations": { "node-id": "accepted" | "rejected" | "timeout" },
    "agent": "mock" | "real"
  }
}

POST /api/simulate adds an eventId (sim-<id>) when the event has none and an occurredAt when it has none or a blank one, and nothing else: no placeholder identifier and no filter evaluation. Include the identifiers of the event's required context kinds (patientId, encounterId…) yourself; the editor adds them for you.

The response is a report: { status, nodes, effects, validations, logs, durationMs, contract }, where nodes are the per-node observations, effects the captured writes, logs the engine's structured trace, and contract the event's contract issues ({ issues }, or null). Under EVENT_CONTRACTS=enforce, a non-conforming event gets 400 { error, issues } instead. A port whose plugin contributes no sandbox is absent in simulation: a node that needs it fails with Port « … » non branché, visible in the report.

Next

Multiple entries

For a workflow with several entries, choose the entry in the simulation panel. Its filter result is reported separately; the selected branch runs even when the filter is false. The other branches stay inactive. See Start a workflow from several events.

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