Agent work queue
Outcome cc-realtime-queue (Eric 2026-10-06): org owners see and reorder what their org's agents work on next, live, and
the agents follow that order.
What it is
One row per queued ticket of an org (agent_work_queue, migration 10880). Agents enqueue tickets; owners and admins
reorder, pin, pause and resume them in Operations > Agent queue; agents read the order before launching their next
item. Every change is audited (agent_work_queue_audit) and wakes the org's Operations stream, so every open
Operations page re-reads within about a second (500 ms coalescer + one snapshot read), with no reload.
Kill switch: platform setting agentQueue.enabled (default on). Off: every queue route returns 404, the MCP tools
are not registered, the panel renders nothing.
Order rule (one implementation, packages/shared/src/agent-queue.ts)
- Pinned items first, then by
rankascending (ranks are dense 1..n per org). - Next = the first item in that order that is not paused, not blocked, and whose ticket is not closed or archived.
- A paused item keeps its place; it is never next.
Status rule (liveness)
An in-progress ticket reads Working only while its newest liveness proof is at most 5 minutes old, Stalled?
up to 15 minutes, otherwise Queued (never "working"). Proofs are the shared work-liveness proofs
(packages/shared/src/work-liveness.ts, route apps/api/src/modules/work-liveness/routes.ts): boxes post process/transcript/commit proofs once a minute; ticket comments or
updates by the item's agent key count too. Why 5 minutes: boxes publish every 60 s, so 5 minutes tolerates four lost or
late batches (network blip, a box under load, a long tool call with no log growth) before the label changes, while still
dropping a dead agent to Stalled? within 5 minutes and to Queued within 15. A shorter window (e.g. 2 minutes) would
flip a healthy agent to Stalled? on a single late batch; a longer one lets a dead agent read Working for most of a
quarter hour. (Rationale, not a measurement: no flapping data has been collected yet.)
Who may do what
| Caller | Read queue / next | Enqueue | Reorder / pin / pause | Remove | Audit |
|---|---|---|---|---|---|
| Owner/admin (signed-in person; reorder/pin/pause and audit refuse every key and user token) | yes | yes | yes | yes | yes |
| Member (person) | yes (projects they can see) | yes | no (403) | no | no |
| Bot API key of the org | yes | yes | no (not on the key allow-list, and refused again in the handler) | only items it enqueued | no |
| Person-scoped key, other org | 403 / 404 |
The order is a person's decision: an agent cannot promote its own work over what its owner chose.
API
GET /v1/orgs/{orgId}/agent-queue->{ orgId, version, canReorder, items[], nextIssueId, generatedAt }GET /v1/orgs/{orgId}/agent-queue/next->{ next | null, paused: issueId[], version }POST /v1/orgs/{orgId}/agent-queue/items{ issueId, worker? }-> 201 created / 200 already queued (keeps its place)PATCH /v1/orgs/{orgId}/agent-queue{ ifVersion?, move?: { issueId, to: top|up|down|bottom }, order?: issueId[], items?: [{ issueId, pinned?, paused? }] }move(what the Operations panel sends) moves ONE item relative to its neighbours in the caller's view, within its pin group; only that item and the visible items it passes change place.ordermoves the listed ids to the front of the caller's view in that order; the caller's other items keep their relative order behind them.moveandordertogether are 400. Hidden items never move: a reorder writes the caller's items back only into the places they already held, so an admin with staffed-only project access cannot push an item they cannot see up or down (owner order[H, a, b, c], that admin sees[a, b, c]and movesadown: result[H, b, a, c]). A staleifVersionis 409 and changes nothing. Any id not in this org's queue, or in a project the caller cannot see, is 404 for the whole patch.DELETE /v1/orgs/{orgId}/agent-queue/items/{issueId}-> 204GET /v1/orgs/{orgId}/agent-queue/audit(owners/admins)
MCP (bot keys): agent_queue_next, agent_queue_list, agent_queue_enqueue. No reorder tool, on purpose.
Contract every agent follows
Before launching any new work item an agent (orchestrator dag-run, QA Commander stage runner, persona job runner):
- calls
GET .../agent-queue/next(oragent_queue_next) with its org bot key; - launches only
next.issueId; ifnextis null it launches nothing and retries on its normal tick; - re-reads right before the launch side effect (a pause that lands between pick and launch must win);
- publishes liveness proofs for what it runs (POST
/v1/orgs/{orgId}/work-liveness, once a minute); - removes the item (
DELETE) or closes the ticket when the work is done.
If the queue route is unreachable the agent falls back to its own local order and says so in its log; it never treats an
unreachable queue as "no pauses". The harness side of this contract is lane harness-queue-sync
(agent-rules harness/queue-sync/).
- API reference:
/v1/openapi.json. - MCP tools: tool reference.
- Runbook: working with people.