benchspec 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- benchspec-0.0.1/.gitignore +16 -0
- benchspec-0.0.1/LICENSE +21 -0
- benchspec-0.0.1/Makefile +41 -0
- benchspec-0.0.1/PKG-INFO +210 -0
- benchspec-0.0.1/README.md +178 -0
- benchspec-0.0.1/docs/concepts.md +174 -0
- benchspec-0.0.1/docs/configuration.md +237 -0
- benchspec-0.0.1/docs/harnesses.md +139 -0
- benchspec-0.0.1/docs/quickstart.md +242 -0
- benchspec-0.0.1/docs/results.md +184 -0
- benchspec-0.0.1/docs/sandbox.md +167 -0
- benchspec-0.0.1/docs/style/development.md +243 -0
- benchspec-0.0.1/docs/writing-evals.md +264 -0
- benchspec-0.0.1/evals/binder/conftest.py +205 -0
- benchspec-0.0.1/evals/binder/corpus.yaml +309 -0
- benchspec-0.0.1/evals/binder/test_corpus.py +147 -0
- benchspec-0.0.1/evals/binder/test_corpus_integrity.py +378 -0
- benchspec-0.0.1/evals/e2e/hello/SKILL.md +23 -0
- benchspec-0.0.1/evals/e2e/hello/evals/hello/greets-by-name.eval.md +12 -0
- benchspec-0.0.1/evals/e2e/hello/evals/hello/setup.sh +7 -0
- benchspec-0.0.1/evals/e2e/hello/evals/hello-file/setup.sh +7 -0
- benchspec-0.0.1/evals/e2e/hello/evals/hello-file/workspace/request.md +1 -0
- benchspec-0.0.1/evals/e2e/hello/evals/hello-file/writes-greeting-file.eval.md +17 -0
- benchspec-0.0.1/pyproject.toml +134 -0
- benchspec-0.0.1/src/benchspec/__init__.py +17 -0
- benchspec-0.0.1/src/benchspec/__main__.py +178 -0
- benchspec-0.0.1/src/benchspec/agents/__init__.py +106 -0
- benchspec-0.0.1/src/benchspec/agents/base.py +305 -0
- benchspec-0.0.1/src/benchspec/agents/claude.py +334 -0
- benchspec-0.0.1/src/benchspec/agents/codex.py +607 -0
- benchspec-0.0.1/src/benchspec/agents/opencode.py +653 -0
- benchspec-0.0.1/src/benchspec/config/__init__.py +3 -0
- benchspec-0.0.1/src/benchspec/config/arms.py +281 -0
- benchspec-0.0.1/src/benchspec/config/sets.py +207 -0
- benchspec-0.0.1/src/benchspec/exit_codes.py +53 -0
- benchspec-0.0.1/src/benchspec/grading/__init__.py +6 -0
- benchspec-0.0.1/src/benchspec/grading/binder.py +388 -0
- benchspec-0.0.1/src/benchspec/grading/checkers.py +272 -0
- benchspec-0.0.1/src/benchspec/grading/judge.py +219 -0
- benchspec-0.0.1/src/benchspec/grading/judges/__init__.py +27 -0
- benchspec-0.0.1/src/benchspec/grading/judges/config.py +164 -0
- benchspec-0.0.1/src/benchspec/grading/judges/registry.py +79 -0
- benchspec-0.0.1/src/benchspec/grading/trajectory.py +230 -0
- benchspec-0.0.1/src/benchspec/grading/trigger.py +118 -0
- benchspec-0.0.1/src/benchspec/orchestration/__init__.py +6 -0
- benchspec-0.0.1/src/benchspec/orchestration/cases.py +225 -0
- benchspec-0.0.1/src/benchspec/orchestration/environments.py +117 -0
- benchspec-0.0.1/src/benchspec/orchestration/execution.py +548 -0
- benchspec-0.0.1/src/benchspec/orchestration/results.py +165 -0
- benchspec-0.0.1/src/benchspec/orchestration/room.py +191 -0
- benchspec-0.0.1/src/benchspec/orchestration/workspace.py +87 -0
- benchspec-0.0.1/src/benchspec/py.typed +0 -0
- benchspec-0.0.1/src/benchspec/reporting/__init__.py +3 -0
- benchspec-0.0.1/src/benchspec/reporting/analyze.py +102 -0
- benchspec-0.0.1/src/benchspec/reporting/manifest.py +180 -0
- benchspec-0.0.1/src/benchspec/reporting/report.py +750 -0
- benchspec-0.0.1/src/benchspec/runners/__init__.py +3 -0
- benchspec-0.0.1/src/benchspec/runners/pytest.py +456 -0
- benchspec-0.0.1/src/benchspec/runners/run.py +111 -0
- benchspec-0.0.1/src/benchspec/sandbox/__init__.py +3 -0
- benchspec-0.0.1/src/benchspec/sandbox/backend.py +386 -0
- benchspec-0.0.1/src/benchspec/sandbox/project.py +138 -0
- benchspec-0.0.1/src/benchspec/sandbox/provenance.py +309 -0
- benchspec-0.0.1/src/benchspec/sandbox/sandbox.py +639 -0
- benchspec-0.0.1/src/benchspec/specs/__init__.py +3 -0
- benchspec-0.0.1/src/benchspec/specs/discovery.py +263 -0
- benchspec-0.0.1/src/benchspec/specs/lint.py +90 -0
- benchspec-0.0.1/src/benchspec/specs/mdformat.py +220 -0
- benchspec-0.0.1/src/benchspec/specs/schema.py +245 -0
- benchspec-0.0.1/src/benchspec/testing.py +57 -0
- benchspec-0.0.1/tests/__init__.py +1 -0
- benchspec-0.0.1/tests/agents/fixtures/codex_parse_error.jsonl +3 -0
- benchspec-0.0.1/tests/agents/fixtures/codex_parse_success.jsonl +8 -0
- benchspec-0.0.1/tests/agents/fixtures/codex_parse_tool_only.jsonl +4 -0
- benchspec-0.0.1/tests/agents/fixtures/codex_route_fired.jsonl +4 -0
- benchspec-0.0.1/tests/agents/fixtures/codex_route_nofire.jsonl +4 -0
- benchspec-0.0.1/tests/agents/fixtures/opencode_route_fired.jsonl +4 -0
- benchspec-0.0.1/tests/agents/fixtures/opencode_route_nofire.jsonl +4 -0
- benchspec-0.0.1/tests/agents/test_base.py +132 -0
- benchspec-0.0.1/tests/agents/test_claude.py +618 -0
- benchspec-0.0.1/tests/agents/test_codex.py +732 -0
- benchspec-0.0.1/tests/agents/test_factory.py +47 -0
- benchspec-0.0.1/tests/agents/test_opencode.py +1150 -0
- benchspec-0.0.1/tests/agents/test_registry.py +52 -0
- benchspec-0.0.1/tests/config/__init__.py +3 -0
- benchspec-0.0.1/tests/config/test_arms.py +629 -0
- benchspec-0.0.1/tests/config/test_sets.py +231 -0
- benchspec-0.0.1/tests/conftest.py +66 -0
- benchspec-0.0.1/tests/fixtures/activation/evals/activation-demo/eval.md +14 -0
- benchspec-0.0.1/tests/fixtures/judge/unset-judge-env.toml +14 -0
- benchspec-0.0.1/tests/fixtures/judge/unsupported-judge-harness.toml +10 -0
- benchspec-0.0.1/tests/fixtures/sandbox/microsandbox.toml +15 -0
- benchspec-0.0.1/tests/fixtures/sandbox/two-backends.toml +24 -0
- benchspec-0.0.1/tests/grading/__init__.py +3 -0
- benchspec-0.0.1/tests/grading/judges/__init__.py +1 -0
- benchspec-0.0.1/tests/grading/judges/test_claude_code.py +114 -0
- benchspec-0.0.1/tests/grading/judges/test_codex.py +149 -0
- benchspec-0.0.1/tests/grading/judges/test_config.py +140 -0
- benchspec-0.0.1/tests/grading/judges/test_judge_registry.py +118 -0
- benchspec-0.0.1/tests/grading/judges/test_opencode.py +155 -0
- benchspec-0.0.1/tests/grading/test_binder.py +440 -0
- benchspec-0.0.1/tests/grading/test_checkers.py +503 -0
- benchspec-0.0.1/tests/grading/test_judge.py +280 -0
- benchspec-0.0.1/tests/grading/test_trajectory.py +303 -0
- benchspec-0.0.1/tests/grading/test_trigger.py +201 -0
- benchspec-0.0.1/tests/orchestration/__init__.py +3 -0
- benchspec-0.0.1/tests/orchestration/test_cases_messages.py +37 -0
- benchspec-0.0.1/tests/orchestration/test_environments.py +71 -0
- benchspec-0.0.1/tests/orchestration/test_execution.py +2198 -0
- benchspec-0.0.1/tests/orchestration/test_results.py +432 -0
- benchspec-0.0.1/tests/orchestration/test_room.py +254 -0
- benchspec-0.0.1/tests/orchestration/test_room_history.py +60 -0
- benchspec-0.0.1/tests/orchestration/test_workspace.py +96 -0
- benchspec-0.0.1/tests/reporting/__init__.py +3 -0
- benchspec-0.0.1/tests/reporting/test_analyze.py +256 -0
- benchspec-0.0.1/tests/reporting/test_manifest.py +208 -0
- benchspec-0.0.1/tests/reporting/test_report.py +937 -0
- benchspec-0.0.1/tests/runners/__init__.py +3 -0
- benchspec-0.0.1/tests/runners/test_pytest.py +1462 -0
- benchspec-0.0.1/tests/runners/test_run.py +295 -0
- benchspec-0.0.1/tests/sandbox/__init__.py +3 -0
- benchspec-0.0.1/tests/sandbox/test_backend.py +293 -0
- benchspec-0.0.1/tests/sandbox/test_project.py +317 -0
- benchspec-0.0.1/tests/sandbox/test_provenance.py +354 -0
- benchspec-0.0.1/tests/sandbox/test_sandbox.py +1605 -0
- benchspec-0.0.1/tests/specs/__init__.py +3 -0
- benchspec-0.0.1/tests/specs/test_discovery.py +476 -0
- benchspec-0.0.1/tests/specs/test_lint.py +82 -0
- benchspec-0.0.1/tests/specs/test_mdformat.py +636 -0
- benchspec-0.0.1/tests/specs/test_schema.py +231 -0
- benchspec-0.0.1/tests/support.py +7 -0
- benchspec-0.0.1/tests/test_e2e_suite.py +267 -0
- benchspec-0.0.1/tests/test_exit_codes.py +43 -0
- benchspec-0.0.1/tests/test_main.py +308 -0
- benchspec-0.0.1/tests/test_readme_examples.py +38 -0
benchspec-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Swift
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
benchspec-0.0.1/Makefile
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
.PHONY: help install test e2e evals assets lint lint\:ruff lint\:houserules clean
|
|
2
|
+
.DEFAULT_GOAL := help
|
|
3
|
+
|
|
4
|
+
help: ## Show this help
|
|
5
|
+
@awk '/^[a-zA-Z0-9_:\\-]+:.*## / {t=$$0; sub(/:[ \t]*##.*/,"",t); gsub(/\\/,"",t); d=$$0; sub(/^.*## /,"",d); printf " \033[36m%-11s\033[0m %s\n", t, d}' $(MAKEFILE_LIST)
|
|
6
|
+
|
|
7
|
+
install: ## Create the venv and install dev dependencies
|
|
8
|
+
uv sync
|
|
9
|
+
|
|
10
|
+
test: ## Run the unit test suite
|
|
11
|
+
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run pytest -p pytester
|
|
12
|
+
|
|
13
|
+
e2e: ## Run benchspec's own end-to-end suite (real microVMs; needs claude+codex CLIs and provider credentials)
|
|
14
|
+
uv run benchspec run --set e2e
|
|
15
|
+
|
|
16
|
+
# Keep modest: high fan-out trips the Gemini call's ~60s timeout (12-way -> throttling).
|
|
17
|
+
BINDER_WORKERS ?= 6
|
|
18
|
+
evals: ## Run the binder corpus (binder quality, not framework function). Pass EVAL_ARGS="--collect-only -q" to dry-run collection.
|
|
19
|
+
uv run pytest -m binder_corpus -n $(BINDER_WORKERS) evals/binder $(EVAL_ARGS)
|
|
20
|
+
|
|
21
|
+
assets: ## Re-render the raster brand assets in docs/assets (terminal mock PNG/GIF, social card)
|
|
22
|
+
uv run scripts/render_assets.py
|
|
23
|
+
|
|
24
|
+
lint: ## Lint with Ruff and houserules
|
|
25
|
+
$(MAKE) lint:ruff
|
|
26
|
+
$(MAKE) lint:houserules
|
|
27
|
+
|
|
28
|
+
lint\:ruff: ## Lint with Ruff
|
|
29
|
+
uv run ruff check .
|
|
30
|
+
|
|
31
|
+
LINT_BASE ?= origin/dev
|
|
32
|
+
lint\:houserules: ## Lint changed and new Python files with houserules (needs GEMINI_API_KEY)
|
|
33
|
+
uv run houserules --base "$$(git merge-base $(LINT_BASE) HEAD)" --verbose .
|
|
34
|
+
@untracked_python_files="$$(git ls-files --others --exclude-standard -- '*.py')"; \
|
|
35
|
+
if [ -n "$$untracked_python_files" ]; then \
|
|
36
|
+
uv run houserules --verbose $$untracked_python_files; \
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
clean: ## Remove the venv and Python caches
|
|
40
|
+
rm -rf .venv .pytest_cache .ruff_cache
|
|
41
|
+
find . -type d -name __pycache__ -prune -exec rm -rf {} +
|
benchspec-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: benchspec
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: benchspec is a framework for evaluating AI agents with repeatable, isolated benchmarks. Write evals as Markdown, run each eval across named benchmark arms, and compare how agent behavior changes by harness, model, effort, and environment.
|
|
5
|
+
Project-URL: Homepage, https://github.com/theycallmeswift/benchspec
|
|
6
|
+
Project-URL: Documentation, https://github.com/theycallmeswift/benchspec/tree/dev/docs
|
|
7
|
+
Project-URL: Issues, https://github.com/theycallmeswift/benchspec/issues
|
|
8
|
+
Author-email: Swift <swift@majorleaguehacking.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Framework :: Pytest
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Software Development :: Testing
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: pytest>=8
|
|
23
|
+
Requires-Dist: python-dotenv>=1.0
|
|
24
|
+
Requires-Dist: pyyaml>=6
|
|
25
|
+
Provides-Extra: claude
|
|
26
|
+
Provides-Extra: codex
|
|
27
|
+
Provides-Extra: microsandbox
|
|
28
|
+
Requires-Dist: microsandbox<0.7,>=0.6.16; extra == 'microsandbox'
|
|
29
|
+
Provides-Extra: opencode
|
|
30
|
+
Provides-Extra: testing
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
<picture>
|
|
34
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/benchspec-wordmark-dark.svg">
|
|
35
|
+
<img alt="benchspec" src="docs/assets/benchspec-wordmark-light.svg" width="188" height="48">
|
|
36
|
+
</picture>
|
|
37
|
+
|
|
38
|
+
**Benchmark what your agent does, not what it says.**
|
|
39
|
+
|
|
40
|
+
[](https://github.com/theycallmeswift/benchspec/actions/workflows/ci.yml)
|
|
41
|
+
[](https://pypi.org/project/benchspec/)
|
|
42
|
+
[](https://pypi.org/project/benchspec/)
|
|
43
|
+
[](LICENSE)
|
|
44
|
+
|
|
45
|
+
benchspec runs an agent (Claude Code, Codex, or OpenCode) against a task
|
|
46
|
+
in a fresh microVM, checks what it actually did in the workspace, and reports the
|
|
47
|
+
result as a comparison: with your skill versus without, one model versus another,
|
|
48
|
+
one harness versus another.
|
|
49
|
+
|
|
50
|
+
<img src="docs/assets/benchmark-terminal.gif" width="800" alt="Animated terminal output: benchspec run prints a benchmark matrix with evals as rows, arms as columns, color-coded rates, and percentage-point deltas">
|
|
51
|
+
|
|
52
|
+
*End-of-run summary for a two-arm run of the in-repo [`hello`](evals/e2e/hello/)
|
|
53
|
+
suite (illustrative numbers).* Rows are evals, columns are arms (`baseline` ran
|
|
54
|
+
the agent bare, `trial` installed the skill), and every non-baseline cell shows
|
|
55
|
+
its assertion pass rate plus the delta against the baseline in percentage
|
|
56
|
+
points. The same matrix lands in `benchmark.md`, with machine-readable artifacts
|
|
57
|
+
alongside.
|
|
58
|
+
|
|
59
|
+
Teams pick harnesses, models, and prompts by anecdote: run it once, eyeball the
|
|
60
|
+
transcript, trust the vibe. benchspec turns that guess into a measurement.
|
|
61
|
+
Write the goal once, run it across the configurations you care about, and read
|
|
62
|
+
off — in percentage points — how good each one actually is at accomplishing it.
|
|
63
|
+
|
|
64
|
+
## Getting Started
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install "benchspec[microsandbox]"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
> **Pre-1.0.** The eval format and the artifact schemas are the surfaces most
|
|
71
|
+
> likely to change. microsandbox is the only sandbox backend today; `docker` is
|
|
72
|
+
> recognized in config but fails fast as not implemented.
|
|
73
|
+
|
|
74
|
+
An eval is one Markdown file: a prompt, then a checklist of plain-prose claims
|
|
75
|
+
about the workspace after the agent is done. There is no checker syntax to learn;
|
|
76
|
+
the wording is the spec. `evals/hello/greets-by-name.eval.md`:
|
|
77
|
+
|
|
78
|
+
```markdown
|
|
79
|
+
---
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Prompt
|
|
83
|
+
|
|
84
|
+
You are working in a workspace rooted at your current working directory.
|
|
85
|
+
Greet Alice by name.
|
|
86
|
+
|
|
87
|
+
## Assertions
|
|
88
|
+
|
|
89
|
+
- [ ] ./Greetings/Alice.md contains the exact line 'Hello, Alice!'
|
|
90
|
+
- [ ] Skill `hello` invoked
|
|
91
|
+
- [ ] The greeting feels warm and personable, not curt or robotic
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The benchmark is a block in `pyproject.toml`. Arms are the report columns; the
|
|
95
|
+
baseline is what the others are measured against:
|
|
96
|
+
|
|
97
|
+
```toml
|
|
98
|
+
[tool.benchspec]
|
|
99
|
+
default-set = "default"
|
|
100
|
+
|
|
101
|
+
[tool.benchspec.sets.default]
|
|
102
|
+
harness = "claude-code"
|
|
103
|
+
model = "sonnet"
|
|
104
|
+
baseline = "baseline"
|
|
105
|
+
arms = [
|
|
106
|
+
{ name = "baseline" }, # installs nothing
|
|
107
|
+
{ name = "trial" }, # setup.sh installs the skill
|
|
108
|
+
]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Then:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
benchspec lint # static checks on the assertions
|
|
115
|
+
benchspec analyze # which assertions grade deterministically, which go to the judge
|
|
116
|
+
benchspec run # every (eval × arm) in its own microVM, graded, reported
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## What you need
|
|
120
|
+
|
|
121
|
+
| | |
|
|
122
|
+
|---|---|
|
|
123
|
+
| Platform | Apple Silicon Mac, or Linux with `/dev/kvm`. Python 3.11+. |
|
|
124
|
+
| Agent CLI | `claude`, `codex`, or `opencode` on `PATH`, with its credential (for Claude Code, `CLAUDE_CODE_OAUTH_TOKEN` or `ANTHROPIC_API_KEY`). |
|
|
125
|
+
| `GEMINI_API_KEY` | The binder: a fixed Gemini call that classifies each assertion. Required for every `analyze` and `run`. |
|
|
126
|
+
| Judge credential | The judge runs on the host through an agent CLI; the default is `claude-code` with `sonnet`. Prefer a different vendor from the arms (this repo's own suite judges Claude arms with Codex). |
|
|
127
|
+
|
|
128
|
+
Credentials can live in a repo-root `.env`. A graded run can touch up to three
|
|
129
|
+
vendors: the agent's, Gemini for the binder, and the judge's. `lint` is free;
|
|
130
|
+
`analyze` and `run` spend API calls, and `run` also boots VMs. Preflight lists
|
|
131
|
+
every missing piece and exits before anything is spent.
|
|
132
|
+
|
|
133
|
+
## How a run works
|
|
134
|
+
|
|
135
|
+
```mermaid
|
|
136
|
+
flowchart LR
|
|
137
|
+
E["greets-by-name.eval.md<br/>prompt + assertions"] --> A1["arm: baseline<br/>fresh microVM,<br/>setup.sh installs nothing"]
|
|
138
|
+
E --> A2["arm: trial<br/>fresh microVM,<br/>setup.sh installs the hello skill"]
|
|
139
|
+
A1 --> F1["facts: files, SHAs,<br/>final message, tool calls"]
|
|
140
|
+
A2 --> F2["facts"]
|
|
141
|
+
F1 --> G["binder: deterministic checkers<br/>everything else: LLM judge"]
|
|
142
|
+
F2 --> G
|
|
143
|
+
G --> R["benchmark.md + benchmark.json<br/>meta.json + index.jsonl"]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Each `(eval × arm)` pair is one parametrized pytest test. A cell:
|
|
147
|
+
|
|
148
|
+
1. **Boots a microVM** from a cached snapshot with the agent CLI already
|
|
149
|
+
installed. The first run builds the snapshot (a few minutes); later runs
|
|
150
|
+
reuse it, or pay the cost up front with `benchspec sandbox:build`.
|
|
151
|
+
2. **Seeds the clean room** — the eval's optional `workspace/` files land in a
|
|
152
|
+
fresh directory mounted at `/workspace`, the agent's working directory.
|
|
153
|
+
3. **Runs `setup.sh`**, where arms diverge: it sees `$BENCHSPEC_ARM`, so the
|
|
154
|
+
baseline branch exits early and the trial branch copies the skill into place.
|
|
155
|
+
4. **Invokes the agent** on the eval's prompt.
|
|
156
|
+
5. **Collects the facts** — file tree, contents, SHA-256s, the final message,
|
|
157
|
+
the tool calls.
|
|
158
|
+
6. **Grades** — the binder maps each assertion to a deterministic checker where
|
|
159
|
+
it can do so without risk; the judge grades everything else from the
|
|
160
|
+
collected evidence alone.
|
|
161
|
+
|
|
162
|
+
Two guarantees hold throughout. Nothing in the guest can write back to your
|
|
163
|
+
checkout: `setup.sh` reaches the skill under test through a read-only staged
|
|
164
|
+
copy of your repo at `/project` (what a `git clone` would contain — never
|
|
165
|
+
`.env`, `.git`, or earlier runs' artifacts). And provider credentials are
|
|
166
|
+
injected at the network boundary, never as readable environment variables in
|
|
167
|
+
the guest.
|
|
168
|
+
|
|
169
|
+
`benchspec run` is pytest underneath, and everything after `--` goes to
|
|
170
|
+
pytest verbatim: `benchspec run -- -k greets-by-name` (equivalently
|
|
171
|
+
`pytest -k greets-by-name`) runs one eval, `-n 8` fans cells across eight
|
|
172
|
+
microVMs, and `--count 5` samples each cell five times so the report can flag a
|
|
173
|
+
delta that sits within noise.
|
|
174
|
+
|
|
175
|
+
## Why benchspec
|
|
176
|
+
|
|
177
|
+
- **Comparison is first-class.** A single pass rate is a number without a
|
|
178
|
+
reference point. Arms and a baseline make the headline a delta; skip the
|
|
179
|
+
baseline when absolute rates are what you want.
|
|
180
|
+
- **Deterministic where possible, judged where necessary.** The binder is tuned
|
|
181
|
+
so a false positive, a surface check passing on wrong output, is the one
|
|
182
|
+
unacceptable error; anything doubtful goes to the judge, which sees the
|
|
183
|
+
collected evidence and never grades from recall.
|
|
184
|
+
- **Self-describing artifacts.** Every run writes `meta.json` (planned config
|
|
185
|
+
plus observed provenance, down to the agent version inside the guest),
|
|
186
|
+
`index.jsonl` (one row per sample), and `benchmark.json`, so other tools can
|
|
187
|
+
aggregate runs without knowing the directory layout.
|
|
188
|
+
|
|
189
|
+
## Documentation
|
|
190
|
+
|
|
191
|
+
| | |
|
|
192
|
+
|---|---|
|
|
193
|
+
| [`docs/quickstart.md`](docs/quickstart.md) | Empty directory to a graded two-arm run. |
|
|
194
|
+
| [`docs/concepts.md`](docs/concepts.md) | The vocabulary: eval, arm, set, baseline, binder, judge. |
|
|
195
|
+
| [`docs/writing-evals.md`](docs/writing-evals.md) | The eval format, workspaces, `setup.sh`, and how grading decides what binds. |
|
|
196
|
+
| [`docs/configuration.md`](docs/configuration.md) | Sets, arms, the judge, every CLI flag, exit codes. |
|
|
197
|
+
| [`docs/sandbox.md`](docs/sandbox.md) | Snapshots, mounts, credentials, host requirements. |
|
|
198
|
+
| [`docs/results.md`](docs/results.md) | Reading `benchmark.md` and the machine-readable artifacts. |
|
|
199
|
+
| [`docs/harnesses.md`](docs/harnesses.md) | `claude-code`, `codex`, `opencode`, and adding your own. |
|
|
200
|
+
|
|
201
|
+
## Contributing
|
|
202
|
+
|
|
203
|
+
Issues and pull requests are welcome at
|
|
204
|
+
[github.com/theycallmeswift/benchspec](https://github.com/theycallmeswift/benchspec).
|
|
205
|
+
Until `CONTRIBUTING.md` lands, [`docs/style/development.md`](docs/style/development.md)
|
|
206
|
+
is the code style, and `make test` plus `make lint` are the bar.
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
[MIT](LICENSE).
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<picture>
|
|
2
|
+
<source media="(prefers-color-scheme: dark)" srcset="docs/assets/benchspec-wordmark-dark.svg">
|
|
3
|
+
<img alt="benchspec" src="docs/assets/benchspec-wordmark-light.svg" width="188" height="48">
|
|
4
|
+
</picture>
|
|
5
|
+
|
|
6
|
+
**Benchmark what your agent does, not what it says.**
|
|
7
|
+
|
|
8
|
+
[](https://github.com/theycallmeswift/benchspec/actions/workflows/ci.yml)
|
|
9
|
+
[](https://pypi.org/project/benchspec/)
|
|
10
|
+
[](https://pypi.org/project/benchspec/)
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
|
|
13
|
+
benchspec runs an agent (Claude Code, Codex, or OpenCode) against a task
|
|
14
|
+
in a fresh microVM, checks what it actually did in the workspace, and reports the
|
|
15
|
+
result as a comparison: with your skill versus without, one model versus another,
|
|
16
|
+
one harness versus another.
|
|
17
|
+
|
|
18
|
+
<img src="docs/assets/benchmark-terminal.gif" width="800" alt="Animated terminal output: benchspec run prints a benchmark matrix with evals as rows, arms as columns, color-coded rates, and percentage-point deltas">
|
|
19
|
+
|
|
20
|
+
*End-of-run summary for a two-arm run of the in-repo [`hello`](evals/e2e/hello/)
|
|
21
|
+
suite (illustrative numbers).* Rows are evals, columns are arms (`baseline` ran
|
|
22
|
+
the agent bare, `trial` installed the skill), and every non-baseline cell shows
|
|
23
|
+
its assertion pass rate plus the delta against the baseline in percentage
|
|
24
|
+
points. The same matrix lands in `benchmark.md`, with machine-readable artifacts
|
|
25
|
+
alongside.
|
|
26
|
+
|
|
27
|
+
Teams pick harnesses, models, and prompts by anecdote: run it once, eyeball the
|
|
28
|
+
transcript, trust the vibe. benchspec turns that guess into a measurement.
|
|
29
|
+
Write the goal once, run it across the configurations you care about, and read
|
|
30
|
+
off — in percentage points — how good each one actually is at accomplishing it.
|
|
31
|
+
|
|
32
|
+
## Getting Started
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install "benchspec[microsandbox]"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
> **Pre-1.0.** The eval format and the artifact schemas are the surfaces most
|
|
39
|
+
> likely to change. microsandbox is the only sandbox backend today; `docker` is
|
|
40
|
+
> recognized in config but fails fast as not implemented.
|
|
41
|
+
|
|
42
|
+
An eval is one Markdown file: a prompt, then a checklist of plain-prose claims
|
|
43
|
+
about the workspace after the agent is done. There is no checker syntax to learn;
|
|
44
|
+
the wording is the spec. `evals/hello/greets-by-name.eval.md`:
|
|
45
|
+
|
|
46
|
+
```markdown
|
|
47
|
+
---
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Prompt
|
|
51
|
+
|
|
52
|
+
You are working in a workspace rooted at your current working directory.
|
|
53
|
+
Greet Alice by name.
|
|
54
|
+
|
|
55
|
+
## Assertions
|
|
56
|
+
|
|
57
|
+
- [ ] ./Greetings/Alice.md contains the exact line 'Hello, Alice!'
|
|
58
|
+
- [ ] Skill `hello` invoked
|
|
59
|
+
- [ ] The greeting feels warm and personable, not curt or robotic
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The benchmark is a block in `pyproject.toml`. Arms are the report columns; the
|
|
63
|
+
baseline is what the others are measured against:
|
|
64
|
+
|
|
65
|
+
```toml
|
|
66
|
+
[tool.benchspec]
|
|
67
|
+
default-set = "default"
|
|
68
|
+
|
|
69
|
+
[tool.benchspec.sets.default]
|
|
70
|
+
harness = "claude-code"
|
|
71
|
+
model = "sonnet"
|
|
72
|
+
baseline = "baseline"
|
|
73
|
+
arms = [
|
|
74
|
+
{ name = "baseline" }, # installs nothing
|
|
75
|
+
{ name = "trial" }, # setup.sh installs the skill
|
|
76
|
+
]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Then:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
benchspec lint # static checks on the assertions
|
|
83
|
+
benchspec analyze # which assertions grade deterministically, which go to the judge
|
|
84
|
+
benchspec run # every (eval × arm) in its own microVM, graded, reported
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## What you need
|
|
88
|
+
|
|
89
|
+
| | |
|
|
90
|
+
|---|---|
|
|
91
|
+
| Platform | Apple Silicon Mac, or Linux with `/dev/kvm`. Python 3.11+. |
|
|
92
|
+
| Agent CLI | `claude`, `codex`, or `opencode` on `PATH`, with its credential (for Claude Code, `CLAUDE_CODE_OAUTH_TOKEN` or `ANTHROPIC_API_KEY`). |
|
|
93
|
+
| `GEMINI_API_KEY` | The binder: a fixed Gemini call that classifies each assertion. Required for every `analyze` and `run`. |
|
|
94
|
+
| Judge credential | The judge runs on the host through an agent CLI; the default is `claude-code` with `sonnet`. Prefer a different vendor from the arms (this repo's own suite judges Claude arms with Codex). |
|
|
95
|
+
|
|
96
|
+
Credentials can live in a repo-root `.env`. A graded run can touch up to three
|
|
97
|
+
vendors: the agent's, Gemini for the binder, and the judge's. `lint` is free;
|
|
98
|
+
`analyze` and `run` spend API calls, and `run` also boots VMs. Preflight lists
|
|
99
|
+
every missing piece and exits before anything is spent.
|
|
100
|
+
|
|
101
|
+
## How a run works
|
|
102
|
+
|
|
103
|
+
```mermaid
|
|
104
|
+
flowchart LR
|
|
105
|
+
E["greets-by-name.eval.md<br/>prompt + assertions"] --> A1["arm: baseline<br/>fresh microVM,<br/>setup.sh installs nothing"]
|
|
106
|
+
E --> A2["arm: trial<br/>fresh microVM,<br/>setup.sh installs the hello skill"]
|
|
107
|
+
A1 --> F1["facts: files, SHAs,<br/>final message, tool calls"]
|
|
108
|
+
A2 --> F2["facts"]
|
|
109
|
+
F1 --> G["binder: deterministic checkers<br/>everything else: LLM judge"]
|
|
110
|
+
F2 --> G
|
|
111
|
+
G --> R["benchmark.md + benchmark.json<br/>meta.json + index.jsonl"]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Each `(eval × arm)` pair is one parametrized pytest test. A cell:
|
|
115
|
+
|
|
116
|
+
1. **Boots a microVM** from a cached snapshot with the agent CLI already
|
|
117
|
+
installed. The first run builds the snapshot (a few minutes); later runs
|
|
118
|
+
reuse it, or pay the cost up front with `benchspec sandbox:build`.
|
|
119
|
+
2. **Seeds the clean room** — the eval's optional `workspace/` files land in a
|
|
120
|
+
fresh directory mounted at `/workspace`, the agent's working directory.
|
|
121
|
+
3. **Runs `setup.sh`**, where arms diverge: it sees `$BENCHSPEC_ARM`, so the
|
|
122
|
+
baseline branch exits early and the trial branch copies the skill into place.
|
|
123
|
+
4. **Invokes the agent** on the eval's prompt.
|
|
124
|
+
5. **Collects the facts** — file tree, contents, SHA-256s, the final message,
|
|
125
|
+
the tool calls.
|
|
126
|
+
6. **Grades** — the binder maps each assertion to a deterministic checker where
|
|
127
|
+
it can do so without risk; the judge grades everything else from the
|
|
128
|
+
collected evidence alone.
|
|
129
|
+
|
|
130
|
+
Two guarantees hold throughout. Nothing in the guest can write back to your
|
|
131
|
+
checkout: `setup.sh` reaches the skill under test through a read-only staged
|
|
132
|
+
copy of your repo at `/project` (what a `git clone` would contain — never
|
|
133
|
+
`.env`, `.git`, or earlier runs' artifacts). And provider credentials are
|
|
134
|
+
injected at the network boundary, never as readable environment variables in
|
|
135
|
+
the guest.
|
|
136
|
+
|
|
137
|
+
`benchspec run` is pytest underneath, and everything after `--` goes to
|
|
138
|
+
pytest verbatim: `benchspec run -- -k greets-by-name` (equivalently
|
|
139
|
+
`pytest -k greets-by-name`) runs one eval, `-n 8` fans cells across eight
|
|
140
|
+
microVMs, and `--count 5` samples each cell five times so the report can flag a
|
|
141
|
+
delta that sits within noise.
|
|
142
|
+
|
|
143
|
+
## Why benchspec
|
|
144
|
+
|
|
145
|
+
- **Comparison is first-class.** A single pass rate is a number without a
|
|
146
|
+
reference point. Arms and a baseline make the headline a delta; skip the
|
|
147
|
+
baseline when absolute rates are what you want.
|
|
148
|
+
- **Deterministic where possible, judged where necessary.** The binder is tuned
|
|
149
|
+
so a false positive, a surface check passing on wrong output, is the one
|
|
150
|
+
unacceptable error; anything doubtful goes to the judge, which sees the
|
|
151
|
+
collected evidence and never grades from recall.
|
|
152
|
+
- **Self-describing artifacts.** Every run writes `meta.json` (planned config
|
|
153
|
+
plus observed provenance, down to the agent version inside the guest),
|
|
154
|
+
`index.jsonl` (one row per sample), and `benchmark.json`, so other tools can
|
|
155
|
+
aggregate runs without knowing the directory layout.
|
|
156
|
+
|
|
157
|
+
## Documentation
|
|
158
|
+
|
|
159
|
+
| | |
|
|
160
|
+
|---|---|
|
|
161
|
+
| [`docs/quickstart.md`](docs/quickstart.md) | Empty directory to a graded two-arm run. |
|
|
162
|
+
| [`docs/concepts.md`](docs/concepts.md) | The vocabulary: eval, arm, set, baseline, binder, judge. |
|
|
163
|
+
| [`docs/writing-evals.md`](docs/writing-evals.md) | The eval format, workspaces, `setup.sh`, and how grading decides what binds. |
|
|
164
|
+
| [`docs/configuration.md`](docs/configuration.md) | Sets, arms, the judge, every CLI flag, exit codes. |
|
|
165
|
+
| [`docs/sandbox.md`](docs/sandbox.md) | Snapshots, mounts, credentials, host requirements. |
|
|
166
|
+
| [`docs/results.md`](docs/results.md) | Reading `benchmark.md` and the machine-readable artifacts. |
|
|
167
|
+
| [`docs/harnesses.md`](docs/harnesses.md) | `claude-code`, `codex`, `opencode`, and adding your own. |
|
|
168
|
+
|
|
169
|
+
## Contributing
|
|
170
|
+
|
|
171
|
+
Issues and pull requests are welcome at
|
|
172
|
+
[github.com/theycallmeswift/benchspec](https://github.com/theycallmeswift/benchspec).
|
|
173
|
+
Until `CONTRIBUTING.md` lands, [`docs/style/development.md`](docs/style/development.md)
|
|
174
|
+
is the code style, and `make test` plus `make lint` are the bar.
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
[MIT](LICENSE).
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Concepts
|
|
2
|
+
|
|
3
|
+
This page is the vocabulary: one short paragraph per term, alphabetized, each
|
|
4
|
+
with a one-line example and a link to the document that goes deepest. Skim the
|
|
5
|
+
index, or come back when a term in another page is unfamiliar.
|
|
6
|
+
|
|
7
|
+
[Arm](#arm) ·
|
|
8
|
+
[Assertion](#assertion) ·
|
|
9
|
+
[Baseline](#baseline) ·
|
|
10
|
+
[Binder](#binder) ·
|
|
11
|
+
[Cell](#cell) ·
|
|
12
|
+
[Checker](#checker) ·
|
|
13
|
+
[Clean room and /workspace](#clean-room-and-workspace) ·
|
|
14
|
+
[Errored versus failed](#errored-versus-failed) ·
|
|
15
|
+
[Eval](#eval) ·
|
|
16
|
+
[Eval set](#eval-set) ·
|
|
17
|
+
[Group](#group) ·
|
|
18
|
+
[Harness](#harness) ·
|
|
19
|
+
[Iteration](#iteration) ·
|
|
20
|
+
[Judge](#judge) ·
|
|
21
|
+
[/project](#project) ·
|
|
22
|
+
[Sample](#sample) ·
|
|
23
|
+
[Sandbox and snapshot](#sandbox-and-snapshot) ·
|
|
24
|
+
[setup.sh](#setupsh) ·
|
|
25
|
+
[Skill](#skill)
|
|
26
|
+
|
|
27
|
+
## Arm
|
|
28
|
+
|
|
29
|
+
One column of the benchmark: a named configuration of harness, model, effort,
|
|
30
|
+
environment variables, and pass-through CLI arguments. Arms differ from each
|
|
31
|
+
other only in that configuration and in what their `setup.sh` branch installs.
|
|
32
|
+
Example: `{ name = "trial", model = "opus", effort = "high" }`. Reference:
|
|
33
|
+
[configuration.md](configuration.md).
|
|
34
|
+
|
|
35
|
+
## Assertion
|
|
36
|
+
|
|
37
|
+
One `- [ ]` line in an eval: a single claim about the final workspace, the
|
|
38
|
+
agent's final message, or what the agent did. Assertions are prose, not checker
|
|
39
|
+
syntax; how each one is graded is decided at run time (see **binder**).
|
|
40
|
+
Example: `- [ ] ./Greetings/Alice.md contains the exact line 'Hello, Alice!'`.
|
|
41
|
+
|
|
42
|
+
## Baseline
|
|
43
|
+
|
|
44
|
+
The arm every other arm's delta is measured against. With a baseline, the report
|
|
45
|
+
headline is a percentage-point delta (`+14pp`); without one, arms report absolute
|
|
46
|
+
pass rates. Example: `baseline = "baseline"`, an arm whose `setup.sh` installs
|
|
47
|
+
nothing. Reading deltas: [results.md](results.md).
|
|
48
|
+
|
|
49
|
+
## Binder
|
|
50
|
+
|
|
51
|
+
A fixed, deliberately conservative classifier (one call to
|
|
52
|
+
`gemini-3.5-flash-lite` per assertion, which is why `GEMINI_API_KEY` is always
|
|
53
|
+
required) that decides, at grade time, whether an assertion can be checked
|
|
54
|
+
mechanically. It either **binds** the line to one checker or **punts** it to the
|
|
55
|
+
judge. It never grades anything itself. Reference:
|
|
56
|
+
[writing-evals.md](writing-evals.md#how-assertions-are-graded).
|
|
57
|
+
|
|
58
|
+
## Cell
|
|
59
|
+
|
|
60
|
+
One `(eval × arm)` pair. Each cell becomes one parametrized pytest test that
|
|
61
|
+
boots its own microVM, runs the agent, and grades the result. A two-eval,
|
|
62
|
+
three-arm set has six cells. Example: `test_eval[hello-greets-by-name-trial]`.
|
|
63
|
+
|
|
64
|
+
## Checker
|
|
65
|
+
|
|
66
|
+
One deterministic check run on the host against the final workspace or the
|
|
67
|
+
run's process facts: `file_exists`, `not_file_exists`, `glob_count`, `regex`,
|
|
68
|
+
`frontmatter_has`, `sha256_match`, `skill_invoked`, `not_skill_invoked`. Zero
|
|
69
|
+
variance, zero judge cost. Example: "./out/report.md exists" binds to
|
|
70
|
+
`file_exists`.
|
|
71
|
+
|
|
72
|
+
## Clean room and `/workspace`
|
|
73
|
+
|
|
74
|
+
The clean room is a fresh temporary directory on the host, seeded from the
|
|
75
|
+
eval's `workspace/` folder (or empty), that is mounted read-write into the
|
|
76
|
+
microVM at `/workspace`. It is the agent's working directory, and the host
|
|
77
|
+
grades the same directory afterward. Every path in an eval is written
|
|
78
|
+
`./`-relative to it. Example: an eval with `workspace/request.md` starts the
|
|
79
|
+
agent in a directory containing exactly `request.md`.
|
|
80
|
+
|
|
81
|
+
## Errored versus failed
|
|
82
|
+
|
|
83
|
+
A **failed** assertion is a measurement: the agent ran and the claim did not
|
|
84
|
+
hold. An **errored** sample is infrastructure: the CLI crashed or timed out,
|
|
85
|
+
`setup.sh` exited non-zero, or the judge failed at the transport level. Errored
|
|
86
|
+
samples are excluded from pass rates but counted in the report, so a
|
|
87
|
+
half-crashed run cannot read like a clean one. See
|
|
88
|
+
[results.md](results.md#noise-samples-and-flakiness).
|
|
89
|
+
|
|
90
|
+
## Eval
|
|
91
|
+
|
|
92
|
+
One task and what success looks like: a prompt the agent receives, an optional
|
|
93
|
+
`history:` of prior turns, and a checklist of plain-prose **assertions**. An eval
|
|
94
|
+
is one Markdown file, `eval.md` or `<stem>.eval.md`, and its id is the folder
|
|
95
|
+
name or the file stem. Example: `evals/hello/greets-by-name.eval.md` asks the
|
|
96
|
+
agent to "Greet Alice by name" and asserts that `./Greetings/Alice.md` contains
|
|
97
|
+
`Hello, Alice!`. Format reference: [writing-evals.md](writing-evals.md).
|
|
98
|
+
|
|
99
|
+
## Eval set
|
|
100
|
+
|
|
101
|
+
A named benchmark declared in `pyproject.toml`: its arms, the defaults they
|
|
102
|
+
inherit, and an optional baseline. One run resolves exactly one set. Example:
|
|
103
|
+
`[tool.benchspec.sets.default]` with arms `baseline` and `trial`. Reference:
|
|
104
|
+
[configuration.md](configuration.md#eval-sets).
|
|
105
|
+
|
|
106
|
+
## Group
|
|
107
|
+
|
|
108
|
+
The folder an eval file sits in. Its name is the first half of an eval's
|
|
109
|
+
identity, `(group, eval_id)`, and the artifact tree is keyed on it. Sibling
|
|
110
|
+
`<stem>.eval.md` files in one folder share that folder's `workspace/` and
|
|
111
|
+
`setup.sh`. Example: `evals/hello/greets-by-name.eval.md` is eval
|
|
112
|
+
`greets-by-name` in group `hello`, and pytest names its cells
|
|
113
|
+
`test_eval[hello-greets-by-name-<arm>]`.
|
|
114
|
+
|
|
115
|
+
## Harness
|
|
116
|
+
|
|
117
|
+
The agent CLI under test: `claude-code`, `codex`, or `opencode`. Each is
|
|
118
|
+
one adapter that knows how to install the CLI into a microVM, which credential it
|
|
119
|
+
needs, how to run it headless, and how to read its output stream. Chosen per arm.
|
|
120
|
+
Details and how to add one: [harnesses.md](harnesses.md).
|
|
121
|
+
|
|
122
|
+
## Iteration
|
|
123
|
+
|
|
124
|
+
One run's artifact tree, `tmp/evals/iteration_NN/`, numbered once per run and
|
|
125
|
+
shared by every cell in it. It holds `benchmark.md`, `benchmark.json`,
|
|
126
|
+
`meta.json`, `index.jsonl`, and every sample's per-cell files. Reference:
|
|
127
|
+
[results.md](results.md).
|
|
128
|
+
|
|
129
|
+
## Judge
|
|
130
|
+
|
|
131
|
+
The LLM that grades every punted assertion, from evidence only: the workspace
|
|
132
|
+
tree, file contents and SHA-256s, the agent's final message, and the tools and
|
|
133
|
+
skills it invoked. The judge runs on the host through one of the same harness
|
|
134
|
+
adapters, is configured once per run, and is independent of the arms. Example:
|
|
135
|
+
`[tool.benchspec.judge] harness = "codex"`. Reference:
|
|
136
|
+
[configuration.md](configuration.md#the-judge).
|
|
137
|
+
|
|
138
|
+
## `/project`
|
|
139
|
+
|
|
140
|
+
A read-only mount of a staged copy of your repository (what a `git clone` would
|
|
141
|
+
contain, minus `.env` files, `.git`, and `tmp/`). It exists so the eval's own
|
|
142
|
+
`setup.sh` can copy the skill under test into the guest. The agent can read it
|
|
143
|
+
too, so keep prompts pointed at `./`. Details:
|
|
144
|
+
[sandbox.md](sandbox.md#what-project-contains).
|
|
145
|
+
|
|
146
|
+
## Sample
|
|
147
|
+
|
|
148
|
+
One execution of a cell. A plain run takes one sample per cell (`sample-0/`);
|
|
149
|
+
`--count N` takes N, so the report can show flakiness and a noise band on each
|
|
150
|
+
delta. Example: `benchspec run -- --count 5`. See
|
|
151
|
+
[results.md](results.md#noise-samples-and-flakiness).
|
|
152
|
+
|
|
153
|
+
## Sandbox and snapshot
|
|
154
|
+
|
|
155
|
+
Every cell runs inside a microVM, a small hardware-isolated virtual machine
|
|
156
|
+
booted from a **snapshot**: a sealed image with the base OS, the harness CLI,
|
|
157
|
+
and any suite-wide tools already installed. Snapshots build once per
|
|
158
|
+
configuration and are cached; cells boot from them in seconds. Reference:
|
|
159
|
+
[sandbox.md](sandbox.md).
|
|
160
|
+
|
|
161
|
+
## `setup.sh`
|
|
162
|
+
|
|
163
|
+
An optional script beside the eval file that runs inside the microVM before the
|
|
164
|
+
prompt, with `BENCHSPEC_ARM` set to the arm's name. It is the one place arms
|
|
165
|
+
diverge: the canonical script installs a skill on `trial` and exits early on
|
|
166
|
+
`baseline`. Reference:
|
|
167
|
+
[writing-evals.md](writing-evals.md#setupsh-what-differs-per-arm).
|
|
168
|
+
|
|
169
|
+
## Skill
|
|
170
|
+
|
|
171
|
+
An instruction file (`SKILL.md`) an agent can load and dispatch; in the common
|
|
172
|
+
"capability lift" benchmark, the skill is the thing under test. Skills install to
|
|
173
|
+
the fixed guest path `/home/benchspec/skills`, which every harness's native
|
|
174
|
+
skill directory links to. Example assertion: `` - [ ] Skill `hello` invoked ``.
|