Skip to content

Vendored Maven refuses a single-module EAR pom as a multi-module aggregator because two declares_modules disagree #716

Description

[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: discussion #560 register.

Kind: bug. Source: new finding, register E55; child of #715 (E10).

Problem

Vendored Maven decides "is this a reactor?" twice, with two different predicates of the same name:

  1. vendor/jvm/mod.rs#L219-L227 detect routes through maven_reactor::declares_modules. It parses the pom into a `Doc` and counts only `<modules>` / `<subprojects>` whose parent is the project or a profile. Plugin configuration, comments and CDATA don't count, and the test [`plugin_configuration_modules_do_not_make_a_reactor`](https://github.com/SocketDev/socket-patch/blob/045d7ec783d788bf3c5a1310724b51e09fb6505d/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs#L3783-L3792``) pins that a maven-ear-plugin <configuration><modules> is Shape::Other.
  2. Shape::Other then falls through to the legacy single-pom path. maven_prelude calls the legacy maven_repo::declares_modules,`` which only strips comments and looks for any <modules open tag anywhere, and refuses with `vendor_maven_multimodule_unsupported`.

So every input where the two disagree is refused. A real reactor never reaches the legacy check, because jvm_shape takes it first (vendor_maven L308; the existing test missing_reactor_module_is_refused_without_writes gets vendor_jvm_shape_unsupported, not the legacy code). The legacy refusal fires only on false positives:

  • <modules> inside plugin <configuration>, as in every maven-ear-plugin project (<packaging>ear</packaging>);
  • <modules> inside a CDATA section, for example in a <description>.

Proof by execution (a unit probe in maven_repo::tests using the existing fixture / run_vendor helpers, run twice on 045d7ec, not committed). The pom is a single-module EAR project:

<project xmlns="http://maven.apache.org/POM/4.0.0">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId><artifactId>app</artifactId><version>1.0.0</version>
  <packaging>ear</packaging>
  <build><plugins><plugin><artifactId>maven-ear-plugin</artifactId><configuration>
    <modules><jarModule><groupId>x</groupId><artifactId>y</artifactId></jarModule></modules>
  </configuration></plugin></plugins></build>
</project>
PROBE jvm::detect = Other
PROBE reactor::declares_modules = false
PROBE legacy declares_modules = true
PROBE vendor refused vendor_maven_multimodule_unsupported: the root pom.xml declares <modules> (a multi-module aggregator); ...

Symptoms

None filed. Impact: vendor (and scan --mode vendored) refuses every EAR-packaged project with a misleading "multi-module aggregator" error, although neither JVM path treats it as one. The fix is small and removes code.

Proposed change

  • Delete the legacy declares_modules, strip_xml_comments and real_open_tag from vendor/maven_repo.rs (about 45 production lines), along with the vendor_maven_multimodule_unsupported refusal branch in maven_prelude, which is unreachable once the false positives are gone. If a guard is still wanted, call super::jvm::maven_reactor::declares_modules (one predicate).
  • Update CLI_CONTRACT.md (the maven row of the vendored table) to say that aggregators route to the JVM reactor backend, instead of naming the dead code.

Size and scope

vendor/maven_repo.rs and its tests, CLI_CONTRACT.md. About −45 production lines and +30 test lines. Out of scope: the other pom scanners (#715).

Acceptance criteria

  • A regression test: the EAR pom above vendors successfully (or at least is not refused with vendor_maven_multimodule_unsupported), and a pom with <modules> inside a CDATA <description> behaves the same
  • missing_reactor_module_is_refused_without_writes, commented_modules_do_not_refuse and plugin_configuration_modules_do_not_make_a_reactor stay green
  • The legacy declares_modules tests (declares_modules_boundary_and_comment_discipline, declares_modules_fail_closed_edges) are deleted or moved onto the reactor predicate
  • grep -rn 'fn declares_modules' crates finds one definition

Dependencies

None. First child of #715. #690 also edits maven_repo.rs (not this function).

Activity

  1. mikolalysenko commented on Oct 3, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged as priority:p3 (Maven). Child of #715; not a duplicate, and no open PR covers it. Confirmed on main 045d7ec: maven_prelude still calls the legacy maven_repo::declares_modules after jvm_shape returned Shape::Other.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 5, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Re-checked on 9c43dfc. #646 rewrote parts of vendor/maven_repo.rs, but both declares_modules are still there and still disagree:

    • jvm::detect routes on maven_reactor::declares_modules,`` which uses the Doc tree and counts only model-root ``/``. The call site is `jvm/mod.rs#L313`.
    • The legacy single-pom path refuses with vendor_maven_multimodule_unsupported (maven_repo.rs#L205-L212) on its own comment-stripping declares_modules,`` which also matches plugin <configuration><modules>.

    The issue still stands as filed.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triagedarch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)bugSomething isn't workingpm:mavenMavenpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions