Expert Queue โ Item States
Reference: the four states an Expert Queue item moves through, and what each one means. Last updated: 2026-06-02 Audience: Experts, Engineering, AM, QA Surface:
/workspace/queue(Expert workspace) Related: Expert Queue feature, Conversation Status Lifecycle, ADR-007 Expert Access Scope
TL;DR โ the one question that distinguishes themโ
Every state answers "who is being waited on right now?"
| UI label | DB value | Who's it waiting on? | Terminal? |
|---|---|---|---|
| Pending | pending | The Expert (review + reply) | No |
| Awaiting | awaiting_client | The client (their reply) | No |
| Resolved | resolved | Nobody โ closed | Yes |
| Deferred | snoozed | Nobody for now โ snoozed | No |
One-liner: Pending = your turn ยท Awaiting = client's turn ยท Resolved = done ยท Deferred = look at it later.
State machineโ
new escalation / client reply / defer expiry
โ
โผ
โโโโโโโโโโโโโโโโ
โ Pending โ โ waiting on Expert
โโโโโโโโฌโโโโโโโโ
โโโโโโโโโโโโโโผโโโโโโโโโโโโโ
Expert replies Defer Resolve
โ โ โ
โผ โผ โผ
โโโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโ โโโโ
โ Awaiting โ โ Deferred โ โ Resolved โ
โ (client) โ โ (snoozed)โ โ (final) โ
โโโโโโโฌโโโโโโ โโโโโโฌโโโโโโ โโโโโโโโโโโโ
โ โ
client replies defer TTL expires
(automatic) (cron, automatic)
โ โ
โโโโโบPendingโโโ
1. Pending โ pendingโ
Meaning: the ball is in the Expert's court. The item is waiting for an Expert to review the AI draft and respond.
Entry triggers (three ways in):
- A conversation is escalated into the queue (
createQueueItem()) - A client replies while the item was
awaiting_clientโ auto-flips back topending - A
deferreditem's timer expires โ cron resurfaces it topending
Transitions out:
- โ Awaiting when the Expert sends a reply (
POST /expert-queue/:id/respond) - โ Resolved when the Expert resolves it (
POST /expert-queue/:id/resolve) - โ Deferred when the Expert defers it (
POST /expert-queue/:id/defer)
2. Awaiting โ awaiting_clientโ
Meaning: the ball is in the client's court. The Expert has already replied and is waiting for the client to respond. (UI may label this "Awaiting Reply".)
Entry trigger: an Expert sends a reply via POST /expert-queue/:id/respond. The atomic claim flips the item to awaiting_client and updates the underlying conversation.
Key constraint: an Expert cannot respond again to an awaiting_client item. They must wait for the client to re-trigger it back to pending (a force=true path is planned but not implemented). This prevents double-sends.
Transitions out:
- โ Pending automatically as soon as the client sends any reply
- โ Resolved / Deferred via explicit Expert action
3. Resolved โ resolvedโ
Meaning: the conversation is closed. The Expert has taken final action; no further work is expected. This is a terminal state.
Entry trigger: an Expert explicitly resolves via POST /expert-queue/:id/resolve (or PATCH /expert-queue/:id/status). Stamps resolvedBy + resolvedAt.
Terminal guard: responding to a resolved item returns 409 Conflict โ re-opening it would overwrite the resolved state and lose the audit trail. Only an admin can manually move it back via PATCH /status (rare).
Transitions out: none automatic (terminal).
4. Deferred โ snoozedโ
Meaning: temporarily set aside. The Expert wants to revisit it later, so it's hidden from the active queue until the snooze timer expires.
โ ๏ธ Naming mismatch โ read this. The UI says Deferred, the action endpoint is
/defer, but the DB value issnoozed. When inspecting logs or the database,snoozed== the UI's "Deferred".
Entry trigger: Expert calls POST /expert-queue/:id/defer with optional deferHours (default 24h). Sets deferredUntil and removes it from the active list.
Auto-resurfacing: a BullMQ scheduled job (snooze-resurface) periodically finds items whose deferredUntil < now() and flips them back to Pending.
Transitions out:
- โ Previous status when the Expert manually reverts (
POST /expert-queue/:id/revert). The status held immediately before deferral is captured inprevious_statusat defer time and restored exactly โ a deferredawaiting_clientitem reverts toawaiting_client, notpending. Falls back to Pending whenprevious_statusis null (items deferred before the column existed). This is the only manual status change the Expert workspace UI offers (#2700). - โ Pending automatically when the defer TTL expires (cron). Auto-expiry intentionally lands on Pending regardless of
previous_statusโ after the snooze window the item resurfaces for Expert attention.
Manual status changes & the PATCH /status override (#2700)โ
The Expert workspace deliberately exposes no free-form status picker. Every transition happens through a dedicated, state-machine-validated action:
| To changeโฆ | Use | Allowed from |
|---|---|---|
| โ Awaiting | POST /respond | Pending (automatic on Expert reply) |
| โ Resolved | POST /resolve or PATCH /status (status=resolved) | Pending / Awaiting |
| โ Deferred | POST /defer | Pending / Awaiting |
| Deferred โ previous | POST /revert | Snoozed only |
PATCH /expert-queue/:id/status validates the transition server-side: a non-admin caller may only move an item โ resolved (the Resolve button). Invalid manual transitions โ pending โ awaiting_client, anything out of resolved (terminal), or out of snoozed (use /revert) โ return 409 Conflict. SuperAdmin bypasses the validation for the rare documented override (e.g. reopening a resolved item). Before #2700 the UI offered a dropdown that allowed all of these; the dropdown was removed and the server now enforces the rules regardless of client.
Naming reference (UI โ endpoint โ DB)โ
| UI label | Action endpoint | DB value |
|---|---|---|
| Pending | (default on create) | pending |
| Awaiting | POST /respond | awaiting_client |
| Resolved | POST /resolve | resolved |
| Deferred | POST /defer | snoozed |
Key endpointsโ
| Endpoint | Effect |
|---|---|
GET /expert-queue?status=pending|awaiting_client|resolved|snoozed|all | List by status |
POST /expert-queue/:id/respond | Pending โ Awaiting |
POST /expert-queue/:id/resolve | Pending/Awaiting โ Resolved |
POST /expert-queue/:id/defer | โ Deferred (snoozed), default 24h; captures previous_status |
POST /expert-queue/:id/revert | Deferred โ previous status (manual un-defer); 409 if not snoozed |
PATCH /expert-queue/:id/status | Validated: non-admin โ resolved only; SuperAdmin override bypasses (see #2700 above) |
Source of truthโ
- Entity + status column:
api/src/common/entities.ts(ExpertQueueItem) - State transitions:
api/src/expert-queue/expert-queue.service.ts - Client-reply auto-reopen:
api/src/conversations/conversations.service.ts - UI labels:
frontend/src/components/ui/TicketStatusBadge.tsx,frontend/src/app/workspace/queue/