Skip to main content

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.6.9+) 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. Add --stream to publish tool events on stdout as they occur, ahead of that same report (docs/streaming.md). Full docs: docs/cli.md.

Concepts

  • Provider is the boundary — onejudge never talks to a model directly. Every model call goes through oneharness, and harness/model selection lives in oneharness's config files, not onejudge.
    • OneharnessProvider (default) shells out to the oneharness CLI (v0.6.9+): the agent side uses the discovered oneharness.toml, and the judge side uses a separate --config file (default oneharness.judge.toml). With stream: true it consumes the agent turn as it happens — NDJSON tool events, then a terminal report — instead of only when it ends (docs/streaming.md).
    • CommandProvider speaks a small JSON-lines protocol, for a custom backend or a deterministic test double.
    • SplitProvider composes 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).
  • Engine runs a Conversation (a Skill, an initial input, and an optional SimulatedUser) into a Transcript, bounded by max_turns / done_when / the skill declaring itself done.
  • Transcript carries each turn plus the normalized **ToolEvent**s the skill took, so the judge — and a ToolQuery — can reason over what the skill did, not just what it said.
  • Report is onejudge's own versioned contract (SCHEMA_VERSION): a serializable bundle of the transcript, verdicts, optional free-text assessment, and usage that higher-level frameworks compose over and re-export. See docs/contract.md.

Two things it improves over the in-skilltest engine:

  1. 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 commit the skill actually ran — not only from what it said. Transcript also exposes a ToolQuery primitive for events-backed assertions with no judge call.
  2. 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 — the OneharnessProvider path against a real harness (see docs/live-tier.md).

See AGENTS.md for the durable contributor guide.

License

MIT.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

onejudge_cli-0.3.5-py3-none-win_amd64.whl (854.2 kB view details)

Uploaded Python 3Windows x86-64

onejudge_cli-0.3.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.0 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

onejudge_cli-0.3.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (864.9 kB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

onejudge_cli-0.3.5-py3-none-macosx_11_0_arm64.whl (854.4 kB view details)

Uploaded Python 3macOS 11.0+ ARM64

onejudge_cli-0.3.5-py3-none-macosx_10_12_x86_64.whl (898.0 kB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

Details for the file onejudge_cli-0.3.5-py3-none-win_amd64.whl.

File metadata

  • Download URL: onejudge_cli-0.3.5-py3-none-win_amd64.whl
  • Upload date:
  • Size: 854.2 kB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for onejudge_cli-0.3.5-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 d77e2a4bf76a13235bad6149c569ff6f5b3ed5d73bed5cc9bb48f74be3f4cb6a
MD5 ed5d07df276a25f03ba0b17b20e6b0da
BLAKE2b-256 b626b76e434cf91f58c2f1caf747f19af7a1342d30837741a61ccf309b71e4f1

See more details on using hashes here.

Provenance

The following attestation bundles were made for onejudge_cli-0.3.5-py3-none-win_amd64.whl:

Publisher: release-pypi.yml on nickderobertis/onejudge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file onejudge_cli-0.3.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for onejudge_cli-0.3.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 b04259c80350fdc7504d75e520f58be2206d094abeda54013522200e67d5debd
MD5 18a70060420f30b51f328da61bc90053
BLAKE2b-256 575db75245a21836d2079da8fc03470b44f3ad6402ca6af9a3cea1e1e030fd2a

See more details on using hashes here.

Provenance

The following attestation bundles were made for onejudge_cli-0.3.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release-pypi.yml on nickderobertis/onejudge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file onejudge_cli-0.3.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for onejudge_cli-0.3.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 4d9304c38402da01b7a71ca5f29db33814b5570c8f1c5ea4f9e71a40abf0cb3c
MD5 c5357cf478e60875a7546cbe08a8681a
BLAKE2b-256 707a572678b07750ab95b8ffa85081015989964d0cfcedbd9dab374ab70d0ac2

See more details on using hashes here.

Provenance

The following attestation bundles were made for onejudge_cli-0.3.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release-pypi.yml on nickderobertis/onejudge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file onejudge_cli-0.3.5-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for onejudge_cli-0.3.5-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 4b2155953936e6fb427d2818e2e7fbcae4ac8a883a44adf3565a9f1e612e72ac
MD5 8b50c17ce552151e100591f0ee5db2ab
BLAKE2b-256 67a1d9bd7d5c5f48b6f79c3842dd2f90629ab05867d14f1d5852f313f67de743

See more details on using hashes here.

Provenance

The following attestation bundles were made for onejudge_cli-0.3.5-py3-none-macosx_11_0_arm64.whl:

Publisher: release-pypi.yml on nickderobertis/onejudge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file onejudge_cli-0.3.5-py3-none-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for onejudge_cli-0.3.5-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 9708c28553e107fbe9047269bd159167b99a57738115f6535f688933a4434160
MD5 d675d902cab587b5f3245d0b5fe50a1b
BLAKE2b-256 3fab3c1225e2390ebca42b653641d70c52815b4cfff67caa355b1796420da5f0

See more details on using hashes here.

Provenance

The following attestation bundles were made for onejudge_cli-0.3.5-py3-none-macosx_10_12_x86_64.whl:

Publisher: release-pypi.yml on nickderobertis/onejudge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.7.0

5 files

0.6.2

5 files

0.6.1

5 files

0.6.0

5 files

0.5.4

5 files

0.5.3

5 files

0.5.2

5 files

0.5.1

5 files

0.5.0

5 files

0.4.0

5 files

0.3.10

5 files

0.3.9

5 files

0.3.8

5 files

0.3.7

5 files

0.3.6

5 files

This release

0.3.5 This release

5 files

0.3.4

5 files

0.3.3

5 files

0.3.2

5 files

0.3.1

5 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page