---
title: "Authoring processes"
source: "https://docs.vertesiahq.com/processes/authoring"
markdown: "https://docs.vertesiahq.com/llms/processes/authoring.md"
---

# Authoring processes

A process is a JSON document. You can write it by hand, generate it with the **Studio Assistant** (which loads the `learn_process_design` skill), or iterate with an LLM in any editor and paste the result. This page covers both paths and the rules a valid definition must satisfy.

## The editor in Vertesia Studio

Studio gives you both a visual authoring surface and a code editor for the same definition:

- **Statechart / process board.** The visual view renders the process as cards and transitions. For larger definitions, use lanes and phases so the graph reads like a business process board instead of a raw dependency graph.
- **Inspector.** Clicking a node or transition opens the selected definition as YAML, plus readable fields such as type, tool, interaction, writes, phase, lane, and order.
- **Explain.** The inspector can call `sys:ExplainProcess` for the selected node, guard, or whole process. Use this before changing an unfamiliar definition.
- **Code tab.** The full definition is still editable as JSON/YAML when you need bulk changes, search/replace, or a reviewable diff.
- **Live parse.** Save is blocked while the document is unparseable.
- **Validation.** The save path runs the same server-side process-definition validation used by the `validate_process` tool. The Studio Assistant calls that tool directly while iterating.

The stored definition contract is explicit about its schema generation: native process definitions carry `format_version: 1`. Studio can prefill that field for a new draft, but the saved JSON keeps it present so future migrations have a stable boundary.

## Authoring with the Studio Assistant

The fastest way to go from "I want a process that does X" to a committed draft is the **Studio Assistant**. When you ask it to author or change a process, it loads the `learn_process_design` skill — which unlocks the process tools and grammar before it drafts anything:

- `learn_process_design` — loads the full process grammar and authoring rules before drafting.
- `list_tools`, `list_interactions`, `get_interaction` — confirm every name it references actually exists.
- `validate_process` — validates a draft JSON against the schema. The assistant iterates until clean.
- `create_process` / `update_process` — write or revise a draft in the catalog.
- `publish_process` — publishes a draft only after explicit confirmation.
- `start_process_run`, `get_process_run` — run and inspect a draft to test it end to end.
- `LayoutProcessDefinition` — improves visual metadata and transition labels without changing runtime behavior.
- `ask_user`, `plan`, `update_plan`, `think` — standard authoring scaffolding.

Its iteration loop is:

1. Discover — one `ask_user` batch with all open questions.
2. Load the grammar (`learn_process_design`).
3. Confirm tool / interaction names with the user before referencing them.
4. Draft the JSON.
5. Validate; fix every error; repeat until clean.
6. Create or update a draft. Do not set `status` or `version`; the server owns them.
7. Publish only after explicit confirmation. Include an optional publish comment when one is provided.
8. Summarize: node graph, inputs expected per run, outputs written to context, where humans intervene.

### Rules a valid definition must satisfy

These are the traps that catch first-time authors. They're codified in the `learn_process_design` skill and in the server-side validator:

1. **`node.input` is a runtime mapping, not a schema.**
   Use a bare template: `"invoice": "{{invoice_doc_id}}"`. Never put a JSON-schema fragment like `{ "type": "string", "editor": "document" }` inside `node.input` — that belongs on the target interaction's input schema.
2. **Document fields in `context.schema` must set both `format` and `editor`.**
   Use `{ "type": "string", "format": "document", "editor": "document", "description": "…" }`. Without `editor`, the Start Run modal falls back to a raw `store:` text field.
3. **Agent nodes return structured output, not control-flow tool calls.**
   Don't prompt worker agents to call process-control tools such as `set_context`, `transition_to`, or `skip_node` — those belong to the top-level supervisor, not node workers. The engine builds a `result_schema` from `node.writes` (plus a `_next_node` enum on multi-transition nodes) and constrains the child's output to match. See [Agent nodes](/processes/agent-nodes).
4. **Agent nodes get only the tools you declare in `node.tools`.**
   Skills (`learn_*`) are available so the agent can self-unlock capabilities.
5. **Agent nodes use `sys:ProcessAgentNode` by default.**
   It injects process orientation automatically. Override with `node.interaction` only when you need a custom interaction with its own input schema.
6. **Every node should have a `human_description`.**
   One or two sentences in plain language, distinct from the developer-facing `description`. Surfaces in observability. Undescribed nodes read as noise in the run report.
7. **Template references throw on missing fields.**
   Only reference context fields guaranteed to exist at the node.
8. **Every `condition` node must match a branch or have `default: true`.**
   No match and no default = runtime error.
9. **Context is capped at 64 KB serialized.**
   Store large payloads as artifact URIs and reference them.
10. **Do not overload collection fanout and fixed split/join in your own mental model.**
    Use `condition` to choose one path, `branch` to run a fixed set of named branches and join, and `foreach` to repeat a child body over a collection.
11. **Fanout child bodies do not own routing.**
    In `foreach` and `branch`, the child body should be `tool`, `interaction`, `agent`, or `process` only, with no nested `transitions` or `branches`.
