Task data model (for agents)

The Tasks/MCP data model a cybo needs to read the board correctly — entities, id spaces, status vs workflow state, task routing, and the cyborg7_* tool map.

Read this before driving the Tasks board through the cyborg7_* MCP tools. It is the shape of the data, not a how-to — for the user-facing walkthrough see Create & track tasks. Facts here are the ones a cybo most often gets wrong by guessing (id prefixes, status vs state, where a task lands).

Entities & relationships

workspace
  └─ Tasks-project (tasks_projects, 1:1 with a chat project)
       └─ task (tasks)
            ├─ workflow state (task_states — the board COLUMN)
            ├─ labels (per-project catalog, by NAME)
            ├─ assignee (a human userId OR a cybo id)
            ├─ channel (optional scope)
            ├─ parent task (optional — a sub-task)
            └─ cycle / modules (optional — sprint / grouping)
  • A Tasks-project (tasks_projects) is the bucket a task is filed under. Each one is linked 1:1 to a chat project; its display name/identifier come from that chat project.
  • The Inbox is a synthetic Tasks-project (isInbox: true, no linked chat project). A task created with no project lands here. Inbox tasks are hidden from the per-project boards.
  • A task always belongs to exactly one Tasks-project, has one workflow state (its board column), and optionally an assignee, labels, parent, channel, and cycle.

Id spaces — resolve by lookup, never by prefix

Four id spaces show up in the tools. They are not interchangeable, and you cannot build one from another by string-manipulation.

Id Belongs to Where you get it Where it's used
tp_… Tasks-project (tasks_projects.id) cyborg7_list_projectsid list_states(projectId), list_tasks({projectId}), create_task.projectId, update_task.projectId
task… / uuid Task (tasks.id) leading token of each list_tasks line; create_task's return every task mutation (update_task, get_task, archive_task, delete_task, bulk_update_tasks)
state id Workflow state (task_states.id) cyborg7_list_states(projectId)id update_task.stateId / bulk_update_tasks.stateId (also accepts the state name)
#N Per-project sequence (sequence_id) shown after the id in lists (… (#12)) display only — NOT accepted by any mutation

:::caution[Don't construct ids] The tp_… and task id spaces are resolved by DB lookup, not by prefix. On the cloud a Tasks-project id happens to be tp_<chatProjectId> (Inbox is tp_inbox_<workspaceId>), but a solo/local daemon's ids are random. Never prepend tp_ to a chat id or hand-build a task id — always take the id from list_projects / list_tasks. The #N sequence is for humans; passing it to a mutation fails. :::

status vs state — the crux

The board renders columns by the workflow state's group. state.group is one of backlog | unstarted | started | completed | cancelled.

Legacy tasks.status is a one-way mirror derived from state.group — you read it, but the state is what actually places the card:

Column state.group stored status (the mirror) status inputs that land here
Backlog backlog pending backlog
Todo unstarted pending todo, pending
In Progress started in_progress in_progress
Done completed done done
Cancelled cancelled cancelled cancelled

Two consequences a cybo must internalize:

  • The Todo column stores status: "pending" — and so does Backlog. todo is only an input alias (it maps to the Todo/unstarted column); it is never a stored value. A freshly-placed task never reads back as todo.
  • The full set of status values you can filter on or set is backlog | todo | pending | in_progress | done | cancelled. (There is no pending_review.)

Moving a card

  • Preferred — set stateId on update_task: an exact state id OR name (case-insensitive) from cyborg7_list_states. The status mirror is then re-derived from the state's group. stateId wins if you pass both.
  • Or set status (no stateId): it now moves the card — it resolves to the target group (per the table above) and lands on that column's default state (else its lowest-sequence state). If the task has no project/state catalog, it falls back to updating status only.

If a stateId doesn't resolve, the tool returns a friendly error telling you to call cyborg7_list_states(projectId) — call it, don't retry blindly.

Task routing — where a create lands

cyborg7_create_task needs exactly one of these to resolve a project (checked in order):

  1. projectId (a tp_… id from list_projects) — files the task under that Tasks-project.
  2. channelId — files it under that channel's Tasks-project (falling back to the workspace Inbox if the channel has none). A channel-bound cybo auto-uses its channel when you pass none.
  3. parentId — the new task inherits its parent's project (a sub-task).

A create with none of the three is rejected with "provide projectId or channelId". When a task ends up with no project it lives in the Inbox (project_id null), hidden from project boards.

:::note[Filtering gotcha] list_tasks({projectId}) is an exact tp_…-id match. Inbox / unfiled tasks have no project, so a project filter never returns them — list with no filter, or pass the Inbox project's id (list_projects → the isInbox: true entry). :::

What wakes a cybo (invocation triggers)

A cybo runs a turn when one of these fires:

  • @-mention in a channel it is a member of — mention it by slug, name, or cybo:<id>. Only human messages invoke; a cybo mentioning another cybo does not chain (by design). Membership is required — mentioning a non-member returns a private "add it first" notice.
  • DM — a direct message to the cybo runs a private turn framed as a DM.
  • Channel watcher — when a channel has auto-tasks turned on, its watcher cybo reads new human messages (with recent transcript) and acts without being @-mentioned (e.g. turning "we still need to fix the login bug" into a task). Opt-in, off by default.
  • Schedule (cron) — a recurring/one-shot autonomous run set up via cyborg7_schedule_create. Requires the spawn-agents capability and an online workspace daemon.

See Invoke a cybo, Mention or notify someone, and Schedule a recurring job.

The task tool map

Tool Use it to
cyborg7_list_projects Find Tasks-projects; get each { id (tp_…), identifier, name, color, isInbox }. The id is create_task's / filter's projectId.
cyborg7_list_states List a project's workflow states (board columns) as { id, name, group, isDefault, sequence } — get a stateId/name before moving a card.
cyborg7_list_tasks List/filter tasks (status, assignee, projectId, state, priority, label). Each line starts with the task id and shows state:Name (id) + project:<tp_…>.
cyborg7_get_task Read ONE task in full: { id, sequence_id, title, description, status, state:{id,name,group}, projectId, assigneeId, priority, labelIds, moduleIds, dueAt }.
cyborg7_create_task Create a task. Route it with projectId, channelId, or parentId (else rejected).
cyborg7_update_task Change any field. stateId (id or name) moves the card — preferred; status also moves it; omitted fields are unchanged.
cyborg7_bulk_update_tasks Apply the same patch to many task ids at once (e.g. mark a batch done, reassign).
cyborg7_archive_task Hide a finished task (reversible — archived=false restores).
cyborg7_delete_task Permanently delete a task (irreversible — prefer archive).
cyborg7_get_workspace_roster All members + cybos with ids ([human] name (role) — <userId> / [agent] name — <cyboId>) to @mention or assign.
cyborg7_list_channel_members Members of one channel with ids — resolve a name → id before mentioning.
cyborg7_read_docs Read these end-user guides (nav / search / get <slug>) to answer a "how do I…?".