queue and concurrency
how admission control protects the machine, with one FIFO queue, a global cap plus per-provider caps, and the stuck-start guard.
heavy interactive TUIs at 100 times over will take a machine down. orcr protects against that with one queue and two caps. every agent run passes through the same admission path, so no caller can flood the box, whether it is you, a script, an agent spawning agents, or a loop.
every run enqueues
agent run is async, always. the verb's whole job is to validate, persist a durable row, and print <path> <uuid>. the server processes the queue and manages the rest of the lifecycle. every managed agent moves through the same statuses:
queued → starting → working → idle → … → endedthe queue worker promotes strictly FIFO by queue_seq, as an atomic store transaction: queued becomes starting only if the row is still queued and a capacity recount under the write lock shows free slots.
two caps
- global cap
concurrency.max(default 25). this is RAM protection. - per-provider caps beneath it, for example
claude = 10. any provider name is a valid config key.
promotion needs a free slot in both. a claude agent is promoted only when the global count is under 25 and the claude count is under the claude cap. see configuration for setting the caps.
stuck-start guard
starting means a concurrency slot is claimed and the pane and TUI are being created. if that creation makes no progress (no pane appears, no agent_session pointer is captured) within an internal bound (timings.max_starting, default 2m, reset by each progress marker), the row is marked ended with exit_reason: failed and stops holding its slot. otherwise one hung herdr call could block the whole queue forever.
this is internal plumbing, not a user timeout. the rule is deliberately simple: if the guard expires with no pane recorded, the agent is failed; a pane that shows up later, matched by its launch token, is closed. progress markers are herdr-reported facts only (pane created; agent_session pointer reported). transcript parsing is never a startup requirement. for the actual per-turn timeout, see the explicit --timeout in agent lifecycle and GC.
acting on queued and starting agents
waiton a queued agent waits through promotion and its first turn.killon a queued agent dequeues it, ending it withexit_reason: canceled.killon astartingagent setscancel_requested, checked between pipeline steps. once a pane exists, cancellation closes it and ends the row withexit_reason: canceled.
no work was done in either case, which is why both are canceled rather than killed. see status model and completion.
loops have a separate knob
loops do not share the agent queue's caps. a loop's --max-concurrency caps concurrent runs of that loop (default 1), which is a different axis: it limits how many copies of the command run at once, not how many agents each run spawns. the agents a run spawns still pass through the global and per-provider caps like any other agent run. see loops.
status model and completion
the one status vocabulary every surface shares, the exit_reason groups, and how orcr verifies that a turn is genuinely complete instead of trusting a stale idle.
managed vs unmanaged agents
what orcr creates and fully manages, what it merely tracks from your own herdr sessions, and the exact behavior contract that separates the two.