Orchestration
Real work rarely fits in a single loop. You fan out to several workers, some of them depend on others, one fails and you have to decide whether the rest can still run, and partway through you discover three more tasks you didn't know about at the start.
Orchestration in flow-state-dev is the machinery for exactly that: many units of work, coordinated. It lives in one package, @flow-state-dev/orchestration, and it's built in layers you can enter at whatever height your problem needs.
The layers
Patterns (patterns) — supervisor, parallelTasks, planAndExecute, …
▲
Task board (orchestration) — a concurrent drain over a collection
▲
Task substrate (orchestration) — the Task record + TaskCollection + dispatchers
Read it bottom-up.
Task substrate. A Task is one unit of work: a goal, a status, optional dependencies, a typed input and output. A TaskCollection holds a set of them with safe concurrent access: two workers can never claim the same task. It's the foundation. Everything above is a way of putting tasks into a collection and taking results out. See Task substrate.
Task board. A board runs a pool of workers that pull ready tasks from a collection, respect dependencies, and drain until nothing is left to run. It's the orchestration primitive. When you need concurrent, dependency-aware execution and none of the higher-level patterns fit, you reach for the board directly. See Task board.
Patterns. The common shapes are already built for you, in @flow-state-dev/patterns. parallelTasks fans out once and collects. supervisor adds a review step before each result is written back. planAndExecute re-plans across drains. Each is a thin composition over the task board. Start here; drop to the board only when your coordination doesn't match one of them.
Who does the work. A board worker is any block you register on the board: a generator with a persona and tools, or a plain handler. See Agents.
Hired workers are Workforce: describe a roster, hire it, and register the copies. Those workers are addresses you open a session against, and names a task can be given to.
Coordinators. A coordinator is a worker that hands each message to other workers, its delegates, and answers come back into the same conversation under each delegate's name. Work that has to be done rather than answered, it files as a task on the conversation's own board. See Coordinators.
A worker built for coding agents. Beside the patterns sits @flow-state-dev/harness-manager: a board worker that turns a task into a supervised coding run — its own checkout, a verdict read before the task settles, a question it can ask a person, and the coding agent itself as a slot you fill. See Harness manager for the worker, and Coding agents for the agents it can drive and the handle they hand back.
Two ways to drive a board
Which path you take depends on who decides the shape of the work.
Code-first. You define the board in TypeScript: its workers, its initial tasks, its dependency graph. Then you mount it as a step in a flow. You know the shape of the work up front, or your code decides it. This is the taskBoard(...) factory.
Agent-first. A generator decides what work to run at runtime. Give it the taskTools capability over a board (eight tools, addTask and completeTask among them) and it plans the work as tasks, with an assignee and deps on each, for the board's workers to take.
Both paths drive the same substrate, and a worker can enqueue follow-up work mid-run either way. In a code-defined board, a worker resolves the collection with getOrCreateTaskCollection and calls addTask. A model calls the addTask task tool.
Where work blocks, and where it doesn't
Dependencies are how you say "this can't start until that finishes." A task lists the ids it depends on, and the default dispatcher won't hand it to a worker until every dependency has completed. That's how a synthesizer waits for its analysts without any glue code on your side.
A board also needs a rule for when to stop. By default it drains until either every task completed or nothing runnable is left (a failure upstream can strand the tasks that depended on it). You can also tell a board to wait indefinitely for work that arrives from outside, or to stop on a task that is waiting for a person and pick it up when the answer arrives. Those termination modes are covered in Task board, and the human-wait one in Waiting on a person.
A drain normally runs inside the request that mounted it, so every worker's task finishes before that request does. The exception is a worker declared to run in a dispatch run instead of inline. The board claims a task for it as usual, hands the task to that run, and moves on. The drain can finish while the task is still running, and the run records the result when the worker is done. See Workers that dispatch for what such a board requires, and Work that outlives the turn for that path beside the other background-work surfaces.
Start here
- Task board — the primitive, its termination modes, and when to reach for it.
- Configuration — field catalog for
taskBoard,goalSeekLoop,createSkillsLibrary, andskills.with. - Build a research team — a guide that goes from an empty flow to a running multi-agent board.
- Task substrate — the
TaskandTaskCollectioncontracts underneath it all.
Related pages
- Patterns overview — the coordination patterns built on the task board.
- Agents — board workers and personas.
- Workforce — describing workers in folders and hiring them as addressable flow copies. A hired worker is an address you open a session against, and a name you can give a task to.
- Coordinators — a worker that hands each message to its delegates, and files tasks for them.
- Harness manager — a board worker that drives a coding agent to a settled verdict.
- Coding agents — the coding agents that worker drives, and the run handle they return.