Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,30 @@

## [Unreleased]

### Changed
- **BREAKING (task.md): the `environment:` frontmatter key is renamed to
`sandbox:`.** The native task-config surface now accepts only `sandbox:`
(plus `verifier.sandbox:` for the verifier's separate sandbox spec and
`verifier.sandbox_mode:` for shared/separate selection); `environment:`,
`verifier.environment:`, and `verifier.environment_mode:` no longer
validate and fail with an actionable message naming the rename. **The
one-line fix for existing task.md files is renaming the key.**
Legacy/Harbor `task.toml` imports are unaffected: the toml loader
converts `[environment]`, `[verifier.environment]`, and
`environment_mode` to the `sandbox` spellings (declaring both spellings
in one file is an error), and `bench tasks export` emits the inverse —
a stock-Harbor `[environment]`-spelled `task.toml`. All native emitters
— `model_dump_toml`, `bench tasks migrate`, task scaffolding,
skill-eval/trace/adapter task generation, rubric-review wrappers — now
write `sandbox`. Python API: the compat property
`TaskConfig.environment` is removed (use `TaskConfig.sandbox`),
`VerifierConfig.environment`/`environment_mode` became
`sandbox`/`sandbox_mode`, and the `VerifierEnvironmentMode` enum is now
`VerifierSandboxMode`. The Environment plane
(`--environment-manifest`, `benchflow.environment.manifest`, the
eval-config `environment:` docker/daytona selector) is a different
subsystem and is unchanged.

## 0.6.7 — 2026-08-09

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ agent:
timeout_sec: 300
verifier:
timeout_sec: 120
environment:
sandbox:
cpus: 1
memory_mb: 2048
allow_internet: true
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/continuallearningbench/benchflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def _render_task_toml(task: ContinualLearningBenchTaskInfo) -> str:
[verifier]
timeout_sec = 300.0

[environment]
[sandbox]
build_timeout_sec = 600
cpus = 2
memory_mb = 4096
Expand Down
4 changes: 2 additions & 2 deletions benchmarks/continuallearningbench/parity_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ def _check_structural(task_dir: Path) -> list[str]:
errors.append(f"{name}: task.toml missing [agent] section")
if "[verifier]" not in content:
errors.append(f"{name}: task.toml missing [verifier] section")
if "[environment]" not in content:
errors.append(f"{name}: task.toml missing [environment] section")
if "[sandbox]" not in content:
errors.append(f"{name}: task.toml missing [sandbox] section")

