Skip to content

ci: pin the build toolchain (cibuildwheel, build, scikit-build-core) - #91

Merged
Ravenwater merged 1 commit into
mainfrom
ci/pin-build-toolchain
Aug 28, 2026
Merged

ci: pin the build toolchain (cibuildwheel, build, scikit-build-core)#91
Ravenwater merged 1 commit into
mainfrom
ci/pin-build-toolchain

Conversation

@Ravenwater

Copy link
Copy Markdown
Contributor

Tier 1 of the dependency float audit. Tier 2 is a policy question and is filed separately as #90.

These three build, repair, test and publish every wheel, and all three were unbounded — the same exposure that took ruff, nanobind and GitPython out of service today.

before after
cibuildwheel unpinned ==4.2.0 (two sites)
build unpinned ==1.6.0
scikit-build-core >=0.10 >=0.10,<2

It had already happened, silently

This is not a hypothetical. Read out of the CI log for the run that published 5.10.2:

Successfully installed ... build-1.6.0 cibuildwheel-4.2.0 ...
*** scikit-build-core 1.0.3 using CMake 4.4.2 (wheel)

Both crossed a major under the open bounds — cibuildwheel 3.x → 4.x and scikit-build-core 0.x → 1.0. The wheels published as 5.10.2 an hour ago were built and tested by tooling versions nobody selected. They worked, which is luck rather than policy.

There was a visible symptom nobody had cause to look at: the arch comment in wheels.yml reasons about "cibuildwheel 3.0" default archs while CI runs 4.2.0. The config is insulated by an explicit CIBW_ARCHS: auto64 / CIBW_BUILD, so this was documentation drift rather than a break — but it is exactly the trace the version drift left. Corrected here.

Why the specifiers differ

cibuildwheel and build are exact-pinned: they are tools, and a reproducible artifact is the entire point of pinning them.

scikit-build-core is capped at the major instead, because patch fixes to a PEP 517 backend are worth taking automatically while a major is not. Worth knowing that it also supplies the CMake that runs — 4.4.2 here, which dropped compatibility with cmake_minimum_required(VERSION < 3.5). That is a real constraint on this build arriving from a dependency the project never names; 3.22 is fine today, and a future MTL5 or universal bump is where it could surface.

Deliberately not pinned

numpy stays >=1.24. This looks like the classic ABI trap — an extension built against numpy 2 headers failing on numpy 1 — and it is not one here. There are no numpy C headers anywhere in python/src/ or python/include/: nb::ndarray goes through DLPack and the buffer protocol. numpy is a stub-generation build dependency and a runtime import, with no compile-time coupling. Pinning it would cost users flexibility for a risk that does not exist.

The ecosystem test dependencies (torch, jax, pandas, scikit-learn) are left floating on purpose. Whether to pin them is a decision about what ecosystem-test is for — early warning of upstream drift, versus a PR queue that upstream cannot block — and it deserves an answer rather than a reflex. #90.

Already correctly pinned, for the record

ruff==0.16.1 · python-semantic-release>=10,<11 + GitPython<3.1.60 · nanobind>=2.0,<4 · FetchContent MTL5 v5.10.0, universal v4.7.9, nanobind v3.0.0 · Highway 1.4.0 (transitively, via MTL5's own pin) · actions/* at major tags · pypa/gh-action-pypi-publish@release/v1, a moving ref but the form Trusted Publishing requires.

Lands as ci:, which is not in patch_tags, so this cuts no release.

🤖 Generated with Claude Code

Tier 1 of the dependency float audit. These three build, repair, test and
publish every wheel, and all three were unbounded -- the same exposure
that took ruff, nanobind and GitPython out of service today.

It had already happened, silently. CI currently resolves cibuildwheel
4.2.0 and scikit-build-core 1.0.3, so BOTH crossed a major under the open
bounds: the wheels published as 5.10.2 were built and tested by tooling
nobody selected. They worked, which is luck rather than policy.

  cibuildwheel        unpinned -> ==4.2.0   (wheels.yml, two sites)
  build               unpinned -> ==1.6.0   (wheels.yml, sdist)
  scikit-build-core   >=0.10   -> >=0.10,<2 (pyproject build-system)

cibuildwheel and build are exact-pinned: they are tools, and reproducible
wheels are the point. scikit-build-core is capped at the major instead,
since patch fixes to a build backend are worth taking automatically and a
major is not. Note it also supplies the CMake that runs (4.4.2 here),
which is a constraint arriving from a dependency the project never names.

Also corrects a stale comment: the arch note reasoned about "cibuildwheel
3.0" default archs while CI ran 4.2.0. The config is insulated by an
explicit CIBW_ARCHS/CIBW_BUILD, so this was documentation drift rather
than a break -- but it is the symptom that showed the version drift.

numpy is deliberately left at >=1.24. It looks like the ABI trap and is
not one: no numpy C headers appear anywhere in the extension, since
nb::ndarray goes through DLPack and the buffer protocol, so numpy is a
stub-generation build dep and a runtime import with no compile-time
coupling.

The ecosystem test dependencies (torch, jax, pandas, scikit-learn) are
left floating on purpose -- that is a policy decision about what
ecosystem-test is for, tracked in #90.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 28, 2026

Copy link
Copy Markdown

Warning

Review limit reached

  • Run on-demand review

On-demand reviews are free for the next 24 days. After that, they cost $0.25 per reviewed file.

Or wait 50 minutes for your next included review.

View limit details

Limit details: You’ve used the included review currently available. Your 76 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5cd0b2b6-79f6-4b39-9ee1-c34fdfd4f124

📥 Commits

Reviewing files that changed from the base of the PR and between 8c84926 and 6ce7f35.

📒 Files selected for processing (3)
  • .github/workflows/wheels.yml
  • CHANGELOG.md
  • pyproject.toml

Comment @coderabbitai help to get the list of available commands.

@Ravenwater Ravenwater self-assigned this Aug 28, 2026
@Ravenwater
Ravenwater merged commit efd98f9 into main Aug 28, 2026
14 checks passed
@Ravenwater
Ravenwater deleted the ci/pin-build-toolchain branch August 28, 2026 02:06
Ravenwater added a commit that referenced this pull request Aug 28, 2026
Resolves #90. The issue framed this as a tradeoff -- early warning of upstream
drift versus a PR queue an upstream release can block -- and named the deciding
fact: whether ecosystem-test is a required check.

It is not, and neither half of the objection survives checking:

  * the protect-main ruleset carries only deletion and non_fast_forward. There
    are NO required status checks, so a red job cannot block a merge.
  * nothing depends on this job. wheels.yml's release chain gates on
    validate-wheels, in a different workflow, so a failure here cannot stop a
    release either.

The dilemma rested on a premise this repo does not have. A failure is loud (a
red check, a failed run) and cheap (blocks nothing), which is what an
early-warning lane should be. Floating is already the right answer and already
in place; what was missing was the reasoning.

These differ from cibuildwheel/build/scikit-build-core, pinned in #91: those
build or publish an artifact, so an upstream release retroactively changes what
ships. This job's value IS moving with upstream.

No behaviour change -- a comment, so the next dependency audit does not 'fix'
this by pinning it. Records the one condition that would flip the tradeoff:
adding required status checks to the ruleset.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant