Orchestration¶
A flow only says how its work went. What happens next is an orchestration's job: a state machine, configured per workspace, driven by the outcomes of the runs it hands out. "Testing failed, back to development" is a transition in an orchestration, not a field on the testing flow.
orchestrations:
- id: feature-delivery
mode: each # single: once | each: per unit of work
subject_type: feature # each: the work type whose items get an instance
initial: design
max_visits: 5 # entering one state more often than this = stuck
states:
- {id: design, flow: technical-design}
- {id: backend, flow: backend-development}
- {id: frontend, flow: frontend-development}
- {id: e2e, flow: e2e-testing}
- {id: release, flow: merge-git-branch} # an `act` flow
- {id: done, final: true, merge_branch: true}
transitions:
- {state: design, on: ok, to: backend}
- {state: backend, on: ok, to: frontend}
- {state: frontend, on: ok, to: e2e}
- {state: e2e, on: ok, to: release}
- {state: e2e, on: failed, to: backend}
- {state: release, on: ok, to: done}
stateDiagram-v2
[*] --> design
design --> backend: ok
backend --> frontend: ok
frontend --> e2e: ok
e2e --> release: ok
e2e --> backend: failed
release --> done: ok
note right of done: merges the knowledge-base branch
done --> [*]
Entering done first moves the feature's work item to its own done state, and
then merges the branch. A merge is refused while the work item isn't finished, so
the final state is the natural place for it.
| Piece | Means |
|---|---|
state with a flow |
work: entering it makes the flow's run ready; the run's outcome is the signal that leaves it |
| state without one | waiting for a signal from a person or an agent (approved, or any word) |
final state |
the instance is done |
merge_branch state |
entering it merges the instance's branch into main; a refused merge leaves the instance stuck with the reason. It can't have a flow of its own |
| transition | in state (* for any), on signal on, go to to |
| no matching transition | error stays in the state, ready to retry; any other signal leaves the instance stuck |
Instances¶
An instance is one walk through the machine. single makes one for the
project; each makes one per unit of work of subject_type (starting again picks
up items added since). Its status is ready, running, waiting, done, stuck
or cancelled.
Entering a state also moves the instance's unit of work to the state with the same id, when its type has one, so the work breakdown shows where each item is. When the work type is missing some of the machine's states, saving or starting the orchestration says so.
The worker loop¶
An AI connected over MCP works through an orchestration like this:
kb_orchestrate("start", id=...)creates the instances in the initial state.kb_orchestrate("next")opens the run for the oldest ready instance and returns an ordinary flow brief, ending with a note on what each outcome leads to.- The AI does the work, writes with
kb_write(..., run=...), then callskb_flow("finish", outcome="ok" | "failed"). The outcome moves the instance on. nextagain, until nothing is ready.
In a branch¶
An instance works in a branch: the open branch of its unit of
work when it's started, unless start names another (main forces main). Its
runs are briefed in that branch, and when the branch names a git branch, the brief
says to work and commit there and to report the commit when finishing.
A feature's delivery is then one machine: design, backend, frontend, end-to-end
tests (failed back to development), a step that merges the code into the main
line, and a final merge_branch state. The knowledge base takes the feature's
topics only once the code has the feature.
The Orchestration settings tab draws each machine as a diagram and lists its instances.