Skip to content

[#12302] Fix TransitiveDependencyManager version downgrade from parent-inherited depMgmt#12316

Closed
gnodet wants to merge 2 commits into
maven-4.0.xfrom
it-gh12302
Closed

[#12302] Fix TransitiveDependencyManager version downgrade from parent-inherited depMgmt#12316
gnodet wants to merge 2 commits into
maven-4.0.xfrom
it-gh12302

Conversation

@gnodet

@gnodet gnodet commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Strip parent-inherited <dependencyManagement> entries from transitive dependency descriptors to prevent TransitiveDependencyManager from propagating them through the dependency graph
  • Add integration test reproducing the version downgrade scenario (RED test turned GREEN)

Problem

When a transitive dependency's parent POM declares <dependencyManagement>, those entries are inherited into the child's effective model. TransitiveDependencyManager collects depMgmt from every POM in the graph and applies it at depth 2+, causing parent-inherited management rules to override version declarations in deeper transitive dependencies.

Example: root → module-a (parent=parent-a, manages lib-c:1.0) → module-b → lib-c:2.0. Parent-a's lib-c:1.0 management leaks through module-a and downgrades lib-c from 2.0 to 1.0.

Fix

In DefaultArtifactDescriptorReader.loadPom(), compare the effective model's depMgmt with the parent model's depMgmt. Entries that appear in both with the same key and version are parent-inherited and stripped. This is safe because ArtifactDescriptorReader is only called for transitive dependencies — the root project's depMgmt goes via CollectRequest.setManagedDependencies().

Applied to both impl/maven-impl and compat/maven-resolver-provider.

Test plan

  • gh-12302 IT: lib-c:2.0 resolved correctly (not downgraded to 1.0)
  • MNG-8347 transitive mode: level4 managed by level2's own depMgmt, level3 unmanaged, root wins for level5/6
  • MNG-7982 non-transitive mode: only root depMgmt applies (Maven 3 behavior preserved)

Fixes #12302

🤖 Generated with Claude Code

Adds integration test that verifies TransitiveDependencyManager does not
silently downgrade dependency versions when an intermediate POM's
<dependencyManagement> declares a lower version of a transitive dependency.

Dependency chain:
  root → module-a:1.0 (parent=parent-a) → module-b:1.0 → lib-c:2.0
  parent-a manages lib-c to 1.0

Expected: lib-c resolves to 2.0 (declared by module-b)
Actual (bug): lib-c downgraded to 1.0 by parent-a's dependencyManagement
because TransitiveDependencyManager has deriveUntil=MAX_VALUE.

This test is expected to FAIL (RED) until the fix is applied.
@gnodet gnodet added this to the 4.0.0-rc-6 milestone Jun 18, 2026
@gnodet gnodet added the bug Something isn't working label Jun 18, 2026
@gnodet gnodet self-assigned this Jun 18, 2026
…dependency descriptors

TransitiveDependencyManager collects <dependencyManagement> from every POM
in the dependency graph. When a POM inherits depMgmt from its parent, those
entries leak into the transitive graph and can override version declarations
in deeper dependencies, causing unintended version downgrades.

The fix strips parent-inherited depMgmt entries in
DefaultArtifactDescriptorReader.loadPom() before passing the model to the
resolver. Only entries directly declared in the POM (or from its own BOM
imports) are kept. This is safe because ArtifactDescriptorReader is only
called for transitive dependencies — the root project's depMgmt goes via
CollectRequest.setManagedDependencies().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gnodet gnodet changed the title [#12302] Add RED IT for TransitiveDependencyManager version downgrade [#12302] Fix TransitiveDependencyManager version downgrade from parent-inherited depMgmt Jun 19, 2026
@gnodet gnodet requested a review from cstamas June 19, 2026 10:14
@gnodet

gnodet commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

Closing this PR — after discussion with Tamas, the current behavior is correct.

TransitiveDependencyManager applies depMgmt "down the tree" within the same branch. When module-a inherits lib-c:1.0 management from parent-a, that management correctly applies to lib-c at depth 3 in module-a's subtree. This is by design, not a bug.

The fix for consumers who need a different version is to explicitly declare the dependency in their own <dependencyManagement> — root's depMgmt is collected at depth 0 and wins via putIfAbsent semantics.

Claude Code on behalf of Guillaume Nodet

@gnodet gnodet closed this Jun 19, 2026
@github-actions github-actions Bot removed this from the 4.0.0-rc-6 milestone Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant