Reproduce a past run
Every Iteration can carry a bit-exact snapshot of the Julia environment that produced it: julia_version, the HEAD commit SHA, the verbatim Project.toml and Manifest.toml active at run start, and the entrypoint script. Months later, restore writes 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 pip freeze strings that re-resolve transitive dependencies on install. 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 with the snapshot keyword for different behavior.
julia> project_id, _ = create_project("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
To attach a snapshot outside the with_iteration flow (for example, 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, useful for inspection or 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 is left to the caller so the function stays side-effect-free outside the temp tree.
julia> depot = mktempdir();julia> result = DearDiary.restore(iteration_id; depot=depot)RestoreResult("/tmp/jl_dM1Akv/iteration_1", "1.12.6", "8aba6a9fb5e303d76908bfce5877228738ff9cf7", 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. 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
| Captured | Not captured |
|---|---|
Julia version (string(VERSION)) | OS / kernel / glibc |
| HEAD commit SHA + dirty bit | The 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 path | Runtime config files outside the project |
If git_dirty is true, the captured Manifest alone is not sufficient for full reproducibility. Uncommitted source changes must be reapplied manually. Run reproducible jobs from a clean working tree; the snapshot lets you verify after the fact whether the tree was clean.