Skip to content
Back to skills

Using The Queue

ASecurity

Use to track your committed work or see who is doing what - 'what is on my plate', 'promote a backlog bead', 'assign work', 'nothing pending'. Also run it before concluding you are done, to reconcile committed work against the queue.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
testingrustgosqlgitsecurity

Works with

  • cli

Security analysis

A100/100

Scanned November 7, 2026

npx -y skills add thrum-team/thrum-pro --skill using-the-queue --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Using The Queue?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Using The Queue
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/thrum-team-using-the-queue/badge)](https://www.skillsdirectory.com/skills/thrum-team-using-the-queue)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: using-the-queue
description: "Use to track your committed work or see who is doing what - 'what is on my plate', 'promote a backlog bead', 'assign work', 'nothing pending'. Also run it before concluding you are done, to reconcile committed work against the queue."
---

# Using the Thrum Queue

## Overview

`thrum queue` is a per-agent, local-only, daemon-resident list of the work an
agent has committed to. One queue per `agent_id`; you EDIT your own, anyone may
READ any queue. It is not beads and not a message inbox - beads is the shared
backlog of what COULD be done; the queue is the private, short-lived list of
what an agent IS doing right now, and it is gone when the agent is deleted.

**One rule governs everything else: the queue never crosses agents by itself.**
You only ever write your OWN queue. Assigning, handing off, and cross-box status
all travel as MESSAGES; the receiving agent adds the work to its own queue. No
command writes another agent's queue.

## When to Use

- You need a task list of what you are actively working on.
- Someone messaged you an assignment and you want it tracked as committed work.
- You are promoting a backlog item (a bead / thrum-issue) into active work.
- You are a coordinator/orchestrator routing work and want to record who you
  handed a bundle to.
- You need to see what another agent is working on or how loaded it is.
- You are moving a task between pending / in_progress / blocked / done, or
  clearing finished work out.

## Mental Model

- **Bundle** = one unit of committed work (the only top-level record). Carries a
  title, optional backlog `refs`, a status, a priority, and provenance.
- **Item** = a short checklist entry EMBEDDED inside a bundle. Items are never
  standalone and never linked across bundles. There is no nesting - a bundle
  holds items, nothing deeper.
- **`title` carries the whole content** - there is no body field (40 KB cap).
- **Local-only**: `.thrum/var/queue/<agent>.jsonl`, projected to SQLite, rebuilt
  on cold boot, included in `thrum backup`. Never peer-synced.

## Who-is-doing-what (read this if that is your goal)

Three distinct fields, do not conflate them:

- **`assigned_by`** - provenance: which agent assigned this to me. Auto-filled
  when a bundle/item is created with `--from-message` (from the message author).
  Answers "who gave me this."
- **`assigned_to`** - routing metadata I put on MY OWN bundle via
  `thrum queue assign`. It is informational only. **It does NOT write the
  named agent's queue and does NOT hand the work off.** Answers "who I intend
  this for." The actual handoff is a separate `thrum send`; that agent runs
  `thrum queue add --from-message` to pull it into its own queue.
- **`thrum queue list --agent <id>` / `show <id> --agent <id>`** - read another
  agent's queue directly. Answers "what is agent X working on / how loaded is
  it." Read-only, no permission gate.

## Quick Reference

| Command | Does |
|---|---|
| `thrum queue add --title "..."` | Create a bundle in your queue; the title may be the single quoted argument instead (`thrum queue add "..."`), not both |
| `thrum queue add --from-message <msg_id>` | Special case: lift ONE specific message you received (content->title, author->assigned_by, refs->refs). Use the exact msg-id you're acting on - never head-1 of the inbox |
| `thrum queue add --title "..." --ref bead:<id>` | Promote a backlog item into active work |
| `thrum queue list [--status <s>] [--stage <s>] [--agent <id>] [--assigned <id>]` | List your queue (or another agent's); `--status`/`--stage` AND-combine when both given; `--assigned` filters to bundles assigned to that agent |
| `thrum queue show <bundle-id> [--agent <id>]` | Full detail for one bundle |
| `thrum queue start <bundle-id>` | -> in_progress (clears block_reason) |
| `thrum queue block <bundle-id> [--reason "..."]` | -> blocked (records the reason; `--reason` required) |
| `thrum queue done <bundle-id>` | -> done (TEMPORARY waypoint, not deleted; `start` reopens it; clears block_reason) |
| `thrum queue wait <bundle-id> [--reason "..."]` | -> waiting (alias for `queue update --status=waiting`; `--reason` optional) |
| `thrum queue ready <bundle-id>` | -> ready (alias for `queue update --status=ready`) |
| `thrum queue update <bundle-id> [flags]` | General partial update: `--status`, `--stage`, `--reason` (status_reason), `--title`, `--ref` (repeatable, replaces existing refs), `--priority`, `--assigned-by`, `--assigned-to-remove` - at least one required; `--status=blocked` requires `--reason` |
| `thrum queue drop <bundle-id>` | DELETE bundle + all its items, any status. This is the normal close-out: drop a bundle in the same turn its work lands |
| `thrum queue assign <bundle-id> <agent-id>...` | Append agent(s) to assigned_to (routing metadata only) |
| `thrum queue unassign <bundle-id> <agent-id>...` | Remove agent(s) from assigned_to (clears a stale assignee) |
| `thrum queue note <bundle-id> --stdin` (or `--body-file <f>`) | Append a dated progress note (append-only; shown by `show`, last note previewed by `list`) |
| `thrum queue item add <bundle-id> --title "..."` | Add one embedded item |
| `thrum queue item batch-add <bundle-id> --title "..." --title "..."` | Add several items in one call (shared --ref/--priority) |
| `thrum queue item start/block/done <bundle-id> <item-id>` | Item-level status (start/done clear the item's block_reason) |
| `thrum queue item update <bundle-id> <item-id> [flags]` | Partial item update: `--title`, `--assigned-by`, `--ref` (repeatable, replaces existing refs), `--priority` - at least one required |

Without a Pro licence (`not entitled` / `requires a license`), skip this write and track it in the message thread instead.

Optional: `--ref <type>:<value>` (repeatable), `--priority N` (higher sorts
first), and `--from-message` (special case, below). `add` / `item add` require at
least one of `--title` or `--from-message`; explicit `--title` wins over
`--from-message`. **`--title` is the normal way to add work.**

### Status and stage: two independent axes

A bundle carries two orthogonal fields, both settable via `queue update`:

- **`status`** - `pending`, `in_progress`, `waiting`, `ready`, `blocked`,
  `done`. Says whether the owning agent can or must act now. `start` / `block`
  / `done` / `wait` / `ready` are all just `queue update --status=<x>` under a
  short name; `wait` and `ready` are exact aliases with no independent
  behavior of their own.
- **`stage`** - `intake`, `planning`, `dispatched`, `implementation`,
  `integration`, `review`, `rework`, `gate`, `merge`, `deploy`,
  `verification`, `cleanup`. Says where the bundle sits in its workflow,
  independent of status. Optional; never set on items, bundle-only. Set/read
  via `queue update --stage <s>` / `queue list --stage <s>` / `queue show`.

`status_reason` (set via `--reason` on `update`/`block`/`wait`) is the
general reason field; `--status=blocked` requires it, `wait` accepts it
optionally, other transitions clear it.

## Common Flows

**Add work you're taking on (the normal case):**
```
thrum queue add --title "Ship the X migration"
```

**Promote a backlog bead into active work:**
```
thrum queue add --title "Ship the X migration" --ref bead:thrum-abc12 --priority 5
```

**Lift a SPECIFIC message you received** (special case - mainly an
orchestrator/agent taking a dispatch it was handed):
```
thrum queue add --from-message <msg_id>
```
Copies the message's content into the bundle title, its author into
`assigned_by`, its refs into `refs`, then discards the message ID. 🔴 Pass the
EXACT id of the assignment you are acting on, matched to its sender - NEVER pipe
`inbox --unread | head -1`, which grabs whatever is newest (often a monitor
alert or an unrelated message) and stores it as your title. For anything you
create yourself, use `--title`.

**Hand work to another agent** (two steps, in this order - there is no one-shot):
```
thrum queue assign <bundle-id> @impl_foo      # record intent on YOUR bundle
thrum send --to @impl_foo --stdin <<'EOF'      # actually route it
Assigned you <bundle>: <what + refs>. Add it with: thrum queue add --from-message <this msg id>
EOF
```

Assign at dispatch so `thrum queue list` shows `-> agent` and flags unassigned in-progress bundles;
remove a stale assignee with `thrum queue unassign <bundle-id> <agent-id>`.
The other half of a load check is the role-filtered roster: `thrum team --all --role <role> --compact`;
only live rows (◍) count, not ☓ retired or ☾ sleeping.

Without a Pro licence (`not entitled` / `requires a license`), skip this write and track it in the message thread instead.

**See what an agent has on its plate:**
```
thrum queue list --agent @impl_foo --status in_progress
```

**Finish vs discard:** `done` is a WAYPOINT, not a resting place - it marks a
bundle done and KEEPS it visible (`start` reopens it), but it is not where
finished work should live. `drop` DELETES it and every item inside, regardless
of status. When the work lands, `drop` it in the same turn (`done` first is
optional). A finished bundle is never kept: its history lives in git and the
bead, not in the queue.

**Close on a TRIGGER, not a reminder.** The moment a bundle's branch merges into
trunk, OR its last referenced bead closes, `done` + `drop` it in that same turn -
closure is part of the merge/close act, exactly as `add` is part of dispatch. A
bundle whose branch is an ancestor of trunk, or whose beads are all closed, is
completed-but-open rot; do not leave it for "later". `add` has a natural trigger
(dispatch) and gets done reliably; the close has no trigger unless you make the
merge/close BE the trigger.

**Reconcile is a bounded sweep, not a review.** At session start / breakpoint,
close only the bundles a check proves done, and skip the rest. Two checks only:
- branch is an ancestor of trunk (`git merge-base --is-ancestor <tip> origin/<trunk>`) -> `done` + `drop`
- every `bead:` ref is closed (`bd show <id>`) -> `done` + `drop`

Do NOT read each bundle's full history - the sweep is those two checks against
merged/closed state, nothing more.

## Identity-field mutation: queue vs. state (two intentional shapes)

`queue update` / `queue item update` and `state set` handle identity-field
mutation differently. This is an intentional two-shape convention chosen per
domain, not an inconsistency - do not "fix" one to match the other.

**`queue.update` / `queue.item.update`**: wrapped by `RejectIdentityKeysHandler`, which returns a typed
`*queue.ErrImmutableField` error (`queue: cannot modify immutable field %q`) if a raw identity key (`id`, `agent_id`,
`created_at` for bundles; `id`, `created_at` for items) appears in the wire payload at all - checked BEFORE decode,
fail-closed. Note: `QueueUpdateRequest`/ `QueueItemUpdateRequest` have no JSON fields for these identity keys anyway, so
this wrapper is defense-in-depth, not load-bearing on its own.

**`state.set`**: no reject path at all, and that is by design, per field:
- **Kind/Scope** are the addressing key - a different `(Kind,Scope)` pair
  addresses a DIFFERENT record, so setting them isn't a mutation attempt and
  there's nothing to reject.
- **Class** is derived server-side from the kind registry; no wire field
  exists for a caller to even attempt to set it.
- **CreatedAt** is server-stamped once at creation (on the fleet tier it's
  actually derived from `UpdatedAt` at read time rather than stored
  independently); not client-spoofable, and `StateSetRequest` has no field to
  smuggle it through.
- **EstablishedBy** is influenceable only via `CallerAgentID` on a
  non-peercred transport (browser/WS, unit tests) - but that is a general,
  already-documented property of the RPC layer (identity trust depends on
  transport), not something unique to `state.set`, and it is explicitly out
  of scope per the project's security model: "Thrum does not attempt
  to prevent agent identity forgery" is an owner ruling, stated as
  [INTENDED] and permanent, not tracked as debt.

**The convention, stated plainly:** queue rejects raw identity keys at the
wire as defense-in-depth (even though its request types don't have those
fields anyway); state has no reject path because its identity/system-derived
fields are either addressing semantics (not a mutation) or server-derived/
unspoofable (nothing to reject). Two different but each internally-consistent
shapes, chosen by domain, not an oversight.

Non-goal: fail-closed reject-symmetry for `state.set` (mirroring queue's
`RejectIdentityKeysHandler`) is a possible future non-security hardening
preference, not built here - out of scope per the project's security model.

## Common Mistakes

- **Treating `add`/`done` as the work itself.** The queue *tracks* your work; it
  does not perform it. `add` is not dispatching — the dispatch is the
  `thrum send` / merge / deploy the bundle *mirrors*. `done` means the tracked
  work actually landed, not "I'm finished thinking about it." Marking bundles
  `done` when nothing was dispatched or deployed is a false record, exactly like
  filing a bead and calling the bug fixed — and it inflates the queue while
  little actually ships. Verify the thing landed before `done` (branch merged /
  bead closed / deploy confirmed); otherwise it is still `in_progress` or
  `blocked`.
- **Piping a guessed id into `--from-message` (e.g. `inbox --unread | head -1`).**
  That grabs whatever is newest - often a monitor alert or an unrelated message -
  and stores it as your bundle title. `--from-message` takes the EXACT id of the
  assignment you are lifting, matched to its sender; for anything self-created use
  `--title`.
- **Expecting `assign` to put work in the other agent's queue.** It does not -
  it only tags your own bundle. Always follow it with a `thrum send`; the
  recipient adds it themselves via `--from-message` (with that message's exact id).
- **Leaving done bundles to pile up.** `done` never removes anything - the
  bundle stays, and only `drop` deletes it. Drop each bundle the turn its work
  lands; a queue full of undropped done bundles is the primary rot mode.
- **Reaching for a `remove`/item-`drop` verb.** Removal (of a bundle and every item inside it) happens only at the
  bundle level via `drop` - there is no finer-grained delete. Renaming/re-titling is NOT in this category:
  `queue update --title` and `queue item update --title` are the supported post-create edit path for a bundle's or
  item's title (and other fields - stage, refs, priority, assigned-by).
- **Trying to edit another agent's queue.** Impossible by design - you only
  write your own. Cross-agent work is messaging plus the recipient's own `add`.
- **Treating the queue as durable/shared history.** It is local, private, and
  deleted with the agent. Anything that must survive belongs in beads or a
  committed doc, referenced from the bundle via `--ref`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.