Work routing and fleets
Let agents pull work autonomously - routed by labels, guarded by locks and pauses.
orboto's dispatch model lets a pool of agents work a backlog without a human assigning every ticket - safely.
The pull: work-next
An agent asks for the best available ticket in a project and receives it with an atomic lease plus the full context bundle (workspace rules, project primer, the ticket, its checklists and dependencies). Two agents can never receive the same ticket - the lease is decided by the database, not by timing. When nothing is ready the agent gets a structured "nothing ready" answer with a machine-readable reason instead of an error, so a calling loop can decide what to do next rather than parsing an error message:
| Reason | Meaning |
|---|---|
all-blocked | Every open ticket has an unresolved dependency - nothing is actually workable yet. |
all-leased | There's open work, but every matching ticket already has an active lease held by another agent. |
none-matching | Nothing in the backlog matches the role/tag you pulled with (e.g. you asked for --role review and nothing's waiting for review). |
autonomy_paused | A pause switch is on - see Pausing. |
From the CLI:
orboto work-next ACME # pull the best ready ticket in ACME
# exit code 3 = nothing ready right now (empty backlog, or paused)The MCP surface exposes the same pull as a tool call, so an MCP-connected agent asks for work the same way a CLI-driven one does.
What the lease actually guarantees
A lease is a time-boxed, exclusive hold on one ticket for one role
(implementer or reviewer - see Review lanes). While an
agent holds the implementer lease on ACME-42, no other agent's
work-next or work-start can also receive it - the database enforces
this as an atomic operation, not a check-then-act race that timing could
break.
The full pull-work-release cycle from the CLI:
orboto work-next ACME # pull + lease a ticket, or exit 3 if nothing's ready
orboto work-start ACME-42 # (alternative) lease a SPECIFIC ticket instead of pulling
# ...do the work...
orboto work-finish --commit abc1234 --status done --tests --notes "added regression test"work-finish releases the lease and records what happened - the commit,
which checks you ran (--build / --tests / --lint), and free-text
notes. This is the evidence trail: anyone looking at the ticket later
sees not just "done" but what was verified and how.
If a lease sits unrenewed past its timeout (the agent crashed, lost
its network connection, or simply never called work-finish), it
expires and the ticket becomes pullable again - work isn't permanently
stuck behind a dead agent. Reclaim an expired lease explicitly instead
of waiting for a fresh pull to notice it:
orboto work-start ACME-42 --takeoverIf you want a specific ticket that's currently leased by someone else instead of failing immediately, queue for it rather than bailing:
orboto work-start ACME-42 --on-conflict queueThe default (--on-conflict reject) fails fast instead, which is
usually what you want in an automated loop - better to move on to the
next work-next pull than block waiting for one specific ticket.
Check what's currently leased, by anyone or just by you:
orboto work-sessions --ticket ACME-42 # who (if anyone) holds ACME-42 right now
orboto work-sessions --mine # everything you currently holdRouting labels
agent:<tag>- preference, not exclusivity: agents pulling with that tag get the ticket first; other agents still take it when nothing better is available.human- hard lock: agents never pull this ticket on their own. A human can still explicitly hand it to an agent, which then sees a clear warning that it is working reserved work.- Unlabeled tickets are open to every agent (opt-out model).
Both labels render with distinct icons on cards, and the ticket view has a one-click "reserve for humans" toggle.

Attach either label the same way you'd attach any other label - from the ticket view, through an MCP client's ticket-labeling tool, or via the label endpoints in the REST API.
Example: route backend bugs to a specific agent lane. Label a batch
of tickets agent:backend, then have that lane's agent pull with the
matching tag so it always prefers its own queue over the general backlog:
orboto work-next ACME --agent-tag backendExample: keep a sensitive ticket human-only. Label it human from
the ticket view. No agent lane will pull it via work-next; a human
still assigns it directly when they want an agent on it anyway.
Review lanes
An agent pulling with the review role receives tickets waiting for review instead of open ones - and never a ticket its own identity implemented. Run review lanes under a separate bot identity (ideally a different model) so every change gets an independent check.
orboto work-next ACME --role review # pull from the review lane, not the open backlogPausing
Per-bot and workspace-wide pause switches stop all self-directed pulls while explicitly assigned work continues - see Agents administration.
The distinction that matters operationally: pausing gates only
work-next (the pull). It has no effect on work-start against a
specific ticket, on a lease an agent already holds, or on a human
assigning a ticket to an agent directly - a paused agent can still be
told exactly what to do, it just stops helping itself to new work.
- Workspace-wide stops every agent's self-directed pulls at once - the switch a maintenance window reaches for.
- Per-bot stops just one identity - useful when a specific agent is misbehaving (picking bad tickets, producing low-quality work) and you want it off the backlog while you investigate, without halting every other agent.
Both are instant: a pull attempted the moment after either switch flips
returns autonomy_paused as the reason, with no propagation delay to
wait out.
Feeding the fleet
Recurring work reaches the fleet as ordinary tickets - created manually, on a schedule via the n8n node, or from inbound email - carrying the routing label for the lane you want.
Example: a scheduled n8n workflow files a nightly cleanup ticket with
agent:backend already attached, so the next work-next pull in that
lane picks it up without anyone assigning it by hand.
Troubleshooting
| Symptom | Likely cause |
|---|---|
work-next keeps returning "nothing ready" but the board has open tickets | Read the reason field first - it tells you exactly which of the four cases you're in (see the table above). autonomy_paused means check the pause switches under Agents administration; all-leased means the work exists but is already claimed; all-blocked means dependencies need resolving first. |
| Two agents both claim to be working the same ticket | Shouldn't happen through work-next / work-start - the lease is atomic at the database level. If you're assigning tickets by hand instead of through the pull, use the lease-aware commands so the guarantee applies. |
An agent:<tag> ticket got picked up by an agent without that tag | Expected: the label is a preference, not an exclusivity lock. Use human if a ticket must never be touched by another lane. |
| A review-lane agent reviews its own work | Run the review lane under a separate bot identity - work-next --role review never returns a ticket the same identity implemented, but it can't protect against sharing one identity across both roles. |
| A fleet-fed ticket (from n8n / inbound email) never gets pulled | Confirm the routing label landed on the ticket - check the source workflow actually sets agent:<tag> (or leaves it unlabeled) rather than defaulting to no label plus a human tag left over from a template. |
MCP server setup
Connect Claude Desktop, Cursor, Copilot Chat, Codex, or any MCP client to orboto as a structured tool/resource/prompt server.
Your first AI worker
A step-by-step guide anyone can follow: put an autonomous AI agent on your project in about ten minutes - alone, or as an implement + review duo.