Subsystem

HTTP API: tasks

The 14 operations tagged tasks, each with its parameters, its body and what it answers.

List tasks

GET /v1/tasks

List tasks, narrowed by whatever the query string asks for.

Parameters

Responses

Create a task

POST /v1/tasks

Create a task, from structured fields or from a captured line.

Request body: Create as application/json, required.

Responses

Read one task

GET /v1/tasks/{id_or_ref}

Return one task, by id or by ref.

Parameters

Responses

Change a task

PATCH /v1/tasks/{id_or_ref}

Change a task. Omitted fields are untouched; nulls clear (docs/design.md §8.3).

Parameters

Request body: Update as application/json, required.

Responses

Move a task to the trash

DELETE /v1/tasks/{id_or_ref}

Soft-delete a task. It stays recoverable (docs/design.md §6.9).

The deleted task is returned rather than an empty 204, so a caller can see when it happened without asking again — and so an agent can tell a repeat call apart from a first one.

Parameters

Responses

Take a task, so nobody else does

POST /v1/tasks/{id_or_ref}/claim

Take a lease on a task, or renew one you already hold.

A lease, not a lock (docs/design.md §14.11): it expires, and an expired one is ignored rather than needing anybody to clear it. Workers die mid-task, and a claim that outlived its holder would strand the work permanently.

Claiming something somebody else holds is a 409 naming who and until when. Claiming something you already hold renews it, and keeps the instant you first took it.

?ready=true hides work another worker holds, and never hides your own.

Parameters

Responses

Mark a task finished

POST /v1/tasks/{id_or_ref}/complete

Mark a task finished, in whatever this workspace calls its finished status.

Parameters

Responses

Move a task under another, or to the top level

POST /v1/tasks/{id_or_ref}/move

Re-parent a task, taking its subtask tree with it.

The endpoint §8 reserved, rather than a field on PATCH. Changing a project is a field being wrong and the subtree following is an invariant; changing a parent can be refused for being a cycle, which is a question about the shape of the tree and cannot be answered from this row alone.

Parameters

Request body: Move as application/json, required.

Responses

When does this come round?

GET /v1/tasks/{id_or_ref}/occurrences

Expand a repeating task's rule into the dates it produces.

§6.7 reserved this, and the decision behind it is why it exists at all: one occurrence is real and the rest are computed, so show me every birthday is a question about a view rather than about the backlog. Nothing is stored and nothing is materialised — a GET that wrote would break a read-only credential and race two concurrent readers.

It answers about the series, from whichever end the caller is holding. A person is always looking at an occurrence, because the template is in no listing; asking an occurrence when it next comes round is asking its series.

Parameters

Responses

Give a task back

POST /v1/tasks/{id_or_ref}/release

Give a task back, so somebody else can take it.

Releasing something nobody holds is not an error and records nothing — a worker tidying up after itself should not have to check first.

Anybody who may change the task may release it, not only the holder. The case this exists for is a worker that died holding a lease, and requiring its credential would put the remedy in the hands of the one principal that cannot act.

Parameters

Responses

Take a task out of the trash

POST /v1/tasks/{id_or_ref}/restore

Restore a soft-deleted task (docs/design.md §6.9).

The half that made soft delete soft, and for a long time it did not exist — §6.9 promised a deleted item was restorable, a trash_retention_days setting was declared, and EventAction.RESTORED has always been in the vocabulary, with nothing clearing deleted_at. That setting is gone — nothing ever purged the trash, so it was one more place the promise was made.

Registered before the parameterised deletes below it for routing.check's reason, and POST rather than DELETE ?restore= because it is not a deletion of anything.

Parameters

Responses

Let one occurrence of a repeat go by

POST /v1/tasks/{id_or_ref}/skip

Cancel this occurrence and bring the next one.

Cancelled rather than done, deliberately: both are finished and both advance the series, and I did not do this is a different fact about the month from I did.

Parameters

Responses

What has been checked against this task

GET /v1/tasks/{id_or_ref}/verifications

Return what has been checked against this task, newest first.

Self-reported evidence is a record, not a proof. An agent can post exit code 0 without running anything. What this is worth is being a durable, attributable, invalidatable record of what was checked — never "verified work", and nothing reading it should say so.

Each record carries the tree it ran against, where there was one. Whether a record has expired is a comparison against the tree you are standing on, and this instance cannot make it: it has no checkout. A record with no tree hash cannot expire at all, which is a different answer from being current.

Newest first, unlike a comment thread: a record is not read as a story, and what a caller wants is the most recent thing that was checked.

Parameters

Responses

Record what was checked against this task

POST /v1/tasks/{id_or_ref}/verifications

Record what was checked against this task.

Self-reported evidence is a record, not a proof. An agent can post exit code 0 without running anything. What this is worth is being a durable, attributable, invalidatable record of what was checked — never "verified work".

tree_hash is what makes it invalidatable, and git rev-parse HEAD^{tree} prints one. Send it where there is one and leave it out where there is not: a record without one is still a record, it simply cannot expire.

A failing record is worth keeping and is the more useful half of the pair. This was tried and did not work is what stops it being tried again.

Parameters

Request body: RecordVerification as application/json, required.

Responses