# Tasks

> Keep task definitions, their references, drafts and links

A task says what a robot should do and how success shows: five fields of prose
(description, scene, steps, success, failure) and an optional procedure, in one
editable draft. Publishing writes an immutable, numbered revision. The
[Robots and Tasks](https://junction-docs.qualiastudios.dev/global/guides/robots-and-tasks.md) guide walks through it.

Every method takes a task's id, put under your account, or its full name,
`accounts/<ACCOUNT_UUID>/tasks/<TASK_ID>`. An API key acts as the person who
created it.

## Create, edit and publish

```python
from qualia import Qualia

client = Qualia()

task = client.tasks.create(
    "Restock shelf",
    draft={"description": "Move every box from the tray to the shelf"},
)
task = client.tasks.update(task.id, success="The tray is empty")
revision = client.tasks.publish(task.id, etag=task.etag, note="first cut")

client.tasks.list(limit=20)  # PaginatedPage of Task, newest first
client.tasks.get(task.id)  # the task with its draft
client.tasks.revisions(task.id)  # PaginatedPage of TaskRevision
client.tasks.revision(task.id, 1)  # one published revision
client.tasks.delete(task.id, force=True)  # account owners only
```

`update` saves only the fields you pass; `draft=` replaces the whole draft. A
stale `etag` is refused with 409.

## References

A reference is an episode that shows success, or a named failure or recovery
mode. A task holds 1 to 3 success references and any number of modes, each
with 1 or 2.

```python
episode = "accounts/<ACCOUNT_UUID>/datasets/<DATASET_ID>/episodes/<EPISODE_ID>"

ref = client.tasks.add_reference(task.id, {"role": "SUCCESS", "episode": episode})
client.tasks.add_reference(
    task.id,
    {"role": "FAILURE", "mode": "slip", "episode": episode,
     "startSeconds": 1.5, "endSeconds": 4.0},
)

page = client.tasks.references(task.id)  # PaginatedPage of TaskReference
for r in page.items:
    print(r.id, r.role, r.mode, r.episode)

client.tasks.delete_reference(task.id, ref.id)  # the episode is untouched
```

## Drafts

`client.task_drafts` asks a VLM to draft the task from exactly its references.
It never publishes.

```python
client.task_drafts.estimate(task.id)  # {"estimated_credits": ..., "min_cost": ...}
draft = client.task_drafts.create(task.id)
draft = client.task_drafts.wait(draft.name)
client.task_drafts.apply(draft)  # saves the proposal as the task's draft
client.task_drafts.list(task.id)  # PaginatedPage of TaskDraft
```

A draft is made as a person. A personal API key acts as the person who created
it, so `estimate`, `create` and `cancel` work with it as they do signed in. A
service key (one owned by a workspace or the organization) acts as no person and
is refused (`a person or their personal API key must draft a Task`). `list`,
`get`, `wait` and `apply` work with any key.

## Linked datasets and episodes

A task links the datasets and episodes recorded against it. A link is not an
edit: the label training reads stays as it is.

```python
where = f'linked_task = "{task.name}"'
client.datasets.list(filter=where)  # PaginatedPage of qualia.v1.Dataset
client.episodes.list("-", filter=where)  # every dataset's linked episodes

client.episodes.link_task(episode, task.id)  # link one episode
client.episodes.link_task(episode, "")  # remove its own link
```

An episode with no link of its own reads its dataset's link
(`linked_task_inherited` is true). Link a whole upload with
`client.datasets.update(dataset, linked_task=task.name)`.

A link keeps the revision the Task was at when it was made
(`linked_task_revision`, 0 for a draft), and checks judge that revision.
Publishing a new revision moves no link and starts no check. A dataset whose
`latest_task_revision` is higher has drifted; relink it and rerun its checks:

```python
bound = client.datasets.relink_task(dataset, validate_only=True).credit_bound
done = client.datasets.relink_task(dataset, max_cost=bound)
done.revision, done.check_runs  # the new revision, the runs started
```
