harness-lock (hlock)
Lock your agent into place.
hlock treats an agent system the way a package manager treats dependencies. You
bring your domain tasks, the models you're allowed to use, and the harness you
like. hlock searches the stack, measures it, and writes a harness.lock: a pinned,
version-controlled, enforceable description of the agent that actually works for
your domain.
| Package manager | hlock |
|---|---|
pyproject.toml |
harness.toml — the manifest: what you allow and require |
| dependency resolver | tuner: search over LLMs × decision models × harnesses × policies |
uv.lock |
harness.lock — the lockfile: what was chosen, pinned, hashed, plus the evidence |
CI --locked check |
hlock check — fail if the running stack drifts from the lock |
See SPEC.md for the full design (manifest, lockfile schema, component model,
task packs, search strategies, the Flyte backend, open-weights models).
The (superseded) reference pure-Python implementation is preserved under
python-legacy/.
Architecture
Python API/CLI (import harness_lock as hlock, `hlock` command) <- PyO3 bindings
│
▼
harness-lock-core (Rust) manifest · lockfile · store ·
task packs · search · tuner
│ │
local (reference)│ └── devbox / cluster: Python worker
▼ (harness_lock.tasks.worker)
in-process │ └── optional Flyte raise
▼ (flyte plugins-rs)
real LLM · data · finetune
crates/harness-lock-core— the core library (pure Rust, no Python). It stays the orchestrator + aggregator; it never does heavy compute itself.crates/harness-lock— PyO3 extension (harness_lock._harness_lock) exposing the same user-facing Python API as the reference implementation via theharness_lockpackage.harness_lock/— the mixed Python package: re-exports the compiledharness_lock._harness_lock, thehlockCLI, and the heavy-work Python worker (harness_lock/tasks/worker.py).crates/harness-lock-core/src/worker.rs— the Rust↔Python bridge: aPythonAdapter(behind theHarnessAdaptertrait) that shells trials out to the worker over a versioned JSON contract. See Backends.crates/harness-lock-core/src/flyte.rs— provisional flyte-sdk-rs integration behind theflytecargo feature (not yet validated).
The Rust vs Python split for hlock optimize: pure/deterministic logic — manifests,
lockfiles, search planning, trial bookkeeping, evidence aggregation, lock construction —
lives in Rust. Ecosystem- and GPU-heavy work — real LLM calls, user-trace loading
(pandas/polars), synthetic data, model grading, prompt search, LoRA fine-tuning — lives in
the Python worker, which runs where the Python toolchain does (devbox / flyte local /
cluster) and may itself raise each body to a real Flyte task. The Rust core only ever
speaks the JSON contract, so it never depends on where the work ran.
Install
From the Rust side you depend on the crate; from Python you get native bindings.
# Python bindings — pip / uv / uvx
# Rust crate(s) (publish later; locally)
# core library, if you depend on it directly
A local editable build needs a Rust toolchain + a Python ≤3.13 interpreter:
&&
Quickstart (local, no accounts)
The built-in reference provider and harness are deterministic and offline, so the
whole loop runs with zero setup and no API keys.
Python API (Layer 1)
=
=
=
Backends
local— in-process Rust reference harness (default; deterministic, offline, CI-friendly).devbox/cluster— the Rust core drives the Python worker (python -m harness_lock.tasks.worker) over a versioned JSON contract. The worker runs each heavy work item where the Python toolchain and GPUs live, and best-effort raises it to a real Flyte task via the Pythonflyte/flyteplugins-rspackages (~/.flyte/config.yaml). When the optional deps are absent it degrades gracefully (deterministic LLM fallback, in-process Flyte lift, availability flags).
Select the backend with --backend local|devbox|cluster (or hlock resolve --backend …), which
overrides the manifest's [backend] target. The interpreter the worker runs under is
taken from $HL_PYTHON (default python3) — pin it to the interpreter that has
harness-lock installed.
# real LLM calls: export HL_LLM_BASE_URL=http://… HL_LLM_MODEL=qwen-32b [HL_LLM_API_KEY=…]
Development
Release & publishing
CI/CD publishes on git tags v* (and via workflow_dispatch):
- Python bindings → PyPI as
harness-lock(.github/workflows/publish-python.yml).maturinbuilds an abi3 wheel (CPython ≥ 3.9) for Linux/macOS/Windows and a source dist. Publishing uses trusted publishing (OIDC) — register this GitHub repo as a PyPI publisher for thepypienvironment — or set a token. For test-PyPI publishes, run the workflow with--pypi testand setTEST_PYPI_TOKEN. - Rust crates → crates.io as
harness-lock-core(dependency) thenharness-lock(.github/workflows/publish-cargo.yml). Set theCARGO_REGISTRY_TOKENsecret.
Daily CI (ci.yml) runs cargo test for the core and pytest + ruff for the bindings on
Python 3.9/3.12/3.13.
&&
Status
Early v0.1 (Rust core): manifest + lockfile schema v1, content-addressed store,
task packs with graders, reference provider/harness, grid search + Pareto
selection, a working init / lock / show / check / tree / run / backend CLI via
PyO3 bindings, and installable via pip/uv (crate not yet published).