Task packs¶
A task pack is the set of tasks that defines "works" for your domain. Every candidate
stack runs against it, and the evidence in harness.lock comes from it, so the pack
matters more than any other input. A good pack looks like the work you actually want
the agent to do.
Layout¶
A pack is a directory with a pack.toml and one folder per task:
tasks/
├── pack.toml
├── clean_csv/
│ ├── prompt.md
│ └── workspace/
│ └── orders.csv
└── summarize_report/
└── prompt.md
pack.toml¶
[pack]
name = "harness-lock/demo"
version = "0.1.0"
license = "Apache-2.0"
domain = "research-analyst"
[[task]]
id = "clean_csv"
prompt = "clean_csv/prompt.md"
fixtures = "clean_csv/workspace/"
tools = ["python"]
expected = { artifacts = ["cleaned.csv"] }
tags = ["data-cleaning"]
timeout-seconds = 600
[[task]]
id = "summarize_report"
prompt = "summarize_report/prompt.md"
tools = ["python", "filesystem"]
expected = { artifacts = ["summary.md"] }
tags = ["writing"]
[pack]¶
| Key | Required | Description |
|---|---|---|
name |
yes | Pack identifier, conventionally owner/name |
version |
yes | Pack version. Change it when you change the tasks. |
domain |
yes | The domain the pack represents |
license |
no | License of the pack contents |
[[task]]¶
| Key | Required | Default | Description |
|---|---|---|---|
id |
yes | Unique task id within the pack | |
prompt |
yes | Path to the prompt file, relative to the pack directory | |
fixtures |
no | Directory copied into the task's workspace before the run | |
tools |
no | [] |
Tools the task may use |
expected |
no | {} |
What a successful run produces, e.g. { artifacts = ["out.csv"] } |
graders |
no | [] |
Grader ids. Empty means artifact checks on expected.artifacts. |
tags |
no | [] |
Free-form labels for slicing results |
timeout-seconds |
no | 600 |
Per-task timeout |
Grading¶
In v0.1, a task succeeds when every file in expected.artifacts exists in the task's
workspace after the run. This is the artifact: grader, and it's the default when
graders is empty. Richer graders (tests, rubric judges) are on the
roadmap.
Using a pack¶
Point the manifest at one or more packs:
[tasks]
packs = ["./tasks", "../shared-packs/support-tickets"]
seeds = 3 # runs per task per candidate; default 3
[tasks.splits]
search = 0.7 # fraction of tasks used to pick the winner; the rest is holdout
Pack paths are relative to the directory you run hlock in. Each pack's contents are
hashed, and the hash is recorded so evidence can be traced back to the exact tasks.
Writing good tasks¶
- Sample real work. Pull tasks from tickets, logs, or traces you already have. Synthetic tasks are useful for filling gaps, but they shouldn't make up the core.
- Keep the split meaningful. With fewer than about ten tasks, the holdout split is too small to say much. Add tasks before you trust a narrow win.
- Make success checkable. Every task should produce something a grader can check without a human.
- Version the pack. Bump
versionwhen tasks change, so evidence from different versions isn't compared by accident.