Add a task

A task is a two-file package. Copy tasks/demo/hello/ (Linux) or hello_win/ (Windows) and edit. They cover the whole input / reference / output surface in ~250 lines. The Task spec & data staging page covers the concepts.

tasks/<domain>/<task>/
├── task_card.json     # metadata + the VM it needs
└── main.py            # load() / start() / evaluate()

task_card.json

FieldValue
taskId<domain>/<task>, must match the folder path.
categoryThe top-level domain.
vm.snapshotWhich image to boot: cpu-free-ubuntu (Linux), cpu-free (Windows), gpu-free, cpu-license, gpu-license.
vm.{vcpus,memory_gb,disk_gb}Sizing hints, resolved against the snapshot's capacity pool.
vm.timeout_sPer-variant wall budget (provision + setup + agent + eval). Allow ~2× the agent's time.

main.py: three hooks

HookRunsDoes
load()at discoveryReturns one cb.Task per variant, each carries the prompt and the metadata the other hooks read back.
start(cfg, session)on the fresh VMStage the workspace: create dirs, write the input/ files and helper scripts.
evaluate(cfg, session)after the agentScore the output against the reference; return list[float] (usually [score] in [0, 1]).

All sandbox I/O goes through session (run a command, read/write a file, screenshot, …). The full surface is docs/SESSION_API.md.

Two rules that bite
Hide the reference. The agent must not see reference/ during start(). The framework stages it only at eval time. End start() with a "reference correctly hidden" check. Never raise from evaluate() on missing output: return [0.0]; raise only on infrastructure failures.

Other conventions: keep start() idempotent (wipe output/ + reference/ first, in case of a reused VM); put per-variant data in a VARIANTS list and read it from cfg.metadata; use \r\n in expected text for Windows tasks.

Shipping task data

PatternForHow
Inline in start()prompts, small JSON, scripts (< a few hundred KB)Just session.write_file(…), what demo/hello does, zero image work.
Baked into the imagedatasets, checkpoints, large binariesPlace input/ + software/ + an encrypted reference.7z under <task_data_root>/<domain>/<task>/<variant>/; set task_data_source: baked_in_sandbox (or a gs:// bucket to stage per run).

Discover & test

No registry. The loader picks up any tasks/<domain>/<task>/main.py with the three hooks. Verify and smoke-test:

uv run python -m ale_run list | grep <domain>/<task>
uv run python -m ale_run run my_exp.yaml --task <domain>/<task> --dry-run
uv run python -m ale_run run my_exp.yaml --task <domain>/<task>

When it's stable, add it to a curated list under selected_tasks/ and open a PR.

No code? Submit a workflow
Domain experts can submit a task idea (description, reference, rubric) through the contributor program at agenthle.org/submit; qualifying submissions become co-authored task packages.