tms-publish-release
Skill279 starsChanged 34 days agoMITView on GitHub
Use when preparing, building, validating, publishing, or verifying torch_memory_saver beta and stable releases across x86_64, aarch64, CUDA 12, and CUDA 13.
- Deletes or force-pushes
- Installs packages
- Commits and pushes
We split this skill into the rules it gives an agent
45 rules. About 0 are common, 30 are distinctive, and 15 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈30
Distinctive · estimate
This author's own choices. Worth a look.
15
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 9
Use canonical PEP 440 versions in setup.py:
- Distinctive: line 20
Never persist PyPI credentials on the GPU host.
- Distinctive: line 21
Never create a GitHub Release.
- Distinctive: line 22
Treat PyPI upload and Git tag push as irreversible.
- Distinctive: line 39
Never reuse a local or remote source or artifact directory, even for the same version and commit.
- Distinctive: line 40
Keep every build, validation, environment, and recheck log under the unique artifact directories.
- Very specific: line 57
Stop if the tree is dirty, HEAD differs from origin/master, or the release commit is detached.
- Very specific: line 58
Read and validate the version without importing setup.py:
- Very specific: line 78
Use c29087a58db9d120b3e69623714c5dd043029d77 as the historical source baseline for the first tagged release after 0.0.9.post1.
- Distinctive: line 79
Use the previous release tag after the first tagged release.
- Distinctive: line 94
Stop if GPU 0 is busy, Docker is unavailable, or the host lacks enough space for the PyTorch builder images.
- Very specific: line 97
Never copy it to tom-workstation.
- Distinctive: line 98
Never mount it into a long-lived container.
- Distinctive: line 103
Check for an existing ARM64 binfmt handler:
- Distinctive: line 111
If it is missing, stop for approval before installing QEMU binfmt.
- Distinctive: line 112
Treat installation as privileged and host-wide.
- Very specific: line 113
Use the pinned tonistiigi/binfmt image.
- Distinctive: line 114
After approval, install only ARM64 and verify it with an ARM64 Alpine container:
- Distinctive: line 126
Treat binfmt as boot-scoped unless the host has an explicit persistent registration.
- Distinctive: line 137
Keep upload and tag creation outside every release script.
- Distinctive: line 138
Do not create, query, attach artifacts to, or require a GitHub Release.
- Very specific: line 139
Use sdist only for focused diagnostics.
- Very specific: line 140
Use artifacts for the canonical release workflow after all three distributions exist.
- Very specific: line 162
Do not use rsync --delete.
- Distinctive: line 180
Run long commands in a background exec session and babysit with:
- Distinctive: line 332
Do not upload directly from the remote build directory.
- Distinctive: line 333
Keep all three distribution files and full logs at the permanent local artifact path.
- Very specific: line 334
Treat artifacts.sha256 as the immutable human-review boundary.
- Distinctive: line 335
Never overwrite or regenerate the manifest after human review.
- Very specific: line 339
Write one Markdown report in the matching local agent-context project agent-drafts/ directory.
- Distinctive: line 340
Use the exact version and run ID in the filename.
- Distinctive: line 342
Do not copy raw logs into the report.
- Distinctive: line 371
Stop after writing the report.
- Very specific: line 377
Run one fail-closed command that appends every immediate pre-upload check and its complete output to publish-recheck.log:
- Very specific: line 393
Treat an existing PyPI version or remote v<VERSION> tag as a hard failure.
- Distinctive: line 394
Treat network and authentication errors as hard failures.
- Distinctive: line 404
Create and push an annotated tag only after PyPI confirms the version:
- Distinctive: line 412
Do not create a GitHub Release for beta, release-candidate, stable, or post releases.
- Distinctive: line 413
Never reuse or move a published version tag.
- Distinctive: line 430
Run this chapter only after PyPI and the Git tag exist.
- Distinctive: line 432
Keep Chapter 9 implemented as auditable shell.
- Very specific: line 434
Start one fresh --rm container for each CUDA major.
- Very specific: line 435
Before installation, require Python to report that torch_memory_saver is absent.
- Distinctive: line 438
Run the complete runtime pytest suite with the exact structured skip gate in both containers.
- Distinctive: line 441
Never delete a published PyPI version to make a retry look clean.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
1 Release contract
- Work from the repository root and publish only a clean commit already merged into the latest
origin/master. - Use canonical PEP 440 versions in
setup.py:- Beta:
0.0.10b1, not0.0.10.beta-1,0.0.10-beta.1, or0.0.10beta1. - Release candidate:
0.0.10rc1. - Stable:
0.0.10. - Post release:
0.0.10.post1only for a source-equivalent packaging correction.
- Beta:
- Publish exactly three distributions:
cp39-abi3-manylinux2014_x86_64wheel.cp39-abi3-manylinux2014_aarch64wheel.- Source distribution.
- Build and test on
tom-workstation. - Publish from the local controller.
- Never persist PyPI credentials on the GPU host.
- Never create a GitHub Release. Publish only the PyPI distributions and an annotated Git tag.
- Treat PyPI upload and Git tag push as irreversible.
- Complete the Section 8 confirmation gate before either action.
2 Prepare isolated paths
set -euxo pipefail
export TMS_RELEASE_VERSION="<VERSION>"
export TMS_RELEASE_SHA="$(git rev-parse --short=12 HEAD)"
export TMS_RELEASE_RUN_ID="$(date -u +%Y%m%dT%H%M%SZ)-$$"
export TMS_REMOTE_ROOT="/home/tom/workspace/torch_memory_saver_release_${TMS_RELEASE_VERSION}_${TMS_RELEASE_SHA}_${TMS_RELEASE_RUN_ID}"
export TMS_REMOTE_ARTIFACTS="${TMS_REMOTE_ROOT}_artifacts"
export TMS_LOCAL_ARTIFACTS="/Users/tom/domains/human/others/artifacts/torch_memory_saver/${TMS_RELEASE_VERSION}-${TMS_RELEASE_SHA}-${TMS_RELEASE_RUN_ID}-release"
export TMS_RELEASE_CHECKS="$PWD/.claude/skills/tms-publish-release/scripts/release_checks.py"
mkdir "$TMS_LOCAL_ARTIFACTS"- Never reuse a local or remote source or artifact directory, even for the same version and commit.
- Keep every build, validation, environment, and recheck log under the unique artifact directories.
3 Preflight
3.1 Repository and version
- Append every command, complete output, and exit result to
release-preflight.log:
set -euxo pipefail
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- git fetch origin master
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- git status --short --branch
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- git rev-parse HEAD
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- git rev-parse origin/master
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- git diff --stat origin/master...HEAD- Stop if the tree is dirty,
HEADdiffers fromorigin/master, or the release commit is detached. - Read and validate the version without importing
setup.py:
set -euxo pipefail
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- \
uv run --script "$TMS_RELEASE_CHECKS" version \
--setup-py setup.py \
--expected-version "$TMS_RELEASE_VERSION"- Confirm the target version does not already exist on PyPI:
set -euxo pipefail
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- \
uv run --script "$TMS_RELEASE_CHECKS" pypi \
--expected-version "$TMS_RELEASE_VERSION"- Review every change since the previous release commit.
- Use
c29087a58db9d120b3e69623714c5dd043029d77as the historical source baseline for the first tagged release after0.0.9.post1. - Use the previous release tag after the first tagged release.
3.2 Host and credential checks
set -euxo pipefail
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- \
ssh tom-workstation 'set -euxo pipefail; curl --retry 5 --retry-delay 1 --retry-all-errors -x http://127.0.0.1:7890 -I --max-time 20 https://registry-1.docker.io/v2/'
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- \
ssh tom-workstation 'set -euxo pipefail; test -x /home/tom/.local/bin/uv; nvidia-smi --query-gpu=index,name,driver_version,memory.total,memory.used,utilization.gpu --format=csv,noheader; docker version; df -h / /home/tom'
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- \
/usr/bin/stat -f '%N mode=%Lp size=%z' "$HOME/.pypirc"- Expect HTTP
401from the Docker Hub registry probe; it proves official registry reachability. - Stop if GPU 0 is busy, Docker is unavailable, or the host lacks enough space for the PyTorch builder images.
- Require local
$HOME/.pypircmode0600. - Never print
$HOME/.pypirc. - Never copy it to
tom-workstation. - Never mount it into a long-lived container.
3.3 ARM64 emulation on the x86_64 host
- The official
manylinuxaarch64-builderimages are Linux ARM64 images, not x86 cross-compiler images. - Check for an existing ARM64 binfmt handler:
set -euxo pipefail
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- \
ssh tom-workstation 'set -euxo pipefail; test -r /proc/sys/fs/binfmt_misc/qemu-aarch64; cat /proc/sys/fs/binfmt_misc/qemu-aarch64'- If it is missing, stop for approval before installing QEMU binfmt.
- Treat installation as privileged and host-wide.
- Use the pinned
tonistiigi/binfmtimage. - After approval, install only ARM64 and verify it with an ARM64 Alpine container:
set -euxo pipefail
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- \
ssh tom-workstation 'set -euxo pipefail
docker run --privileged --rm tonistiigi/binfmt@sha256:400a4873b838d1b89194d982c45e5fb3cda4593fbfd7e08a02e76b03b21166f0 --install arm64
cat /proc/sys/fs/binfmt_misc/qemu-aarch64
docker run --rm --platform linux/arm64 alpine:3.22 uname -m'- Require the smoke test to print
aarch64. - Treat binfmt as boot-scoped unless the host has an explicit persistent registration. Recheck this section after every workstation reboot.
3.4 Release script roles
| Script | Responsibility | Mutates release state |
.claude/skills/tms-publish-release/scripts/release_checks.py | Version, namespace, artifact, manifest, one-command pre-upload, and append-only command-log gates | No |
.claude/skills/tms-publish-release/scripts/gpu_validation.py | Four-cell fresh-container GPU runtime harness with exact skip enforcement | Creates only temporary validation resources |
.claude/skills/tms-publish-release/scripts/pytest_skip_gate.py | Exact pytest skipped-node and reason validator loaded by the harness | No |
.claude/skills/tms-publish-release/scripts/verify_published_release.sh | Auditable post-release PyPI, CUDA 12, and CUDA 13 verification | Creates only temporary validation containers |
- Keep upload and tag creation outside every release script.
- Do not create, query, attach artifacts to, or require a GitHub Release.
- Use
sdistonly for focused diagnostics. - Use
artifactsfor the canonical release workflow after all three distributions exist.
3.5 Create and synchronize the remote source tree
set -euxo pipefail
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- \
ssh tom-workstation "set -euxo pipefail; test ! -e '$TMS_REMOTE_ROOT'; test ! -e '$TMS_REMOTE_ARTIFACTS'; mkdir '$TMS_REMOTE_ROOT' '$TMS_REMOTE_ARTIFACTS'"
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- \
rsync -a \
--exclude .git \
--exclude build \
--exclude dist \
--exclude torch_memory_saver.egg-info \
--exclude .pytest_cache \
--exclude '*.so' \
./ "tom-workstation:$TMS_REMOTE_ROOT/"
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- shasum -a 256 setup.py
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/release-preflight.log" -- \
ssh tom-workstation "set -euxo pipefail; sha256sum '$TMS_REMOTE_ROOT/setup.py'"- Do not use
rsync --delete. - Require the local and remote
setup.pyhashes to match. - The fail-if-present remote
mkdirmakes every rehearsal independent.
4 Build release distributions
4.1 x86_64 wheel
- Build with the official PyTorch CUDA 12.8 and CUDA 13.0 manylinux images.
set -euxo pipefail
ssh tom-workstation "set -euxo pipefail; cd '$TMS_REMOTE_ROOT' \
&& make clean \
&& TMS_PYTHON_BUILD_IMAGE=python:3.11 make build-wheel-multi-cuda \
> '$TMS_REMOTE_ARTIFACTS/build-x86_64.log' 2>&1"- Run long commands in a background exec session and babysit with:
set -euxo pipefail
ssh tom-workstation "set -euxo pipefail; tail -80 '$TMS_REMOTE_ARTIFACTS/build-x86_64.log'"4.2 aarch64 wheel on the x86_64 host
- Build with the official ARM64 PyTorch CUDA 12.8 and CUDA 13.0 manylinux images.
set -euxo pipefail
ssh tom-workstation "set -euxo pipefail; cd '$TMS_REMOTE_ROOT' \
&& rm -rf build torch_memory_saver.egg-info \
&& DOCKER_DEFAULT_PLATFORM=linux/arm64 \
TMS_PYTHON_BUILD_IMAGE=python:3.11 \
make build-wheel-multi-cuda-aarch64 \
> '$TMS_REMOTE_ARTIFACTS/build-aarch64.log' 2>&1"4.3 Source distribution and artifact gate
set -euxo pipefail
ssh tom-workstation "set -euxo pipefail; cd '$TMS_REMOTE_ROOT' \
&& TMS_PYTHON_BUILD_IMAGE=python:3.11 make build-sdist \
> '$TMS_REMOTE_ARTIFACTS/build-sdist.log' 2>&1 \
&& HTTP_PROXY=http://127.0.0.1:7890 HTTPS_PROXY=http://127.0.0.1:7890 \
/home/tom/.local/bin/uv run --script .claude/skills/tms-publish-release/scripts/release_checks.py artifacts \
--dist-dir dist \
--expected-version '$TMS_RELEASE_VERSION' \
--repo-root . \
> '$TMS_REMOTE_ARTIFACTS/validate-artifacts.log' 2>&1"Input: $TMS_REMOTE_ROOT/dist
Exact set: x86_64 wheel, aarch64 wheel, sdist
Wheel gate:
- Filename and internal metadata tags
- ELF machine for every binary
- CUDA 12 and CUDA 13 binary families
- Unsuffixed CUDA 12 compatibility binaries
- RECORD integrity
Sdist gate: canonical version, metadata, complete native sources
Ownership: dist, build, and torch_memory_saver.egg-info belong to the SSH user
Failure policy: stop on any mismatch or extra artifact5 Validate both wheels in fresh GPU containers
5.1 Harness specification
| Case | Runtime path |
| x86_64 CUDA 12 | PyTorch CUDA 12 container, direct NVIDIA runtime |
| x86_64 CUDA 13 | PyTorch CUDA 13 container, direct NVIDIA runtime |
| ARM64 CUDA 12 | Official Lupine 12.8.1 PyTorch worker under QEMU to matched x86 server |
| ARM64 CUDA 13 | Official Lupine 13.0.2 PyTorch worker under QEMU to matched x86 server |
5.1.1 Common contract
Host: x86_64 tom-workstation
Inputs: final x86_64 wheel, final aarch64 wheel, runtime tests only
Image acquisition: docker run --pull missing
Source mount: release root at /workspace, read-only
Install root: final wheel into site-packages or dist-packages
Test root: /validation, outside the source checkout
x86_64 test suite: full single-GPU runtime pytest
ARM64 test suite: full runtime pytest with in-test Lupine skip markers
Lupine signal: TMS_TEST_LUPINE=1 in ARM64 validation containers only
Lupine skip 1: test_cpu_backup_preload_backend_from_env
Lupine skip 2: test_disk_backup, including both hook-mode parameters
CUDA major: matches the matrix cell
CUDA availability: torch.cuda.is_available() is true
GPU identity: NVIDIA GeForce RTX 4090 D
CUDA runtime: matching packaged libcudart added before preload children start
Process architecture: matches the matrix cell
Binary architecture: every loaded ELF machine matches the matrix cell
Inventory gate: every repository test_*.py classified as runtime or build tooling
Build-tool UT: build phase only
Success: zero failures in all four cells
Allowed skips: documented Lupine host-memory cases, single-GPU multi-device cases, XPU-only cases
Skip gate: exact node ID and normalized reason map passed through TMS_EXPECTED_PYTEST_SKIPS
Skip result: SKIP_GATE_ACTUAL and SKIP_GATE_RESULT recorded in the cell log
CLI deselection: forbidden
Missing, additional, duplicated, or reason-changed skip: cell failure
Evidence: image identity, environment, commands, installed paths, binaries, pytest output
Command log: EXEC line, complete stdout/stderr, terminal RESULT line
Container shell trace: set -euxo pipefail records each in-container command
Cleanup: named clients, servers, containers, and private networks removed on success or failure5.1.2 Lupine contract
Matrix source: .claude/skills/tms-publish-release/scripts/gpu_validation.py
Image identity: architecture-specific worker/server refs pinned by digest
Worker: official ARM64 lupine-pytorch-worker
Server: official x86_64 lupine-server with GPU 0 attached
Source revision: ebf4c2784e5756891ed8c2439fec37ed1a4e6b51 for worker and server
Network: private per-cell bridge; RPC port not published to the workstation LAN
Proxy: host.docker.internal:host-gateway -> workstation Clash
Preinstalled: ARM Python, PyTorch, CUDA user-space libraries, CUDA/NVML shims
Added test dependencies: pinned binary NumPy, pytest, nvidia-ml-py wheels
Execution: real ARM64 Python, PyTorch, and TMS wheel under QEMU
GPU path: CUDA driver and NVML calls forwarded to the physical 4090D
Test signal: TMS_TEST_LUPINE=1
Qualification: ARM user-space GPU behavior through Lupine
Not qualified: ARM NVIDIA kernel driver or native ARM PCIe path
Disk-backup boundary: Lupine protects pinned host mirrors read-only, so kernel pread returns EFAULT
RSS boundary: Lupine first-transfer buffers invalidate the preload RSS delta assertion
Product-code workaround: forbidden; keep both behaviors covered by the direct x86_64 cells
Derived client image: forbidden
Server evidence: complete Lupine server output captured with docker logs before cleanup5.2 Run the harness
set -euxo pipefail
ssh tom-workstation "set -euxo pipefail; HTTP_PROXY=http://127.0.0.1:7890 HTTPS_PROXY=http://127.0.0.1:7890 \
/home/tom/.local/bin/uv run --script \
'$TMS_REMOTE_ROOT/.claude/skills/tms-publish-release/scripts/gpu_validation.py' \
--release-root '$TMS_REMOTE_ROOT' \
--artifact-root '$TMS_REMOTE_ARTIFACTS' \
--expected-version '$TMS_RELEASE_VERSION'"6 Pull and recheck artifacts locally
set -euxo pipefail
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/local-recheck.log" -- \
rsync -a "tom-workstation:$TMS_REMOTE_ARTIFACTS/" "$TMS_LOCAL_ARTIFACTS/"
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/local-recheck.log" -- \
rsync -a "tom-workstation:$TMS_REMOTE_ROOT/dist/" "$TMS_LOCAL_ARTIFACTS/dist/"
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/local-recheck.log" -- \
uv run --script "$TMS_RELEASE_CHECKS" artifacts \
--dist-dir "$TMS_LOCAL_ARTIFACTS/dist" \
--expected-version "$TMS_RELEASE_VERSION" \
--repo-root .
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/local-recheck.log" -- \
zsh -lc 'set -euxo pipefail; UV_CACHE_DIR="$TMS_LOCAL_ARTIFACTS/uv-cache" uv run --no-project --with twine python -m twine check "$TMS_LOCAL_ARTIFACTS"/dist/*'
uv run --script "$TMS_RELEASE_CHECKS" run --log-path "$TMS_LOCAL_ARTIFACTS/local-recheck.log" -- \
uv run --script "$TMS_RELEASE_CHECKS" write-manifest \
--dist-dir "$TMS_LOCAL_ARTIFACTS/dist" \
--output "$TMS_LOCAL_ARTIFACTS/artifacts.sha256"- Do not upload directly from the remote build directory.
- Keep all three distribution files and full logs at the permanent local artifact path.
- Treat
artifacts.sha256as the immutable human-review boundary. - Never overwrite or regenerate the manifest after human review; start a new run instead.
7 Write the release evidence report
- Write one Markdown report in the matching local agent-context project
agent-drafts/directory. - Use the exact version and run ID in the filename.
- Link every raw artifact and log with its final absolute local path.
- Do not copy raw logs into the report.
- Set
Human review: pendinguntil the report has been inspected.
Source: full release SHA, branch, clean-tree evidence
Version: canonical version and collision-check results
Host: workstation, GPU, driver, kernel, Docker, binfmt
Artifacts: filename, size, SHA-256, metadata result, twine result, file link
Preflight evidence: release-preflight.log
Build evidence: x86_64 log, aarch64 log, sdist log, artifact-validator log
Local evidence: local-recheck.log and artifacts.sha256
GPU matrix row: architecture, CUDA major, image digest, PyTorch/CUDA/GPU identity
GPU matrix result: pytest summary, every skip reason, terminal RESULT, log link
Lupine boundary: TMS_TEST_LUPINE signal, exact skipped tests, qualification limit
Cleanup: validation containers, server, and network removal evidence
Safety: PyPI upload and release tag not performed; GitHub Release creation forbidden
Human review: pending- Link all five harness logs:
preflight.log.x86_64-cuda12-gpu.log.x86_64-cuda13-gpu.log.aarch64-cuda12-gpu.log.aarch64-cuda13-gpu.log.
- Link
release-preflight.log,local-recheck.log, andartifacts.sha256. - Transcribe the three manifest lines into the artifact table without changing their hashes.
- Require every harness-invoked external command to have an
EXECline and a terminalRESULTline. - Require every GPU row to link the exact log containing image identity, pytest output, skip reasons, and cleanup.
- Stop after writing the report.
- Give the human the report link and wait for explicit publish approval.
8 Publish
- Continue only after the Section 7 report has been reviewed and the human gives explicit confirmation.
- Run one fail-closed command that appends every immediate pre-upload check and its complete output to
publish-recheck.log:
set -euxo pipefail
UV_CACHE_DIR="$TMS_LOCAL_ARTIFACTS/uv-cache" uv run --script "$TMS_RELEASE_CHECKS" pre-upload \
--dist-dir "$TMS_LOCAL_ARTIFACTS/dist" \
--manifest "$TMS_LOCAL_ARTIFACTS/artifacts.sha256" \
--expected-version "$TMS_RELEASE_VERSION" \
--log-path "$TMS_LOCAL_ARTIFACTS/publish-recheck.log" \
--repo-root . \
--remote origin- The command fetches
origin/master; requires a clean tree andHEAD == origin/master; verifies the approved manifest; reruns the complete artifact and Twine gates; and rejects existing PyPI or tag identities. - Require the verified manifest to equal the three hashes approved in the Section 7 report.
- Upload only the files covered by that manifest.
- Treat an existing PyPI version or remote
v<VERSION>tag as a hard failure. - Treat network and authentication errors as hard failures.
- Upload without
--skip-existing; a collision is a hard failure:
set -euxo pipefail
UV_CACHE_DIR="$TMS_LOCAL_ARTIFACTS/uv-cache" uv run --no-project --with twine \
python -m twine upload --non-interactive --repository pypi "$TMS_LOCAL_ARTIFACTS"/dist/*- Poll PyPI with bounded waits until all three files appear under the exact version.
- Create and push an annotated tag only after PyPI confirms the version:
set -euxo pipefail
git tag -a "v$TMS_RELEASE_VERSION" -m "Release $TMS_RELEASE_VERSION" "$(git rev-parse HEAD)"
git push origin "v$TMS_RELEASE_VERSION"- Do not create a GitHub Release for beta, release-candidate, stable, or post releases.
- Never reuse or move a published version tag.
9 Verify the published release from fresh containers
set -euxo pipefail
rsync -a "$TMS_LOCAL_ARTIFACTS/artifacts.sha256" \
"tom-workstation:$TMS_REMOTE_ARTIFACTS/artifacts.sha256"
ssh tom-workstation "set -euxo pipefail
cd '$TMS_REMOTE_ROOT'
TMS_PROXY_URL=http://127.0.0.1:7890 \
.claude/skills/tms-publish-release/scripts/verify_published_release.sh \
'$TMS_RELEASE_VERSION' \
'$TMS_REMOTE_ARTIFACTS/artifacts.sha256'" \
2>&1 | tee "$TMS_LOCAL_ARTIFACTS/verify-published-release.log"- Run this chapter only after PyPI and the Git tag exist.
- Copy the approved
artifacts.sha256to$TMS_REMOTE_ARTIFACTSbefore invoking the script. - Keep Chapter 9 implemented as auditable shell; do not move it into a Python orchestration command.
- Require PyPI to contain exactly the two wheels and sdist recorded by the approved manifest, with matching SHA-256 digests.
- Start one fresh
--rmcontainer for each CUDA major. - Before installation, require Python to report that
torch_memory_saveris absent. - Install the exact published version from PyPI, never from the local
dist/directory. - Require the installed package to resolve from
site-packages, PyTorch to report the matching CUDA major, and the GPU to be the 4090D. - Run the complete runtime pytest suite with the exact structured skip gate in both containers.
- Require local
verify-published-release.logto contain two passing pytest summaries and terminalRESULT: returncode=0. - Report any partial release state explicitly.
- Never delete a published PyPI version to make a retry look clean.
- Increment the prerelease number for a retry.
10 Reliability
| Symptom | Cause | Action |
| Docker Hub pull resets or refuses | Docker daemon is not using tms-clash-main, or the selected Clash node is unhealthy | Repair the daemon proxy or refresh the remote Clash config; do not switch registries |
ARM container reports exec format error | qemu-aarch64 binfmt is absent | Stop; request approval for the privileged binfmt bootstrap |
| Lupine client cannot reach the server | Server startup, Docker network, or matched CUDA pair is wrong | Inspect the cell log; keep the private network and use the exact client/server pair from the matrix |
| ARM PyTorch reports no GPU | The Lupine shim is not first in the client library path or the server lacks the GPU mount | Require /opt/lupine/lib in LD_LIBRARY_PATH, LUPINE_SERVER=<container>:14833, and --gpus device=0 only on the server |
| ARM and server CUDA tags differ | Client and server protocol/runtime behavior is not the validated pair | Stop and use exactly matched Lupine tags; never mix CUDA 12 and CUDA 13 endpoints |
| ARM validation starts downloading PyTorch | The harness is using a generic client or builder instead of the published worker | Stop and use the digest-pinned lupine-pytorch-worker; only NumPy, pytest, and nvidia-ml-py should be installed before the final TMS wheel |
ARM NumPy/pytest install cannot reach 127.0.0.1:7890 | Loopback inside the private-network client is the client itself | Add host.docker.internal:host-gateway and use http://host.docker.internal:7890 inside the ARM client |
make build-sdist cannot import setuptools | The sdist ran against an unprepared host Python | Use the repository's containerized target with TMS_PYTHON_BUILD_IMAGE=python:3.11 |
make clean reports Permission denied under dist/ | An older build left root-owned bind-mount output | Remove only the dedicated rehearsal output through a root Docker container, then use the current scripts that normalize ownership |
Wheel test imports /workspace/torch_memory_saver | Pytest ran against the source tree | Copy only test/ to /validation and rerun against the installed wheel |
CUDA 13 preload tests report libcudart.so.13 missing | The runtime image installs CUDART under Python site-packages/nvidia/cu13/lib without registering it in ldconfig | Discover the matching CUDART path before pytest and export it through LD_LIBRARY_PATH; do not skip preload tests |
SKIP_GATE_RESULT=fail | A skip is missing, additional, duplicated, or has a changed reason | Inspect SKIP_GATE_ACTUAL; update code or the reviewed matrix contract instead of silently accepting it |
| Cleanup raises after a passing cell | Docker cleanup timed out or could not start | Inspect the complete cell log; the harness attempted every cleanup command and preserved any earlier validation error |
| Manifest verification fails | An artifact changed or the file set differs after human review | Stop and start a new build and review run; never regenerate the approved manifest |
Post-release verification reports jq: command not found | The workstation lacks the host-side JSON verifier | Install jq with sudo apt-get install -y jq; the script checks every host dependency before network access |
dist/ has extra wheels | Stale or partial build output | Stop and start a new run directory; never repair or reuse a release tree |
| PyPI upload reports an existing filename | Version or artifact was already published | Stop; never use --skip-existing or overwrite a release |
In this repository3 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- tms-testingSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

