Use it for work that waits, loops, branches or reacts to events.
# review.yaml
statechart: review
input: { document: string }
initial: drafting
states:
drafting:
job: { script: ./draft.js, input: { document: "${input.document}" } }
on:
done: waiting
failed: gave_up
waiting:
ticket: verdict
timers: { too_long: 48h }
on:
verdict:
- { go: approved, when: "${event.data.ok}" }
- { go: drafting }
too_long: gave_up
approved: { end: done, output: "${states.drafting.output}" }
gave_up: { end: failed }// draft.js
return "A draft of " + input.document
corbie init --ceiling allcorbie start &corbie run submit review.yaml --input '{"document": "the plan"}'corbie run show <id> --ticketscorbie run reply <id> <ticket> '{"ok": true}' takencorbie run show <id> --wait output: "A draft of the plan"
review.yaml writes a draft, then waits up to 48 hours for a verdict from outside the run. <id> is the ID that corbie run submit printed, and <ticket> the value that corbie run show <id> --tickets printed.
Waiting for an answer
A ticket is a random value that lets whoever holds it answer once.
- On entering
waiting, the statechart issues a ticket namedverdict. corbie run show <id> --ticketsprintsverdictand the ticket's value.- Whoever has the value answers with
corbie run reply, from any client. - The answer arrives as the event
verdict, with the answer as its data.
- Only the client that started the run can print the ticket. It hands the value to whoever should answer.
- A ticket is answered once. Its rule takes only the ticket's answer: an event named
verdictfrom the parent, a child, the client or the state's own job finds no rule. A timer's rule takes only the timer. - A statechart run as a child hands the ticket to its parent with a send, such as
send: { to: parent, event: review_wanted, data: "${tickets.verdict}" }.
Fields
initialthe first state, needed where there is more than one
statesthe states, by name; a name is used once in a statechart
A state has these fields, and at most one of job:, jobs: and states:.
joba script or a machine, started on entry and cancelled on leaving
jobsseveral named jobs: the state is done when all are, and its output is each job's output by name
statesstates inside this one, with their own initial:
ticketthe name of a ticket the state issues while it is active
defer[<event>, ...]: events kept while the state is active and no rule takes them, and offered again once no active state defers them
timers{ <name>: <time> }: timers started on entry, cancelled on leaving
entry{ send: ... }, sent on entering the state
exit{ send: ... }, sent on leaving it
on{ <event>: transition }: where to go on each event, by its name
A final state has only these fields, so a last send goes on the transition into it.
enddone or failed: the state ends the state it is inside, or the statechart, as done or as failed
outputthe result, for the statechart or the state it is inside
The events under on: are these.
donethe state's jobs have finished, or a state inside it has ended
faileda job has failed
<timer>a timer of the state is due
<ticket>the state's ticket is answered
<other>an event from the parent, a child or the client, or a value a job emitted
Transitions
A transition is a state's name, or these fields, or a list of either. The first one whose when: is true is taken.
gothe state to go to; with none, the statechart stays and only sends
whena condition, one expression: "${event.data.ok}", in quotes between { }
sendone send or a list: { to, event, data }, where to: is parent, the state or named job whose job is a machine, or { key, machine }
A send to a key names the run that has the key, as in Definitions.
to: { key: "chat:${event.data.id}" }the run with the key
to: { machine: ./chat.yaml, key: "chat:${event.data.id}" }that run, or a new run of the machine
keythe key of the run
machinea file or a definition written in place, with no expression; a new run gets the event's data as its input
- An event that the active state has no transition for goes to the state it is inside, and so on outward.
- A job's failure that no state has a transition for fails the statechart.
- Any other event that no state has a transition for is dropped, unless an active state names it under
defer:. A chart keeps at most 100 events. - An
entry:send to the state's own child arrives once the child has started. Anexit:send cannot go to it. - When a client started the run, a send to parent is only written to the record. That client sends an event with
corbie run send <id> <event> <data>. - A ticket's name is used once in a statechart.
- A send cannot be named start, done, failed or timer.
to: { key }naming no running run is noted in the record as undelivered.
A timer's time is one of these.
90s, 48h, 7da length of time, from when the state is entered
"18:00"a time of day in UTC, at its next occurrence
"mon 09:00"a time on one day of the week
"09:00 Europe/London"a time of day in a named time zone
"${input.at}"one of these, from input, when the state is entered
What an expression reads
inputthe statechart's input
runthe run's id and key
states.<name>.outputthe state's last output
tickets.<name>a ticket of this state, or of a state it is inside
eventthe event that caused the transition: { name, data, from }
the event event.data event.from
done the job's output the state
failed { status, error, output } the state or named job
a timer nothing the state
a ticket's answer the answer the state that issued it
a value a job emitted the value the state or named job
an event from a child its data the state or named job
an event from the parent its data "parent"
an event from the client its data "client"
an event sent to the key its data the sender's run ID
The state that a transition enters reads the same event in its job's input, its entry: and its output:. A final state returns a ticket's answer with output: "${event.data}".
A statechart has no variables. To build a value over a loop, pass it through a script job.