Repository navigation
Decide: vendor single-module Maven poms through the suffixed-version jvm planner and retire the same-GAV <repository> wiring #973
Description
Activity
- addedpm:mavenMavenMavenarch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)Filed by a scheduled architecture audit routine (see the architecture review discussion)refactorStructural change: duplicated code or logic, missing abstraction, layering, dead codeStructural change: duplicated code or logic, missing abstraction, layering, dead code
on Oct 7, 2026 - added a commit that references this issue
on Oct 7, 2026 mikolalysenko commented
on Oct 7, 2026 CollaboratorAuthorMore actions[agent] Triage:
priority:p3(Maven). This is a maintainer decision (options A/B/C), so it staysagent:needs-human; agents won't claim it until an owner picks an option.
Generated by Claude Code
mikolalysenko commented
on Oct 7, 2026 CollaboratorAuthorMore actionsMaven is still pretty experimental so we can aggressively move forward to the new backend to keep things simpler and more reliable. Let's get rid of the old backend and fully commit to shipping the suffixed planner.
mikolalysenko commented
on Oct 7, 2026 CollaboratorAuthorMore actions[agent] Decision recorded, per the maintainer's comment above: retire the legacy single-POM backend and ship the suffixed planner for every Maven root. This is option A without the legacy forward path. Legacy entries can only be reverted, and nothing migrates automatically.
What changes
-
Routing.
jvm::Detected::shape(crates/socket-patch-core/src/vendor/jvm/mod.rs:315-325ondb83f01) maps a lone single-module pom toShape::MavenReactor(a reactor of one) instead ofShape::Other. A single-module pom then gets the same wiring as a reactor:- a pinned
<base>-socket.<hex8><version>plus a<dependencyManagement>pin; .mvn/maven.config;- the fallback
socket-patch-vendorrepository; - the committed
.socket/vendor/maven2tree; - a
"jvm"ledger entry.
Shape::Otheris left only for roots with nopom.xmland no Gradle/sbt/scala-cli build. - a pinned
-
Deleted from
vendor/maven_repo.rs(about 600 production lines plus their inline tests):vendor_maven_single(L329) andmaven_prelude/MavenPrelude(L91-L233);build_repo_editwithfind_wireable_anchor/comment_spans/profiles_spans/find_outside/insert_block_at(L1941-L2060);- the comment-stripping
declares_modules/strip_xml_comments/real_open_tag(L2082-L2128); project_has_gradleandlocal_cache_shadow_warning(L2130-L2175);- the
jvm_shape/legacy_mixed_rootbridge (L696-L727); service_preflight's legacy arm andartifact_in_sync.
vendor_mavenbecomesnot_build_root→ refuse a legacy root →vendor_maven_jvm. -
Kept, revert only.
revert_maven_opts' legacy arm withrevert_repo_record,repository_blockandstrip_empty_repositories.vendor --revert,remove, rollback and the hosted takeover can still unwind amaven_pom_repositoryentry byte for byte.path.rskeeps recognising.socket/vendor/maven/<uuid>so the orphan sweep can still clean old trees. -
Existing legacy projects. If a root's ledger holds a
maven_pom_repositoryentry, Maven vendoring on that root is refused withvendor_jvm_shape_unsupportedand reasonlegacy_maven_root. The refusal says to runsocket-patch vendor --revertand vendor again. Today that reason is a degraded warning that only applies to mixed roots. It becomes a refusal that applies to the whole root, and nothing is written.We don't auto-migrate. The legacy revert restores a whole-file pom snapshot, so planner edits laid over it would make that revert unsafe. A one-time revert-and-revendor step in the v5 migration guide is simpler and more reliable.
-
Retired codes:
vendor_maven_local_cache_shadowvendor_maven_multimodule_unsupportedvendor_maven_pom_project_missing(a Mill-only or empty root now getsvendor_jvm_shape_unsupportedwith reasonno_build_file)vendor_gradle_unsupportedvendor_maven_pom_unreadable
Effect on the symptom issues
- Vendored Maven refuses a single-module EAR pom as a multi-module aggregator because two declares_modules disagree #716 is fixed: the second
declares_modulesis deleted. - Vendored Maven silently resolves the unpatched jar when an earlier
<repository>or amirrorOf *mirror serves the same GAV, and VEX still attests #263 and Same-GAV Maven patches are shadowed when a build plugin depends on the same GAV in a reactor build #274: the unsuffixed GAV is no longer written for any new vendor, so neither a warm or plugin-populated~/.m2nor an earlier<repository>can serve it. With Maven 3.9.2+ (maven.repo.local.tail),mirrorOf *can't either; older Maven keeps the existingmaven_mirror_of_alldegraded warning. Both close with the PR. Legacy entries that haven't been reverted yet stay exposed until the user reverts. - Vendored Maven 4 reactor with implicit subprojects (model 4.1.0, no <subprojects>) is wired as a single POM, so a warm ~/.m2 builds the unpatched jar while vex attests not_affected #622 narrows to subproject discovery. An implicit Maven 4 reactor would now be planned as a root-only reactor with suffixed pins instead of the same-GAV
<repository>. Children that declare their own<version>are still unpinned untilReactor::discoverlearns implicit subprojects.
Things the PR must check
maven_reactor::plan_with_config(maven_reactor.rs:134-140) adds themaven_f_outside_rootandmaven_mirror_of_alldegraded warnings whenever there is no.mvn/wrapper/maven-wrapper.properties, becausewrapper_versionreturnsNoneandis_none_oris true. That covers most single-module projects. The PR pins the resulting single-module warnings and VEX outcome in tests. It also says indocs/ecosystems.mdwhat a wrapper-less project gets, so the change is visible rather than a surprise.- Sequencing: this lands before the Move the v5 JVM vendor orchestration and Maven acquisition out of maven_repo.rs into vendor/jvm #972 move. Move the v5 JVM vendor orchestration and Maven acquisition out of maven_repo.rs into vendor/jvm #972 then shrinks to relocating what's left (the JVM orchestrator and the fetch layer). The PR leaves
fetch_registry_bytesuntouched to stay clear of Bound registry downloads by ApiTimeouts instead of a 60 s total deadline (#872) #876.
Tests and docs in the PR
- Tests:
- core:
jvm::detecttables (mod.rs:837-881) expectMavenReactorfor a single pom; - a single-module plan/re-plan/unplan round trip, including CRLF;
- a legacy-ledger root refuses with
legacy_maven_rootand writes nothing; - the legacy-ledger fixture (
tests/fixtures/legacy-ledgers/maven/wired) still reverts byte for byte.
- core:
- E2E and CI:
e2e_vendor_maven_build.rsanddocker_e2e_vendor_maven.rsmove to the suffixed tree;- they build the patched jar with a warm
~/.m2and withmirrorOf external:*; - the shadow-warning assertions are dropped;
vendor_jvm_cli.rs's legacy-mixed-root test becomes the refusal test;contract_gradle_codes.rsis updated;- the CI matrix legs stay as they are.
- Docs:
docs/ecosystems.md: the Maven vendored cell, and the "Warm~/.m2shadowing (legacy single-POM vendoring)" caveat;docs/design/maven-vendoring.md: L9-10, L106-108 and L230;docs/usage.md:117;docs/design/sbt-support.md:208;CLI_CONTRACT.md: the maven row at L723, L1823, thelegacy_maven_rootrow at L2102, thevendor_gradle_unsupportedrow at L2105, and the retired codes;docs/migrating-to-v5.md: a new "Vendored Maven" section with the revert-and-revendor step.
A PR implementing this will follow and close #263, #274 and #716 when it merges. #971's checklist moves on to child 4 (one ledger ecosystem name) after #972.
Generated by Claude Code
-
- added 6 commits that reference this issue
on Oct 7, 2026 mikolalysenko commented
on Oct 7, 2026 CollaboratorAuthorMore actions- added 5 commits that reference this issue
on Oct 7, 2026
[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: discussion #560 register.
Kind: decision. Source: review Part 5.7; register E26. Child 2 of #971.
Question
Should
vendoron a project whose rootpom.xmldeclares no modules, and that has no Gradle build, use the v5 planner instead of the legacy same-GAV<repository>wiring? If so, what happens to projects already vendored the legacy way?Options
<base>-socket.<hex8>, served from.socket/vendor/maven2, plus.mvn/maven.configand a fallback repository. Existingmaven_pom_repositoryentries keep reverting through today's code, and a root that still has one stays legacy until it is reverted.legacy_mixed_rootalready applies exactly this rule to mixed roots (Vendored Maven on a project with both pom.xml and build.gradle reports success with no Gradle warning, the Gradle build keeps the unpatched jar, and VEX attests not_affected #395).vendor/scan --mode vendoredreverts a legacy entry and re-plans it in the same run. That is more code, and the run changespom.xmlfor users who didn't ask.<repository>or amirrorOf *mirror serves the same GAV, and VEX still attests #263, Same-GAV Maven patches are shadowed when a build plugin depends on the same GAV in a reactor build #274, Vendored Maven 4 reactor with implicit subprojects (model 4.1.0, no <subprojects>) is wired as a single POM, so a warm ~/.m2 builds the unpatched jar while vex attests not_affected #622 and Vendored Maven refuses a single-module EAR pom as a multi-module aggregator because two declares_modules disagree #716 one at a time inside the same-GAV model. Some can only be warned about, not fixed, because the unsuffixed GAV is shadowed by design.Why it needs an owner
It changes what
vendorwrites for the most common Maven shape:.mvn/maven.config;<version>and a<dependencyManagement>pin;.socket/vendor/maven2/…instead of.socket/vendor/maven/<uuid>/…;"jvm";vendor_maven_local_cache_shadowwarning.docs/ecosystems.md,CLI_CONTRACT.mdand the Maven CI matrix would change with it.Evidence (main @
9c43dfc)Detected::shapemaps a lone single-module pom toShape::Other, sovendor_mavenfalls back tovendor_maven_single. The same pom beside a Gradle build is already planned bymaven_reactor::plan_with_config.maven_reactortest planned a lone CRLF single-module pom (detect=Other) that depends oncommons-text:1.10.0..mvn/maven.config, themaven2tree andpom.xml, with no warnings.unplanrestoredpom.xmlbyte for byte and removed.mvn/maven.config.vendor_maven_local_cache_shadow,`` because a warm~/.m2serves the unpatched same-GAV jar. Vendored Maven silently resolves the unpatched jar when an earlier<repository>or amirrorOf *mirror serves the same GAV, and VEX still attests #263 (earlier repository / `mirrorOf *`) and Same-GAV Maven patches are shadowed when a build plugin depends on the same GAV in a reactor build #274 (a plugin realm populating `~/.m2`) are more cases of that shadow, and VEX still attests them.If A is chosen
#971 child 3:
detectreturns a planner shape forSingle;vendor_maven_single,maven_prelude's legacy-only checks, thebuild_repo_editwriter, the comment-strippingdeclares_modulesandlocal_cache_shadow_warning(about −600 production lines);revert_repo_recordfor old entries.That also closes #716, makes #622 a smaller routing fix, and removes the single-module half of #263 and #274.
Acceptance criteria for the follow-up
e2e_vendor_maven_build.rs) builds the patched jar with a warm~/.m2and withmirrorOf external:*.maven_pom_repositoryentry still reverts byte for byte, and its root is not re-planned until it is reverted.