Release checklist¶
This checklist is the short path from a clean development branch to a release candidate. It is intentionally command-driven so release hygiene stays repeatable and independent of local notebooks or generated optimization artifacts.
Required pre-push gate¶
Run the opt-in local CI gate before pushing a release-candidate commit:
python tools/diagnostics/local_ci_gate.py
This mirrors the hosted CI lanes that should be safe on a normal developer
machine: CLI smoke, compile check, repository size audit, fast pytest with the
current 95% coverage fail-under, selected bounded physics smoke, wheel/sdist
build, fast docs, and full docs. It is deliberately not a Git hook; use
--dry-run, --list, --only STAGE, and --skip STAGE when you need
to inspect or narrow the gate locally.
The authoritative expanded command form is printed by:
python tools/diagnostics/local_ci_gate.py --dry-run
These tests cover the required local lanes: continuation semantics, exact
accepted-point history/output selection, no-executable VMEC residual parity,
Boozer input spectra including lasym=True channels, QI diagnostic metadata
including the bundled solved-state QI seed gate and public
rank_qi_seed_records export, QI example objective assembly including the
optional BoozerBTarget homotopy term, solve-free JVP/VJP routing checks,
pure driver/runtime policy helpers, implicit/wout serialization helpers,
free-boundary/solver helper branches, VMEC-kernel helper branches, packaging
hygiene, additional solve/implicit/wout/driver branch coverage, the required
Python 3.11 coverage gate, and warning-clean documentation. Record the exact
pass/skip/deselect count and coverage percentage from the release-candidate
commit in the release notes. The enforced local and CI coverage gate is
95%.
Before cutting a new release, bump project.version in pyproject.toml
and choose the matching tag name:
python - <<'PY'
import tomllib
version = tomllib.load(open("pyproject.toml", "rb"))["project"]["version"]
print(f"current project.version = {version}")
PY
git tag --list 'v*' | tail -20
git ls-remote --tags origin 'v*' | tail -20
Do not reuse an existing local or remote tag. The release scope should be summarized in the release notes before tagging.
Required GitHub Actions gate¶
After pushing, verify the newest workflow run on main:
gh run list --repo uwplasma/vmec_jax --branch main --workflow CI --limit 5
gh run view RUN_ID --repo uwplasma/vmec_jax --json status,conclusion,jobs
The required release baseline is:
Fast tests pass on all configured Python versions.
The wheel/sdist build succeeds.
Full docs build with warnings as errors.
The parity-manifest smoke job succeeds.
The bounded physics smoke job succeeds.
The
Coverage Gate (py3.11 combined)job succeeds and reports at least 95% combined coverage.Manual/nightly physics jobs may be skipped, but must not fail.
During release preparation, record the latest completed green main CI
baseline for the exact release-candidate commit. Do not reuse historical run
IDs from earlier audits after pushing coverage, docs, or validation changes.
Re-check GitHub Actions before cutting any release candidate.
The current CI workflow also includes a dedicated full-docs job. Treat both
Build (wheel/sdist + docs) and Docs (full guide) as release blockers.
Artifact hygiene gate¶
Before tagging, keep the repository free of transient outputs:
git status --short
rm -rf build dist vmec_jax.egg-info
python tools/diagnostics/repo_size_audit.py --top 40 --max-total-mib 50 --max-file-mib 2
git check-ignore -v docs/_build/html/index.html docs/api/generated/vmec_jax.solve.rst .DS_Store
Do not commit optimization result trees, rerun wout files, profiler traces,
or generated PDFs unless a small artifact is explicitly referenced by README or
docs and has a documented validation purpose.
For one-case optimization showcase smoke checks, use the bounded renderer instead of refreshing the full production matrix:
PYTHONPATH=. python examples/optimization/render_minimal_seed_showcase.py \
--output-root /path/to/minimal_seed_showcase \
--figure-dir /path/to/figures \
--cases qi_nfp1 --skip-missing
Use the production --publication-matrix render only after the full
showcase matrix has been regenerated.
Release tagging gate¶
Tag only after the local and GitHub gates are green:
git tag -a vX.Y.Z -m "vmec-jax vX.Y.Z"
git push origin vX.Y.Z
cat > /tmp/vmec_jax_release_notes.md <<'EOF'
User-visible changes:
- ...
Validation:
- ...
Known limitations:
- For direct-coil free-boundary optimization, state whether gradients are
fixed accepted-branch, same-branch complete-solve validated, or a promoted
adaptive full-loop derivative. Do not imply full adaptive coil-to-Boozer
differentiation unless the release candidate includes that specific
finite-difference gate.
EOF
gh release create vX.Y.Z --repo uwplasma/vmec_jax --title "vmec-jax vX.Y.Z" --notes-file /tmp/vmec_jax_release_notes.md
For GitHub releases, publish-pypi.yml validates that the release tag
matches project.version in pyproject.toml after stripping an optional
leading v. Do not publish a tag unless pyproject.toml has the same
version and the CI gates above are green.
Current repository release note¶
The latest repository tag prepared from this checklist is v0.0.14, built from the matching release tag after the required CI gates passed. Verify PyPI with a no-dependencies wheel download after each publication. Package-index descriptions are immutable for already-published files, so any README or installation wording merged after the latest public tag will not appear on PyPI until the next release upload. Do not describe the repository tag as the latest PyPI or conda-forge package unless the corresponding package index has been checked.
The release notes should list user-visible changes, validation coverage, known
limitations, and any optional external validation that was not run. For
direct-coil free-boundary milestones, the limitations must explicitly separate
JAX-native direct-coil fields, fixed accepted-branch replay/custom-VJP gates,
same-branch complete-solve finite-difference evidence, and any still-unclaimed
adaptive host-controller or Boozer/QS full-loop derivative.
The next release candidate should cite the latest completed green CI run after
re-checking the newest main workflow and the latest passing local coverage
result from the release-candidate commit at the enforced 95% coverage gate.
After the release is public, verify the package index and installation state:
PyPI should show the new release and
pip install vmec-jax==X.Y.Zshould import the same version from a clean virtual environment.Conda-forge may lag PyPI, and PyPI may lag repository tags; README wording should not imply any package index is already synchronized.
Release notes should use post-publication wording once the tag is published, not “must be green before this release is published” phrasing.
Optional research-grade gates¶
These checks are not required for every PR, but they are expected before claiming a broader physics milestone:
RUN_FULL=1 pytest tests/test_wout_comprehensive_parity.py -vfor bundled fixed-boundary, finite-beta, andlasymreferences.VMEC2000 executable parity with
~/bin/xvmec2000for newly added input decks or convergence-policy changes.Do not claim broad strict external LASYM parity until the nightly executable-backed
up_down_asymmetric_tokamakresidual gap is resolved.basic_non_stellsym_pressureis the promoted finite-beta LASYM executable check.SIMSOPT comparison scripts for optimization objective and derivative parity.
CPU/GPU profiling sweeps for any accepted-point replay, scan, or device default change; verify final artifacts still select the best finite exact accepted point when trial and exact residual histories disagree.
Seed-robust QI probes from QI, QP, QH, QA, and non-omnigenous seeds before advertising global QI robustness. Far-seed probes should include the optional same-NFP
BoozerBTargethomotopy lane when a solved QI reference wout is available. Treat trial-solve landscape/basin scans as triage only; use exact-solve diagnostics before promotion claims.
Historical local release-hygiene snapshot¶
The latest documented local rerun evidence for the post-v0.0.14 candidate
is the June 3 release-hygiene/refactor refresh through commit 8c7ffcd.
It is not a release tag by itself, but it is the current evidence bundle to
cite until a newer release-candidate run replaces it:
Local required coverage gate:
JAX_ENABLE_X64=1 pytest -q -n 4 -m 'not full and not vmec2000 and not simsopt' --cov=vmec_jax --cov-report=xml --cov-report=term:skip-covered --cov-fail-under=95passed with2423 passed, 20 skipped, 1 xfailedand95.16%coverage at the enforced95%gate.Focused solve/refactor checks passed:
ruff check vmec_jax/solve.py tests/test_solve_dump_helpers.py,JAX_ENABLE_X64=1 pytest -q tests/test_solve_dump_helpers.py, andJAX_ENABLE_X64=1 pytest -q tests/test_solve_dump_helpers.py tests/test_solve_additional_helpers.py tests/test_solve_runtime.py.GitHub Actions run
26885868424passed build, full docs, CLI smoke, parity dry-run, physics smoke, and Python 3.10/3.11/3.12 fast-test lanes for8c7ffcd; the manual/nightly physics lane was skipped as expected.
Before tagging, replace this snapshot with fresh local/CI evidence from the
exact release-candidate commit if any additional source, test, or docs changes
land after 8c7ffcd.