Repository navigation
Fix Maven reactor pinning unresolved properties (#513, #459) - #1787
Mikola Lysenko (mikolalysenko) merged 5 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
A reactor module whose version is an undefined ${prop} or one a
profile raises still got a root pin and a rewrite, so the build kept
an unpatched jar while vex attested not_affected. These tests capture
#513 (external-parent property, Maven 4 <parent/> inference) and
#459 (profile overrides) and fail on main.
Assisted-by: Claude Code:claude-opus-5-5
The vendored Maven reactor planner pinned and rewrote a ${prop}
version it could not resolve with certainty, so the build kept
Central's unpatched jar while vendor reported applied and vex attested
not_affected.
- An undefined property now leaves the local root unpinned, like a
conflicting literal: the module's explicit version beats any pin.
- Profile <properties> are read. A version a profile on the chain of
the declaring pom (or of a pom inheriting it) would move off the
base is left as is with conflicting_literal_version, and the root
is not pinned. maven.config user properties still win.
- A Maven 4 <parent/> without coordinates resolves to ../pom.xml, so
a property defined in the checkout's root is found and patched.
Fixes #513, #459.
Assisted-by: Claude Code:claude-opus-5-5
Unpinning the root was not enough: vex counted the patch as wired
whenever any pom named the suffixed version, so a rewritten sibling
let it attest while a module left at an uncertain ${prop} built the
unpatched jar.
- vex liveness for a reactor now re-plans the declarations and
withholds when one is left at an undefined, inheritor-overridden or
profile-overridden version. The GC and --check reference test is
unchanged, so a referenced tree is never pruned.
- A possibly-matching declaration with an undefined ${...} groupId or
artifactId and its own version also leaves the root unpinned.
- Parent inference is limited to model 4.1.0, and a Maven 4 child's
${project.version}/${project.groupId} come from the inferred parent.
- The inheritor and profile override searches are one walk.
Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
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: VEX ignores uncertain Maven declarations
- Changed vex_sources to gate on both Err and Ok(false) from entry_wired_checked, preventing attestation when Maven declarations may still read the base due to unresolved properties.
Or push these changes by commenting:
@cursor push b856788bb6
Preview (b856788bb6)
diff --git a/crates/socket-patch-cli/src/commands/vex_sources.rs b/crates/socket-patch-cli/src/commands/vex_sources.rs
--- a/crates/socket-patch-cli/src/commands/vex_sources.rs
+++ b/crates/socket-patch-cli/src/commands/vex_sources.rs
@@ -464,16 +464,26 @@
.as_ref()
.filter(|e| socket_patch_core::vendor::jvm::apply::is_jvm_entry(e))
{
- if let Err(detail) =
- socket_patch_core::vendor::jvm::apply::entry_wired_checked(root, entry)
- {
- const CODE: &str = "vendor_jvm_shape_unsupported";
- gated.push(failed(&cand.key, CODE));
- notes.push(note(
- CODE,
- format!("{}: cannot establish JVM wiring: {detail}", cand.key),
- ));
- continue;
+ match socket_patch_core::vendor::jvm::apply::entry_wired_checked(root, entry) {
+ Err(detail) => {
+ const CODE: &str = "vendor_jvm_shape_unsupported";
+ gated.push(failed(&cand.key, CODE));
+ notes.push(note(
+ CODE,
+ format!("{}: cannot establish JVM wiring: {detail}", cand.key),
+ ));
+ continue;
+ }
+ Ok(false) => {
+ const CODE: &str = "vendor_jvm_uncertain";
+ gated.push(failed(&cand.key, CODE));
+ notes.push(note(
+ CODE,
+ format!("{}: Maven declaration may still read the base", cand.key),
+ ));
+ continue;
+ }
+ Ok(true) => {}
}
}
let basis = if let Some(vref) = candYou can send follow-ups to the cloud agent here.
A profile (or inheriting pom) that points the version property at a name the checkout does not define made the planner skip it and still rewrite the declaration to the suffixed base. Such an override now counts as moving the version, so the declaration is left as is and the root is not pinned. The CLI regression test now also covers a sibling module at the literal base: it is rewritten and wired, yet vex still withholds because the uncertain module builds the unpatched jar. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review 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 4c1b5b2. Configure here.
|
Ready for review at
Generated by Claude Code |
|
Ready for review at
Generated by Claude Code |
Final review briefWhat it does. The v5 Maven reactor planner no longer pins the local root when a dependency's Risk: medium. The logic only unpins or withholds more often, never less, but the stricter liveness answer reaches Look here
Verified
Changes I made. No code. Added a "Known gaps" section to the description (the two open questions below). Open questions
Auto-merge is armed, so approving sends it straight to the merge queue. Generated by Claude Code |

LLM Description written by Claude Code:claude-opus-5-5
Refs #513 (partial: no false
vexand no misleading pin;vendorstill counts the patch as applied when nothing is wired, so #513 stays open for that)Fixes #459
Both are p3 (Maven). I took them this run because every p1/p2 issue is already claimed, waiting on
agent:needs-human, or covered by an open PR (#549 → #1698). Both are false-VEX correctness bugs that share one code path.Root cause
The v5 Maven reactor planner (
vendor/jvm/maven_reactor.rs,Reactor::rewrite_declarations) handled a${prop}version it could not resolve with certainty as if pinning were safe.vexliveness then attested whenever any pom held the suffixed version.${prop}in the checkout (an external parent defines it, or a Maven 4<parent/>is never inferred), theproperty_unresolvedbranch warned andcontinued, but it left the local root pinned. The module's explicit version overrides that pin, so the build kept Central's jar. Meanwhilevendorreported applied, andvexattestednot_affectedbecause the pin's suffixed version sits in the root pom.Pom::parseread only top-level<properties>, so a profile override was invisible.${prop}was rewritten to the suffixed base, which silently downgraded the version the profile selects.Changes
property_unresolvedwarning that says so). This matches the existingconflicting_literal_versionhandling. The same applies to a possibly-matching declaration whose${...}groupId/artifactId is undefined and that carries its own version.<properties>are parsed. One walk (version_override) checks every pom that reads the declaration, meaning the declaring pom and each inheritor. It covers both an inheritor's own properties (existing behavior) and every profile on that pom's chain layered over its pom. If any of them moves the version off the base, the declaration is left as is withconflicting_literal_versionand the root is unpinned..mvn/maven.configuser properties still beat profiles.<parent/>with no coordinates resolves to../pom.xml, so a property defined in the checkout's root is found and patched (Vendored Maven reactor reports applied and VEX attests not_affected when a ${property} version is left unresolved, so the build keeps Central's unpatched jar (also triggered by Maven 4 <parent/> inference) #513 case 2). A Maven 4 child's${project.version}/${project.groupId}come from that inferred parent. Inference is limited to model 4.1.0.vexliveness for a reactor (apply::entry_wired_checked→ newmaven_reactor::attestable_checked) re-plans the declarations. It withholds when any declaration is left at a version that may still read the base. Without this, a rewritten sibling module kept attesting.wired_checkeditself is unchanged, because the prune GC and--checkuse it to decide whether a tree is still referenced, so a referenced tree is never pruned.Tests (red → green)
maven_reactor::tests::a_property_from_an_external_parent_leaves_the_root_unpinnedunresolvable_property_and_range_warn(its "still pinned" assertion encoded the bug and now asserts unpinned)a_maven_4_inferred_parent_is_localan_unresolved_coordinate_with_a_version_leaves_the_root_unpinnedan_uncertain_module_beside_a_rewritten_one_is_not_attestablea_profile_overriding_the_version_property_is_left_alonea_profile_up_the_chain_or_on_a_nested_property_is_weighed(parent profile; profile on a nested property)an_inheriting_modules_profile_keeps_the_managed_propertya_profile_agreeing_with_the_base_or_beaten_by_maven_config_is_rewrittenmaven_4_parent_inference_is_4_1_only_and_carries_the_versiona_profile_up_the_chain_or_on_a_nested_property_is_weighedvendor_jvm_cli::maven_reactor_uncertain_property_version_withholds_vex(vendor exits 0 with the warning and leaves the poms untouched;vexskips withvendor_unwired, including when a sibling module at the literal base was rewritten. That sibling case attestsnot_affectedifattestable_checkedis reverted)Commands run locally:
cargo test -p socket-patch-core --all-features --lib vendor::jvm: 240 passed. On the test-only commit (253ab73), 6 of the new or updated unit tests failed.cargo test -p socket-patch-cli --all-features --test vendor_jvm_cli: 52 passed. Red on 253ab73.cargo fmt --all -- --checkandcargo clippy --workspace --all-features -- -D warnings: clean.cargo test -p socket-patch-core --all-features --lib: 6871 passed. The 4 failures are chmod-based write-failure tests in untouched modules (copy_tree,vlt_heal,pypi_poetry,pypi_requirements) that can't fail as intended because this container runs as root. A fullcargo test --workspacebuild would not fit this container's disk allowance, so CI covers the remaining suites.SOCKET_PATCH_MAVEN_E2E_REQUIRED=1):e2e_vendor_jvm_buildmaven reactor tests (2) ande2e_vendor_maven_build(1) pass.No wrapper (
npm/,pypi/,gem/) changes are needed. They only dispatch to the binary./code-review high findings not fixed
property_unresolved("not defined in the checkout") and the root is unpinned. That is conservative: before, the pin was overridden by the explicit version anyway, andvexattested falsely.conflicting_literal_version. A pin that one module's explicit version overrides can't be trusted, andvexmust not attest from it.Known gaps (added by the final reviewer)
vendorstill reports applied when it leaves the root unpinned. In the no-sibling case (the only declaration is uncertain),vendorexits 0 withsummary.applied: 1and writes no pin, and an immediatevendor --checkexits 1 with "vendored artifact is no longer wired into the build". Vendored Maven reactor reports applied and VEX attests not_affected when a ${property} version is left unresolved, so the build keeps Central's unpatched jar (also triggered by Maven 4 <parent/> inference) #513 asks forvendorto report the patch as not applied; this PR does not do that. The existingconflicting_literal_versionpath on main behaves the same way.vex-only.entry_wired_checkedfeedsDiscovery::vendor_entry_live, sovendor'skeep_older_vendored_patch(an older, partly wired vendoring is no longer kept) andscan's takeover warnings also see the stricter answer.wired_checked(prune GC,vendor --check) is unchanged.https://claude.ai/code/session_011f9aePMHGzPvTJK2MCHChi
Generated by Claude Code