Repository navigation
Fix vendored Maven pin overriding BOM/parent management (#488) - #1359
Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
Conversation
The vendored Maven reactor pinned every wired local root, and a local dependencyManagement pin beats both imported BOMs and parents resolved outside the checkout. A project whose corporate BOM or Spring Boot parent managed commons-text at 1.11.0 was silently moved to 1.10.0-socket.*, vex attested it, and a later BOM bump never took effect because re-runs reported the pin in sync. Before pinning a root the planner now reads the external parent chain and every import-scope BOM (local caches or the registry, like other upstream metadata), interpolating properties in Maven's order. Management or an inherited literal at another version leaves the root unpinned with conflicting_managed_version; metadata that cannot be read leaves it unpinned with management_unresolved. A re-run after a BOM bump drops the pin. vendor --check reads the metadata from the local repository only and accepts either decision when some of it is missing. Fixes #488. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
BugBot review |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Early match skips later module versions
- Changed external_management to continue checking all modules via labeled loop instead of returning Ok(None) on first base version match, preventing incorrect downgrades when later modules have different effective versions.
Or push these changes by commenting:
@cursor push ac82cd3ea8
Preview (ac82cd3ea8)
diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs b/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs
--- a/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs
+++ b/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs
@@ -917,7 +917,10 @@
fn bun_lock_remedies_name_the_forced_reinstall() {
for file in ["bun.lockb", "bun.lock", "packages/app/bun.lockb"] {
let remedy = checkout_remedy(&[file.to_string()]);
- assert!(remedy.contains(&format!("`git checkout -- {file}`")), "{remedy}");
+ assert!(
+ remedy.contains(&format!("`git checkout -- {file}`")),
+ "{remedy}"
+ );
assert!(remedy.ends_with(
", then run `bun install --force` (a plain `bun install` keeps the patched copy)"
), "{remedy}");
diff --git a/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs b/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs
--- a/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs
+++ b/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs
@@ -782,7 +782,7 @@
) -> Result<Option<ManagedConflict>, Missing> {
let (g, a) = (patch.group_id, patch.artifact_id);
let ext_chain = self.external_chain(root, lookup)?;
- for rel in self.scope.iter().filter(|rel| self.local_root(rel) == root) {
+ 'module_loop: for rel in self.scope.iter().filter(|rel| self.local_root(rel) == root) {
let locally_versioned = self.chain(rel).any(|p| {
let doc = &self.poms[p].doc;
doc.keyed_declarations(g, a)
@@ -822,7 +822,7 @@
// overridden by management, so a pin cannot reach it
// even at the base version.
if managed && is_base_like(&v, patch.version) {
- return Ok(None);
+ continue 'module_loop;
}
let how = if managed {
"managed by"
@@ -866,7 +866,7 @@
let mut seen = BTreeSet::new();
if let Some(v) = bom_manages(bom, g, a, lookup, &mut seen, 0)? {
if is_base_like(&v, patch.version) {
- return Ok(None);
+ continue 'module_loop;
}
let (bg, ba, bv) = bom;
return Ok(Some(ManagedConflict {You can send follow-ups to the cloud agent here.
external_management returned Ok(None) as soon as one module's inherited parent or imported BOM resolved the base version, so a later module that imports its own BOM (or sets its own property) at another version was never checked. The root pin then overrode that module's management and reintroduced the #488 downgrade. Continue to the next module instead and only clear the root once every module agrees. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KoEk4Y9wedBsjtt9kTUPVY
imports_of read only the project's top-level dependencyManagement, so a <scope>import</scope> BOM inside a profile (activeByDefault included) was never weighed and the root pin could override the version Maven actually uses. Read every profile's management too, after the project's, as Maven appends it. Activation is not evaluated, so a profile BOM that might apply keeps the root unpinned rather than risking a silent downgrade. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KoEk4Y9wedBsjtt9kTUPVY
|
bugbot run Generated by Claude Code |
|
Burn-down agent: disabled auto-merge before pushing fixes for the two open Bugbot findings (9858181: every module is now weighed before skipping the root pin, and profile BOM imports count). The final reviewer should re-review this head and re-arm. Awaiting CI and Bugbot. Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 9858181. Configure here.

LLM Description written by Claude Code:claude-opus-5-5
Fixes #488
Summary
vendoron a Maven reactor no longer silently downgrades a build whose version of the patched artifact comes from an imported BOM or from a parent outside the checkout. It also no longer freezes the pin after the BOM is bumped. Before pinning a local root, the planner now finds out what that management says. When the managed version is not the patch's base version, or cannot be determined, the root is left unpinned and a specific warning says why.Root cause
vendor/jvm/maven_reactor.rspinned every wired local root unless a declaration inside the checkout conflicted (rewrite_declarations→conflicting_literal_version). A local<dependencyManagement>pin beats both of these:<scope>import</scope>);<relativePath/>, a corporate or Spring Boot parent).The planner never read either one.
Fix
maven_reactor::plan_with_externalandexternal_poms_needed. For each wired root not already unpinned,Reactor::external_managementchecks every pom that resolves through that root and declares no version for g:a in the checkout. It looks for:<dependencies>literal in the external parent chain. Properties follow Maven's order: the reactor's own values override the parent's, which covers the${ct.version}sibling from the issue comment;The outcome for the root:
vendor_jvm_degradedreason: conflicting_managed_version.reason: management_unresolved. The remedy namesmvn -q dependency:resolve.already_vendored.Fetching.
vendor_maven_jvm(maven_repo.rs) loops overexternal_poms_neededand gets each POM withacquire_upstream_metadata: local caches first, then the registry when online, verified the same way as the Gradle parent/BOM metadata.vendor --check(apply::check_entry) reads the same POMs from the local repository only, with no network. When some are missing it accepts either decisionvendorcould have made. A BOM bump it can read shows up as drift.Probe and redownload paths (
maven_repo.rsprobe,redownload.rs) only use the plan's tree writes, so they keepplan_with_config(no external weighing).Docs:
docs/design/maven-vendoring.mdandCLI_CONTRACT.md(vendor_jvm_degradedreasons).Per-issue checklist
imported_bom_managing_another_version_leaves_the_root_unpinned. The test also asserts that the pre-fix plan (plan, no external weighing) pins. That assertion is the red evidence.an_external_parent_managing_another_version_leaves_the_root_unpinned.${ct.version}overridden locally:a_local_property_overriding_an_external_parents_version_is_honored.a_bom_bump_after_vendoring_removes_the_pin.vendor --checkcovers it too:check_weighs_an_imported_bom_from_the_local_repository.imported_bom_at_the_base_version_is_pinned.a_bom_that_does_not_manage_the_artifact_keeps_the_pin.a_nested_bom_import_is_followed.unavailable_external_management_leaves_the_root_unpinned.Commands run
cargo test -p socket-patch-core --no-fail-fast: green.cargo test -p socket-patch-cli --no-fail-fast: green except twoe2e_vendor_cargo_buildold-toolchain cases. They depend on locally installed old cargo toolchains, are unrelated, and fail the same way on the other branches.cargo test -p socket-patch-cli --test e2e_vendor_maven_build -- --ignored(real Maven 3.9.16): green.cargo test -p socket-patch-cli --test e2e_vendor_jvm_build -- --ignored maven(maven_reactorcapstone): green.cargo clippy --workspace --all-features -- -D warnings: clean.cargo fmt --check: clean for the changed files. Local rustfmt flags only the untouchedupstream/mod.rs:917.🤖 Generated with Claude Code
Note
Medium Risk
Changes Maven vendoring and pin/check behavior for reactors with external BOMs and parents; incorrect management analysis could leave builds unpinned or accept stale pins, but scope is limited to Maven reactor/mixed shapes with extensive new tests.
Overview
Fixes #488 by changing Maven reactor vendoring so a local root is not pinned when an imported BOM or external parent (outside the checkout) manages the patched artifact at a version other than the patch base—or when that management cannot be resolved.
The planner gains
plan_with_external/external_poms_needed, which walk external parent chains and BOM imports (including nested imports), interpolate properties in Maven order, and emitvendor_jvm_degradedwithconflicting_managed_versionormanagement_unresolvedwhile leaving the affected root unpinned; re-runs after a BOM bump drop an obsolete pin instead of reporting in sync.vendorfetches those external POMs from local caches or the registry viaexternal_maven_poms;vendor --checkreplans using only the local Maven repository and tolerates missing metadata when either pin/no-pin decision would match whatvendorwrote. Docs andCLI_CONTRACT.mddocument the new degraded reasons.Reviewed by Cursor Bugbot for commit 9858181. Configure here.
Generated by Claude Code