Skip to content

Versioning & Release Policy

voiage uses semantic versioning and tag-driven releases. The Cargo workspace version is authoritative, Maturin exposes it as dynamic Python package metadata, and release tags are validated fail-closed against it. Polyglot manifests remain explicit and synchronized to the same version.

The normative policy is published in specs/v1/compatibility-policy.json. Breaking changes to stable Rust, Python, C ABI, binding, CLI, schema, or serialized-output contracts require a major release. Minor releases may add backward-compatible capabilities, while patch releases may only make backward-compatible fixes.

Provisional and experimental surfaces are excluded from these guarantees until promotion. Promotion requires a normative contract, executable compatibility fixtures, and a migration assessment.

A stable feature remains available for at least two minor releases and six months, whichever is longer. At v1, Python emits FutureWarning; all surfaces provide a stable diagnostic code, replacement path, and migration documentation. Removal is permitted only in the next major release and requires updated compatibility fixtures and release notes.

The 0.x raw-dictionary EVPPI compatibility alias emits FutureWarning with the stable deprecated_raw_dict_parameter_samples diagnostic code and points callers to ParameterSet. Its warning class, code, replacement, and migration documentation are executable v1 compatibility contracts.

The Python package declares a dynamic version in pyproject.toml. Maturin reads the authoritative Cargo workspace version when building package metadata. The version-sync validator compares external binding manifests to that version, and release validation rejects a v* tag that does not match it exactly.

Binding manifests are expected to match the canonical version exactly:

  • bindings/julia/Project.toml
  • rust/Cargo.toml
  • r-package/voiageR/DESCRIPTION

The repository keeps ecosystem-specific tag and registry conventions:

  • Python publishes from v* tags
  • Julia publishes from julia-v* tags
  • Four binding-independent Rust core crates publish to crates.io from signed rust-v* tags through Trusted Publishing; FFI, PyO3, and test-support crates remain private
  • R publishes from r-v* tags

The version numbers in package manifests must align with the latest released Python tag. Tag prefixes and registry targets remain ecosystem-specific; only the version value is shared.

For a Python release candidate, Cargo and Julia retain the canonical SemVer identity (for example, 2.0.1-rc.1) while Python uses its normalized PEP 440 distribution identity (2.0.1rc1). R cannot represent an alphanumeric prerelease in DESCRIPTION, so it uses a numeric development version below the target stable release and records the canonical identity in Config/voiage/Release-Version. Such a candidate is Python/TestPyPI-only: it does not create r-v*, julia-v*, or rust-v* publication tags.

The repo-local validator checks the canonical version against binding manifests:

Terminal window
tox -e version-sync

Or equivalently:

Terminal window
uv run python scripts/validate_version_sync.py
  1. Update the Cargo workspace version and synchronized binding manifests
  2. Run the version-sync validator and fail-closed release-tag validation
  3. Cut the matching v<version> tag
  4. Let the tag workflow build and attest a private draft
  5. Review exact artifact digests, then dispatch TestPyPI publication
  6. Permit PyPI and the public GitHub Release only after the TestPyPI smoke gate