# instruction.md
instruction = task_dir / "instruction.md"
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/harvey-lab/benchflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def _build_task_toml(
[verifier.env]
ANTHROPIC_API_KEY = "${{ANTHROPIC_API_KEY}}"

[environment]
[sandbox]
build_timeout_sec = 600
cpus = 1
memory_mb = 4096
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/hilbench/benchflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def _render_task_toml(task: HILBenchTask) -> str:
[verifier]
timeout_sec = {verifier_timeout}

[environment]
[sandbox]
cpus = 2
memory_mb = 4096
storage_mb = 20480
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/mle-bench/benchflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ def _render_task_toml(competition: MLEBenchCompetition) -> str:
timeout_sec = 3600
user = "root"

[environment]
[sandbox]
build_timeout_sec = 3600
cpus = 4
memory_mb = 16384
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/opaquetoolsbench/benchflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def _render_task_toml(task: BFCLTask) -> str:
[verifier]
timeout_sec = {_VERIFIER_TIMEOUT}

[environment]
[sandbox]
cpus = 1
memory_mb = 1024
storage_mb = 2048
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/programbench/benchflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def _render_task_toml(task: ProgramBenchTask) -> str:
[verifier]
timeout_sec = {verifier_timeout}

[environment]
[sandbox]
cpus = 2
memory_mb = 4096
storage_mb = 20480
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ agent:
timeout_sec: 600
verifier:
timeout_sec: 120
environment:
sandbox:
cpus: 1
memory_mb: 2048
allow_internet: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ agent:
timeout_sec: 600
verifier:
timeout_sec: 120
environment:
sandbox:
cpus: 1
memory_mb: 2048
allow_internet: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ agent:
timeout_sec: 600
verifier:
timeout_sec: 120
environment:
sandbox:
cpus: 1
memory_mb: 2048
allow_internet: true
Expand Down
6 changes: 3 additions & 3 deletions docs/examples/task-md/harbor-parity/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,16 @@ verifier:
JUDGE_API_KEY: ${JUDGE_API_KEY:-test}
user: root
network_mode: public
environment_mode: separate
sandbox_mode: separate
pytest_plugins: [pytest_playwright]
hardening:
cleanup_conftests: false
environment:
sandbox:
docker_image: ghcr.io/example/grader:latest
cpus: 2
memory_mb: 1024
network_mode: no-network
environment:
sandbox:
network_mode: allowlist
allowed_hosts: [datasets.example.com]
build_timeout_sec: 600
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/task-md/multi-scene/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ agent:
timeout_sec: 900
verifier:
timeout_sec: 180
environment:
sandbox:
cpus: 2
memory_mb: 4096
agents:
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/task-md/nudgebench-team/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ agent:
timeout_sec: 1200
verifier:
timeout_sec: 240
environment:
sandbox:
cpus: 2
memory_mb: 4096
agents:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ verifier:
timeout_sec: 900
agent:
timeout_sec: 900
environment:
sandbox:
build_timeout_sec: 600
cpus: 1
memory_mb: 4096
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ declares a hardened **`network_mode: allowlist`** scoped to exactly the hosts th

## What it is

- `task.md` — same prompt / metadata as the base task, but its `environment:`
- `task.md` — same prompt / metadata as the base task, but its `sandbox:`
block sets:

```yaml
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ verifier:
timeout_sec: 900
agent:
timeout_sec: 900
environment:
sandbox:
build_timeout_sec: 600
cpus: 1
memory_mb: 2048
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ verifier:
timeout_sec: 900
agent:
timeout_sec: 900
environment:
sandbox:
build_timeout_sec: 600
cpus: 1
memory_mb: 2048
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ verifier:
timeout_sec: 900
agent:
timeout_sec: 900
environment:
sandbox:
build_timeout_sec: 600
cpus: 1
memory_mb: 4096
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ agent:
timeout_sec: 600
verifier:
timeout_sec: 120
environment:
sandbox:
network_mode: no-network
cpus: 1
memory_mb: 2048
Expand Down
6 changes: 3 additions & 3 deletions docs/task-authoring-task-md.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ agent:
timeout_sec: 300 # strongly recommended — unset means no wall-clock cap
verifier:
timeout_sec: 120
environment:
sandbox:
cpus: 1
memory_mb: 2048
---
Expand Down Expand Up @@ -76,7 +76,7 @@ so typos fail at parse time instead of becoming silently-ignored config:
| `metadata` | Freeform mapping — difficulty, category, tags, anything descriptive |
| `agent` | Agent run policy: `timeout_sec`, `user`, `network_mode`, `allowed_hosts` |
| `verifier` | Verifier run policy: `timeout_sec` (default 600), `env`, `user`, `service`, … |
| `environment` | Sandbox: `docker_image`, `cpus`, `memory_mb`, `storage_mb`, `network_mode`, `env`, `workdir`, … |
| `sandbox` | Sandbox: `docker_image`, `cpus`, `memory_mb`, `storage_mb`, `network_mode`, `env`, `workdir`, … (legacy `task.toml` imports convert the Harbor `environment` table to this key; `environment:` in `task.md` is rejected with a rename hint) |
| `oracle` | Oracle run policy: `env`, `timeout_sec` (import alias: `solution`) |
| `source`, `artifacts`, `steps`, `multi_step_reward_strategy`, `reward` | Provenance, artifact, and reward metadata |

Expand All @@ -99,7 +99,7 @@ canonical config under their short names:
| Shorthand | Expands to |
|---|---|
| `name: hello-world` | `task.name: benchflow/hello-world` (a `/` in the value keeps your org) |
| `image: ubuntu:24.04` | `environment.docker_image: ubuntu:24.04` |
| `image: ubuntu:24.04` | `sandbox.docker_image: ubuntu:24.04` |
| `verifier: verifier/` (string form) | `benchflow.verifier.path` / `.spec` / `.entrypoint` defaults |
| `oracle: oracle/` (string form) | `benchflow.oracle.path` |
| `profile: code-change` | Merges a named defaults bundle (see below) |
Expand Down
10 changes: 5 additions & 5 deletions docs/task-standard.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ when unknown in native authoring mode:
- `metadata`
- `agent`
- `verifier`
- `environment`
- `sandbox` (legacy `task.toml` import spelling: `environment`)
- `oracle` (validation alias: `solution`)
- `source`
- `artifacts`
Expand Down Expand Up @@ -512,7 +512,7 @@ assets as first-class objects:
benchflow:
provenance:
images:
- field: environment.docker_image
- field: sandbox.docker_image
reference: ghcr.io/org/task-image:2026-06
digest: sha256:...
registry: ghcr.io
Expand Down Expand Up @@ -767,11 +767,11 @@ Target compatibility rules:
compat:
source: harbor
extra_paths:
- environment.modal.image
- sandbox.modal.image
- steps[0].runner
- verifier.reward_kit.metric
extra:
environment:
sandbox:
modal:
image: registry.example.com/task:latest
steps:
Expand Down Expand Up @@ -949,7 +949,7 @@ semantics, and non-`main` verifier services on backends that cannot run them.
It is wired into `bench tasks check --sandbox <backend>` and the shared sandbox
factory used by rollouts and `Environment.from_task()`. Unsupported parsed
semantics now raise `UnsupportedTaskFeatureError` before Docker, Daytona, or
Modal construction. Safe absolute non-root `environment.workdir` values are
Modal construction. Safe absolute non-root `sandbox.workdir` values are
materialized before agent and verifier setup.

P2: Split native and adapter validation modes.
Expand Down
5 changes: 3 additions & 2 deletions src/benchflow/_utils/config_override.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,9 @@ def apply_config_override(config: Any, overlay: dict[str, Any] | None) -> Any:

# Merge against the FIELD-NAME dump (``by_alias=False``) so overlays use the
# canonical field names (``agent``, ``sandbox``, …); ``populate_by_name`` lets
# re-validation accept them. ``by_alias=True`` would force callers to write the
# alias (e.g. ``environment`` for ``sandbox``) — the bug this avoids.
# re-validation accept them. (``sandbox`` is now the only spelling — the
# legacy ``environment`` alias was removed in the rename — but ``oracle``
# still serializes via alias, so the field-name dump stays load-bearing.)
merged = deep_merge(config.model_dump(by_alias=False), overlay)
patched = TaskConfig.model_validate(merged)
logger.debug(
Expand Down
4 changes: 2 additions & 2 deletions src/benchflow/_utils/task_authoring/scaffolding.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ def _write_legacy_task_files(task_dir: Path, name: str) -> None:
[verifier]
timeout_sec = 120

[environment]
[sandbox]
cpus = 1
memory_mb = 2048
""")
Expand Down Expand Up @@ -412,7 +412,7 @@ def _write_task_md(task_dir: Path, name: str) -> None:
timeout_sec: 300
verifier:
timeout_sec: 120
environment:
sandbox:
cpus: 1
memory_mb: 2048
---
Expand Down
2 changes: 1 addition & 1 deletion src/benchflow/acp/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ async def connect_acp(
dormant and the auto-approve policy ran unconditionally (#382 follow-up).

``mcp_servers`` are the task's configured MCP servers (mapped from
``[[environment.mcp_servers]]``); they are attached to the ACP session at
``[[sandbox.mcp_servers]]``); they are attached to the ACP session at
``session/new`` so the agent can reach them. ``None`` attaches none.

Retries with exponential backoff on ConnectionError (Daytona SSH storms).
Expand Down
2 changes: 1 addition & 1 deletion src/benchflow/adapters/_toolathlon.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@ def _toolathlon_task_toml(
"metadata": metadata,
"agent": {"timeout_sec": 1800.0},
"verifier": {"timeout_sec": 900.0},
"environment": environment,
"sandbox": environment,
}


Expand Down
14 changes: 8 additions & 6 deletions src/benchflow/adapters/harbor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
└── tests/ # test.sh — the verifier

This adapter is consequently a thin *normalizer*: it loads the foreign
``task.toml`` through the native :class:`TaskConfig` validator (which already
handles Harbor's ``[environment]``-keyed sandbox section and the
``version`` -> ``schema_version`` rename), reads ``instruction.md``, and
records the build/solution/test files under their native relative paths. No
field remapping is needed — Harbor *is* the native format, which is exactly
what makes Terminal-Bench backward-compatible through this edge.
``task.toml`` through :func:`~benchflow.task.imports.import_task_config_toml`,
whose toml import boundary translates Harbor's ``[environment]``-keyed
sandbox section to the native ``sandbox`` key and handles the ``version`` ->
``schema_version`` rename before native :class:`TaskConfig` validation. It
then reads ``instruction.md`` and records the build/solution/test files under
their native relative paths. Beyond that spelling translation no field
remapping is needed — Harbor is structurally the native format, which is
exactly what makes Terminal-Bench backward-compatible through this edge.
"""

from __future__ import annotations
Expand Down
2 changes: 1 addition & 1 deletion src/benchflow/adapters/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ def _materialize_mcp_atlas(ctx: _SourceContext, output_dir: Path) -> None:
"MCP_ATLAS_JUDGE_MODEL": "${MCP_ATLAS_JUDGE_MODEL:-qwen/qwen-plus}",
},
},
"environment": {
"sandbox": {
"cpus": 4,
"memory_mb": 8192,
"storage_mb": 10240,
Expand Down
2 changes: 1 addition & 1 deletion src/benchflow/demo_task/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ verifier:
cleanup_conftests: true
agent:
timeout_sec: 120.0
environment:
sandbox:
network_mode: public
build_timeout_sec: 600.0
os: linux
Expand Down
2 changes: 1 addition & 1 deletion src/benchflow/experimental/mcp/reviewer_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
never has write access to /app/).

Usage in task.toml:
[[environment.mcp_servers]]
[[sandbox.mcp_servers]]
name = "reviewer"
transport = "streamable-http"
url = "http://localhost:8100/mcp"
Expand Down
2 changes: 1 addition & 1 deletion src/benchflow/review/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def main() -> int:
timeout_sec: {verifier_timeout}
agent:
timeout_sec: {agent_timeout}
environment:
sandbox:
docker_image: {image}
workdir: /app{network_line}
cpus: 1
Expand Down
Loading
Loading