From 65993be1b2ecdc590f566b2bcfea803d0d08b5e6 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 3 May 2026 23:44:47 +0900 Subject: fix(resolver): cap inline package loads by root require constraint Mirror Composer's PoolBuilder::markPackageNameForLoading: when the root requires a name with a version constraint, loads of that name (seed and transitive) are filtered down to candidates whose own version (or any emitted branch-alias version) satisfies the constraint. Without this, the actual package at a non-matching version slips into the pool alongside a provider satisfying the root require, masking what should be a conflict (provider-gets-picked-together-with-other-version-of- provided-conflict.test). Also restore the Composer v1 compat path in inline_package: when the JSON sets version_normalized to the legacy 9999999-dev sentinel, re-normalize from the human-readable version field so a root require for `dev-master` matches the loaded package. Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/mozart-registry/src/inline_package.rs | 32 +++++++++++++++++++--------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'crates/mozart-registry/src/inline_package.rs') diff --git a/crates/mozart-registry/src/inline_package.rs b/crates/mozart-registry/src/inline_package.rs index a7df987..95f842f 100644 --- a/crates/mozart-registry/src/inline_package.rs +++ b/crates/mozart-registry/src/inline_package.rs @@ -143,17 +143,29 @@ fn parse_inline_package(value: &serde_json::Value) -> Option { // PackagistVersion requires `version_normalized`. If the inline definition // omits it (the common case), compute it the same way Packagist does: // run the version through Mozart's normalizer. + // + // Mirrors Composer's `ArrayLoader::parsePackage` Composer v1 compat path: + // when `version_normalized` is exactly `9999999-dev` (the legacy default + // branch sentinel), re-normalize from the human-readable `version` field + // instead. Without this, the package's version stays as `9999999-dev` + // even though its pretty form is e.g. `dev-master`, and a root require + // for `dev-master` then can't match the loaded package. let mut value_for_parse = value.clone(); - if let serde_json::Value::Object(ref mut map) = value_for_parse - && !map.contains_key("version_normalized") - { - let normalized = mozart_semver::Version::parse(&version_str) - .map(|v| v.to_string()) - .unwrap_or_else(|_| version_str.clone()); - map.insert( - "version_normalized".to_string(), - serde_json::Value::String(normalized), - ); + if let serde_json::Value::Object(ref mut map) = value_for_parse { + let needs_normalize = match map.get("version_normalized") { + None => true, + Some(serde_json::Value::String(s)) => s == "9999999-dev", + _ => false, + }; + if needs_normalize { + let normalized = mozart_semver::Version::parse(&version_str) + .map(|v| v.to_string()) + .unwrap_or_else(|_| version_str.clone()); + map.insert( + "version_normalized".to_string(), + serde_json::Value::String(normalized), + ); + } } let version: PackagistVersion = serde_json::from_value(value_for_parse).ok()?; -- cgit v1.3.1