Mutate a task or sprint. Input is { action, payload, workspaceId? }.
Actions (→ marks required fields; the rest are optional). dueAt is ISO-8601, e.g. 2026-09-20T17:00:00Z:
- create → projectId, title; optional: description, priority, status, owner (null: unowned), worker, reviewer, reviewRule, points, assignee, tags[], acceptanceCriteria[], dueAt. A new projectId is created by this call (see Projects).
- update → id, updatedAt; optional: title, description, priority, worker (your agent name claims it; null releases), owner (member email = the responsible human; null clears), reviewer, reviewRule, points (null clears either), assignee (legacy), tags[] (replaces) or addTags[]/removeTags[], acceptanceCriteria[], dependsOn[] (task ids; replaces, [] clears), dueAt (null clears it).
- move → id, updatedAt, status (a key from task_search workflow.statuses, or a category).
- review → id, updatedAt, decision: approve | request_changes (needs note). Reviewer, or an admin where allowed.
- comment → id, note; optional: notify=true for a meaningful progress update (routine ones don't notify).
- block → id, updatedAt, reason (sets status=blocked).
- sprint_create → name and projectId; or scope:"workspace" (+ projectIds[], default all) for a cross-project sprint; optional: goal. A new projectId is created too (see Projects).
- sprint_assign → id, updatedAt, sprintId (null removes it from its sprint).
- sprint_activate → sprintId (one active per project, plus one workspace sprint).
- sprint_complete → sprintId.
- project_policy_update → projectId, expectedVersion, content, changeSummary (a new policy version; needs projects:write).
Projects: there is no project_create action and none is needed — create and sprint_create auto-create the project when given a projectId this workspace lacks, echoing projectCreated: true. A statelode.json slug missing from statelode://workspace is a forward declaration, not an error: just create the task, and never ask a human to pre-create it in the dashboard. (Only task_search rejects an unknown projectId; reads must not mint projects.)
If implicit creation returns project_workspace_collision, this slug already exists in another readable granted workspace. No write occurred. Resolve the intended project with the human and persist workspaceId in the repository pin; do not blindly retry using the default workspace. An explicit workspaceId permits intentionally separate projects with the same slug.
Project creation workspace precedence: explicitly named workspaceId, then the repository-pinned workspaceId in statelode.json, then the human-selected default from statelode://workspace. One accessible workspace needs no prompt. If multiple workspaces have no explicit or pinned/default signal, ASK the human and retry with workspaceId; the server rejects silent guesses. Before create/sprint_create, state the selected workspace and offer a quick switch. Explicit input always overrides defaults. Ungranted ids are rejected. Attribution comes from the bearer token.
Scheduling: dueAt is the only forward-looking field — createdAt/updatedAt both record the past. An unscheduled task is not the same as one due now, so set dueAt only when a real deadline exists.
Enums: priority = urgent|high|medium|low. status = a workflow key from task_search workflow.statuses[].key (projects define their own columns; keys are stable, names are labels and are rejected) or a category backlog|active|in_progress|review|done|archive|blocked, which lands in that category's first column. Owner and worker are separate: owner is the responsible human (a member email from statelode://workspace), worker is the agent doing the work. Claiming sets worker and never replaces the owner; an unowned task you claim or create is owned by your human supervisor, unless create sends owner:null. reviewer is a member (never the owner, nor the human whose agent works it) who approves the task before done: approve moves it to the first done column, request_changes back to in progress and tells the owner. reviewRule: auto (default) lets the reviewer rotation pick the next member in turn when the task is claimed or enters review and has no usable reviewer; the response's reviewerRotation names them, or says nobody was eligible (the write still succeeds). pinned = the rotation never changes it; none = no review (owners/admins only). Each project's review mode sets who may move its tasks to done: anyone, only the reviewer, or the reviewer or an owner/admin; a refused move returns review_required naming who can. points = story points 1|2|3|5|8|13|21. Legacy assignee still works: a member email/name sets owner, any other string (for example "codex") sets worker.
Optimistic locks: update/move/block/sprint_assign require the task's last-seen updatedAt; on mismatch STALE_WRITE carries the fresh task. comment takes no lock and leaves updatedAt unchanged. project_policy_update requires the last-seen expectedVersion; on mismatch STALE_PROJECT_POLICY carries the fresh policy, and on success it returns the new version without content. Sprints and project policy are managed through this existing tool, never extra tools.
Responsibility guidance: Team lead, Contributor, and Observer describe how an agent should collaborate on behalf of its human supervisor; they do not authorize or forbid actions. The credential scopes returned in delegation.capabilities are the permission boundary. Any Delegate with the required scope may search, update, create, assign, review, manage sprints, or publish policy.
Planning: represent a plan as a sprint goal plus concrete tasks. Call sprint_create, create each task with a human member email from statelode://workspace, then sprint_assign each task. There is no separate plan tool.
Squads (only where the workspace uses them; repeated in the server instructions): a write in another squad's lane returns SQUAD_BOUNDARY (or, in advisory mode, a warning) carrying suggestedRequest — send it unchanged as request_create and let that squad decide.
- request_create → toSquad (slug), projectId, title; optional: description, acceptanceCriteria[], priority, neededBy, blockingTaskId (your task that waits: accepting wires the dependency, and finishing the work unblocks it).
- request_respond → id, decision: accept | decline | needs_info (the receiving squad's leads) or answer | cancel (the requester); note is required to decline, ask or answer; on accept, optional owner, priority, dueAt.
task_search with requests:"incoming"|"outgoing" lists requests instead of tasks.