You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Layer 5 of 7 of the staged "CUDA 12 bindings on main" stack. Previous: #3318. Next: #3321. Stack map at the bottom.
What this PR does
Makes git tag v12.9.N on main (or on a release branch) a complete release path, while CUDA 13 releases keep working exactly as today.
The one idea to hold on to: a release reads its configuration from the tagged source, not from the workflow revision. A new resolve-bindings-package job checks out the tag into release-source/, reads itsci/versions.yml, and decides the package root and toolkit (cuda_bindings_12 / 12.9.1 for v12.*). Retained CUDA 13 tags with the old cuda.build.version format and the old cuda_bindings/ directory are recognized too, so a v13.4.4 dry run still works from main's workflow.
Both metapackage wheel builds select their CUDA major explicitly, so a CUDA 12 tag arriving first cannot produce a CUDA 12 wheel in a CUDA 13 artifact.
Read-only jobs prepare release artifacts and build documentation. Publishing jobs consume the validated immutable artifact. They do not check out source, install dependencies, or rerun repository tools; existing environment and Pathfinder verification gates remain.
ci/tools/download-wheels selects artifacts by toolkit, inspects artifact names before downloading, and can no longer pick up *-tests artifacts (a hazard Keith flagged on cuda.bindings: support multiple CTK release lines on main #2737). Dependent Core/Pathfinder tag documentation selects one metapackage using the resolved bindings toolkit.
ci/tools/validate_release_wheels.py (new; replaces the shell wrapper) checks the complete wheel matrix (Python versions x platforms, read from the tagged source) and rejects dev/local versions and duplicates.
ci/tools/check_release_notes.py drops the backport_branch logic. Notes must exist for the exact version in the tagged source; .postN releases are exempt; Pathfinder keeps its own rule.
ci/tools/lookup-run-id --run-id validates a supplied run ID (same tag, same SHA, successful push run) instead of trusting it.
build-docs.yml: a .ci-control checkout supplies the tooling, the release source supplies package data; CUDA 13 docs are built against the checkout's bindings (pixi docs feature uses path = "."); build_docs.sh keeps .dev and rc versions in their own directory so they cannot overwrite stable pages.
ci.yml: a bare v* tag run builds the bindings docs only.
RELEASE-bindings.md (new), RELEASE-core.md, and the release checklist describe the procedure, including two independent releases from two tags on one commit. CI-control Python defaults to 3.11.
Shared action: setup-hardened-python defaults to Python 3.11.
Tests: test_release_publish_security.py (publishing authority boundary), test_bindings_release_compat.py (new), test_download_wheels.py (new), test_validate_release_wheels.py (new), test_check_release_notes.py (rewritten for the new policy).
Docs and environments: .github/RELEASE-bindings.md (new), RELEASE-core.md, ISSUE_TEMPLATE/release_checklist.yml, cuda_bindings_13/docs/build_docs.sh, cuda_bindings_13/pixi.toml + lock (docs feature builds from source), cuda_python/docs/environment-docs.yml, toolshed/setup-docs-env.sh (furo for the CUDA 12 docs theme).
Validation and what has not been exercised
Full CI passed at the pre-rebase head 6600f4cd (run 41194257043, 106 jobs).
Unit tests cover tag resolution for schema-2 and legacy sources, artifact selection by toolkit, test-artifact exclusion, the complete-matrix check, and the notes policy.
Not exercised: a real tag-triggered CI run and a CI: Release dry run with this workflow. A dry run for an existing tag (for example v13.4.4) from the merged main is the natural first check.
Local validation at 6e0ba485f2a7bf804d07a1cf0baa060c593f54ae: 345 CI-tool tests and configured workflow lint passed. Full-stack CI on #3322 passed at 126ed74fb5bbfbc91a04e50557cf936efef4da7f (105 successful jobs; one expected skip).
Stack map: what main can do after each layer merges
carries the schema-2 registry, a manual-only backport workflow, a metadata consistency hook, and the maintenance docs; 12.9.x is frozen
Every layer was designed to be mergeable on its own and had a green full CI run at its pre-rebase head on 2026-10-31. The stack is the "individually mergeable pieces" alternative to #3174-#3182; both stacks reach the same end state except that this one keeps compatibility routes for older artifact sources and retained CUDA 13 tags.
The reason will be displayed to describe this comment to others. Learn more.
🤖 None of the new actions/checkout steps for release-source, and none of the control-plane checkouts in release.yml or release-upload.yml, set persist-credentials: false. build-docs.yml does set it. The tag source is untrusted from the tooling's point of view, and release-source/.git/config keeps the token. Suggested fix: add persist-credentials: false everywhere.
Aside from that, there is a pretty major issue here described below. Fortunately it has a very easy fix (and also will get fixed by #3321 up the stack) -- but I think since the fix is easy to also do here we should. It's a bit concerning that I had to probe my agent about this potential issue 3 times before it agreed it's a real issue.
Everything below is from my 🤖
CUDA 12 cuda-python wheel built and labeled as CUDA 13 (PR #3319, fixed by #3321)
Status: analysis from reading the PR heads (#3319 at 55d68691, #3321 at ef141268). Nothing here was run or reproduced in CI.
Summary
In #3319, the CI step that builds the current-line (CUDA 13) cuda-python
metapackage wheel does not say which CUDA major to build. cuda_python/setup.py
guesses from the git tags on HEAD. If a commit is tagged v12.9.N and CI starts
before the matching v13.x.y tag exists, the guess is 12. CI then builds a cuda_python-12.9.N wheel and uploads it as cuda-python-wheel-cuda13.5.0
(and as the legacy cuda-python-wheel alias). The artifact name says CUDA 13;
the wheel inside is CUDA 12.
#3321 fixes this by passing the major explicitly. #3319 does not, so #3319 alone
leaves a window. The fix for #3319 is a one-line change (below).
Background: how the major is chosen
cuda_python is one directory that builds either line, so it cannot pin a git describe --match pattern statically the way cuda_bindings_12 (v12.*)
and cuda_bindings_13 (v13.*) do. cuda_python/setup.py does this instead:
If CUDA_PYTHON_BUILD_MAJOR is 12 or 13, it uses that major and the
matching --match pattern from [tool.cuda-python.scm-describe-match-by-major]
in cuda_python/pyproject.toml.
If it is unset, it runs git tag --points-at HEAD --list 'v12.*' 'v13.*'. It picks 12 only when a v12.* tag is at HEAD and no v13.* tag is. In every other case, including
"both tags at HEAD" and "no tags", it picks 13.
The auto-detect exists for people building directly from a tagged checkout.
It depends on which tags exist at the moment of the build.
"Build and check cuda-python wheel" (current line)
none: bare pip wheel -v --no-deps . (auto-detect)
cuda-python-wheel-cuda13.5.0, plus legacy alias cuda-python-wheel
The maintenance step is always correct. The current-line step is correct only
when the auto-detect happens to return 13.
The failure scenario
You tag one commit with both v12.9.10 and v13.5.1, as the stack intends to
allow.
v12.9.10 is pushed first. Its tag-triggered CI run starts. At that moment
only v12.* points at HEAD.
The current-line step auto-detects major 12 and builds cuda_python-12.9.10.
The wheel is uploaded as cuda-python-wheel-cuda13.5.0 and as the legacy cuda-python-wheel alias.
If both tags are present before the second tag's run builds (for example, pushed
together in one git push), the auto-detect returns 13 and everything is
correct. The problem depends on tag-push order, which is why it is easy to miss
and hard to reproduce.
Make the current-line step explicit, the same way the maintenance step already
is. ci/tools/env-vars build already exports BUILD_CUDA_MAJOR (derived from CUDA_VER, which is inputs.cuda-version), and the same job already uses BUILD_CUDA_MAJOR elsewhere (for example cuda_bindings-"${BUILD_CUDA_MAJOR}".*.whl), so it is in scope at this step.
Why the registry-derived major and not the triggering tag: each tag-triggered CI
run builds both lines, so the tag that triggered it says nothing about which line
a given step is building. A v12.9.10 run still has to produce the CUDA 13
wheel for the cuda-python-wheel-cuda13.5.0 artifact. The artifact name already
comes from the toolkit version, so the build major should come from the same
source. Then the wheel's major and the artifact's name cannot disagree.
Optional hardening (a few lines, same file): after building, assert that the
wheel's version major equals BUILD_CUDA_MAJOR and fail the step otherwise.
That would turn any future regression of this kind into an immediate build
failure instead of a downstream mismatch.
That is the same explicit-major fix, taken from the registry-derived CURRENT_CUDA_MAJOR. It also builds into separate per-variant directories
(dist/cu12, dist/cu13) so the two wheels cannot overwrite each other, and
skips the current-line step entirely when the plan says it is not needed.
Recommendation
Fix it in #3319 with the one-line change above, even though #3321 supersedes it:
The stack is described as individually mergeable. Between #3319 merging and #3321 merging, main would have the two-tag release path but not the
explicit major.
The change is tiny, in a file #3319 already touches.
The cost is a trivial rebase conflict in #3321, which rewrites the step anyway.
The reason will be displayed to describe this comment to others. Learn more.
— codex GPT-6 on behalf of Ralf —
Done: the CI-control interpreter now uses the shared Python 3.11 default. The Conda docs environment remains on 3.12 to match its wheel artifacts; the rendered-link checker stays on 3.14.
The reason will be displayed to describe this comment to others. Learn more.
I take this to mean that if we are tagged with vX.Y.Z, we only build the cuda-bindings docs. Why? Why not do the same for cuda-core, cuda-pathfinder etc?
The reason will be displayed to describe this comment to others. Learn more.
— codex GPT-6 on behalf of Ralf —
Yes: a shared bindings/metapackage vX.Y.Z tag checks the selected bindings documentation. Metapackage docs are validated by the separate component=cuda-python release dry run; tag CI does not publish docs. Core and Pathfinder retain their existing tag behavior to keep this migration focused. The release guide now explains this boundary.
The reason will be displayed to describe this comment to others. Learn more.
As an aside, since I'm suggesting replacing Python 3.12 with 3.11 in most places, maybe we could just modify setup-hardened-python so that is the default version?
The reason will be displayed to describe this comment to others. Learn more.
Nit: We should have 12 and 13 use the same theme. Let's file an issue for doing that after (as part of the deduplication work that will happen afterward).
The reason will be displayed to describe this comment to others. Learn more.
🤖 publish-testpypi and publish-pypi now run pip install -e ./ci in the same job that holds the PyPI OIDC trusted-publishing token. ci/pyproject.toml pins packaging and PyYAML to exact versions. That is good, but there are no hashes, and setuptools>=77 is fetched unpinned as the build backend. The privileged job therefore pulls and executes third-party package code before the publish step. Suggested fix: do the wheel-version and matrix validation in an unprivileged job [or a separate venv -- @mdboom] and pass only the verified wheels to the publish jobs. Alternatively, install with --require-hashes.
The reason will be displayed to describe this comment to others. Learn more.
— codex GPT-6 on behalf of Ralf —
Moved dependency installation, wheel validation and source archiving into a read-only preparation job, without a publishing environment or OIDC permission. All three publishers consume its immutable artifact ID. The TestPyPI publisher and PyPI publisher now contain only pinned artifact-download and publishing actions; their existing environment and verification gates remain. A separate venv would still share the job's publishing authority, so the isolation is at the job boundary.
The documentation build also explicitly runs with read-only permissions; its separate deployment job retains its own write permissions.
The reason will be displayed to describe this comment to others. Learn more.
🤖 The matrix check reproduces build logic by hand. It hard-codes that win-arm64 exists only for toolkit 13.4 or newer, and that win-arm64 is skipped for Python 3.10. list.remove("win-arm64") only removes the first occurrence. _release_platforms also parses ci.yml job structure (uses, with.host-platform, strategy.matrix). Any change to those build rules or to ci.yml makes the validator reject good releases, or accept bad ones, and nothing ties the two together. Suggested fix: derive the exclusions from the same source build-wheel.yml uses, or add a test that fails when they diverge.
The reason will be displayed to describe this comment to others. Learn more.
— codex GPT-6 on behalf of Ralf —
The toolkit/Python exclusions now live in one wheel_python_versions() policy, used by both CI's Windows ARM64 build matrix and release validation. Platform discovery still reads the tagged source's build jobs, but now deduplicates them with a set. Existing outcome coverage checks CUDA 12, pre-13.4, 13.4+ and CUDA 14, including retained Python 3.10 sources; the current 12/13 wheel targets are unchanged.
The reason will be displayed to describe this comment to others. Learn more.
Efficiency: 🤖 fetch-tags: true with fetch-depth: 1 fetches every tag's commit just to run git archive. You already have release-source checked out at the tag, so git -C release-source archive HEAD would avoid the extra tag fetch.
The reason will be displayed to describe this comment to others. Learn more.
🤖 nit
.ci-control now sits inside the workspace, so the unchanged ls -lahR . steps list it recursively. The .ci-control and ls steps also run on every PR docs build, adding checkout and pip-install time. Suggested fix: narrow the ls targets, or check out .ci-control only for release runs.
The reason will be displayed to describe this comment to others. Learn more.
— codex GPT-6 on behalf of Ralf —
Restricted all four artifact listings to downloaded wheel files, so they no longer walk .ci-control or temporary environments. The control checkout remains useful for historical/nightly sources as well as releases: those source trees can predate the helpers required by the current workflow.
The reason will be displayed to describe this comment to others. Learn more.
— codex GPT-6 on behalf of Ralf —
Addressed both findings in this review: the current-line wheel build now selects its CUDA major explicitly, and all release-source/control checkouts disable persisted credentials. Dependency installation and validation also moved out of the publishing jobs, as discussed inline.
Local backend-wheel builds reproduced the CUDA-12-tag-first case and verified correct versions and dependencies in both tag orders. All 13 inline comments have direct replies; the shared-theme follow-up is tracked in #3403.
Added a dedicated guide for releasing CUDA bindings and the matching cuda-python package, including guidance for legacy release lines.
Updated core release guidance to clarify validation, publication, and release-note requirements.
Improvements
Documentation builds now select the appropriate bindings package for release tags and can build documentation for a selected component.
Release workflows now validate prepared release artifacts before publication.
The pull request updates bindings package resolution, release-note checks, wheel selection and validation, and release workflow artifact handling. It adds a bindings release guide and changes documentation build inputs, artifact selection, and environment setup.
Changes: Release and documentation pipeline
Layer / File(s)
Summary
Bindings package and wheel configuration ci/tools/bindings_config.py, ci/tools/tests/test_bindings_release_compat.py, .github/workflows/ci.yml, .pre-commit-config.yaml
Bindings configuration supports legacy and schema-v2 package registries, tag-based package resolution, and package-root validation. CI derives Windows ARM64 Python versions from the bindings configuration. Tests cover configuration, release resolution, and environment-file output.
Release-note checks resolve tag versions and package locations from configuration or supplied metadata. Wheel downloads select artifacts for the release package, and the validator checks versions and expected wheel targets against the tagged source. Build and test workflows use toolkit-qualified artifact names.
The release workflow prepares wheels, a source archive, and a checksum as an artifact, then passes its ID to upload and publishing jobs. Run-ID lookup validates supplied IDs against the tag. The guides and checklist describe release scope, validation, dry runs, and publication steps.
The docs workflow uses the resolved bindings package and gates artifact downloads and package installation by component. Documentation setup changes version handling, uses the local bindings project, and adds Furo to the environment.
Suggested reviewers:mdboom, juenglin
Change: Feature
Merge Risk: Minimal · up to d966c
This change adds a release path for CUDA 12 bindings and moves publishing to a prepared-artifact handoff. The review found no concrete merge-blocking defect. One optional consistency suggestion remains on the docs workflow. The author notes that a real tag-triggered run and a release dry run were not exercised, so a dry run is worthwhile before the first real release.
Security architecture risk: Low · up to d966c
The design reduces privileged execution by separating release preparation from publication. No introduced security defect was established. Confidence remains limited by unavailable release-protection settings and unverified failure-recovery behavior.
Retained concerns
No architecture-level concerns identified.
Security review details
Security Blast Radius
inferred — A compromised authorized release input could affect the selected component's public package distributions, GitHub release files, and documentation. The reviewed authority is repository-release authority, not a newly exposed tenant or production-service boundary.
Security Findings and Attack Paths
observed — The supplied security assessment retains no findings. Its path-traversal candidate was rejected because lexical traversal is blocked and nested relative paths alone do not establish a boundary violation. This is a bounded rejection, not a claim that deferred surfaces are safe.
Trust Boundaries and Controls
observed — The selected CI run must be completed, successful, push-triggered, and match the expected workflow, tag name, and resolved commit. This check also applies to explicitly supplied run IDs. It establishes run correspondence, not the authorization of the tag creator or release dispatcher.
observed — Publishers no longer execute repository validation code with package-publication authority. Their integrity boundary is the prepared artifact ID and the pinned download and publishing actions; preparation must succeed before either publisher can run.
Resilience and Maintainability Implications
observed — Pathfinder publication retains its index-verification gates. Final GitHub publication waits for documentation, package publication, verification, and release-file upload, and checks that the release is still a draft before changing its public state.
- [ ] Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
The reason will be displayed to describe this comment to others. Learn more.
Nitpick comments: 1
.github/workflows/build-docs.yml: 1
157-157: Trivial · Maintainability & Code Quality · Low value
suggestion: This line still runs python ci/tools/checkout_source_links.py from the source checkout. For release tags that predate the script, the path does not exist. This branch runs only for non-release builds, so the release case is safe. The other CI helpers in this workflow now use .ci-control. Use ./.ci-control/ci/tools/checkout_source_links.py for consistency.
Reviewing files that changed from the base of the PR and between cdebef2 and d966cca.
Files ignored due to path filters: 1
cuda_bindings_13/pixi.lock is excluded by !**/*.lock
Files selected for processing: 30
.github/ISSUE_TEMPLATE/release_checklist.yml
.github/RELEASE-bindings.md
.github/RELEASE-core.md
.github/actions/setup-hardened-python/action.yml
.github/workflows/build-docs.yml
.github/workflows/build-wheel.yml
.github/workflows/ci.yml
.github/workflows/release-upload.yml
.github/workflows/release.yml
.github/workflows/test-sdist-linux.yml
.github/workflows/test-sdist-windows.yml
.github/workflows/test-wheel-linux.yml
.github/workflows/test-wheel-windows.yml
.pre-commit-config.yaml
ci/tools/bindings_config.py
ci/tools/check_release_notes.py
ci/tools/download-wheels
ci/tools/env-vars
ci/tools/lookup-run-id
ci/tools/tests/test_bindings_release_compat.py
ci/tools/tests/test_check_release_notes.py
ci/tools/tests/test_download_wheels.py
ci/tools/tests/test_release_publish_security.py
ci/tools/tests/test_validate_release_wheels.py
ci/tools/validate-release-wheels
ci/tools/validate_release_wheels.py
cuda_bindings_13/docs/build_docs.sh
cuda_bindings_13/pixi.toml
cuda_python/docs/environment-docs.yml
toolshed/setup-docs-env.sh
Files with no reviewable changes: 1
ci/tools/validate-release-wheels
Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 10 remain after this review.
This branch has not been deployed
No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CI/CDCI/CD infrastructurecuda.bindingsEverything related to the cuda.bindings moduleenhancementAny code-related improvements
2 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
— codex GPT-6 on behalf of Ralf —
Layer 5 of 7 of the staged "CUDA 12 bindings on
main" stack. Previous: #3318. Next: #3321. Stack map at the bottom.What this PR does
Makes
git tag v12.9.Nonmain(or on a release branch) a complete release path, while CUDA 13 releases keep working exactly as today.The one idea to hold on to: a release reads its configuration from the tagged source, not from the workflow revision. A new
resolve-bindings-packagejob checks out the tag intorelease-source/, reads itsci/versions.yml, and decides the package root and toolkit (cuda_bindings_12/ 12.9.1 forv12.*). Retained CUDA 13 tags with the oldcuda.build.versionformat and the oldcuda_bindings/directory are recognized too, so av13.4.4dry run still works frommain's workflow.Everything else follows from that:
cuda-bindings-python<py>-cuda<toolkit>-<platform>-<sha>for both roots andcuda-python-wheel-cuda<toolkit>for both metapackages. The temporarymaintenance-*names from ci: build and test local CUDA 12 bindings #3317/ci: use same-run CUDA 12 bindings for Core and metapackages #3318 are gone. The oldcuda-python-wheelalias is still uploaded for older consumers (removed in ci: retire legacy branch-based bindings maintenance #3322).ci/tools/download-wheelsselects artifacts by toolkit, inspects artifact names before downloading, and can no longer pick up*-testsartifacts (a hazard Keith flagged on cuda.bindings: support multiple CTK release lines on main #2737). Dependent Core/Pathfinder tag documentation selects one metapackage using the resolved bindings toolkit.ci/tools/validate_release_wheels.py(new; replaces the shell wrapper) checks the complete wheel matrix (Python versions x platforms, read from the tagged source) and rejects dev/local versions and duplicates.ci/tools/check_release_notes.pydrops thebackport_branchlogic. Notes must exist for the exact version in the tagged source;.postNreleases are exempt; Pathfinder keeps its own rule.ci/tools/lookup-run-id --run-idvalidates a supplied run ID (same tag, same SHA, successful push run) instead of trusting it.build-docs.yml: a.ci-controlcheckout supplies the tooling, the release source supplies package data; CUDA 13 docs are built against the checkout's bindings (pixi docs feature usespath = ".");build_docs.shkeeps.devandrcversions in their own directory so they cannot overwrite stable pages.ci.yml: a barev*tag run builds the bindings docs only.RELEASE-bindings.md(new),RELEASE-core.md, and the release checklist describe the procedure, including two independent releases from two tags on one commit. CI-control Python defaults to 3.11.After this merges
v13.4.N: unchanged procedure.v12.9.Nonmain: tag CI builds both lines (per-line selection arrives with ci: plan builds and artifact reuse per bindings root #3321); CI: Release with componentcuda-bindings, thencuda-python, publishes the CUDA 12 line.Where to look
.github/workflows/release.yml:resolve-bindings-package,determine-run-id, and how the resolved package flows into docs, upload, and publish jobs.ci/tools/bindings_config.py:resolve_release_bindings_package(), including the retained-CUDA-13 branch.ci/tools/download-wheelsandci/tools/validate_release_wheels.py: what can reach PyPI..github/RELEASE-bindings.md: the operator's view.All changes (31 files)
Workflows:
release.yml,release-upload.yml,build-docs.yml,build-wheel.yml(artifact names),ci.yml(docs component on tags),test-sdist-*.ymlandtest-wheel-*.yml(artifact names),.pre-commit-config.yaml(hook dependency).Tools:
bindings_config.py(tag matching, release resolution,write-github-env),check_release_notes.py,download-wheels,lookup-run-id,validate_release_wheels.py(new),validate-release-wheels(deleted),env-vars.Shared action:
setup-hardened-pythondefaults to Python 3.11.Tests:
test_release_publish_security.py(publishing authority boundary),test_bindings_release_compat.py(new),test_download_wheels.py(new),test_validate_release_wheels.py(new),test_check_release_notes.py(rewritten for the new policy).Docs and environments:
.github/RELEASE-bindings.md(new),RELEASE-core.md,ISSUE_TEMPLATE/release_checklist.yml,cuda_bindings_13/docs/build_docs.sh,cuda_bindings_13/pixi.toml+ lock (docs feature builds from source),cuda_python/docs/environment-docs.yml,toolshed/setup-docs-env.sh(furo for the CUDA 12 docs theme).Validation and what has not been exercised
6600f4cd(run 41194257043, 106 jobs).v13.4.4) from the mergedmainis the natural first check.6e0ba485f2a7bf804d07a1cf0baa060c593f54ae: 345 CI-tool tests and configured workflow lint passed. Full-stack CI on #3322 passed at126ed74fb5bbfbc91a04e50557cf936efef4da7f(105 successful jobs; one expected skip).Stack map: what
maincan do after each layer mergesmain...v12.*tagcuda_bindings_12/; the PR-time CPU source-build smoke covers both roots; three CUDA 12 runtime fixes are incuda.coreand the metapackage still consume12.9.xartifacts12.9.xbranchv12.9.Ntag onmain; CUDA 13 releases are unchanged12.9.xis frozenEvery layer was designed to be mergeable on its own and had a green full CI run at its pre-rebase head on 2026-10-31. The stack is the "individually mergeable pieces" alternative to #3174-#3182; both stacks reach the same end state except that this one keeps compatibility routes for older artifact sources and retained CUDA 13 tags.