Env harness (HARNESS-01)
rollout-harness-text is the bundled EnvHarness: a text-in / text-out
environment where Observation = prompt and Action = completion.
Batched lifecycle
reset(Vec<Prompt>) -> Vec<Episode> # open episodes
step(Vec<EpisodeStep>) -> Vec<StepResult> # one turn each
close(Vec<EpisodeId>) -> () # release episode state
All three methods are batched (spec 07 principle 2). Episode state lives in an
in-memory store keyed by EpisodeId (a fresh ULID per reset).
Multi-turn capable (D-ENV-01)
The step loop supports N steps per episode. The bundled text env is
single-turn text completion, but the contract carries EpisodeStep +
per-episode state across multiple steps so v1.2 conversational / tool-using
environments need no contract change.
Reward via the plugin host (D-ENV-03)
There is no built-in reward trait. On step, if a reward plugin is
configured, the env encodes a postcard RewardInput { prompt, completion },
calls PluginHost::call(handle, "score", payload), and decodes a Reward. A
decode failure surfaces as a typed Fatal(PluginContract).
This reuses the Phase-2 rollout-plugin-host (cdylib / PyO3 / sidecar) — the
env crate never embeds scoring logic.
No trajectory persistence in v1.1 (D-ENV-02)
step returns StepResults in memory. There is no ObjectStore
persistence of trajectories in v1.1; the content-addressed Trajectory type +
serializer lands with RL-03 in v1.2.
Witnesses
EchoEnv— canned reward, no plugin (the trivial happy path).MockRewardEnv— wires a deterministic mock plugin handle to exercise the plugin-host reward path end-to-end.env_deterministic_replay— a seededSplitMix64RNG (noranddep) folds a nonce intoStepResult.infoso the seed materially changes the trajectory; same seed reproduces the trajectory(observation, reward, done, info), a different seed diverges. (episode_idis normalized out of the equality check since it is a fresh ULID per reset.)
All three are GPU-free and need no cloud credentials.