Skip to content

Versioning and stability

Nothing is published

No version of atune exists on crates.io or PyPI. Every manifest in the repository reads 0.1.0, and that number describes the tree, not a release. This page therefore states what a version number will mean and what is true today, and marks which is which.

What a version number means

Every crate in the workspace carries the same version, and the internal dependencies are pinned to it exactly (atune_core = { path = …, version = "0.1.0" }). A release is therefore all of them at one number, not four independently versioned crates. The Python wheel is built from the same tree and carries the same version.

The numbers follow cargo's reading of semantic versioning, which before 1.0 is stricter than people expect:

Change Before 1.0 After 1.0
Breaking API change a minor bump — 0.1 → 0.2 a major bump
New capability, nothing removed a patch bump — 0.1.0 → 0.1.1 a minor bump
Bug fix a patch bump a patch bump

So while atune is 0.x, treat every minor bump as breaking. A dependency written as atune = "0.1" will not silently pick up 0.2.

What is stable today

Nothing. No API is frozen, and the pre-1.0 period exists precisely so that the seams can still move. What is already guaranteed is narrower and more useful than a stability promise:

  • The four plugin seams are frozen in shape, not in signature: a atune::Storage, atune::Sampler, atune::Scheduler or atune::Objective implemented against today's traits is the shape they will keep, and the storage conformance suite is what an out-of-tree backend is held to. See Extending.
  • The on-disk journal has a version field and a compatibility rule. This build writes format version 5; a reader refuses a journal whose header names a higher version rather than guessing at it, and still opens a lower one. The format is documented field by field on the journal reference and pinned by a committed golden file.
  • Optuna's journal format is a stable import and export target, because Optuna itself guarantees it. See Optuna interop.
  • Determinism is asserted, within a version. See the section below, which is the one stability question that bites people who record results.

Determinism is a within-version promise

Given the same version, seed, closed search space, and stateless sampler, a study reproduces exactly across thread counts and across the Rust and Python surfaces. History-dependent samplers, schedulers, and open search spaces have the narrower replayability contract described in Determinism; a fixed-seed single-worker run remains fully reproducible.

Across versions it is deliberately not a promise. Improving a sampler changes which configuration it suggests, so the byte-for-byte outputs change with it; the repository's parity and journal goldens are re-blessed on purpose when that happens, and the diff is the record of the change. If a result has to be reproducible later, pin the exact version that produced it — a caret requirement is not enough.

An open search space stores its growth policy in the study. A build that predates open search spaces does not read that policy: it samples the declared seed range while earlier trials recorded wider distributions. The compatibility check accepts that bound drift, so such a study degrades — growth stops — rather than corrupting.

The surfaces, and what each one commits to

Surface What it is Pre-1.0 expectation
atune (Rust facade) The crate to depend on The main API surface; anything may change with a minor bump
atune_core The model layer and the four seams Depend on it directly only when implementing a seam
atune_derive #[derive(Space)] Generated code names the facade, so the two travel together
atune_cli (binary atune) The command-line tool Flags and subcommands may change; the CLI reference is generated from the code, so it cannot describe a flag that no longer exists
atune on PyPI The Python package Mirrors the facade's shape rather than its exact API
Cargo features Part of the public API Adding one is additive; removing or renaming one is breaking. The feature reference is generated from the manifests
The journal file The on-disk study Versioned, with the compatibility rule above

Two details of the Python surface that are deliberate and will not surprise you twice:

  • Sampler seed= arguments raise. They exist in the signature for familiarity with Optuna's spelling, and passing one is answered with a message pointing at create_study(seed=…), because determinism belongs to the study seed. Keeping the keyword and rejecting its value is the loud version of a divergence; accepting and ignoring it, which is what these arguments used to do, was a silent one.
  • The wheels are abi3-py310 for GIL-enabled CPython, so one wheel per platform serves every GIL-enabled CPython from 3.10 upward. Free-threaded builds, including cp314t, are not shipped. Raising that floor is a breaking change for whoever is below it.

Rust version

The workspace sets rust-version = 1.92 and edition 2024. An older toolchain refuses the build outright rather than failing halfway through it. An MSRV increase is recorded in the changelog as a breaking change, because for a consumer pinned to an older toolchain it is one.

The publish order, and why it is not one command

The four published crates must go to crates.io bottom-up:

atune_core → atune_derive → atune → atune_cli

This is not a preference. The internal dependencies are path + version entries, and a normal publish package resolves the version half against the registry. Before the first release, an unpatched cargo package -p atune fails with:

no matching package named atune_core found … location searched: crates.io index

The push gate uses cargo package --locked --list for a fast include/exclude check. The dispatch-only release-staging rehearsal packages all four crates bottom-up with temporary same-workspace patches, verifies each archive, and compiles disposable external consumers from those archives. It also copies the real atune_plugin_example unchanged into an isolated workspace, proves that its only atune dependency is the extracted atune_core package with no features, and runs the plugin's build and tests. The patches are runner-local and do not alter publication order or registry state; none of this is registry-publication proof.

atune_bench, atune_oniro, atune_gui, atune_dev and atune_plugin_example are publish = false and are never released. They are internal tooling, a demo and a compile-checked example; their Rust API reference is published on this site rather than on docs.rs, because they will never appear there.

Staging, authorization, and rollback

The repository's release automation stops at a manual, read-only staging rehearsal. A staging run must build the supported crate, wheel, sdist, and documentation artifacts from one commit; consume each build in an isolated environment; run the current dependency-policy gate; and retain a sorted manifest, SHA-256 sums, a standard SBOM, and available hosted provenance. A successful workflow run is review evidence, not permission to publish.

Publication requires a maintainer to review that evidence, select and protect the release identity or signing mechanism, and authorize the exact version and destinations. Registry credentials, signing keys, GitHub Pages mutation, crate yanks, and PyPI uploads are intentionally absent from the staging workflow. Nothing should be published from a dirty worktree or from artifacts rebuilt after the reviewed staging run.

If a problem is found before publication completes, stop and leave every remaining destination untouched. If an already published artifact is unsafe, the maintainer follows the registry's supported withdrawal mechanism (for example, a crates.io yank), publishes a corrected version rather than replacing immutable bytes, and moves documentation aliases only after the corrected artifacts pass staging. PyPI and crates.io receipts, provenance, signatures, and the incident decision are retained with the release evidence. The local rollback rehearsal exercises only disposable indexes and aliases; it never writes a public registry or production documentation target.

Documentation versions

The site is versioned from the start: /latest/ moves with each release, each release also keeps an immutable /0.1.0/-style snapshot, and /dev/ follows the main branch. Retrofitting that later would rewrite every published URL, so the mechanism is in place before there is anything to publish with it. So far only a development snapshot is published, as /dev/ with /latest/ pointing at it; no release has been deployed.

Where to go next

If you want to Go to
What changed, release by release Changelog
To contribute, and the gates a change must pass Contributing
What determinism actually guarantees Determinism
Which feature carries which algorithm Feature reference
To report a vulnerability Security policy