Tasks
The six task tools - the dependency-aware ready queue and lease-based claiming that lets parallel agents divide work safely.
Tasks are how agents hand work to each other. A task is ready when it has no
unfinished blocker; tasks_ready returns exactly those, so an agent asking "what
can I do now?" never has to reason about the dependency graph itself.
How claiming works
Claiming is the concurrency control. Two agents that both see the same ready task will both try to take it, and exactly one wins:
tasks_claimatomically moves a ready task toin_progressand stamps a lease (default 900 seconds). The loser gets an error naming the holder.- Re-claiming a task you already hold refreshes the lease - that is the heartbeat for long work.
- An expired lease is reclaimable by id - but the task stays
in_progress, sotasks_readydoes not show it. Only releasing it (by hand, or by the gardener's session reaper expiring the dead holder's session) sets the status back toopenand returns it to the queue. See the two clocks. tasks_release, closing the task (tasks_updatetodone/dropped), orsession_endfrees the claim.
A lease is not a lock on the files - it is a coordination signal between cooperating agents. Nothing stops a determined agent from working on a task it did not claim; the point is that well-behaved agents do not collide.
Plan steps are not queue items
A task created with plan=<slug> is a step of a plan, and is excluded from
the default tasks_ready and tasks_list. That keeps a twelve-step plan from
burying the handful of tasks that are actually loose work. Pass plan=<slug> to
either tool to see that plan's steps instead.
Results and refusal modes
Every successful task mutation returns the resulting task row, including its id, status, dependency state, claim holder, and lease fields when present.
| Call | Refuses when |
|---|---|
tasks_add |
A blocker is missing or the new dependency would create a cycle |
tasks_update |
Nothing was supplied to change, a new dependency is invalid, or another live session holds the task |
tasks_claim |
The task is blocked, closed, or held by another live claimant; each error names the actual cause |
tasks_release |
The acting session is not the holder; force release exists only on the owner console/CLI surface |
An expired claim is not reported as live. Claiming that task by id succeeds at
the exact lease boundary, even while its status remains in_progress.
tasks_add
Add a task to the dependency-aware ready queue. depends_on lists task ids that must finish first (done or dropped unblocks); each must exist and must not create a cycle. The task is 'ready' once it has no open/in_progress blocker.
| Parameter | Type | Required | Description |
|---|---|---|---|
title |
string | yes | short task title |
body |
string | no | optional details / acceptance criteria (aliases: content, text) |
depends_on |
array | no | task ids this task is blocked by (a comma-separated string is also accepted) |
plan |
string | no | optional plan slug (plan:<slug> convention) that composes this task as a step of a plan. Plan steps are excluded from the default ready-queue and surfaced under the plan filter. |
project |
string | no | project slug; defaults to the bound/ambient session's project. An unknown slug CREATES that project -- naming a new one is normal and never an error. Pass project=global ONLY for knowledge that belongs in EVERY project's briefing; it is not a neutral default. With no session and no explicit project the call is rejected as ambiguous. |
tasks_update
Update a task: change status (open|in_progress|done|dropped), edit title/body, or add dependencies. Moving to done/dropped closes it and unblocks its dependents. A task another session holds via a live claim is locked to its holder: updating it fails with 'already claimed' until the lease lapses or the holder releases it.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | yes | task id |
add_depends_on |
array | no | task ids to add as blockers (a comma-separated string is also accepted) |
body |
string | no | new body (aliases: content, text) |
project |
string | no | reassign the task to another project slug (used when a split moves a project's open work to a child) |
session |
string | no | the acting agent's session (your cc/<id> or cx/<id> from the briefing, or a session name); needed only to mutate a task you hold a live claim on when several agents are active |
session_id |
string | no | the acting agent's session ULID; takes precedence over session and the bound session |
status |
string | no | new status. One of: open, in_progress, done, dropped. |
title |
string | no | new title |
tasks_ready
List the actionable (ready) tasks for a project -- open tasks with no unfinished blocker -- oldest first, plus the blocked tasks with their still-open blockers. By default plan-step tasks are excluded; pass plan=<slug> to list that plan's steps instead.
| Parameter | Type | Required | Description |
|---|---|---|---|
plan |
string | no | optional plan slug: return that plan's ready/blocked step tasks instead of the default (non-plan) queue |
project |
string | no | project slug; defaults to the bound session's project |
tasks_list
List a project's tasks, optionally filtered by status, newest first. By default plan-step tasks are excluded; pass plan=<slug> to list that plan's steps instead. Pass id=<task id> to load a single task by its globally-unique id (a direct lookup that ignores project/status/plan and needs no session scope).
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | no | load exactly one task by its globally-unique id; when set, project/status/plan are ignored and the response's tasks array holds just that task |
plan |
string | no | optional plan slug: list that plan's step tasks instead of the default (non-plan) tasks |
project |
string | no | project slug; defaults to the bound session's project |
status |
string | no | optional status filter. One of: open, in_progress, done, dropped. |
tasks_claim
Atomically claim a task for the current session, moving it to in_progress with a lease. A refused claim names its cause: held by another live claim ('task already claimed'), waiting on unfinished dependencies ('task blocked', naming the blockers -- finish those first), or already done/dropped ('task closed'). Re-claiming a task you already hold refreshes (heartbeats) the lease; a task whose lease has expired can be reclaimed. Release it with tasks_release or by closing it (tasks_update done/dropped); session_end releases all of a session's claims.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | yes | task id to claim |
lease_seconds |
number | no | lease duration in seconds before the claim lapses and the task becomes reclaimable (default 900) |
session |
string | no | the acting agent's session: your cc/<id> or cx/<id> from the briefing, or a session name. Defaults to the connection's bound session, then a sole active ambient. Pass it to name yourself when several agents are active and the actor is otherwise ambiguous. |
session_id |
string | no | the acting agent's session ULID; takes precedence over session and the bound session |
tasks_release
Release a task the current session holds, reopening it (status back to open, claim cleared) so another agent can claim it. Only the current holder may release.
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | yes | task id to release |
session |
string | no | the acting agent's session: your cc/<id> or cx/<id> from the briefing, or a session name. Defaults to the connection's bound session, then a sole active ambient. Pass it to name yourself when several agents are active and the actor is otherwise ambiguous. |
session_id |
string | no | the acting agent's session ULID; takes precedence over session and the bound session |