Tasks
A task is the atomic unit of execution: one worker agent, one git worktree, one branch, one outcome. Tasks are what fan out in parallel inside a project’s Coding Workspace.
Prerequisites
Section titled “Prerequisites”- An imported or created project — see the Quickstart
- An API key and workspace ID, or a signed-in dashboard session
- Agency role (
owner,admin, ormember) to create a task; client collaborators can read and comment but not create
Where tasks come from
Section titled “Where tasks come from”Most tasks are not written by hand. A project run (POST /v1/api/projects/:id/start) plans a roadmap, the roadmap becomes cycles, and each cycle’s planner decomposes it into tasks. Creating a task directly is the exception — useful when you already know the unit of work and want it attached to a specific cycle.
| Origin | How | Executes? |
|---|---|---|
| Project run | POST /v1/api/projects/:id/start with a brief |
Yes — the orchestrator dispatches it |
| Cycle planner | Automatic, during a cycle run | Yes |
POST /v1/api/cycles/:id/tasks |
You add it to an existing cycle | Yes, on the next cycle run |
POST /v1/api/tasks |
You create it standalone | No — see below |
Creating a task
Section titled “Creating a task”Over the API:
curl -sS -X POST "$HLIX_BASE_URL/v1/api/tasks" \ -H "x-api-key: $HLIX_API_KEY" \ -H "X-Organization-Id: $HLIX_WORKSPACE_ID" \ -H 'content-type: application/json' \ -d '{"projectId":"'"$PROJECT_ID"'","title":"Add GET /healthz","description":"Return 200 with the build SHA and cover it with a test."}'projectId (uuid) and description (1–5000 characters) are required; title (1–200 characters) is optional. A 201 returns the created task row.
With the TypeScript SDK — see TypeScript SDK for building the hlix client:
const task = await hlix.tasks.create({ projectId, title: 'Add GET /healthz', description: 'Return 200 with the build SHA and cover it with a test.',})In the dashboard: go to Tasks → New task, pick the project, write the description, and submit. You land on the task detail page.
To attach the task to a specific cycle instead — which is what makes it runnable — post it to the cycle:
curl -sS -X POST "$HLIX_BASE_URL/v1/api/cycles/$CYCLE_ID/tasks" \ -H "x-api-key: $HLIX_API_KEY" \ -H "X-Organization-Id: $HLIX_WORKSPACE_ID" \ -H 'content-type: application/json' \ -d '{"title":"Add GET /healthz","description":"…","dependsOn":[]}'dependsOn is an array of task UUIDs that must finish first.
There is no standalone task run
Section titled “There is no standalone task run”Execution always enters through a cycle. Either run the cycle directly (Cycles) or start a project run and let the orchestrator dispatch it.
Task statuses
Section titled “Task statuses”Thirteen values, from the task_status database enum. A new task starts at queued.
| Status | Meaning |
|---|---|
queued |
Created, waiting for a wave to pick it up |
blocked |
Held by an unmet dependency or an unresolved request |
building |
A worker agent is editing the worktree |
testing |
Automated checks are running |
reviewing |
The QA evaluator is judging the result |
approved |
The evaluator approved the change |
merging |
The task branch is being merged into the project’s default branch |
resolving |
A merge conflict is being worked |
merged |
The branch landed |
pr_created |
A pull request was opened on the connected repository |
ci_passing |
Provider CI reported success |
done |
Terminal — the task finished |
failed |
Terminal — the task failed |
PATCH /v1/api/tasks/:id accepts title, description, and status — but not resolving, which only the merge path sets.
Watching a task
Section titled “Watching a task”GET /v1/api/tasks/:id/stream is a Server-Sent Events stream of status changes.
With the CLI:
hlix tasks watch "$TASK_ID"Expected result — one line per status change, exiting at a terminal status:
queuedbuildingtestingreviewingdone--json prints one envelope per line (JSONL) so a consumer can read frames as they arrive.
Over the API:
curl -N "$HLIX_BASE_URL/v1/api/tasks/$TASK_ID/stream" \ -H "x-api-key: $HLIX_API_KEY" \ -H "X-Organization-Id: $HLIX_WORKSPACE_ID"Four properties of this stream are worth knowing before you build on it:
- Frames are unnamed events. Each
data:payload is the full task row, emitted on the first frame and on every status change. - There is no
doneevent. The stream simply closes once the status reachesdone,failed,pr_created, orci_passing. Read the status field; do not wait for a terminal event name. (mergedis deliberately not a closing status — a merged task usually continues todone.) keepaliveis a named event with an empty payload, roughly every 30 seconds while nothing changes.- A missing task is a frame, not a 404. The stream opens
200and sends{"error":"Task not found"}before closing. The status code cannot tell you the task is gone.
The stream is bounded: it polls every 2 seconds for at most 600 iterations, so it ends after roughly 20 minutes whether or not the task finished. Long tasks need a reconnect.
Listing tasks
Section titled “Listing tasks”hlix tasks listInside a folder bound to a project, this lists that project’s tasks; anywhere else it lists the whole workspace. Expected result:
ID STATUS TITLE------------------------------------ -------- ------------------b4a1f0d2-3c77-4e1a-9a2b-1d5e6f7c8a90 building Add GET /healthz--project <id>(string, default the bound project): restrict to one project.--status <status>(string): restrict to one of the statuses above.
hlix tasks list --status failed --jsonWith no matches it prints No tasks. and exits 0 — an empty result is not a failure.
Reading one task
Section titled “Reading one task”hlix tasks get "$TASK_ID"Expected result:
id b4a1f0d2-3c77-4e1a-9a2b-1d5e6f7c8a90status donetitle Add GET /healthzproject 6f1c2a9e-8f0b-4a7d-9d33-2f0b1c7e5a41description Return 200 with the build SHA and cover it with a test.hlix tasks get --json passes the API response through unchanged — the human table picks columns, the JSON envelope never drops a field.
Task conversation
Section titled “Task conversation”GET and POST /v1/api/tasks/:id/comments are the task’s conversation — messages between you and the agents working it. They are not the same surface as line-level review comments, which are anchored to a commit and a file.
curl -sS -X POST "$HLIX_BASE_URL/v1/api/tasks/$TASK_ID/comments" \ -H "x-api-key: $HLIX_API_KEY" \ -H "X-Organization-Id: $HLIX_WORKSPACE_ID" \ -H 'content-type: application/json' \ -d '{"body":"Use the existing logger rather than console."}'body is required (1–10 000 characters). An optional messageId (uuid) makes the post idempotent: replaying the same id with the same body returns the stored message instead of duplicating it. A view-only client collaborator may comment.
Common questions
Section titled “Common questions”Why did my standalone task never start? Because a task created on its own is a record, not a run. Execution comes from a cycle — the orchestrator dispatches a wave, and tasks in that wave run. A task created through POST /v1/api/tasks waits until something dispatches it.
Can I cancel a running task? Not from the CLI, which has no execution commands at all. PATCH /v1/api/tasks/:id accepts a status change, with one exception: it will not set resolving, which only the merge path may write.
Why did hlix tasks watch exit while the task was still going? The stream is bounded — it polls every 2 seconds for at most 600 iterations, so it ends after roughly 20 minutes regardless. A clean exit at a non-terminal status means “reconnect”, not “finished”. Read the status field, not the exit code.
Where are the logs? There is no log stream. The log endpoint is not part of the published contract, so no client can reach it, and tasks watch streams task status — a different thing, named accordingly.
Two tasks touched the same file. What happens? Each task works in its own git worktree, so they never share a checkout. The collision surfaces at merge time as a genuine conflict and fails that task only; its siblings are unaffected.
If a task will not run
Section titled “If a task will not run”{"error":"Standalone task execution is not implemented. Run the task's cycle instead (POST /v1/api/cycles/:id/execute)."}— expected, and a501. Execute the task’s cycle instead.{"error":"Task not found"}with a404from/execute— the task resolved to nothing in this workspace. Check the id andX-Organization-Idbefore assuming the endpoint is at fault.- Task stays
queuedforever — it is not attached to a cycle, or its cycle has not been executed. CheckcycleIdon the task row. - Task stays
blocked— an unmetdependsOn, or a pending request is holding it. See Approvals. {"error":"Task not found"}as a stream frame — the id is wrong, or it belongs to another workspace. CheckX-Organization-Id.{"error":"Project administration is restricted to the agency"}— you are authenticated as a client collaborator. Clients read and comment; they do not create tasks.- The watch command exits after ~20 minutes with the task still running — the stream’s own bound, not a failure. Reconnect.