Skip to content

Vendor single-module Maven poms through the suffixed jvm planner and retire the legacy <repository> backend (#973) - #1036

Open
Mikola Lysenko (mikolalysenko) wants to merge 9 commits into
mainfrom
arch-refactor/973-maven-single-pom-planner
Open

Mikola Lysenko (mikolalysenko) wants to merge 9 commits into
mainfrom
arch-refactor/973-maven-single-pom-planner

Conversation

@mikolalysenko

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

Copy link
Copy Markdown
Collaborator

[agent] Implements the maintainer's decision on #973: every Maven root now vendors through the suffixed JVM planner, and the pre-v5 same-GAV <repository> backend is removed. Breaking v5 change. See the new "Vendored Maven" section in docs/migrating-to-v5.md.

Closes #973, closes #263, closes #274, closes #716. Refs #971, #622, #972.

What changes

  • Routing (vendor/jvm/mod.rs): Detected::shape maps any root pom.xml without a Gradle build to Shape::MavenReactor. A single-module pom is a reactor of one. It gets the <version>-socket.<hex8> pin, the <dependencyManagement> pin, .mvn/maven.config, the socket-patch-vendor fallback repository and the .socket/vendor/maven2 tree, all in a jvm ledger entry. Shape::Other now means there is no pom, Gradle, sbt or scala-cli build. It is refused as vendor_jvm_shape_unsupported with reason no_build_file.
  • Deleted from vendor/maven_repo.rs: about 600 production lines and their inline tests:
  • Kept, revert only: revert_maven_opts with revert_repo_record, repository_block and strip_empty_repositories. v4-era ledgers still unwind byte for byte through vendor --revert, remove, rollback and the hosted takeover.
  • Legacy roots are refused, not migrated. If a root's ledger still holds a maven_pom_repository entry, Maven vendoring on it is refused whole as vendor_jvm_shape_unsupported with reason legacy_maven_root. Nothing is written, service_preflight plans no download for it, and jvm_gate_preflight keeps a hosted pin there. The remedy is socket-patch vendor --revert, then vendor again.
  • sbt detection: a root pom wired by the planner now counts as "already wired by the Maven backend" even when it has no <modules>.
  • fetch_registry_bytes is untouched (it belongs to Bound registry downloads by ApiTimeouts instead of a 60 s total deadline (#872) #876).

Things reviewers should know

  • No Maven Wrapper means two warnings. Without .mvn/wrapper/maven-wrapper.properties, which covers most single-module projects, vendor reports vendor_jvm_degraded with maven_f_outside_root and maven_mirror_of_all. This is the planner's existing contract. It is pinned in tests and documented, and VEX is not withheld.
  • A tampered jar is accepted on Maven 3.9.2+. On 3.9.2 and later, Maven reads the committed tree through maven.repo.local.tail, a local repository it does not checksum. A tampered jar left with its stale .sha1 is therefore built. Before 3.9.2 the fallback checksumPolicy=fail repository rejects it. Reactors already behave this way. The real-Maven capstone pins both behaviours, and VEX never attests a tampered tree. If maintainers want a build-time check here, that is a follow-up.
  • 8-hex prefix collisions (existing limitation). Two patches of the same version whose uuids share their first 8 hex digits produce the same suffixed string. The planner's revert then cannot tell them apart and keeps the first entry as drifted. The shared ecosystem fixture used two such uuids, so Maven's second fixture uuid changed.
  • The artifact-barrier crash test now runs nuget instead of maven. The JVM tree is written durably as it goes, so a Maven run gives that barrier nothing to sync.
  • The branch is rebased onto origin/main c5be5d1. utils::digest::tests::production_digests_go_through_the_helpers failed before the rebase because its file list was stale; Fix main CI red on stale digest pending-list entries #1016 on main fixed that.

Docs updated

  • docs/ecosystems.md, docs/usage.md, docs/maven-vendoring.md, docs/sbt-support.md, crates/socket-patch-cli/CLI_CONTRACT.md.
  • New "Vendored Maven" section in docs/migrating-to-v5.md. It says a v5 single-module project is attested by vex only from the committed .socket/vendor/state.json.

Review findings fixed (ad4f7bc, now rebased)

  • The CLI_CONTRACT VEX discovery row for vendored Maven now says it covers pre-v5 <repository> wiring only. A v5 pin is gated by its ledger entry.
  • The migration guide now documents ledger-only VEX for v5 Maven vendoring.
  • Renamed maven_vendor_command_wiring_reattests_without_manifest_or_ledger to maven_vendor_command_wiring_reattests_from_its_ledger_only, which matches what the test asserts.
  • Not fixed, out of scope: the 8-hex pin collision is a general planner limitation (reactors and sbt have it too) and should get its own issue.

Tests

  • After rebasing onto c5be5d1 (main had also touched maven_repo.rs and vendor/mod.rs), these were re-run: core vendor:: vex:: (2955 passed), and CLI vendor_jvm_cli, vendor_ledger_schema_e2e and e2e_vex_lockfile, all green.
  • cargo test -p socket-patch-core --lib -- vendor:: vex::: 2943 passed. New:
  • cargo test -p socket-patch-cli --test vendor_jvm_cli --test contract_gradle_codes --test vendor_ledger_schema_e2e --test vendor_group_commit_e2e --test e2e_vex_lockfile --test e2e_vex_vendor --test e2e_vex --test in_process_vendor --test in_process_scan --test in_process_rollback_all_ecosystems --test ecosystem_dispatch_e2e --test maven_sidecar_cli --test e2e_vendored_production --test in_process_get_hosted_ecosystems: all green.
  • e2e_vendor_maven_build (ignored, real Maven 3.9.16 + Maven Central) passes locally. The CI matrix legs are unchanged.
  • docker_e2e_vendor_maven was rewritten for the new layout and compiles (--all-features). It was not run locally.
  • cargo clippy --workspace --all-features --all-targets shows no new warnings in the touched files. The -D warnings failure in crawlers/python_crawler.rs on macOS also happens on main.

CHANGELOG.md is not touched; it is written at release time.

🤖 Generated with Claude Code


Note

High Risk
This is a breaking vendoring and attestation contract for Maven: legacy projects are hard-refused until revert, and builds depend on suffixed coordinates, .mvn config, and ledger-backed VEX rather than the old repository wiring.

Overview
Breaking v5 change: every Maven root (including single-module pom.xml) now vendors through the suffixed JVM planner—<version>-socket.<hex8>, .socket/vendor/maven2/, .mvn/maven.config, and the socket-patch-vendor fallback repository—instead of the retired pre-v5 same-GAV file:// .socket/vendor/maven/<uuid>/ + <repository> wiring.

Roots whose ledger still has maven_pom_repository are refused whole (legacy_maven_root) until vendor --revert and a fresh vendor; revert/remove paths for old ledgers stay. VEX for v5 Maven pins is ledger-only (no attribution from pom.xml + tree without state.json). Detection treats a lone pom as MavenReactor; Shape::Other / no_build_file applies only when there is no JVM build at the root.

Docs (CLI_CONTRACT, ecosystems, migrating-to-v5, maven-vendoring) and Maven e2e/docker tests are updated for the new layout, wrapper-less vendor_jvm_degraded warnings (maven_f_outside_root, maven_mirror_of_all), and removal of vendor_maven_local_cache_shadow.

Reviewed by Cursor Bugbot for commit 0178c50. Configure here.


Generated by Claude Code

@mikolalysenko
Mikola Lysenko (mikolalysenko) force-pushed the arch-refactor/973-maven-single-pom-planner branch from ad4f7bc to cbdfc69 Compare October 7, 2026 15:46
@mikolalysenko Mikola Lysenko (mikolalysenko) added the arch-refactor PR opened by the scheduled architecture refactor routine label Oct 7, 2026
@mikolalysenko Mikola Lysenko (mikolalysenko) changed the title Vendor single-module Maven poms through the suffixed jvm planner (#973) Vendor single-module Maven poms through the suffixed jvm planner and retire the legacy <repository> backend (#973) Oct 7, 2026
Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Oct 7, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A root pom.xml with no <modules> is now a Maven reactor of one: the
dependency is pinned to <base>-socket.<hex8>, served from the committed
.socket/vendor/maven2 tree, with .mvn/maven.config and the fallback
socket-patch-vendor repository. Shape::Other now means no pom.xml and no
Gradle, sbt or scala-cli build, refused as vendor_jvm_shape_unsupported
(reason no_build_file).

The legacy same-GAV <repository> forward path is deleted: MavenPrelude,
vendor_maven_single, materialise_and_write, acquire_upstream_pom,
artifact_in_sync, build_repo_edit and its anchor helpers, the
comment-stripping declares_modules (#716), project_has_gradle,
local_cache_shadow_warning and the jvm_shape/legacy_mixed_root bridge.
Only the revert of maven_pom_repository entries stays, so pre-v5 ledgers
still unwind byte for byte.

A root whose ledger still holds a maven_pom_repository entry is refused
whole (vendor_jvm_shape_unsupported, reason legacy_maven_root) with
nothing written; service_preflight plans no download for it and
jvm_gate_preflight keeps a hosted pin there. The remedy is
`socket-patch vendor --revert`, then vendor again.

sbt detection treats any planner-wired root pom (not only a multi-module
one) as already wired by the Maven backend.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- vendor_jvm_cli: the legacy mixed-root test becomes a refusal test
  (reason legacy_maven_root, nothing written) for a lone pom and a mixed
  root, then `vendor --revert` restores every byte and the next vendor
  plans through the planner. A new test pins the wrapper-less single-pom
  vendor: suffixed tree and pin, .mvn/maven.config, both
  vendor_jvm_degraded warnings, in-sync re-run, --check, VEX, revert.
- vendor_ledger_schema_e2e: Maven no longer writes whole-file snapshots
  and is skipped in the legacy wiring-shape comparison; a new test
  replays the checked-in legacy Maven ledger: refused, reverted byte for
  byte, re-vendored as a jvm entry.
- vendor_ecosystem_fixtures: Maven's second patch gets a uuid whose
  first 8 hex digits differ from the first, since both pins would
  otherwise be the same `1.0.0-socket.11111111` string.
- vendor_group_commit_e2e: the artifact-barrier crash test runs nuget in
  place of maven, whose JVM tree is written durably as it goes.
- e2e_vex_lockfile/maven: the real-writer cell pins planner VEX
  (attested from the ledger; nothing discovered without it;
  vendor_unwired once reverted).
- e2e_vendor_maven_build: rewritten for the suffixed layout; the fresh
  checkout builds against a warm local repository and behind
  `mirrorOf external:*`, and the stale-sidecar tamper probe pins both
  Maven behaviours (checksum failure before 3.9.2, the unchecked
  repository tail from 3.9.2).
- docker_e2e_vendor_maven: same layout change; the shadow-warning
  assertion is gone and the local repository stays warm.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
docs/migrating-to-v5.md gains a "Vendored Maven" section: the files a
single-module project now gets, the revert-and-revendor step for a
project vendored before v5, the wrapper-less warnings and the retired
codes. ecosystems.md, usage.md, the Maven vendoring and sbt design docs
and CLI_CONTRACT.md drop the single-POM backend, move legacy_maven_root
from the degraded reasons to the shape refusal, and remove the
vendor_gradle_unsupported row.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The VEX discovery table still described the pre-v5 <repository> wiring as
the vendored Maven reference. Mark it pre-v5 only and say a v5 suffixed
pin is gated by its ledger entry. The migration guide now says to commit
.socket/vendor/state.json, since a single-module project vendored by v5
is attested only from it. Rename the e2e test whose name still claimed
it re-attests without a ledger.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The host capstone's tamper probe re-resolved against a local repository
that step 3 had already warmed with the suffixed artifact, so Maven 3.6
and 3.8 served the cached copy and never re-read the checksumPolicy=fail
fallback repository. Drop the cached suffixed version first (a cold
re-resolve, as the docker twin and the pre-#973 test do).

tree_snapshot keyed files by the OS path, so the planner unit test's
removal of .socket/vendor/state.json missed on Windows. Key it with '/'.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikolalysenko
Mikola Lysenko (mikolalysenko) force-pushed the arch-refactor/973-maven-single-pom-planner branch from cbdfc69 to 2e0040f Compare October 7, 2026 23:10
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] CI status: fixed the real failures and rebased onto origin/main (head 2e0040f).

  • e2e_vendor_maven_build on Maven 3.6.3 / 3.8.9: the planner wiring was fine. The tamper probe re-resolved against a local repository that the fresh-checkout step had already filled with the suffixed artifact, so old Maven used the cached copy and never re-checked the checksumPolicy=fail fallback repository. The probe now drops the cached suffixed version first, the same cold re-resolve the docker twin and the pre-Decide: vendor single-module Maven poms through the suffixed-version jvm planner and retire the same-GAV <repository> wiring #973 test do. Passes locally on Maven 3.6.3 and 3.9.16.
  • test (windows-latest): tree_snapshot keyed files by the OS path, so removing .socket/vendor/state.json did nothing on Windows. Keys are now /-separated.
  • lock-diff (vlt): a downstream symptom of the matrix cells that never got a runner. It is not a code failure.

The CI run for 2e0040f was cancelled by a repo-wide manual cancel at about 23:17Z that hit several branches. I did not re-run it. It needs a re-run once PR CI is back on.


Generated by Claude Code

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
maven_repo.rs: keep this PR's deletion of the legacy same-GAV
<repository> backend; #1050's B61 change (comment-aware "already
wired" check via find_wireable_anchor) and its test only touched that
deleted backend.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brings in #1032 (one JVM layout module). Conflicts:
- maven_repo.rs: keep this PR's router (legacy <repository> backend
  deleted) and port #1032 onto what survives: layout:: paths, registry
  URLs, coordinate guards, Gradle marker tables, MARKER_FILE and
  LEDGER_ECOSYSTEM; split jvm_prelude out of vendor_maven_jvm so
  service_preflight applies the same coordinate, ledger and sbt /
  scala-cli gate stops (main's preflight-parity test ported).
- jvm/sbt.rs: reactor_wired keeps this PR's single-pom coverage (no
  declares_modules gate) with main's BEGIN_MARKER / PIN_TAG / MAVEN2_TREE.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 8, 2026 14:15
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

[agent] Merged main, CI green; ready for review.


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread crates/socket-patch-core/src/vendor/maven_repo.rs
Every root pom now routes through the planner as a MavenReactor, so a
module of an ancestor reactor reaches vendor_maven, which refuses it
`not_build_root` before writing anything. jvm_gate_preflight did not
check that, so a hosted-to-vendored takeover or eject from such a module
restored the hosted pin upstream and then the vendor refused, leaving the
package neither hosted nor vendored. Reuse not_build_root in the preflight
(ahead of the legacy_maven_root check, mirroring vendor_maven's order) so
the hosted pin is kept instead.

Co-Authored-By: Claude <noreply@anthropic.com>
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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 0178c50. Configure here.

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

Copy link
Copy Markdown
Collaborator Author

Burn-down agent: labeled Ready for review at 0178c50.

  • CI: 427/427 non-skipped checks green (ci-ok success), 14 skipped
  • Bugbot: reviewed 0178c50, no unresolved findings.
  • Mergeable: yes, no conflicts with base.

Generated by Claude Code

This branch has not been deployed

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