Skip to content

Fix Maven 4 implicit subprojects missed outside vendored (#1809) - #1886

Open
Mikola Lysenko (mikolalysenko) wants to merge 3 commits into
mainfrom
agent/fix-maven4-implicit-subprojects
Open

Mikola Lysenko (mikolalysenko) wants to merge 3 commits into
mainfrom
agent/fix-maven4-implicit-subprojects

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #1809

Summary

Maven 4 builds every direct subdirectory holding a pom.xml under a 4.1.0, pom-packaged root that lists no <subprojects>/<modules>. #1807 taught this to the vendored reactor planner. Every other place that asks "is this a reactor?" still used maven_reactor::declares_modules, which only sees lists that are written out. The user-visible results:

  • Hosted scan pinned only the root pom while a module kept its own literal <version>. It reported the dependency as redirected, and the build still used the unpatched jar.
  • VEX attested that root pin as not_affected instead of reporting vex_maven_reactor_root.
  • Vendored sbt: the vendor_jvm_build_ambiguous guard let such a pom root sitting next to build.sbt through.
  • Project-mode Maven cache scope didn't seed the discovered modules' dependencies, so a hosted or agent crawl scoped them out.

Root cause and fix

The reactor rule existed only inside Reactor::discover. It is now exposed from vendor/jvm/maven_reactor.rs, and every caller uses it:

  • is_reactor_root(read, list): declared lists, or Maven 4 discovery with at least one discovered subproject.
  • may_discover_subprojects(pom, maven_config): the planner's discovers_subprojects, plus the maven.project.discoverSubprojects opt-out (now one discovery_on helper, shared with the planner).
  • implicit_subprojects / implicit_subprojects_in(view): the one probe for "a direct subdirectory holding a pom.xml", with .git excluded.

Callers:

  • patch/redirect/mod.rs rewrite_maven_pom refuses the root with redirect_maven_multimodule_unsupported.
  • hosted/engine.rs read_candidate_files reads each discovered <dir>/pom.xml as advisory input. These files are never edited, and an unreadable one still counts as present. The rewriter needs them to decide.
  • hosted/engine.rs confirm: module poms never feed the needle probe. Once the root is refused as a reactor, a stale suffixed version in pom.xml / .mvn/ (written by an older release) no longer confirms the dependency. Code review raised this; it also covers declared reactors from Hosted Maven mode edits only the root pom of a multi-module build, leaves child literals unpatched, and still attests VEX #261.
  • vex/discover/maven.rs: an implicit reactor root's hosted pin is marked unattested (MavenReactorRoot).
  • vendor/jvm/sbt.rs plan / plan_with_digest now take a ListFn, so the ambiguity guard sees discovered subprojects.
  • vendor/jvm/mod.rs detect / detect_builds take a ListFn, so MavenShape classifies correctly.
  • crawlers/maven_scope.rs: discovered subprojects (recursively, as Maven does) seed the scope.
  • CLI_CONTRACT.md: the hosted Maven rewriter and vex_maven_reactor_root text now cover discovered reactors.

Tests (red → green)

Each new test fails with discovery stubbed out (may_discover_subprojects forced to false) and passes with the fix. Six tests failed in the red run. The stale-pin case was checked separately by disabling the new probe filter.

Caller Test
Hosted rewriter patch::redirect::tests::maven_4_implicit_subprojects_root_is_refused (also controls: no subproject, model 4.0.0, discovery off)
Hosted engine, disk and memory, plus stale pins hosted::engine::tests::a_maven_4_implicit_reactor_root_is_refused
VEX vex::discover::maven::tests::hosted_pin_in_a_maven_4_implicit_reactor_root_is_not_attested (same controls)
sbt ambiguity guard vendor::jvm::sbt::tests::detect_routes_sbt_and_keeps_existing_backends (new implicit-root case, plus a lone-root control)
MavenShape vendor::jvm::tests::a_maven_4_implicit_subprojects_root_is_a_reactor
Cache scope crawlers::maven_scope::tests::maven_4_discovered_subprojects_seed_the_scope (discovery on and off)

Run locally:

  • cargo fmt --all -- --check: clean
  • cargo clippy --workspace --all-features -- -D warnings: clean
  • cargo test --workspace --all-features --no-fail-fast: every Maven/JVM, hosted, VEX and crawler suite passes. 11 failures, all from the sandbox and none in code this PR touches: (a) write/unremovable-file tests that can't simulate a failure as root (copy_tree::relax_loop_must_not_traverse_symlinked_root, vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry, pypi_poetry::wire_write_failure_…, pypi_requirements::wire_failure_rolls_back_…, three in in_process_redirect, two in repair), which other agent PRs also report; (b) two e2e_bun_lockb tests that need the live patch API (patches-api.socket.dev), blocked by the sandbox proxy (tunnel error: unsuccessful)

There is no local Maven 4 e2e. The CI real-Maven legs (e2e_redirect_maven_build, e2e_vendor_maven_build) run against this change.

Review notes (code-review high)

Fixed:

  • Advisory module poms fed the confirmation probe.
  • A stale root pin was confirmed after a refusal.
  • The subproject probe was implemented four ways.
  • A doc comment was misplaced in maven_scope.rs.
  • The CLI contract was out of date.

Not changed, deliberately:

  • sbt::legacy_pom_wired keeps declares_modules. It recognizes pre-v5 single-pom wiring, which read only the root pom and so could have wired a Maven 4 implicit root. Switching it to is_reactor_root would route such a legacy-wired root to sbt and orphan its <repository> wiring, which vendor --revert must unwind first.
  • detect / detect_builds take a ListFn only for MavenShape, which nothing outside tests reads today. I kept it so Detected doesn't misreport an implicit reactor as Single. The extra listing happens only when the root pom is a discovering 4.1.0 pom.
  • maven_scope reads a reactor pom a second time to check discovery. That cost is one small file read per reactor pom on the crawl path. Moving the flag into PomModel would mean changing the parser shared with the planner, so I left it out of this fix.

https://claude.ai/code/session_011f9aeRdqEhwVwx91en466Q


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
A Maven 4 root (model 4.1.0, pom packaging, no <subprojects> list)
builds every direct subdirectory holding a pom.xml. Only the vendored
planner knew this. Hosted scan pinned just the root pom while a
module kept its own version, VEX attested that pin as not_affected,
the sbt ambiguity guard let such a root through, and the project
scope of the Maven cache skipped the modules' dependencies.

Every caller now asks one maven_reactor predicate that follows the
planner's own discovery rule, including the discoverSubprojects
opt-out in .mvn/maven.config. The hosted engine reads each
subdirectory's pom.xml beside such a root so the rewriter can refuse
it as a reactor.

Fixes #1809

Assisted-by: Claude Code:claude-opus-5-5
Review follow-ups for the Maven 4 reactor fix. The hosted engine,
VEX and the Maven cache scope now find discovered subprojects
through the planner's own probe, so a .git directory or a directory
named pom.xml never makes a root a reactor in one place but not
another.

A refused reactor root pins nothing, so the hosted confirmation
probe no longer counts a suffixed version an older release left in
it, or one a module pom names, as a redirected dependency. The CLI
contract now describes Maven 4 discovered reactors.

Refs #1809

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 30, 2026 07:36
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 30, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[burn-down agent] Labeled Ready for review at b57c4a5.

  • CI: 82/82 checks green on b57c4a5; mergeable.
  • Bugbot: Cursor Bugbot check passed on b57c4a5 with no findings.
  • Reviewer note: callers outside the vendored reactor planner now detect Maven 4 implicit subprojects, not just <modules> declarations.

Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hosted Maven and VEX treat a Maven 4 implicit-subproject root as one pom

2 participants