Skip to content

Release evidence

Evidence a release candidate needs beyond the local gates. None of it is part of the ordinary change loop.

Evidence outside local gates

Local green output does not prove these surfaces:

Surface Prerequisite Required evidence
Real NFS Linux; readable /proc/self/mountinfo; an absolute, canonicalizable, writable ATUNE_NFS_DIR whose filesystem type is exactly nfs or nfs4 Mount identity, commit, six-worker × 25-trial ignored-test log, explicit exit code
Self-hosted platform matrix Linux x86_64/aarch64, macOS x86_64/aarch64, and Windows x86_64 native self-hosted runners Full gate logs, five native wheel installs, and production manylinux_2_17 proof
Hosted CI/Pages Maintainer-enabled protected runs; final repository/site identity; Pages source set to GitHub Actions; github-pages environment allows the authorized default branch or release tag Run IDs, immutable complete-version artifact, deployment commit, and public URL checks
Fresh advisories An online isolated advisory index Exact cargo-deny version, index revision, lockfile, and full standard-fetch report; protected rerun remains a release gate
Crate/wheel/sdist artifacts Locally buildable artifacts Isolated consumer installs/imports and a deterministic manifest
Publication/signing/rollback Explicit maintainer authorization Registry receipts, signatures or provenance, and a staging rollback rehearsal

All CI runs on explicitly routed self-hosted runners: a missing runner is unavailable evidence, never permission to fall back to GitHub-hosted or Linux-only checks, and cross-compilation does not prove native imports. Fork pull requests never execute on the fleet. Canonical Pages deployment needs a runner and workflow in the canonical repository.

The NFS row comes from the Rust workflow's live job, dispatched manually onto a runner with the prerequisites (a scheduled run would fail for the wrong reason), or locally with ATUNE_NFS_DIR=/absolute/nfs/scratch cargo test --locked -p atune_core --test journal_nfs -- --ignored --nocapture. The live job refuses to start without a Linux nfs/nfs4 mount, and keeps manifests, exit codes, logs, and run roots for ninety days. A failure is a regression or a changed external contract; read the logs to tell which. Local substitutes do not close this evidence row.

Python ownership safety runtimes

Release evidence only: these runs complement the installed-wheel gate and add no supported wheel. Record the source identity, interpreter and toolchain versions, exact command, test count, and diagnostics; keep evidence outside the checkout. A successful --no-run build is preparation, not evidence. Never set LSAN_OPTIONS=exitcode=0 (with this runtime it also makes an address error exit zero), never suppress a whole library, and never subtract aggregate leak totals.

Embedded Rust tests under AddressSanitizer (Linux x86_64; pinned nightly-2026-07-26 with rust-src; a CPython with its development library and NumPy for the dataframe tests). This instruments Rust only, not CPython or NumPy, so it is complementary evidence, not whole-process proof; keep extension-module off. PYTHONMALLOC=malloc is required: pymalloc keeps objects in mmap arenas that LeakSanitizer does not scan, so a heap block referenced only from a Python object is reported as leaked. Under pymalloc the run fails on such reports; under malloc it still fails on a real leak or any other sanitizer error:

env -u LD_PRELOAD -u LSAN_OPTIONS \
  CARGO_TARGET_DIR=/tmp/atune-python-asan CARGO_BUILD_JOBS=2 \
  PYO3_PYTHON=/absolute/path/to/python RUSTFLAGS=-Zsanitizer=address \
  PYTHONMALLOC=malloc \
  ASAN_OPTIONS=detect_leaks=1:halt_on_error=1:abort_on_error=0:fast_unwind_on_malloc=0 \
  cargo +nightly-2026-07-26 test --locked -Zbuild-std \
  --target x86_64-unknown-linux-gnu -p atune_py --lib -- --test-threads=1

Installed-wheel lifecycle probe (crates/atune_py/tests/safety/lifecycle.py): fixed-work create/run/drop cycles covering callback and objective failures, stop, abandoned trials, queue ownership, parallel workers, and NumPy exports, with bounded post-warmup growth. Run it from a disposable directory with absolute paths:

env -u LD_PRELOAD -u PYTHONHOME -u PYTHONPATH -u PYTHONMALLOC \
  OPENBLAS_NUM_THREADS=1 timeout 120 /absolute/path/to/wheel-venv/bin/python -I \
  /absolute/path/to/atune/crates/atune_py/tests/safety/lifecycle.py \
  --allocator pymalloc --cycles 64 --warmup 8 \
  --output /absolute/path/to/evidence/lifecycle.json

Keep both --negative-control and --negative-growth-control results; each must exit nonzero for its own reason. Repeat with PYTHONMALLOC=malloc, --allocator malloc, and -s instead of -I (isolated mode ignores PYTHONMALLOC). Under the sanitizer interpreter use --allocator sanitizer --memory-observer none; traced growth is then unavailable, so both traced runs stay required. Unset LD_LIBRARY_PATH there too (actions/setup-python sets it): it outranks the interpreter's RUNPATH, so it can load an uninstrumented libpython in place of the sanitizer build's own.

Sanitizer interpreter. .github/release/build_python_safety_runtime.sh builds an isolated ASan CPython (--without-pymalloc) without touching the host Python. Select a matching Clang with --cc/--cxx, a private compiler library directory with --compiler-library-path (not LD_LIBRARY_PATH), and an absent or empty --output. Its exit proves preparation and native controls only. --disable-float-freelist is for allocation-origin investigation and needs fresh captures and controls.

Classify captures with the actual captured exit status:

python .github/release/check_python_safety.py \
  --log /absolute/path/to/evidence/raw.stderr --exit-status 1 \
  --identity /absolute/path/to/evidence/identity.json \
  --baseline /absolute/path/to/evidence/reviewed-baseline.json \
  --completion /absolute/path/to/evidence/completion.json \
  --stdout /absolute/path/to/evidence/raw.stdout

A clean report needs status zero. A leak-only report may keep status one only with a reviewed exact-stack baseline and identity-bound completion evidence, or with the atune-free control below; the checker never writes a baseline, and an empty one accepts no leaks. Acceptance runs use ASAN_OPTIONS=detect_leaks=1:halt_on_error=1:abort_on_error=0:fast_unwind_on_malloc=0 with LSAN_OPTIONS unset (record null in runtime.options); slow unwinding is required to resolve frames through NumPy. Unresolved signatures cannot enter a baseline.

Classify against the atune-free control (D82, by the owner's decision of 2026-10-20; sanitizer.yml does this). Run the lifecycle probe on the sanitizer interpreter with --no-atune-free-control, then run the same command again with --atune-free-control in its place and another --output. The control imports NumPy through the probe's own call chain, takes the one reference to NumPy's _ARRAY_API capsule that rust-numpy 0.30 keeps (get_numpy_api), and exits without importing atune. Both runs must parse the same arguments, because CPython's free lists recycle objects: the allocation stack a report shows for a leaked object depends on every allocation before it. Then:

python .github/release/check_python_safety.py \
  --log /absolute/path/to/evidence/sanitizer.stderr --exit-status 1 \
  --probe-evidence /absolute/path/to/evidence/sanitizer.json \
  --control-log /absolute/path/to/evidence/atune-free-control.stderr \
  --control-exit-status 1 \
  --control-evidence /absolute/path/to/evidence/atune-free-control.json

It accepts a leak report only when every stack in it appears in the control's report with the same kind and the same full normalized frames, and with no more bytes or objects; when both evidence files show the same interpreter, NumPy, allocator and configuration, and the control imported no atune module; and when no stack has an unresolved frame or a frame in atune's package. Nothing is matched by library or function name and no totals are subtracted. A leak atune adds appears as a stack the control lacks, or as more bytes or objects on one it has, and fails the run until a fix or a reviewed baseline covers it.

Free-threaded ownership run. Build a cp314-cp314t-* safety wheel (not cp310-abi3-*; this does not change the release matrix):

env -u LD_PRELOAD CARGO_BUILD_JOBS=2 maturin build --locked \
  --manifest-path crates/atune_py/Cargo.toml \
  --interpreter /absolute/path/to/python3.14t --compatibility linux \
  --target-dir /tmp/atune-python-free-threaded \
  --out /absolute/path/to/empty-wheel-output

Confirm cp314-cp314t-* in the wheel's WHEEL metadata, install that exact artifact with --no-deps into a disposable free-threaded environment, and check sysconfig.get_config_var("Py_GIL_DISABLED") == 1, that atune imports from inside that environment, and, after the import, not sys._is_gil_enabled(). Then run the ownership scenarios (no NumPy) from any pytest environment:

env -u LD_PRELOAD PYTHONPATH= \
  ATUNE_TEST_PYTHON=/absolute/path/to/free-threaded-venv/bin/python \
  /absolute/path/to/pytest-venv/bin/python -m pytest \
  crates/atune_py/tests/test_installed_wheel.py -q