2026.9.6.6: one package, one version — and a rule brings its own environment by Sunrisepeak · Pull Request #584 · mcpp-community/mcpp · GitHub
Skip to content

2026.9.6.6: one package, one version — and a rule brings its own environment - #584

Merged
Sunrisepeak merged 12 commits into
mainfrom
feat/one-package-one-version
Sep 7, 2026
Merged

2026.9.6.6: one package, one version — and a rule brings its own environment#584
Sunrisepeak merged 12 commits into
mainfrom
feat/one-package-one-version

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

The defect

The version belonged to a package's identity in one place and not in another. merge_conditional_xlings deduplicated by PACKAGE, so xim:glibc and xim:glibc@2.40 were one entry; graph_xlings_split compared whole address STRINGS, so the same two were two packages.

A project pinning xim:cuda-nvcc@13.3.33 while a rule package declared @>=12.9.86 therefore installed both — several gigabytes each — and fillXpkgDirs (keep the first value for a name) answered xpkg_dir with one of them. Installed twice, answered once, and nothing said so.

The rule

A package's identity is (namespace, name). The version is always a constraint on it.

Two steps decide which version:

  • Adjudication — the declaration nearer the artifact wins (project > dependency). A declaration naming no version abstains: it says the package is wanted and nothing about which version. Disagreements are reported (xlings/version-override).
  • Validation — the winner must satisfy every requirement that lost. >=, ^, ~ and comma-combined forms are requirements; a bare version is a choice, adjudicated rather than refused. Without that asymmetry every dependency that pinned a tool the project also pins would become a hard failure on upgrade.

This is a comparison, not a search. The version is chosen by adjudication and then checked, so the engine never asks the index which versions exist and carries no constraint solver. The cost is stated in docs/05 rather than hidden: a combination a solver could satisfy is refused instead, and the refusal names the way out.

xpkg_dir now answers a range

The version position has accepted ranges all along and xlings resolves one when it installs — but this lookup compared the whole position against a directory name, so >=8.5.0 installed a payload and then reported that nothing was installed. That is the gap that made a rule package unable to state a floor, and the reason every project repeated its rule's package list.

What it enables

[build-dependencies.mcpp]
plugins = { version = "0.2.4", features = ["rules-cuda"], host-module = true }

and nothing else. The follow-up in mcpp-plugins moves each rule's payloads into [target.'cfg(accelerator = ...)'.feature-xlings.rules-*]; this PR is the engine half that makes overriding one of them safe rather than a second install.

Also here

  • Four refusals get tokens in docs/11: device-source-unconsumed, accel-backend-undeclared, host-module-missing, tool-version-conflict. The first three shipped in 2026.9.6.5 without one.
  • The xlings pin moves 2026.8.30.2 → 2026.9.5.1 (check_version_pins.sh is the guard).
  • docs/specs/manifest-semantics.md §4.3.1 said selectors may not name accelerator; 2026.9.6.5 accepts it. Rewritten around the schedule, which is what the split has always been about.
  • examples/09-heterogeneous/{hip,sycl} declare cuda in [package] accelerators, which their globs name. Both are skipped by this repo's example job, so only the plugins CI could have caught it.

Criteria

627_one_package_one_version one version installed and xpkg_dir answers it and the override is reported. Any one of the three alone passes on a broken engine
628_a_pin_below_a_stated_floor_is_refused refused naming both sides, the --format json reason is tool-version-conflict, and raising the pin builds
629_a_version_range_is_answered_by_xpkg_dir a range is installed and answered, with the project declaring no tool at all
630_a_rule_brings_its_own_environment both gates: no accelerator installs nothing, the accelerator installs and answers
test_xlings_address_set 12 cases over adjudication and validation, including the two that must not refuse
XpkgPayload.* ranges, a bounded range whose answer is not the newest, an absent exact pin, and an unparseable version (8.0.RC1)

627, 628, 629 and 630 were run against the released 2026.9.6.5 and are red there.

版本在一个地方属于包的身份,在另一个地方不属于。条件合并按包去重
(`xim:glibc` 与 `xim:glibc@2.40` 是一个条目),而跨包的划分比对整个地址
串(是两个包)。于是工程钉 `xim:cuda-nvcc@13.3.33`、规则包钉
`@>=12.9.86` 时两份都装 —— 每份数 GB —— 而 `fillXpkgDirs` 里「同名保留第
一个值」让 `xpkg_dir` 只答其中一个。装了两份、用了一份,没有一句话提到它。

身份统一为 `(namespace, name)`,版本永远是这个包上的约束。装哪一版分两
步:裁决 —— 离产物更近的声明赢,不带版本的声明弃权,结果被报出来;校验
—— 赢家必须满足每一条落败的要求,不满足就拒绝并同时点出两侧与出路。裸版
本是选择不是要求,两条不同的精确钉走裁决而不被拒绝,否则每一个「依赖钉了
工具、工程也钉了」的组合会在升级当天变成硬失败。

校验是一次比较不是一次搜索:版本由裁决选定再被检查,所以引擎从不需要问索
引「有哪些版本」,也就不带约束求解器。代价写进文档而不是藏起来。

`mcpp::xpkg_dir` 现在回答范围。版本位一直接受范围表达式且 xlings 真的求解
它,而这个查询把整个位置当目录名比对 —— 于是 `>=8.5.0` 装上了载荷然后回答
「没装」。这正是让规则包无法声明下界的那道缝。

四处新拒绝有了名字:`device-source-unconsumed`、`accel-backend-undeclared`、
`host-module-missing`、`tool-version-conflict`。

xlings pin 从 2026.8.30.2 抬到 2026.9.5.1。

判据:e2e 627(装了什么与答了什么必须同时断言)、628(拒绝 + 抬钉后通过的
反向腿 + `--format json` 的 token)、629(范围被安装并被回答)、630(两重门
的两条腿);单测 test_xlings_address_set 与 xpkg_payload_at 的范围/不可解析
版本两组。三条 e2e 在已发布的 2026.9.6.5 上实测为红。

规范:SPEC-001 §10、SPEC-004 §4.5 与判据 5–7。
@Sunrisepeak
Sunrisepeak merged commit 83c9100 into main Sep 7, 2026
38 checks passed
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.

2 participants