sandboxReuse: true on the Experiment so multiple Attempts use the same Sandbox in sequence. Sandbox creation and shared setup happen once per Sandbox, and NiceEval restores the workdir between evals.
Before enabling reuse, understand its cost:
- Results can still be carried. When a pair has a stable carry identity and the terminal result matches, NiceEval carries it without creating a Sandbox. A Sandbox Plugin’s attachment owner, name, instance key, behavior revision, declared identity, order, and setup/teardown shape are part of that carry identity, so changing any of them makes the slot fresh. Callback function bodies remain opaque; update the declared identity or use
--rerun allwhen their behavior changes. Only Attempts that are not carried run in the shared Sandbox. - An interruption does not roll back external state. A resumed run is the same trajectory only when cross-attempt state can return to the last terminal commit boundary. Otherwise start a clean cohort.
- State outside the workdir remains. Preparation code must tolerate that state or clean it deliberately. A reused Sandbox is not full isolation.
--keep-sandboxandlocalSandbox()cannot be combined withsandboxReuse: true.
Enable reuse in an Experiment
timeoutMs and lifetimeMs are two clocks for two objects. The first limits one Attempt; the second limits one Sandbox. Increase lifetimeMs when the Sandbox needs to live longer — do not increase timeoutMs to solve that problem. A Provider account tier can cap lifetimeMs; exceeding the cap fails during creation with the Provider’s reason.
Lifecycle frequency
After each Attempt, NiceEval runs
git reset --hard and git clean in the workdir and returns to the reset point before starting the next Attempt. The reset does not clear /opt, $HOME, /tmp, global installs, package caches, build caches, or background processes. A persistent build/cache therefore needs an author-owned capacity limit, threshold diagnostic, cleanup or rotation policy, and a retirement path when it cannot be kept safe.
Layer preparation by change scope
- Heavy dependencies every experiment needs — Agent CLIs and runtimes — belong in the Provider’s image, template, or snapshot rather than in
setup. - Preparation shared by the batch — tool installation, a common checkout, or build-cache warm-up — belongs in Sandbox-level
.setup(). It runs once per Sandbox and becomes part of the reset point. - Materials needed by one eval — its repository, data, or dependencies — belong in that eval’s
setuportest(t). They are replayed after each reset, so the code must converge when replayed. - Background processes and ports belong to the eval’s
teardownunless the author deliberately makes them part of the reusable state and governs their lifetime.
facts; emit a diagnostic only after a stated, explainable risk threshold is reached. If cleanup or rotation cannot make the next Attempt safe, retire the Sandbox or throw instead of continuing with an unbounded cache.
Make preparation idempotent
Sandbox- and eval-level preparation may encounter partial state left by the previous Attempt. Describe the target state directly:Mixed environments in one batch
When selected evals resolve to different prebuilt artifacts, the Runner groups them by the resolved environment profile. A Sandbox accepts Attempts from one group, and each group gets its own reset point. Groups do not share Sandboxes or Sandbox-level setup results. ExperimentmaxConcurrency limits the total number of concurrent Attempts across all groups.
Typical workflow
--keep-sandbox: the two modes are mutually exclusive, and the retained state belongs to the batch rather than one eval.
Use concurrency when preparation cannot be replayed
If preparation cannot be made idempotent, or an eval requires a fresh$HOME or stateful service, do not enable sandboxReuse. Use bounded concurrency instead:
Related
- Rerun and cache — result carry and
--rerun. - Sandbox providers — configure templates, images, and snapshots.