chore: correct the SPDX licence, state the resolver, and pin the MSRV - #273
Merged
Conversation
Set `license = "AGPL-3.0-or-later"` on `[workspace.package]`. Bare `AGPL-3.0` is deprecated in the SPDX list. Every member inherits the value, and README.md now agrees with it. State `resolver = "3"` explicitly rather than remove the key. Issue #258 said edition 2024 implies resolver 3 and the key is redundant. That is wrong for this workspace: a virtual manifest has no edition of its own, so an absent key falls back to resolver 1 no matter what the members declare. With the key removed, cargo reports: warning: virtual workspace defaulting to `resolver = "1"` despite one or more workspace members being on edition 2024 which implies `resolver = "3"` and `cargo tree -e features --workspace --all-features` then prints a different feature graph, 4054 lines against 4247. The same command under `resolver = "2"` and under `resolver = "3"` is byte-identical, so stating 3 changes nothing that ships and puts the resolver 1 fallback out of reach. Keep `panic = "abort"` on both `[profile.dev]` and `[profile.release]` so a debug run dies the way a release run dies. Cargo forces unwind on the test and bench profiles, so the eight `#[should_panic]` tests are unaffected; all eight pass and no cargo diagnostic about the setting appears. The cost is a second compile of the dependency graph for the test units. Add `rust-version = "1.94.0"` to `[workspace.package]` and the inherit key to every member manifest. Three files state the toolchain version independently and none of them fails to compile when one drifts, so `scripts/msrv-lint.sh` compares the manifest against the flake pin and the rust-setup action input, and fails when a member omits the inherit key. It reads text and compiles nothing, so it runs in the toolchain-free content job and in `just ci`. Hold the licence in the same check, because nothing else holds it. Cargo performs no local SPDX validation: `cargo publish --dry-run -p nexum-world` packages, verifies, compiles and reaches `warning: aborting upload due to dry run` with exit 0 under `AGPL-3.0-or-later`, under the deprecated `AGPL-3.0` and under `NOT-A-REAL-LICENCE` alike, and prints no licence diagnostic in any of the three. The dry run is therefore not evidence for this change; the SPDX list is. The check instead rejects a bare GPL-family identifier in the root manifest and requires `license.workspace = true` on every member, so a licence typo fails a pull request rather than an upload. Closes #258 AI Assistance: Claude Opus 5 used for the implementation.
mfw78
force-pushed
the
chore/spdx-resolver-msrv
branch
from
August 17, 2026 04:44
8e315c0 to
84ad7c5
Compare
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
What this unblocks
The workspace declared
license = "AGPL-3.0", a bare GPL-family identifier that the SPDX list deprecates, so crates.io rejects the manifest of every member at upload time.No crate in the workspace could be published, so no v1 tag could exist, and nothing downstream could pin to one.
Phase 0 of #258 clears that, and adds the drift guard that keeps it cleared.
Be precise about what the issue's done-when actually proves, because I tested it and it proves nothing about the licence.
cargo publish --dry-run -p nexum-worldwas run with three values oflicense, each with--allow-dirtyand afterrm -rf target/packageso neither the dirty-tree guard nor a stale artifact could confound the result.Before, with the deprecated
AGPL-3.0:After, with
AGPL-3.0-or-later:Both exit 0, and so does a run with the outright invalid
NOT-A-REAL-LICENCE.Grepping the full output of all three for
licenreturns nothing.Cargo 1.94.0 performs no local SPDX validation at all, so the dry run is not the gate on the licence and cannot distinguish the fix from the bug it replaced.
The justification for the licence change is the SPDX list; the guard against a regression is
scripts/msrv-lint.sh, added here.What the dry run does prove is that nothing else stops
nexum-worldfrom packaging: it packages, verifies and compiles the packaged crate, and stops only because it is a dry run.Resolver: the issue text was wrong
Issue #258 said that edition 2024 implies
resolver = "3"and theresolverkey can therefore be removed.That is wrong for this workspace, and removing the key would have been a silent behavioural change rather than a cleanup.
The root manifest is a virtual manifest, so it has no
editionof its own, and the implication runs off the manifest's own edition, not off its members'.With the key removed, cargo prints:
Removing it would have demoted the workspace from resolver 2 to resolver 1.
That is not cosmetic:
cargo tree -e features --workspace --all-featuresprints a materially different graph with the key absent, several hundred lines shorter, with features genuinely unified differently, for examplehashbrownlosing itsfoldhashedge andserdefeature rows moving.The feature graph did not change between resolver 2 and resolver 3.
The same
cargo tree -e features --workspace --all-featuresrun underresolver = "2"and underresolver = "3"is byte-identical,diffexits 0 with zero differing lines.So stating
3moves nothing that ships, and it puts the resolver 1 fallback permanently out of reach.The key is now stated explicitly with a comment recording why it cannot be inferred.
Licence
license = "AGPL-3.0-or-later"on[workspace.package], inherited by all 20 members.The
-or-laterhalf is the maintainer's explicit choice, not a default: bareAGPL-3.0is deprecated precisely because it states no position on later versions of the licence, and this workspace takes the permissive position.README.md now agrees.
The only remaining
AGPLstring outside the manifest is the prose reference inside LICENSE itself.Panic profile
panic = "abort"on[profile.dev]and[profile.release]is pre-existing on main.The decision here was to keep it rather than to add it, so this change adds only the comment explaining why, and carries zero behavioural change.
Both contested claims about it were verified.
The workspace has exactly eight
#[should_panic]tests, and cargo forces unwind on the test and bench profiles regardless of the setting, so they are unaffected: run by name under the committed profiles,8 tests run: 8 passed, and the full suite is795 tests run: 795 passed, 0 skipped.No cargo diagnostic about the panic setting appears anywhere in the full
just cilog or thejust buildlog.The real cost is a second compile of the dependency graph for the test units, which the comment now states.
MSRV and the drift check
rust-version = "1.94.0"on[workspace.package], withrust-version.workspace = trueon every member.Three files state the toolchain version independently and none of them fails to compile when one drifts:
rust-versionin the root manifest is a promise to a consumer, the flake pin is what a developer actually runs, and the.github/actions/rust-setup/action.ymlinput is what a pull request is judged by.scripts/msrv-lint.shholds all three on one version.The manifest and the flake pin must be byte-identical; the action names a two-component toolchain that rustup resolves to the newest patch, so it is compared on major.minor.
The script reads text and compiles nothing, so it runs in the toolchain-free content job and in
just ci, and is available asjust msrv.Every failure mode was verified by mutating one file at a time and restoring, and the clean tree exits 0 in each case:
Cargo.tomlrust-version to 1.93.0flake.nixpin to 1.95.0stableset -eAGPL-3.0-onlyor-or-laterThe check deliberately does not police the prose "Rust 1.94" strings in README.md and AGENTS.md, only the three machine-readable sources.
Review
The review raised three findings.
All three were confirmed empirically and fixed; none was rejected.
scripts/msrv-lint.shguardedrust-versioninheritance per member but said nothing aboutlicense, so the identifier this change just corrected could silently return through a single member restating it. Reproduced: withlicense = "AGPL-3.0"oncrates/nexum-sdk/Cargo.toml, the check printedmsrv-lint: okwhilecargo metadataresolved that one member to the deprecated value and the other nineteen toAGPL-3.0-or-later. Fixed by extending the existing member loop tofor key in rust-version license, and by closing the root-manifest half of the same hole with a rule that rejects a bare GPL-family identifier by shape,^(AGPL|LGPL|GPL)-[0-9]+\.[0-9]+$, rather than against this workspace's literal choice, so a future licence change needs no edit to the script.flake.nixpointed a maintainer at.github/workflows/ci.ymlfor the CI toolchain version, which that file does not state; the value lives in.github/actions/rust-setup/action.yml. Pre-existing on main, but this change makes exactly that pairing load-bearing, so a maintainer bumping the MSRV would have been actively misdirected. The comment now names the right file and the script that holds the pair together.One suggestion was declined, and it is worth naming.
scripts/msrv-lint.shnow covers the licence as well as the MSRV, so its name is no longer complete.A rename would churn the CI workflow beyond the single step this change is allowed to touch, plus the justfile recipe, AGENTS.md and the commit body, for no behavioural gain, so it is left for a later change if wanted.
Deliberately absent, belongs to #145
No crate metadata beyond what already existed, no
publish = falseon the fixtures and examples, no CHANGELOG.md, and no release workflow..github/workflows/ci.ymlreceives the MSRV step and nothing else.Verification
nix develop --command just ciexits 0.It was run again after the commit so that
content-lint.sh main..HEADinspected a non-empty range and actually judged the new commit message.Suite: 795 tests run, 795 passed, 0 skipped, doctests ok.
origin/mainat fb20a73 is an ancestor of HEAD, nothing was rebased or rewritten, and the working tree is clean.Closes #258
AI Assistance: Claude Opus 5 used for the implementation, review, and this description.