Reference
Setup files
A setup is everything built in the app as one YAML file: MCP servers, structs, procedures and flows, with every board, process, object, link and schedule on them. Export it to read, review or keep under version control; edit it or write one; import it back.
Exporting and importing
On the flow list, Export downloads spoletta-setup.yaml, with every flow, procedure, struct and MCP server. Import reads a .yaml, .yml or .json file back in and reports Imported <file>: N created, M updated. Both buttons are shown to admins (or to everyone when sign-in is off).
A setup leaves out what runs produce: runs, versions, undo history and the values stored objects kept. MCP sign-ins stay out too, so after importing a new server, each person connects to it again.
The shape of the file
spoletta: 1 # the format's version, always first
mcpServers: [...] # name, url, description
structs: [...] # objects defined once and used by flows
procedures: [...] # flows other flows run through procedure processes
flows: [...] # the jobs that runEvery field except names is optional. Export writes only what differs from the default.
What importing does
- A flow, procedure or struct the app doesn't have yet is created.
- One it has, by name, is made over to match the file. Processes, objects, states and attributes that match by name keep their identity, so past runs and stored values still point to them. Anything the file doesn't have is removed from its boards.
- Flows the file doesn't mention are left alone. Import never deletes a flow.
- The file is imported whole or not at all. A problem anywhere changes nothing, and the reply says what was wrong and where. A flow that is running can't be updated.
- A flow made over is saved as a version first, and the import is a step of its history: Cmd Z on the board undoes it. Undo restores the board only; repositories, schedules and MCP servers stay as imported.
Conventions
| Rule | What it means |
|---|---|
| Names are references | Processes name objects in takes and yields, and processes in next, onFailure and start.next. Process and object names are unique across a whole flow, inner boards included; state names within their object. |
| Same names need keys | If two share a name, give each after the first a key and refer to it by that. Simplest: name everything apart. |
| Unknown fields are refused | A typo fails the import rather than being dropped silently. |
| Positions are optional | x, y, width, route and at only round-trip a drawn board. Left out, new things are laid out from the start; existing things keep their place. Press Tidy up afterwards. |
| Shorthands | A link with nothing but its object is just the name; a state without a key or rule is just the name; kind defaults to agent. |
| Copies | copyOf: {flow: Ticket} uses a struct, copyOf: {flow: P, object: X} one of a procedure's objects. A copy lists none of its source's fields. |
Stored, shared and looked up
persistent: true on an object stores what runs generate of it, and unique: true on an attribute tells stored values apart. shared: true on a struct shares its records with every flow using it; it needs a unique attribute and can't go with persistent. An input link looks up stored records with a query:
takes:
- object: Review
query:
where: score >= 4 # a rule on the fields; all records when left out
state: approved # any state when left out
limit: 5 # 20 when left out, 100 at most
oldest: true # oldest first; newest first when left outSee Looking up stored records and Shared records.
A complete example
Release notes, drafted by an agent from the merged pull requests, approved by a person, revised on request and posted to Slack. Every construct in it is explained in People in the loop and Review loops.
spoletta: 1
mcpServers:
- name: Slack
url: https://mcp.example.com/slack
flows:
- name: Release notes
repositories:
- name: shop
url: https://github.com/acme/shop.git
base: main
start:
next: Pick release
objects:
- name: Release manager
human: true
- name: Release
attributes:
- name: version
definition: The version being released, such as 4.12.0.
- name: previous
definition: The version released before it, to compare against.
- name: Notes
states: [approved, changes requested, rejected]
processes:
- name: Pick release
kind: human
takes: [Release manager]
yields: [Release]
- name: Draft
prompt: |
Write release notes for the Release, covering every pull request
merged between the previous version's tag and the new one. Group
them under Features, Fixes and Internal. One line each, in plain
language a customer understands; link the pull request.
takes: [Release]
yields: [Notes]
- name: Approve
kind: human
takes: [Release manager, Notes]
yields:
- object: Notes
states: [approved, changes requested, rejected]
- name: Revise
prompt: Revise the release notes for the Release as the reviewer asks.
takes:
- Release
- object: Notes
state: changes requested
yields: [Notes]
- name: Post
prompt: Post the Notes to the #releases channel, as they are.
mcpServers: [Slack]
repositories: []
takes:
- Release
- object: Notes
state: approvedChanging a flow through its file
Export, edit, import
The flow is made over by name, keeping what matches, so its runs and stored values survive.
Rename in the app, not the file
Renaming a process, object, state or attribute in the file makes a new one and drops the old. A stored object's values outlive it, but the new one doesn't see them, so duplicate detection on a unique attribute starts over. When stored values matter, rename on the board.