Harness bundle¶
New in Harness Lab 0.2.0. Upgrade an older install with
pip install -U harnesslab.
A harness bundle is the growable outer layer of a coding-agent harness: a plain directory that a
variant carries with harness: <path> and that a grow session edits.
harnesses/baseline/
harness.yaml optional manifest: name, description (never edited by an optimizer)
system_prompt.md appended to the agent's system prompt
skills/<name>/SKILL.md YAML frontmatter with `name:` required; other files under skills/ allowed
hooks.json Claude Code hooks, plugin format: {"hooks": {...}}
agents/<name>.md Claude Code custom agents
fake.yaml simulation only: solve_tasks, fail_tasks, llm_calls (read by the fake runner)
Rules¶
- Allowed paths: the five top-level files above and anything under
skills/oragents/. No other files, no symlinks, no... - Caps: 64 000 bytes per file, 512 000 bytes per bundle, 64 files.
hooks.jsonmust be JSON with a top-levelhooksobject;fake.yamlandharness.yamlmust be YAML mappings; everyskills/<name>/SKILL.mdstarts with frontmatter containingname.- Hidden files and caches (
.DS_Store,__pycache__, dotfiles) are ignored on load. - An empty directory is a valid bundle: the "strategy-free scaffold".
harnesslab harness check <dir> [--suite <suite>] validates a bundle and, with a suite, runs the
leak lint against it.
The hash¶
harness_hash is a SHA-256 over every file's relative path, size and bytes, sorted by path. It is
recorded on every variant and run that used the bundle, and it is part of the variant's
config_hash. The experiment service snapshots each bundle under
.harnesslab/artifacts/<experiment>/harness/<hash>/, and the variant record lists the files.
How runners apply it¶
| Runner | system_prompt.md |
skills/, hooks.json, agents/ |
fake.yaml |
|---|---|---|---|
claude |
written to a file with append_system_prompt and the action policy, passed as --append-system-prompt-file |
materialized as a plugin directory (.claude-plugin/plugin.json, skills/, hooks/hooks.json, agents/) and passed as --plugin-dir; works under --bare |
ignored |
codex |
prompt prefix, followed by each skill under ## Skill: <name> |
hooks and agents ignored, recorded in a harness_components_ignored event |
ignored |
generic |
prepended to the prompt | available to the command through {harness_dir} and HARNESSLAB_HARNESS_DIR |
ignored |
fake |
ignored | ignored | solve_tasks and fail_tasks override the variant's behavior per task (solve wins); component_solves solves tasks only while a component is present; llm_calls sets the simulated count and component_llm_calls adds to it per present component |
Hooks are code¶
hooks.json is not text the agent reads: Claude Code executes its commands on your machine, with
your privileges, outside the agent's --allowedTools allowlist, and they can read anything you
can, including a suite's hidden tests. harnesslab harness check prints a warning for every bundle
that contains one. Review hook commands like any other code you run.
The bundled harnesses/sentinel is a bundle whose only component is such a hook: a pre-tool-use
decider that blocks risky actions. Its command runs "${HARNESSLAB_PYTHON:-python3}" -I -m
harnesslab.safety.decide; the Claude runner sets HARNESSLAB_PYTHON, HARNESSLAB_WORKTREE
and HARNESSLAB_SUITE_DIR so the hook finds Harness Lab and knows the run's boundaries. Run your
own Python hooks with -I too, so modules in the agent's worktree cannot shadow theirs. See
Safety.
Optimizer edit rules¶
An optimizer may edit or add content files only. It may not delete a file, edit harness.yaml,
add or edit hooks.json (unless the grow spec sets optimizer.allow_hooks: true), change more
than optimizer.max_files files, exceed the caps, mention a suite task id or a hidden test name,
or copy a line of 24 characters or more verbatim from a hidden test. fake.yaml is
exempt from the task-id rule only, because real runners ignore it and the fake optimizer needs it.