Drive a harness through a simulated conversation and score the transcript.
Project description
onejudge
A Rust library that drives a simulated interaction and evaluation loop on top
of oneharness: take a skill or
agent, drive it through a multi-turn conversation with a simulated user, and score
the resulting transcript with natural-language (judge) verdicts and tool-event
queries.
It is the engine extracted from
skilltest (see
nickderobertis/skilltest#31).
The layering:
oneharness → one harness invocation, one JSON report (pure substrate)
onejudge → simulated interaction + judging loop (this crate)
skilltest → test-framework surface: cases, evals-as-assertions, SDKs
Reach for onejudge when you want to "drive a harness through a simulated conversation and score the transcript" without skilltest's YAML / case framing.
Install
cargo add onejudge
Minimum supported Rust version: 1.82.
The onejudge CLI
The same engine that tests a skill can drive real work. onejudge run points a
harness at a task and lets an LLM-driven simulated user supervise it — pushing
back, asking for verification, re-prompting — until a done_when condition holds
or max_turns is hit. Configured by YAML; the library API is unchanged and CLI
deps (clap, a YAML parser) are opt-in behind the non-default cli feature.
Spin up a run in three steps:
cargo install onejudge --features cli # or: install.sh (prebuilt archives)
onejudge init # scaffold onejudge.yaml + oneharness configs
onejudge run # reads ./onejudge.yaml, drives to completion
init shells out to oneharness init (needs oneharness 0.3.20+) to scaffold
oneharness.toml (the agent side) and oneharness.judge.toml (the judge side),
then writes a fully-commented loop-only onejudge.yaml. The fields that make a run
yours are task (what to do), the system framing — a skill (a SKILL.md
directory) and/or a system_prompt, both optional — and the user block
(persona / done_when / max_turns — omit it for a single-turn run). After
each nonterminal agent turn, one unified supervisor call either completes with a
reason or supplies the exact next user message. It sees compact normalized tool
summaries by default, never raw dumps; when needed it may inspect the agent-side
recording with oneharness history show <session>-skill --project <worktree> --format text. Agent and judge harnesses run in that worktree, but only agent
runs are automatically history-recorded. Harness
and model selection lives in those oneharness.toml
files, not onejudge.yaml. onejudge schema prints the annotated config, the
single source of truth for every field.
Flags override the file (flags > file > defaults), so one config serves many tasks:
onejudge run --task - < task.txt, --max-turns 8, --format json -o result.json.
Config
A run is a YAML file carrying only the loop's own concerns. The fields that make it
yours — task, the system framing (skill and/or system_prompt), and the user
block. Everything else has a default; omit user for a single-turn run. A minimal
config:
system_prompt: You are a senior engineer. Complete the task and keep tests green.
# skill: ./skills/my-skill # optional: a SKILL.md dir; its body is appended
task: Add a --version flag to the CLI.
user: # the simulated supervisor that drives the loop
persona: A demanding tech lead. Do not accept "done" until you have verified it.
done_when: the task is complete and all tests pass
max_turns: 8
evals: # optional: score the finished transcript
- criterion: the change is well-scoped and readable
kind: numeric
scale: [1, 5]
assessment: Identify useful follow-up work left out of scope.
The harness and model come from oneharness's own config (oneharness.toml for the
agent, oneharness.judge.toml for the judge side) — onejudge init scaffolds
them. More keys — provider (oneharness / command / split, with the
oneharness judge_config path), session, boolean evals. onejudge init writes a
fully-commented starter and onejudge schema prints the annotated field reference
(the single source of truth); it is validated strictly (deny_unknown_fields) so a
typo is a loud error.
Human output is the conversation + tool actions + completion status + eval
verdicts; --format json emits the versioned Report. The
exit code is 0 only when the task completed and every boolean eval passed, 1
if it hit max_turns or a boolean eval failed, 2 on a bad config. Full docs:
docs/cli.md.
Concepts
Provideris the boundary — onejudge never talks to a model directly. Every model call goes throughoneharness, and harness/model selection lives in oneharness's config files, not onejudge.OneharnessProvider(default) shells out to theoneharnessCLI (v0.3.20+): the agent side uses the discoveredoneharness.toml, and the judge side uses a separate--configfile (defaultoneharness.judge.toml).CommandProviderspeaks a small JSON-lines protocol, for a custom backend or a deterministic test double.SplitProvidercomposes two providers — one that runs the skill, one that judges and role-plays the user (e.g. run the skill on one harness, judge on another).
Engineruns aConversation(aSkill, an initial input, and an optionalSimulatedUser) into aTranscript, bounded bymax_turns/done_when/ the skill declaring itself done.Transcriptcarries each turn plus the normalized **ToolEvent**s the skill took, so the judge — and aToolQuery— can reason over what the skill did, not just what it said.Reportis onejudge's own versioned contract (SCHEMA_VERSION): a serializable bundle of the transcript, verdicts, optional free-textassessment, and usage that higher-level frameworks compose over and re-export. See docs/contract.md.
Two things it improves over the in-skilltest engine:
- The judge sees tool events. Verdicts render the transcript with a compact,
token-budget-aware summary of each turn's tool calls, so a criterion like
"the change was committed" can be decided from the
git committhe skill actually ran — not only from what it said.Transcriptalso exposes aToolQueryprimitive for events-backed assertions with no judge call. - One caller-owned session name. The engine always threads a single
--session <name>across turns instead of extracting and re-passing a native id; if a harness cannot bind a session, the provider gracefully retries the call without it, re-prompting the inlined transcript.
Example
use onejudge::{Conversation, Engine, OneharnessProvider, Settings, SimulatedUser, Skill};
let provider = OneharnessProvider::new();
// Harness/model selection lives in oneharness's config files, not here; Settings
// carries only the loop's own concerns (turn cap, session name).
let settings = Settings::new();
let engine = Engine::new(&provider, settings);
let skill = Skill::new("greeter", "./skills/greeter", "Greet the user warmly.");
let user = SimulatedUser::new("A curious first-time visitor.")
.done_when("the assistant has answered the visitor's question")
.max_turns(6);
let outcome = engine.run(&Conversation::multi_turn(skill, "hi", user))?;
let verdict = engine.judge_boolean("the reply was welcoming", &outcome.transcript)?;
println!("{:?}: {}", verdict.value, verdict.reason);
# Ok::<(), onejudge::Error>(())
Drive a deterministic backend instead of a live harness by pointing a
CommandProvider at any command that speaks the protocol.
Development
The command surface is a just recipe set; just --list is the index.
just bootstrap # clean-clone setup: toolchain + cargo tools + fetch
just check # the full gate: format, lint, doc, coverage-enforced tests, audit
just test # fast unit + integration + e2e
The gate is deterministic and offline — the model is faked by real subprocess
test doubles, never mocked. The one path that needs a real external service is
proven in an opt-in tier, kept out of check:
just test-live— theOneharnessProviderpath against a real harness (see docs/live-tier.md).
See AGENTS.md for the durable contributor guide.
License
MIT.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file onejudge_cli-0.3.4-py3-none-win_amd64.whl.
File metadata
- Download URL: onejudge_cli-0.3.4-py3-none-win_amd64.whl
- Upload date:
- Size: 637.7 kB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a5ce4d3a4234fe72de86789b7063bc7680b7a626badae264468d3748be3ba192
|
|
| MD5 |
03a49374f90f767407694f0af5b70152
|
|
| BLAKE2b-256 |
5f820117ac516bc7bf2a5ffa196c230eb94556b85bafd14f09bc29b354f69560
|
Provenance
The following attestation bundles were made for onejudge_cli-0.3.4-py3-none-win_amd64.whl:
Publisher:
release-pypi.yml on nickderobertis/onejudge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
onejudge_cli-0.3.4-py3-none-win_amd64.whl -
Subject digest:
a5ce4d3a4234fe72de86789b7063bc7680b7a626badae264468d3748be3ba192 - Sigstore transparency entry: 2206263383
- Sigstore integration time:
-
Permalink:
nickderobertis/onejudge@0f1e97740f7f3b700d70c8858ebb9d93bbf435ac -
Branch / Tag:
refs/tags/v0.3.4 - Owner: https://github.com/nickderobertis
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-pypi.yml@0f1e97740f7f3b700d70c8858ebb9d93bbf435ac -
Trigger Event:
release
-
Statement type:
File details
Details for the file onejudge_cli-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: onejudge_cli-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 712.8 kB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
62c6191d3fec08299adccf2f6c72064eac25ea76056498ca8756c867b7965bc9
|
|
| MD5 |
1c18bc349d4ef51c1ec74d32312faf96
|
|
| BLAKE2b-256 |
2da3de2a76e9bba42838576323c61980e331c09eb06a17fdbeeca3b92ab0993d
|
Provenance
The following attestation bundles were made for onejudge_cli-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release-pypi.yml on nickderobertis/onejudge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
onejudge_cli-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
62c6191d3fec08299adccf2f6c72064eac25ea76056498ca8756c867b7965bc9 - Sigstore transparency entry: 2206263427
- Sigstore integration time:
-
Permalink:
nickderobertis/onejudge@0f1e97740f7f3b700d70c8858ebb9d93bbf435ac -
Branch / Tag:
refs/tags/v0.3.4 - Owner: https://github.com/nickderobertis
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-pypi.yml@0f1e97740f7f3b700d70c8858ebb9d93bbf435ac -
Trigger Event:
release
-
Statement type:
File details
Details for the file onejudge_cli-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: onejudge_cli-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 668.0 kB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7844d4c7d95b0f8b88ce73f80609d47ea44cee03fb8cdb3da0bc1c0e6f892283
|
|
| MD5 |
40701ecc5ffab1e38123092765bf75a9
|
|
| BLAKE2b-256 |
a435408dec267a4589041dfb1dc455661a0726c552365f386ceeeaa6d0c686d5
|
Provenance
The following attestation bundles were made for onejudge_cli-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release-pypi.yml on nickderobertis/onejudge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
onejudge_cli-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
7844d4c7d95b0f8b88ce73f80609d47ea44cee03fb8cdb3da0bc1c0e6f892283 - Sigstore transparency entry: 2206263370
- Sigstore integration time:
-
Permalink:
nickderobertis/onejudge@0f1e97740f7f3b700d70c8858ebb9d93bbf435ac -
Branch / Tag:
refs/tags/v0.3.4 - Owner: https://github.com/nickderobertis
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-pypi.yml@0f1e97740f7f3b700d70c8858ebb9d93bbf435ac -
Trigger Event:
release
-
Statement type:
File details
Details for the file onejudge_cli-0.3.4-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: onejudge_cli-0.3.4-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 660.2 kB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
58b990515f9de6998ae57357afcf9417f853f0ae3e7fdbaa9a4bee2e1458da45
|
|
| MD5 |
0b461a28279d626cb9f440a12e2f17cc
|
|
| BLAKE2b-256 |
6a28a680bfb377ed30a7db3d007d51865ce9e049df42496a125dc14ce75f35c9
|
Provenance
The following attestation bundles were made for onejudge_cli-0.3.4-py3-none-macosx_11_0_arm64.whl:
Publisher:
release-pypi.yml on nickderobertis/onejudge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
onejudge_cli-0.3.4-py3-none-macosx_11_0_arm64.whl -
Subject digest:
58b990515f9de6998ae57357afcf9417f853f0ae3e7fdbaa9a4bee2e1458da45 - Sigstore transparency entry: 2206263450
- Sigstore integration time:
-
Permalink:
nickderobertis/onejudge@0f1e97740f7f3b700d70c8858ebb9d93bbf435ac -
Branch / Tag:
refs/tags/v0.3.4 - Owner: https://github.com/nickderobertis
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-pypi.yml@0f1e97740f7f3b700d70c8858ebb9d93bbf435ac -
Trigger Event:
release
-
Statement type:
File details
Details for the file onejudge_cli-0.3.4-py3-none-macosx_10_12_x86_64.whl.
File metadata
- Download URL: onejudge_cli-0.3.4-py3-none-macosx_10_12_x86_64.whl
- Upload date:
- Size: 693.8 kB
- Tags: Python 3, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
883de25517e36bc2bb7f27cf33fa25856621a73a861c964dcfbb741a5cc79265
|
|
| MD5 |
40417aa721c3f83f765faf7a71e35bdd
|
|
| BLAKE2b-256 |
09dd3270839e0bd7718c7fb0ac6cb8d13f0456cb58ccf9b2ae24fd86601c93f1
|
Provenance
The following attestation bundles were made for onejudge_cli-0.3.4-py3-none-macosx_10_12_x86_64.whl:
Publisher:
release-pypi.yml on nickderobertis/onejudge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
onejudge_cli-0.3.4-py3-none-macosx_10_12_x86_64.whl -
Subject digest:
883de25517e36bc2bb7f27cf33fa25856621a73a861c964dcfbb741a5cc79265 - Sigstore transparency entry: 2206263409
- Sigstore integration time:
-
Permalink:
nickderobertis/onejudge@0f1e97740f7f3b700d70c8858ebb9d93bbf435ac -
Branch / Tag:
refs/tags/v0.3.4 - Owner: https://github.com/nickderobertis
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-pypi.yml@0f1e97740f7f3b700d70c8858ebb9d93bbf435ac -
Trigger Event:
release
-
Statement type: