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