Skip to content

AI agent guide — authoring Event Script flows

At a glance

  • Read this if you are an AI agent asked to write or modify an Event Script flow. It is the context you need — you should not have to read the engine source.
  • Generate from rules. The flow grammar and its machine-readable form event-script-flow.json are the source of truth. Validate against them.
  • A flow is a YAML file compiled and validated by the engine (CompileFlows). Your job is to produce one that passes — the invariants are the contract.

How flows are deployed & triggered

A flow is not called directly:

  1. Write the flow YAML under src/main/resources/flows/.
  2. Register its file in the flows.yaml manifest.
  3. Map an HTTP endpoint to it in rest.yaml (service: 'http.flow.adapter' plus flow: {flow-id} — both keys are required) — or trigger it by event.

The engine compiles every registered flow at startup; a flow that violates the grammar fails to load. So correctness is checkable before runtime.

Generate deterministically

Use event-script-flow.json to look up the exact execution types, task fields, and mapping namespaces; use flow-grammar.md for the rules. Then verify:

Pre-write checklist - [ ] flow.id, flow.description, flow.ttl (≥1s), first.task, and tasks are present. - [ ] Every task has description, input, output, and a valid execution. - [ ] There is ≥1 task with execution: end. - [ ] next matches the execution type: decision ≥2; sequential/response/pipeline exactly 1; end/sink none. - [ ] fork has a join; pipeline has a pipeline list; a decision task's output maps a value into decision. - [ ] If the same process is used twice, each such task has a unique name. - [ ] Every source -> target uses valid namespaces (see the grammar's mapping section); ext: targets require external.state.machine.

Recipe

  1. Header: set flow.id, description, ttl, optional exception; pick first.task.
  2. Tasks: for each, set process (or flow://), description, input, output, execution, and the next/join/pipeline its type requires.
  3. Terminate: ensure at least one end task whose output builds the response.
  4. Wire up: register in flows.yaml; add a rest.yaml mapping if HTTP-facing.

Worked example

A minimal flow: receive a POST, call a greeting function, return its result.

flow:
  id: 'greeting-flow'
  description: 'Greet a caller by name'
  ttl: 10s
first.task: 'greeting.service'
tasks:
  - input:
      - 'input.body.name -> name'
    process: 'greeting.service'
    output:
      - 'result.message -> output.body.message'
    description: 'Build the greeting'
    execution: end

A decision example (branch on a function result):

  - input:
      - 'input.body.amount -> amount'
    process: 'risk.scorer'
    output:
      - 'result.high_risk -> decision'
    description: 'Score and branch'
    execution: decision
    next:
      - 'review.task'       # decision = true (high_risk) -> next[0] (first)
      - 'approve.task'      # decision = false            -> next[1] (second)

Scaffolding a project from the template

Start a new Event Script application from examples/composable-example and trim against this manifest:

File Role Trim?
pom.xml Build; set your own artifact/group ids keep (edit ids)
application.properties (main and test) App name, rest.server.port — give the test copy a distinct port. Caution: the example's test properties set snake.case.serialization=true, which silently renames every JSON response field if copied wholesale keep (edit values)
flows.yaml + flows/*.yml Your flow definitions — list every flow file you ship replace with yours
rest.yaml Binds URLs to flows via http.flow.adapter replace example routes with yours
event-over-http.yaml Outbound event-over-HTTP targets drop unless you call another app
Static/demo assets (errorPage.html, public/, app-log-context.yaml) Example plumbing drop unless used
Main class annotated @MainApplication App entry point keep (rename)

The minimal rest.yaml for a derived project is one http.flow.adapter entry per endpoint, each naming its flow: (plus the cors/headers blocks those entries reference). No function-route entries are needed — the flow adapter is the only HTTP boundary, which is exactly the point of this layer.

See also