Reproducibility

Every Iteration can carry a bit-exact snapshot of the Julia environment that produced it — julia_version, the HEAD commit SHA of the working tree, the verbatim Project.toml and Manifest.toml that were active at run start, and the entrypoint script. Months later restore materialises that snapshot back to disk, so the exact dependency tree can be Pkg.instantiate-d in a fresh depot. Other ML trackers capture environments as a pip freeze string; Julia's Manifest.toml is byte-exact, so the captured tree round-trips with no resolution drift.

Capture happens automatically on the driver iteration

DearDiary.with_iteration calls snapshot_environment! right after creating the iteration, but only when the new run has no parent. Driver runs capture; child runs (HPO trials, distributed workers) inherit. Override via the snapshot keyword if you need different behaviour.

julia> user = DearDiary.get_user("default");
julia> project_id, _ = create_project(user.id, "Repro Project");
julia> experiment_id, _ = create_experiment(project_id, DearDiary.IN_PROGRESS, "Training");
julia> iteration_id = DearDiary.with_iteration(experiment_id) do iter create_parameter(iter.id, "lr", 1e-3) iter.id end;
julia> iteration = get_iteration(iteration_id);
julia> iteration.julia_version"1.12.6"
julia> iteration.git_sha |> length40
julia> iteration.entrypoint, iteration.git_dirty("docs/make.jl", false)

Capture an iteration manually

When you need to attach a snapshot outside the with_iteration flow — say, in a long-lived service that opens iterations imperatively — call snapshot_environment! directly:

julia> manual_id, _ = create_iteration(experiment_id);
julia> DearDiary.snapshot_environment!(manual_id; entrypoint="train.jl");
julia> get_iteration(manual_id).entrypoint"train.jl"

DearDiary.capture_environment returns the snapshot without persisting it, which is useful for inspection or for shipping the capture across a process boundary:

julia> snapshot = DearDiary.capture_environment();
julia> snapshot.julia_version"1.12.6"

Replay an environment

restore writes the captured Project.toml and Manifest.toml into a fresh directory under depot. It does not activate the project or run Pkg.instantiate — that's left to the caller so the function is side-effect-free outside the temp tree.

julia> depot = mktempdir();
julia> result = DearDiary.restore(iteration_id; depot=depot)RestoreResult("/tmp/jl_RT9n7r/iteration_1", "1.12.6", "2580e8646e9e4123ac5303ffca48b7add2d6e52e", false, "docs/make.jl")
julia> isfile(joinpath(result.project_path, "Project.toml")), isfile(joinpath(result.project_path, "Manifest.toml"))(true, true)

The on-disk files are byte-identical to what was captured, so loading them with using Pkg; Pkg.activate(result.project_path); Pkg.instantiate() reconstructs the exact dependency tree the iteration ran against:

using Pkg
Pkg.activate(result.project_path)
Pkg.instantiate()
# ...then optionally check out the captured commit and run the entrypoint:
# `git checkout $(result.git_sha)` and `julia --project=$(result.project_path) $(result.entrypoint)`

What is and isn't captured

CapturedNot captured
Julia version (string(VERSION))OS / kernel / glibc
HEAD commit SHA + dirty bitThe actual code if git_dirty == true and changes are uncommitted
Active Project.toml (verbatim)Per-package C library versions outside the JLL system
Active Manifest.toml (verbatim)Datasets used by the run (separate concern)
Entrypoint script pathRuntime config files outside the project

If git_dirty is true, the captured Manifest alone is no longer sufficient for true reproducibility — there are uncommitted source changes that must be reapplied manually. Always run reproducible jobs from a clean working tree; the snapshot lets you verify after the fact whether a job's tree was clean.