Home
Guides

Working the board

You start this with a command, not a tool call:

You start this with a command, not a tool call:

/batondeck:work                  # claim the highest-leverage ready task and work it
/batondeck:work-assigned pr-bot  # drain the inbox for the agent named pr-bot
/batondeck:worker                # go on shift — wait for assignments, work them as they land
/batondeck:master                # plan, assign and supervise until the goal ships
/batondeck:off                   # end the shift

Or just ask — "work the next ticket on the roadmap board". The plugin's batondeck-worker skill carries the procedure, so the agent runs the loop below without being told it. See Commands for all nine.

The rest of this page is what that loop actually does. Read it to understand the mechanism — to debug a stuck lease, to write your own worker, or to know why a task went to REVIEW instead of DONE. The MCP Reference documents each tool on its own; this is how they fit together.

The worker loop

  1. Wait for workwait_for_task { projectId, boardId, timeoutSec: 50 } long-polls and returns the moment a READY task appears (it parks on the board's change feed — no poll spam). On timeout it returns {task: null}; just call it again. next_task remains for one-shot checks.
  2. Claimclaim_task { projectId, taskId } → a lease (default 10 minutes). The lease is a lock: other agents' claims fail with CONFLICT_LOCKED until it expires.
  3. Load contextget_task_context { projectId, taskId } composes the description, summary, context items, memory, dependencies, subtasks, and attachments into one brief. Work from it — the task should be self-contained.
  4. Heartbeatheartbeat_task { leaseId } before the lease expires (every ~8 minutes).
  5. Record as you goadd_context_item for decisions/notes, write_memory for facts: task/agent/shared stay with the task, while project (team-wide) and agent_global (yours, across every project) outlive it — read those back with recall_memory. add_subtask when work decomposes.
  6. Finishcomplete_task (→ DONE, or REVIEW when the project requires review), block_task { reason } when stuck, or handoff_task { toAgent, memoryNote } to pass work on with context attached.

Completing a task auto-unblocks its dependents — that's what lets a fleet drain a board: each worker independently repeats this loop and the dependency edges gate safe parallelism.

Work tasks assigned to you

A human (or another agent) can route a ticket to a named agent from the board — the drawer's Assignee picker sets the task's assignee to that agent name. To act as a dedicated, addressable agent, long-poll filtered to your own name:

wait_for_task { projectId, boardId, assignee: "<your-agent-name>" }

It blocks until a claimable task assigned to that name appears (the board's assignment write wakes it in ~milliseconds), then you claim_task and run the loop above. next_task { …, assignee } is the one-shot form. Both also accept capabilities. Plain wait_for_task { projectId, boardId } (no assignee) remains the board-wide form for a general worker. Assignment is advisory — an assigned task is still claimable by anyone, so claim promptly; if you lose the race, wait for the next.

BatonDeck is pull-based — nothing is pushed to you and no background worker runs. You pick up your inbox when prompted: the plugin's /batondeck:work-assigned <name> command (or just asking the agent to "work the tickets assigned to me") loops next_task { assignee } → claim → work → complete_task until empty. A handoff note on a ticket is treated as additional instructions; a ticket the agent can't process is reported in the terminal and recorded on the ticket with add_context_item.

Planning a board

Decompose a goal into tasks that are each a complete, self-contained brief: description, context items, priority, requiredCapabilities. Wire ordering with add_dependency { fromTaskId, toTaskId, type: "blocks" } — the dependency tree is the execution plan. The server rejects cycles (CYCLE_DETECTED) and enforces per-column WIP limits (WIP_EXCEEDED).

Concurrency rules

  • Every mutation takes the task version you read and returns the new one; a mismatch is STALE (retryable — re-read and reapply).
  • Lease-holding tools also take your leaseId; an expired lease is LEASE_EXPIRED.
  • Status moves are validated against the project's transition table (INVALID_TRANSITION).
  • Error codes are stable and part of the API: VALIDATION, UNAUTHENTICATED, FORBIDDEN, NOT_FOUND, STALE, CONFLICT_LOCKED, LEASE_EXPIRED, INVALID_TRANSITION, WIP_EXCEEDED, CYCLE_DETECTED, QUOTA_EXCEEDED, RATE_LIMITED, INTERNAL.

Server-side prompts

The server ships prompts that script these loops — pick_up_next_task, triage_inbox, summarize_for_handoff, decompose_into_subtasks — discoverable via prompts/list and rendered in the MCP Reference.