The Environment plane
The Environment plane is the stateful world the agent acts in — Han’s “S” inE = {T, H, V, S, C}. It is one of BenchFlow’s four swappable planes
(Sandbox, Agent, Environment, Reward). See architecture.md,
“The Environment plane & the manifest”.
A benchmark author never subclasses the framework. They write one file — an
environment.toml manifest — and the default adapter (ManifestEnvironment)
runs it on any Sandbox provider. The manifest is the entire integration
surface.
The manifest schema
The manifest’s keys live under an[environment] table.
[environment]
Exactly one of
image / base_image must be set. When owns_lifecycle is
false the manifest must declare [[environment.services]]; when it is
true it must not.
[environment.task_selection]
[[environment.services]]
An array — one table per service the framework starts (only when
owns_lifecycle = false). It is the declarative replacement for the
hard-coded SERVICES dict in benchflow/sandbox/services.py.
[environment.readiness]
[environment.forward_env]
[environment.state]
Present only for an environment that supports roll-back — snapshot /
restore. Absent this table, the environment is treated as stateless and
snapshot/restore raise RuntimeError.
Worked example — ClawsBench
benchmarks/clawsbench/environment.toml — the internal-dogfood stateful
multi-service benchmark (mock Gmail / Slack / Calendar / Docs / Drive):
ManifestEnvironment
probes each service’s entry point with --help and starts only the services
whose package is actually installed in this per-task image.
Worked example — chi-bench
benchmarks/chi-bench/environment.toml — the other topology, and the
external proof that a heavy environment onboards untouched. chi-bench is a
~25k-LOC healthcare simulator that ships one ready-to-run image whose
entrypoint starts its own services, so the manifest declares no
[[services]]:
base_image + framework-started [[services]]) and chi-bench
(image + owns_lifecycle = true) are the two topologies behind one
contract. See benchmarks/chi-bench/README.md
for the field-by-field mapping.
How it runs
ManifestEnvironment runs the in-sandbox topology (the architecture’s
core): the services run inside the rollout’s own sandbox, so the agent
reaches them on localhost. During a rollout:
Rollout.start()provisions the environment — starts the declared services inside the sandbox.- It gates on
readiness()— the agent never runs before the environment is healthy. Rollout.cleanup()tears the environment down.
bench eval create --tasks-dir .... --environment-manifest applies the
Environment-plane manifest to every rollout in the Job pipeline.
environment_manifest: <path> at the top level so the batch run is reproducible from disk.
--environment-manifest is distinct from --sandbox: the sandbox is where
it runs (the Sandbox plane); the environment manifest is the world (the
Environment plane).
Exporting for training
A scored rollout’s trajectory exports to the Verifiers / ORS dataset format that prime-rl ingests —benchflow.trajectories.export:
prompt, completion, reward, metrics,
is_completed, is_truncated, example_id, info — the shape pinned
against the Verifiers RolloutOutput type.
Roll-back — snapshot / restore
snapshot / restore are real. For an environment that declares an
[environment.state] table, snapshot() copies each declared SQLite file
with sqlite3 .backup (a consistent online backup) into a per-snapshot
directory inside the sandbox, and restore(snap) copies the captured files
back over the live paths. This is the substrate Rollout.branch() runs on:
a branch quiesces the agent and services, restores a snapshot, and explores
an alternative continuation. An environment with no [environment.state]
table is stateless — snapshot/restore raise RuntimeError.
Reset — reset
reset returns the environment to the per-task baseline so it can be reused
for a fresh episode without tearing down the sandbox (distinct from
restore, which rolls back to an arbitrary snapshot). For an environment
that declares an [environment.state] table, provision captures a
baseline; reset then stops the framework-started services, restores the
baseline, and restarts the services. For an owns_lifecycle = true manifest
the framework cannot restart entrypoint-owned services; reset is then a
no-op (and the host must recycle the container for a hard reset).
Not yet implemented
ManifestEnvironment does not exercise:
- Sidecar / shared-fleet topology — host-exposed ports,
AccountBroker.