12. **Any node that writes context must declare `writes`.**
    A non-empty `context_update`, human task answer, interaction result, agent result, collected `foreach` output, or collected `branch` output is rejected unless the parent node declares the target fields in `writes`.
13. **Supervisor overrides must be explicit.**
    `transition_to` follows declared exits by default, and `skip_node` requires `skippable: true`. Use `metadata.supervisor.allow_transition_override` or `metadata.supervisor.allow_skip` only for deliberate break-glass cases.
14. **Large processes need visual metadata.**
    Set `metadata.phase`, `metadata.lane`, and `metadata.order` on nodes when the graph has more than a straight-line handful of steps. The run UI uses those values for the Process Navigator and stable layout.
15. **`set_context` tool nodes use an `updates` envelope.**
    A process tool node that calls `set_context` must pass `{ "updates": { ... } }`, not raw context fields at the top level. The node still declares `writes` for every updated field.
16. **Use real interaction names.**
    For open-ended agent behavior, prefer `sys:GeneralAgent` or a custom agent interaction. Use `sys:ProcessAgentNode` for normal worker-agent nodes unless you have a specific interaction contract to override.

### Assignees

Human-task `assignee` is either a group reference (`group:`) or a concrete user id. `role:` is not supported — use `group:` instead. Leave unset if the task should be available to anyone who can see the inbox.

## Process board metadata

The process graph is readable only when authors describe the business structure, not just the transitions. For larger processes, model the visual board deliberately:

- **`metadata.phase`** is the stage of work: intake, AI review, approval, fulfillment, closeout.
- **`metadata.lane`** is the responsible actor or system: requester, AI assistant, policy, legal, procurement, outcome.
- **`metadata.order`** is the stable sort key inside that lane.
- **Transition and branch `label`** values are the business names of exits: approved, rejected, needs legal, amount >= 10000.

In the swimlane view, lanes render as vertical responsibility bands and nodes stack top-to-bottom within each lane. The main flow usually moves left-to-right across lanes; alternate paths can loop down or back across lanes. Keep lane names short and consistent.

Add layout metadata to every node in multi-stage processes:

```json
{
    "agent2_asset_generation": {
        "type": "agent",
        "title": "Agent 2 — Campaign Asset Generation",
        "metadata": {
            "phase": "Gate 2",
            "lane": "agent",
            "order": 10
        },
        "writes": ["asset_package"],
        "transitions": [
            {
                "to": "gate2_asset_review",
                "label": "Send package to review"
            }
        ]
    },
    "gate2_asset_review": {
        "type": "human_task",
        "title": "Gate 2 — Asset Package Review",
        "metadata": {
            "phase": "Gate 2",
            "lane": "review",
            "order": 20
        }
    }
}
```

Use a small, stable vocabulary for `lane`: business roles (`requester`, `legal`, `finance`, `claims_adjuster`) are better than implementation terms when humans need to read the board. Use implementation lanes (`ai_assistant`, `policy`, `tooling`) when ownership is genuinely a system.

Add `label` to transitions and branches when the guard is not obvious. The graph renders those labels on the edge, and the inspector shows the selected transition / branch as YAML.

### Improving layout with the assistant

When a graph becomes hard to read, use the Studio Assistant or the **Improve layout** action. It calls `LayoutProcessDefinition` with the current definition and optional notes about the current rendering. The interaction is constrained to visual fields:

- node `title`, `description`, and `human_description`
- node `metadata.phase`, `metadata.lane`, `metadata.order`, and optional `metadata.position`
- transition / branch `label`

It should not change runtime behavior: no node ids, node types, context schema, inputs, writes, tools, interactions, guards, targets, defaults, or tasks.

## Hand-authoring workflow

If you're editing directly in Studio or in a Git checkout:

1. Start from the schema: define `context.schema` first. Every field a node writes must exist here.
2. Lay out nodes and transitions. Name them for what they do, not how they work (e.g. `legal_review`, not `human_task_1`).
3. Fill each node's `human_description`. Ask yourself: what would a reviewer read here during observability?
4. For `agent` nodes, set the minimum `writes` scope — the result_schema is derived from it, so tighter writes means stricter agent output.
5. Add visual metadata and transition labels before the graph gets large. It is much easier to keep the run view readable while authoring than to repair it later.
6. Validate. Validation catches unreferenced nodes, branches with no default, and schema mismatches.
7. Save the draft. Run it against a known-good input. Iterate. Publish only when it is stable and the publishing user confirms.

## Versioning

Process definitions use draft/published versioning:

- New definitions are drafts at `version: 1`.
- Draft edits mutate the latest draft.
- Published revisions are immutable.
- Editing a published head creates the next draft head instead of changing the published version.
- Listings show only the latest/head revision by default; use the Versions tab or `all_versions` only when you need history.
- Publishing requires explicit confirmation and can include a comment.
- A historical version can be reverted into the current draft from the Versions tab.

Already-running processes continue to execute the definition that was passed into their Temporal workflow at start. Editing or reverting the catalog entry does not change an in-flight run.

## Next

- [Node types](/processes/node-types) — the reference for each `type`.
- [Observability](/processes/observability) — how to watch a run and debug what an agent node actually produced.