This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/master by this push:
     new 71bd10f28c Fail on legacy config in modular projects (#11702)
71bd10f28c is described below

commit 71bd10f28c50a915a68cf04a18fe083655a6a833
Author: Gerd Aschemann <[email protected]>
AuthorDate: Fri Feb 20 14:03:13 2026 +0100

    Fail on legacy config in modular projects (#11702)
    
    In modular projects, legacy directories and resources that would be
    silently ignored now trigger an ERROR instead of WARNING:
    
    - Explicit <sourceDirectory>/<testSourceDirectory> differing from defaults
    - Default src/main/java or src/test/java existing on filesystem
    - Explicit <resources>/<testResources> differing from Super POM defaults
    
    This prevents silent loss of user-configured sources/resources.
    
    Co-Authored-By: Claude Opus 4.5 <[email protected]>
---
 .../maven/project/DefaultProjectBuilder.java       | 188 ++++++----
 .../maven/project/SourceHandlingContext.java       |  50 ++-
 .../apache/maven/project/ProjectBuilderTest.java   | 381 +++++++++++++++++----
 .../classic-sources-with-explicit-legacy/pom.xml   |  30 ++
 .../projects/project-builder/mixed-sources/pom.xml |  41 ---
 .../modular-java-with-explicit-source-dir/pom.xml  |  33 ++
 .../pom.xml                                        |  34 ++
 .../pom.xml                                        |  42 +++
 .../src/main/java/.gitkeep                         |   0
 .../src/test/java/.gitkeep                         |   0
 .../modular-with-physical-legacy/pom.xml           |  41 +++
 .../src/main/java/.gitkeep                         |   0
 .../src/test/java/.gitkeep                         |   0
 .../pom.xml                                        |  42 +++
 .../non-modular-resources-only/pom.xml             |  38 ++
 15 files changed, 746 insertions(+), 174 deletions(-)

diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
 
b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
index 9de2ca1348..7b13a9c12a 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java
@@ -692,49 +692,105 @@ private void initProject(MavenProject project, 
ModelBuilderResult result) {
                 }
 
                 /*
-                 * `sourceDirectory`, `testSourceDirectory` and 
`scriptSourceDirectory`
-                 * are ignored if the POM file contains at least one enabled 
<source> element
-                 * for the corresponding scope and language. This rule exists 
because
-                 * Maven provides default values for those elements which may 
conflict
-                 * with user's configuration.
-                 *
-                 * Additionally, for modular projects, legacy directories are 
unconditionally
-                 * ignored because it is not clear how to dispatch their 
content between
-                 * different modules. A warning is emitted if these properties 
are explicitly set.
-                 */
-                if (!sourceContext.hasSources(Language.SCRIPT, 
ProjectScope.MAIN)) {
+                  Source directory handling depends on project type and 
<sources> configuration:
+
+                  1. CLASSIC projects (no <sources>):
+                     - All legacy directories are used
+
+                  2. MODULAR projects (have <module> in <sources>):
+                     - ALL legacy directories cause the build to fail (cannot 
dispatch
+                       between modules)
+                     - The build also fails if default directories 
(src/main/java)
+                       physically exist on the filesystem
+
+                  3. NON-MODULAR projects with <sources>:
+                     - Explicit legacy directories (differ from default) 
always cause
+                       the build to fail
+                     - Legacy directories for scopes where <sources> defines 
Java are ignored
+                     - Legacy directories for scopes where <sources> has no 
Java serve as
+                       implicit fallback (only if they match the default, 
e.g., inherited)
+                     - This allows incremental adoption (e.g., custom 
resources + default Java)
+                */
+                if (sources.isEmpty()) {
+                    // Classic fallback: no <sources> configured, use legacy 
directories
                     
project.addScriptSourceRoot(build.getScriptSourceDirectory());
-                }
-                if (isModularProject) {
-                    // Modular projects: unconditionally ignore legacy 
directories, warn if explicitly set
-                    warnIfExplicitLegacyDirectory(
-                            build.getSourceDirectory(),
-                            baseDir.resolve("src/main/java"),
-                            "<sourceDirectory>",
-                            project.getId(),
-                            result);
-                    warnIfExplicitLegacyDirectory(
-                            build.getTestSourceDirectory(),
-                            baseDir.resolve("src/test/java"),
-                            "<testSourceDirectory>",
-                            project.getId(),
-                            result);
+                    project.addCompileSourceRoot(build.getSourceDirectory());
+                    
project.addTestCompileSourceRoot(build.getTestSourceDirectory());
+                    // Handle resources using legacy configuration
+                    
sourceContext.handleResourceConfiguration(ProjectScope.MAIN);
+                    
sourceContext.handleResourceConfiguration(ProjectScope.TEST);
                 } else {
-                    // Classic projects: use legacy directories if no sources 
defined in <sources>
-                    if (!sourceContext.hasSources(Language.JAVA_FAMILY, 
ProjectScope.MAIN)) {
-                        
project.addCompileSourceRoot(build.getSourceDirectory());
+                    // Add script source root if no <sources lang="script"> 
configured
+                    if (!sourceContext.hasSources(Language.SCRIPT, 
ProjectScope.MAIN)) {
+                        
project.addScriptSourceRoot(build.getScriptSourceDirectory());
                     }
-                    if (!sourceContext.hasSources(Language.JAVA_FAMILY, 
ProjectScope.TEST)) {
-                        
project.addTestCompileSourceRoot(build.getTestSourceDirectory());
+
+                    if (isModularProject) {
+                        // Modular: reject ALL legacy directory configurations
+                        failIfLegacyDirectoryPresent(
+                                build.getSourceDirectory(),
+                                baseDir.resolve("src/main/java"),
+                                "<sourceDirectory>",
+                                project.getId(),
+                                result,
+                                true); // check physical presence
+                        failIfLegacyDirectoryPresent(
+                                build.getTestSourceDirectory(),
+                                baseDir.resolve("src/test/java"),
+                                "<testSourceDirectory>",
+                                project.getId(),
+                                result,
+                                true); // check physical presence
+                    } else {
+                        // Non-modular: always validate legacy directories 
(error if differs from default)
+                        Path mainDefault = baseDir.resolve("src/main/java");
+                        Path testDefault = baseDir.resolve("src/test/java");
+
+                        failIfLegacyDirectoryPresent(
+                                build.getSourceDirectory(),
+                                mainDefault,
+                                "<sourceDirectory>",
+                                project.getId(),
+                                result,
+                                false); // no physical presence check
+                        failIfLegacyDirectoryPresent(
+                                build.getTestSourceDirectory(),
+                                testDefault,
+                                "<testSourceDirectory>",
+                                project.getId(),
+                                result,
+                                false); // no physical presence check
+
+                        // Use legacy as fallback only if:
+                        // 1. <sources> doesn't have Java for this scope
+                        // 2. Legacy matches default (otherwise error was 
reported above)
+                        if (!sourceContext.hasSources(Language.JAVA_FAMILY, 
ProjectScope.MAIN)) {
+                            Path configuredMain = 
Path.of(build.getSourceDirectory())
+                                    .toAbsolutePath()
+                                    .normalize();
+                            if (configuredMain.equals(
+                                    mainDefault.toAbsolutePath().normalize())) 
{
+                                
project.addCompileSourceRoot(build.getSourceDirectory());
+                            }
+                        }
+                        if (!sourceContext.hasSources(Language.JAVA_FAMILY, 
ProjectScope.TEST)) {
+                            Path configuredTest = 
Path.of(build.getTestSourceDirectory())
+                                    .toAbsolutePath()
+                                    .normalize();
+                            if (configuredTest.equals(
+                                    testDefault.toAbsolutePath().normalize())) 
{
+                                
project.addTestCompileSourceRoot(build.getTestSourceDirectory());
+                            }
+                        }
                     }
-                }
 
-                // Validate that modular and classic sources are not mixed 
within <sources>
-                sourceContext.validateNoMixedModularAndClassicSources();
+                    // Fail if modular and classic sources are mixed within 
<sources>
+                    sourceContext.failIfMixedModularAndClassicSources();
 
-                // Handle main and test resources using unified source handling
-                sourceContext.handleResourceConfiguration(ProjectScope.MAIN);
-                sourceContext.handleResourceConfiguration(ProjectScope.TEST);
+                    // Handle main and test resources using unified source 
handling
+                    
sourceContext.handleResourceConfiguration(ProjectScope.MAIN);
+                    
sourceContext.handleResourceConfiguration(ProjectScope.TEST);
+                }
             }
 
             project.setActiveProfiles(
@@ -906,44 +962,60 @@ private void initProject(MavenProject project, 
ModelBuilderResult result) {
         }
 
         /**
-         * Warns about legacy directory usage in a modular project. Two cases 
are handled:
+         * Validates that legacy directory configuration does not conflict 
with {@code <sources>}.
+         * <p>
+         * When {@code <sources>} is configured, the build fails if:
          * <ul>
-         *   <li>Case 1: The default legacy directory exists on the filesystem 
(e.g., src/main/java exists)</li>
-         *   <li>Case 2: An explicit legacy directory is configured that 
differs from the default</li>
+         *   <li><strong>Configuration presence</strong>: an explicit legacy 
configuration differs from the default</li>
+         *   <li><strong>Physical presence</strong>: the default directory 
exists on the filesystem (only checked
+         *       when {@code checkPhysicalPresence} is true, typically for 
modular projects where
+         *       {@code <source>} elements use different paths like {@code 
src/<module>/main/java})</li>
          * </ul>
-         * Legacy directories are unconditionally ignored in modular projects 
because it is not clear
-         * how to dispatch their content between different modules.
+         * <p>
+         * The presence of {@code <sources>} is the trigger for this 
validation, not whether the
+         * project is modular or non-modular.
+         * <p>
+         * This ensures consistency with resource handling.
+         *
+         * @param configuredDir the configured legacy directory value
+         * @param defaultDir the default legacy directory path
+         * @param elementName the XML element name for error messages
+         * @param projectId the project ID for error messages
+         * @param result the model builder result for reporting problems
+         * @param checkPhysicalPresence whether to check for physical presence 
of the default directory
+         * @see SourceHandlingContext#handleResourceConfiguration(ProjectScope)
          */
-        private void warnIfExplicitLegacyDirectory(
+        private void failIfLegacyDirectoryPresent(
                 String configuredDir,
                 Path defaultDir,
                 String elementName,
                 String projectId,
-                ModelBuilderResult result) {
+                ModelBuilderResult result,
+                boolean checkPhysicalPresence) {
             if (configuredDir != null) {
                 Path configuredPath = 
Path.of(configuredDir).toAbsolutePath().normalize();
                 Path defaultPath = defaultDir.toAbsolutePath().normalize();
                 if (!configuredPath.equals(defaultPath)) {
-                    // Case 2: Explicit configuration differs from default - 
always warn
+                    // Configuration presence: explicit config differs from 
default
                     String message = String.format(
-                            "Legacy %s is ignored in modular project %s. "
-                                    + "In modular projects, source directories 
must be defined via <sources> "
-                                    + "with a module element for each module.",
-                            elementName, projectId);
-                    logger.warn(message);
+                            "Legacy %s cannot be used in project %s because 
sources are configured via <sources>. "
+                                    + "Remove the %s configuration.",
+                            elementName, projectId, elementName);
+                    logger.error(message);
                     result.getProblemCollector()
                             .reportProblem(new 
org.apache.maven.impl.model.DefaultModelProblem(
-                                    message, Severity.WARNING, Version.V41, 
null, -1, -1, null));
-                } else if (Files.isDirectory(defaultPath)) {
-                    // Case 1: Default configuration, but the default 
directory exists on filesystem
+                                    message, Severity.ERROR, Version.V41, 
null, -1, -1, null));
+                } else if (checkPhysicalPresence && 
Files.isDirectory(defaultPath)) {
+                    // Physical presence: default directory exists but would 
be ignored
                     String message = String.format(
-                            "Legacy %s '%s' exists but is ignored in modular 
project %s. "
-                                    + "In modular projects, source directories 
must be defined via <sources>.",
-                            elementName, defaultPath, projectId);
-                    logger.warn(message);
+                            "Legacy directory '%s' exists but cannot be used 
in project %s "
+                                    + "because sources are configured via 
<sources>. "
+                                    + "Remove or rename the directory.",
+                            defaultPath, projectId);
+                    logger.error(message);
                     result.getProblemCollector()
                             .reportProblem(new 
org.apache.maven.impl.model.DefaultModelProblem(
-                                    message, Severity.WARNING, Version.V41, 
null, -1, -1, null));
+                                    message, Severity.ERROR, Version.V41, 
null, -1, -1, null));
                 }
             }
         }
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/project/SourceHandlingContext.java
 
b/impl/maven-core/src/main/java/org/apache/maven/project/SourceHandlingContext.java
index e7691eb86b..400f9f5dc0 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/project/SourceHandlingContext.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/project/SourceHandlingContext.java
@@ -152,7 +152,7 @@ boolean hasSources(Language language, ProjectScope scope) {
     }
 
     /**
-     * Validates that a project does not mix modular and classic (non-modular) 
sources.
+     * Fails the build if modular and classic (non-modular) sources are mixed 
within {@code <sources>}.
      * <p>
      * A project must be either fully modular (all sources have a module) or 
fully classic
      * (no sources have a module). Mixing modular and non-modular sources 
within the same
@@ -161,7 +161,7 @@ boolean hasSources(Language language, ProjectScope scope) {
      * This validation checks each (language, scope) combination and reports 
an ERROR if
      * both modular and non-modular sources are found.
      */
-    void validateNoMixedModularAndClassicSources() {
+    void failIfMixedModularAndClassicSources() {
         for (ProjectScope scope : List.of(ProjectScope.MAIN, 
ProjectScope.TEST)) {
             for (Language language : List.of(Language.JAVA_FAMILY, 
Language.RESOURCES)) {
                 boolean hasModular = declaredSources.stream()
@@ -200,6 +200,8 @@ void validateNoMixedModularAndClassicSources() {
      *   <li>Modular project: use resources from {@code <sources>} if present, 
otherwise inject defaults</li>
      *   <li>Classic project: use resources from {@code <sources>} if present, 
otherwise use legacy resources</li>
      * </ol>
+     * <p>
+     * The error behavior for conflicting legacy configuration is consistent 
with source directory handling.
      *
      * @param scope the project scope (MAIN or TEST)
      */
@@ -221,11 +223,19 @@ void handleResourceConfiguration(ProjectScope scope) {
             if (hasResourcesInSources) {
                 // Modular project with resources configured via <sources> - 
already added above
                 if (hasExplicitLegacyResources(resources, scopeId)) {
-                    LOGGER.warn(
-                            "Legacy {} element is ignored because {} resources 
are configured via {} in <sources>.",
-                            legacyElement,
-                            scopeId,
-                            sourcesConfig);
+                    String message = String.format(
+                            "Legacy %s element cannot be used because %s 
resources are configured via %s in <sources>.",
+                            legacyElement, scopeId, sourcesConfig);
+                    LOGGER.error(message);
+                    result.getProblemCollector()
+                            .reportProblem(new DefaultModelProblem(
+                                    message,
+                                    Severity.ERROR,
+                                    Version.V41,
+                                    project.getModel().getDelegate(),
+                                    -1,
+                                    -1,
+                                    null));
                 } else {
                     LOGGER.debug(
                             "{} resources configured via <sources> element, 
ignoring legacy {} element.",
@@ -236,13 +246,13 @@ void handleResourceConfiguration(ProjectScope scope) {
                 // Modular project without resources in <sources> - inject 
module-aware defaults
                 if (hasExplicitLegacyResources(resources, scopeId)) {
                     String message = "Legacy " + legacyElement
-                            + " element is ignored because modular sources are 
configured. "
+                            + " element cannot be used because modular sources 
are configured. "
                             + "Use " + sourcesConfig + " in <sources> for 
custom resource paths.";
-                    LOGGER.warn(message);
+                    LOGGER.error(message);
                     result.getProblemCollector()
                             .reportProblem(new DefaultModelProblem(
                                     message,
-                                    Severity.WARNING,
+                                    Severity.ERROR,
                                     Version.V41,
                                     project.getModel().getDelegate(),
                                     -1,
@@ -265,11 +275,19 @@ void handleResourceConfiguration(ProjectScope scope) {
             if (hasResourcesInSources) {
                 // Resources configured via <sources> - already added above
                 if (hasExplicitLegacyResources(resources, scopeId)) {
-                    LOGGER.warn(
-                            "Legacy {} element is ignored because {} resources 
are configured via {} in <sources>.",
-                            legacyElement,
-                            scopeId,
-                            sourcesConfig);
+                    String message = String.format(
+                            "Legacy %s element cannot be used because %s 
resources are configured via %s in <sources>.",
+                            legacyElement, scopeId, sourcesConfig);
+                    LOGGER.error(message);
+                    result.getProblemCollector()
+                            .reportProblem(new DefaultModelProblem(
+                                    message,
+                                    Severity.ERROR,
+                                    Version.V41,
+                                    project.getModel().getDelegate(),
+                                    -1,
+                                    -1,
+                                    null));
                 } else {
                     LOGGER.debug(
                             "{} resources configured via <sources> element, 
ignoring legacy {} element.",
@@ -319,7 +337,7 @@ private DefaultSourceRoot createModularResourceRoot(String 
module, ProjectScope
      *
      * @param resources list of resources to check
      * @param scope scope (main or test)
-     * @return true if explicit legacy resources are present that would be 
ignored
+     * @return true if explicit legacy resources are present that conflict 
with modular sources
      */
     private boolean hasExplicitLegacyResources(List<Resource> resources, 
String scope) {
         if (resources.isEmpty()) {
diff --git 
a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java
 
b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java
index 5da89a0f58..c079278ba1 100644
--- 
a/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java
+++ 
b/impl/maven-core/src/test/java/org/apache/maven/project/ProjectBuilderTest.java
@@ -46,6 +46,7 @@
 import org.junit.jupiter.api.io.TempDir;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -422,19 +423,21 @@ void testModularSourcesInjectResourceRoots() throws 
Exception {
     }
 
     /**
-     * Tests that when modular sources are configured alongside explicit 
legacy resources,
-     * the legacy resources are ignored and a warning is issued.
+     * Tests that when modular sources are configured alongside explicit 
legacy resources, an error is raised.
      * <p>
      * This verifies the behavior described in the design:
-     * - Modular projects with explicit legacy {@code <resources>} 
configuration should issue a warning
+     * - Modular projects with explicit legacy {@code <resources>} 
configuration should raise an error
      * - The modular resource roots are injected instead of using the legacy 
configuration
      * <p>
-     * Acceptance Criterion: AC2 (unified source tracking for all lang/scope 
combinations)
+     * Acceptance Criteria:
+     * - AC2 (unified source tracking for all lang/scope combinations)
+     * - AC8 (legacy directories error - supersedes AC7 which originally used 
WARNING)
      *
      * @see <a href="https://github.com/apache/maven/issues/11612";>Issue 
#11612</a>
+     * @see <a 
href="https://github.com/apache/maven/issues/11701#issuecomment-3858462609";>AC8 
definition</a>
      */
     @Test
-    void testModularSourcesWithExplicitResourcesIssuesWarning() throws 
Exception {
+    void testModularSourcesWithExplicitResourcesIssuesError() throws Exception 
{
         File pom = getProject("modular-sources-with-explicit-resources");
 
         MavenSession mavenSession = createMavenSession(null);
@@ -447,19 +450,19 @@ void 
testModularSourcesWithExplicitResourcesIssuesWarning() throws Exception {
 
         MavenProject project = result.getProject();
 
-        // Verify warnings are issued for ignored legacy resources
-        List<ModelProblem> warnings = result.getProblems().stream()
-                .filter(p -> p.getSeverity() == ModelProblem.Severity.WARNING)
-                .filter(p -> p.getMessage().contains("Legacy") && 
p.getMessage().contains("ignored"))
+        // Verify errors are raised for conflicting legacy resources (AC8)
+        List<ModelProblem> errors = result.getProblems().stream()
+                .filter(p -> p.getSeverity() == ModelProblem.Severity.ERROR)
+                .filter(p -> p.getMessage().contains("Legacy") && 
p.getMessage().contains("cannot be used"))
                 .toList();
 
-        assertEquals(2, warnings.size(), "Should have 2 warnings (one for 
resources, one for testResources)");
+        assertEquals(2, errors.size(), "Should have 2 errors (one for 
resources, one for testResources)");
         assertTrue(
-                warnings.stream().anyMatch(w -> 
w.getMessage().contains("<resources>")),
-                "Should warn about ignored <resources>");
+                errors.stream().anyMatch(e -> 
e.getMessage().contains("<resources>")),
+                "Should error about conflicting <resources>");
         assertTrue(
-                warnings.stream().anyMatch(w -> 
w.getMessage().contains("<testResources>")),
-                "Should warn about ignored <testResources>");
+                errors.stream().anyMatch(e -> 
e.getMessage().contains("<testResources>")),
+                "Should error about conflicting <testResources>");
 
         // Verify modular resources are still injected correctly
         List<SourceRoot> mainResourceRoots = 
project.getEnabledSourceRoots(ProjectScope.MAIN, Language.RESOURCES)
@@ -478,27 +481,64 @@ void 
testModularSourcesWithExplicitResourcesIssuesWarning() throws Exception {
     }
 
     /**
-     * Tests that legacy sourceDirectory and testSourceDirectory are ignored 
in modular projects.
-     * <p>
-     * In modular projects, legacy directories are unconditionally ignored 
because it is not clear
-     * how to dispatch their content between different modules. A warning is 
emitted if these
-     * properties are explicitly set (differ from Super POM defaults).
+     * Tests AC8: ALL legacy directories are rejected when {@code <sources>} 
is configured.
      * <p>
-     * This verifies:
-     * - WARNINGs are emitted for explicitly set legacy directories in modular 
projects
-     * - sourceDirectory and testSourceDirectory are both ignored
-     * - Only modular sources from {@code <sources>} are used
+     * Modular project with Java in {@code <sources>} for MAIN scope and 
explicit legacy
+     * {@code <sourceDirectory>} that differs from default. The legacy 
directory is rejected
+     * because modular projects cannot use legacy directories (content cannot 
be dispatched
+     * between modules).
+     *
+     * @see <a 
href="https://github.com/apache/maven/issues/11701#issuecomment-3897961755";>Issue
 #11701 (AC8/AC9)</a>
+     */
+    @Test
+    void testModularWithJavaSourcesRejectsLegacySourceDirectory() throws 
Exception {
+        File pom = getProject("modular-java-with-explicit-source-dir");
+
+        MavenSession mavenSession = createMavenSession(null);
+        ProjectBuildingRequest configuration = new 
DefaultProjectBuildingRequest();
+        
configuration.setRepositorySession(mavenSession.getRepositorySession());
+
+        ProjectBuildingResult result = getContainer()
+                .lookup(org.apache.maven.project.ProjectBuilder.class)
+                .build(pom, configuration);
+
+        MavenProject project = result.getProject();
+
+        // Verify ERROR for <sourceDirectory> (MAIN scope has Java in 
<sources>)
+        List<ModelProblem> errors = result.getProblems().stream()
+                .filter(p -> p.getSeverity() == ModelProblem.Severity.ERROR)
+                .filter(p -> p.getMessage().contains("Legacy") && 
p.getMessage().contains("cannot be used"))
+                .filter(p -> p.getMessage().contains("<sourceDirectory>"))
+                .toList();
+
+        assertEquals(1, errors.size(), "Should have 1 error for 
<sourceDirectory>");
+
+        // Verify modular source is used, not legacy
+        List<SourceRoot> mainJavaRoots = 
project.getEnabledSourceRoots(ProjectScope.MAIN, Language.JAVA_FAMILY)
+                .toList();
+        assertEquals(1, mainJavaRoots.size(), "Should have 1 modular main Java 
source root");
+        assertEquals("org.foo.app", 
mainJavaRoots.get(0).module().orElse(null), "Should have module org.foo.app");
+
+        // Legacy sourceDirectory is NOT used
+        assertFalse(
+                
mainJavaRoots.get(0).directory().toString().contains("src/custom/main/java"),
+                "Legacy sourceDirectory should not be used");
+    }
+
+    /**
+     * Tests AC8: Modular project rejects legacy {@code <testSourceDirectory>} 
even when
+     * {@code <sources>} has NO Java for TEST scope.
      * <p>
-     * Acceptance Criteria:
-     * - AC1 (boolean flags eliminated - uses hasSources() for main/test 
detection)
-     * - AC7 (legacy directories warning - {@code <sourceDirectory>} and 
{@code <testSourceDirectory>}
-     *   are unconditionally ignored with a WARNING in modular projects)
+     * Modular project with NO Java in {@code <sources>} for TEST scope and 
explicit legacy
+     * {@code <testSourceDirectory>} that differs from default. The legacy 
directory is rejected
+     * because modular projects cannot use legacy directories (content cannot 
be dispatched
+     * between modules).
      *
-     * @see <a href="https://github.com/apache/maven/issues/11612";>Issue 
#11612</a>
+     * @see <a 
href="https://github.com/apache/maven/issues/11701#issuecomment-3897961755";>Issue
 #11701 (AC8/AC9)</a>
      */
     @Test
-    void testMixedSourcesModularMainClassicTest() throws Exception {
-        File pom = getProject("mixed-sources");
+    void testModularWithoutTestSourcesRejectsLegacyTestSourceDirectory() 
throws Exception {
+        File pom = 
getProject("modular-no-test-java-with-explicit-test-source-dir");
 
         MavenSession mavenSession = createMavenSession(null);
         ProjectBuildingRequest configuration = new 
DefaultProjectBuildingRequest();
@@ -510,48 +550,271 @@ void testMixedSourcesModularMainClassicTest() throws 
Exception {
 
         MavenProject project = result.getProject();
 
-        // Verify WARNINGs are emitted for explicitly set legacy directories
-        List<ModelProblem> warnings = result.getProblems().stream()
-                .filter(p -> p.getSeverity() == ModelProblem.Severity.WARNING)
-                .filter(p -> p.getMessage().contains("Legacy") && 
p.getMessage().contains("ignored in modular project"))
+        // Verify ERROR for <testSourceDirectory> (modular projects reject all 
legacy directories)
+        List<ModelProblem> errors = result.getProblems().stream()
+                .filter(p -> p.getSeverity() == ModelProblem.Severity.ERROR)
+                .filter(p -> p.getMessage().contains("Legacy") && 
p.getMessage().contains("cannot be used"))
+                .filter(p -> p.getMessage().contains("<testSourceDirectory>"))
+                .toList();
+
+        assertEquals(1, errors.size(), "Should have 1 error for 
<testSourceDirectory>");
+
+        // No test Java sources (legacy rejected, none in <sources>)
+        List<SourceRoot> testJavaRoots = 
project.getEnabledSourceRoots(ProjectScope.TEST, Language.JAVA_FAMILY)
+                .toList();
+        assertEquals(0, testJavaRoots.size(), "Should have no test Java 
sources");
+    }
+
+    /**
+     * Tests AC9: explicit legacy directories raise an error in non-modular 
projects when
+     * {@code <sources>} has Java for that scope.
+     * <p>
+     * This test uses a non-modular project (no {@code <module>} attribute) 
with both:
+     * <ul>
+     *   <li>{@code <sources>} with main and test Java sources</li>
+     *   <li>Explicit {@code <sourceDirectory>} and {@code 
<testSourceDirectory>} (conflicting)</li>
+     * </ul>
+     * Both legacy directories should trigger ERROR because {@code <sources>} 
has Java.
+     *
+     * @see <a 
href="https://github.com/apache/maven/issues/11701#issuecomment-3897961755";>Issue
 #11701 (AC8/AC9)</a>
+     */
+    @Test
+    void testClassicSourcesWithExplicitLegacyDirectories() throws Exception {
+        File pom = getProject("classic-sources-with-explicit-legacy");
+
+        MavenSession mavenSession = createMavenSession(null);
+        ProjectBuildingRequest configuration = new 
DefaultProjectBuildingRequest();
+        
configuration.setRepositorySession(mavenSession.getRepositorySession());
+
+        ProjectBuildingResult result = getContainer()
+                .lookup(org.apache.maven.project.ProjectBuilder.class)
+                .build(pom, configuration);
+
+        // Verify errors are raised for conflicting legacy directories (AC9)
+        List<ModelProblem> errors = result.getProblems().stream()
+                .filter(p -> p.getSeverity() == ModelProblem.Severity.ERROR)
+                .filter(p -> p.getMessage().contains("Legacy") && 
p.getMessage().contains("cannot be used"))
                 .toList();
 
-        // Should have 2 warnings: one for sourceDirectory, one for 
testSourceDirectory
-        assertEquals(2, warnings.size(), "Should have 2 warnings for ignored 
legacy directories");
+        assertEquals(2, errors.size(), "Should have 2 errors (one for 
sourceDirectory, one for testSourceDirectory)");
+
+        // Verify error messages mention the conflicting elements
         assertTrue(
-                warnings.stream().anyMatch(w -> 
w.getMessage().contains("<sourceDirectory>")),
-                "Should warn about ignored <sourceDirectory>");
+                errors.stream().anyMatch(e -> 
e.getMessage().contains("<sourceDirectory>")),
+                "Should have error for <sourceDirectory>");
         assertTrue(
-                warnings.stream().anyMatch(w -> 
w.getMessage().contains("<testSourceDirectory>")),
-                "Should warn about ignored <testSourceDirectory>");
+                errors.stream().anyMatch(e -> 
e.getMessage().contains("<testSourceDirectory>")),
+                "Should have error for <testSourceDirectory>");
+    }
+
+    /**
+     * Tests AC9: Non-modular project with only resources in {@code <sources>} 
uses implicit Java fallback.
+     * <p>
+     * When {@code <sources>} contains only resources (no Java sources), the 
legacy
+     * {@code <sourceDirectory>} and {@code <testSourceDirectory>} are used as 
implicit fallback.
+     * This enables incremental adoption of {@code <sources>} - customize 
resources while
+     * keeping the default Java directory structure.
+     *
+     * @see <a 
href="https://github.com/apache/maven/issues/11701#issuecomment-3897961755";>Issue
 #11701 (AC8/AC9)</a>
+     */
+    @Test
+    void testNonModularResourcesOnlyWithImplicitJavaFallback() throws 
Exception {
+        File pom = getProject("non-modular-resources-only");
 
-        // Get main Java source roots - should have modular sources, not 
classic sourceDirectory
+        MavenSession mavenSession = createMavenSession(null);
+        ProjectBuildingRequest configuration = new 
DefaultProjectBuildingRequest();
+        
configuration.setRepositorySession(mavenSession.getRepositorySession());
+
+        ProjectBuildingResult result = getContainer()
+                .lookup(org.apache.maven.project.ProjectBuilder.class)
+                .build(pom, configuration);
+
+        MavenProject project = result.getProject();
+
+        // Verify NO errors - legacy directories are used as fallback (AC9)
+        List<ModelProblem> errors = result.getProblems().stream()
+                .filter(p -> p.getSeverity() == ModelProblem.Severity.ERROR)
+                .filter(p -> p.getMessage().contains("Legacy") && 
p.getMessage().contains("cannot be used"))
+                .toList();
+
+        assertEquals(0, errors.size(), "Should have no errors - legacy 
directories used as fallback (AC9)");
+
+        // Verify resources from <sources> are used
+        List<SourceRoot> mainResources = 
project.getEnabledSourceRoots(ProjectScope.MAIN, Language.RESOURCES)
+                .toList();
+        assertTrue(
+                mainResources.stream().anyMatch(sr -> sr.directory()
+                        .toString()
+                        .replace(File.separatorChar, '/')
+                        .contains("src/main/custom-resources")),
+                "Should have custom main resources from <sources>");
+
+        // Verify legacy Java directories are used as fallback
         List<SourceRoot> mainJavaRoots = 
project.getEnabledSourceRoots(ProjectScope.MAIN, Language.JAVA_FAMILY)
                 .toList();
+        assertEquals(1, mainJavaRoots.size(), "Should have 1 main Java source 
(implicit fallback)");
+        assertTrue(
+                mainJavaRoots
+                        .get(0)
+                        .directory()
+                        .toString()
+                        .replace(File.separatorChar, '/')
+                        .endsWith("src/main/java"),
+                "Should use default src/main/java as fallback");
 
-        // Should have 2 modular main Java sources (moduleA and moduleB)
-        assertEquals(2, mainJavaRoots.size(), "Should have 2 modular main Java 
source roots");
+        List<SourceRoot> testJavaRoots = 
project.getEnabledSourceRoots(ProjectScope.TEST, Language.JAVA_FAMILY)
+                .toList();
+        assertEquals(1, testJavaRoots.size(), "Should have 1 test Java source 
(implicit fallback)");
+        assertTrue(
+                testJavaRoots
+                        .get(0)
+                        .directory()
+                        .toString()
+                        .replace(File.separatorChar, '/')
+                        .endsWith("src/test/java"),
+                "Should use default src/test/java as fallback");
+    }
 
-        Set<String> mainModules = mainJavaRoots.stream()
-                .map(SourceRoot::module)
-                .flatMap(Optional::stream)
-                .collect(Collectors.toSet());
+    /**
+     * Tests AC9 violation: Non-modular project with only resources in {@code 
<sources>} and explicit legacy directories.
+     * <p>
+     * AC9 allows implicit fallback to legacy directories (when they match 
defaults).
+     * When legacy directories differ from the default, this is explicit 
configuration,
+     * which violates AC9's "implicit" requirement, so an ERROR is raised.
+     *
+     * @see <a 
href="https://github.com/apache/maven/issues/11701#issuecomment-3897961755";>Issue
 #11701 (AC8/AC9)</a>
+     */
+    @Test
+    void testNonModularResourcesOnlyWithExplicitLegacyDirectoriesRejected() 
throws Exception {
+        File pom = getProject("non-modular-resources-only-explicit-legacy");
 
-        assertEquals(2, mainModules.size(), "Should have main sources for 2 
modules");
-        assertTrue(mainModules.contains("org.foo.moduleA"), "Should have main 
source for moduleA");
-        assertTrue(mainModules.contains("org.foo.moduleB"), "Should have main 
source for moduleB");
+        MavenSession mavenSession = createMavenSession(null);
+        ProjectBuildingRequest configuration = new 
DefaultProjectBuildingRequest();
+        
configuration.setRepositorySession(mavenSession.getRepositorySession());
 
-        // Verify the classic sourceDirectory is NOT used (should be ignored)
-        boolean hasClassicMainSource = mainJavaRoots.stream().anyMatch(sr -> 
sr.directory()
-                .toString()
-                .replace(File.separatorChar, '/')
-                .contains("src/classic/main/java"));
-        assertTrue(!hasClassicMainSource, "Classic sourceDirectory should be 
ignored");
+        ProjectBuildingResult result = getContainer()
+                .lookup(org.apache.maven.project.ProjectBuilder.class)
+                .build(pom, configuration);
+
+        MavenProject project = result.getProject();
+
+        // Verify ERRORs for explicit legacy directories (differ from default)
+        List<ModelProblem> errors = result.getProblems().stream()
+                .filter(p -> p.getSeverity() == ModelProblem.Severity.ERROR)
+                .filter(p -> p.getMessage().contains("Legacy") && 
p.getMessage().contains("cannot be used"))
+                .toList();
+
+        assertEquals(2, errors.size(), "Should have 2 errors for explicit 
legacy directories");
+        assertTrue(
+                errors.stream().anyMatch(e -> 
e.getMessage().contains("<sourceDirectory>")),
+                "Should error about <sourceDirectory>");
+        assertTrue(
+                errors.stream().anyMatch(e -> 
e.getMessage().contains("<testSourceDirectory>")),
+                "Should error about <testSourceDirectory>");
+
+        // Verify resources from <sources> are still used
+        List<SourceRoot> mainResources = 
project.getEnabledSourceRoots(ProjectScope.MAIN, Language.RESOURCES)
+                .toList();
+        assertTrue(
+                mainResources.stream().anyMatch(sr -> sr.directory()
+                        .toString()
+                        .replace(File.separatorChar, '/')
+                        .contains("src/main/custom-resources")),
+                "Should have custom main resources from <sources>");
+
+        // Verify NO Java source roots (legacy was rejected, none in <sources>)
+        List<SourceRoot> mainJavaRoots = 
project.getEnabledSourceRoots(ProjectScope.MAIN, Language.JAVA_FAMILY)
+                .toList();
+        assertEquals(0, mainJavaRoots.size(), "Should have no main Java 
sources (legacy rejected)");
 
-        // Test sources should NOT be added (legacy testSourceDirectory is 
ignored in modular projects)
         List<SourceRoot> testJavaRoots = 
project.getEnabledSourceRoots(ProjectScope.TEST, Language.JAVA_FAMILY)
                 .toList();
-        assertEquals(0, testJavaRoots.size(), "Should have no test Java 
sources (legacy is ignored)");
+        assertEquals(0, testJavaRoots.size(), "Should have no test Java 
sources (legacy rejected)");
+    }
+
+    /**
+     * Tests AC8: Modular project with Java in {@code <sources>} and physical 
default legacy directories.
+     * <p>
+     * Even when legacy directories use Super POM defaults (no explicit 
override),
+     * if the physical directories exist on the filesystem, an ERROR is raised.
+     * This is because modular projects use paths like {@code 
src/<module>/main/java},
+     * so content in {@code src/main/java} would be silently ignored.
+     *
+     * @see <a 
href="https://github.com/apache/maven/issues/11701#issuecomment-3897961755";>Issue
 #11701 (AC8/AC9)</a>
+     */
+    @Test
+    void testModularWithPhysicalDefaultLegacyDirectory() throws Exception {
+        File pom = getProject("modular-with-physical-legacy");
+
+        MavenSession mavenSession = createMavenSession(null);
+        ProjectBuildingRequest configuration = new 
DefaultProjectBuildingRequest();
+        
configuration.setRepositorySession(mavenSession.getRepositorySession());
+
+        ProjectBuildingResult result = getContainer()
+                .lookup(org.apache.maven.project.ProjectBuilder.class)
+                .build(pom, configuration);
+
+        // Verify ERRORs are raised for physical presence of default 
directories (AC8)
+        List<ModelProblem> errors = result.getProblems().stream()
+                .filter(p -> p.getSeverity() == ModelProblem.Severity.ERROR)
+                .filter(p -> p.getMessage().contains("Legacy directory")
+                        && p.getMessage().contains("exists"))
+                .toList();
+
+        // Should have 2 errors: one for src/main/java, one for src/test/java
+        assertEquals(2, errors.size(), "Should have 2 errors for physical 
legacy directories");
+        // Use File.separator for platform-independent path matching 
(backslash on Windows)
+        String mainJava = "src" + File.separator + "main" + File.separator + 
"java";
+        String testJava = "src" + File.separator + "test" + File.separator + 
"java";
+        assertTrue(
+                errors.stream().anyMatch(e -> 
e.getMessage().contains(mainJava)),
+                "Should error about physical src/main/java");
+        assertTrue(
+                errors.stream().anyMatch(e -> 
e.getMessage().contains(testJava)),
+                "Should error about physical src/test/java");
+    }
+
+    /**
+     * Tests AC8: Modular project with only resources in {@code <sources>} and 
physical default legacy directories.
+     * <p>
+     * Even when {@code <sources>} only contains resources (no Java), if the 
physical
+     * default directories exist, an ERROR is raised for modular projects.
+     * Unlike non-modular projects (AC9), modular projects cannot use legacy 
directories as fallback.
+     *
+     * @see <a 
href="https://github.com/apache/maven/issues/11701#issuecomment-3897961755";>Issue
 #11701 (AC8/AC9)</a>
+     */
+    @Test
+    void testModularResourcesOnlyWithPhysicalDefaultLegacyDirectory() throws 
Exception {
+        File pom = getProject("modular-resources-only-with-physical-legacy");
+
+        MavenSession mavenSession = createMavenSession(null);
+        ProjectBuildingRequest configuration = new 
DefaultProjectBuildingRequest();
+        
configuration.setRepositorySession(mavenSession.getRepositorySession());
+
+        ProjectBuildingResult result = getContainer()
+                .lookup(org.apache.maven.project.ProjectBuilder.class)
+                .build(pom, configuration);
+
+        // Verify ERRORs are raised for physical presence of default 
directories (AC8)
+        // Unlike non-modular (AC9), modular projects cannot use legacy as 
fallback
+        List<ModelProblem> errors = result.getProblems().stream()
+                .filter(p -> p.getSeverity() == ModelProblem.Severity.ERROR)
+                .filter(p -> p.getMessage().contains("Legacy directory")
+                        && p.getMessage().contains("exists"))
+                .toList();
+
+        // Should have 2 errors: one for src/main/java, one for src/test/java
+        assertEquals(
+                2, errors.size(), "Should have 2 errors for physical legacy 
directories (no AC9 fallback for modular)");
+        // Use File.separator for platform-independent path matching 
(backslash on Windows)
+        String mainJava = "src" + File.separator + "main" + File.separator + 
"java";
+        String testJava = "src" + File.separator + "test" + File.separator + 
"java";
+        assertTrue(
+                errors.stream().anyMatch(e -> 
e.getMessage().contains(mainJava)),
+                "Should error about physical src/main/java");
+        assertTrue(
+                errors.stream().anyMatch(e -> 
e.getMessage().contains(testJava)),
+                "Should error about physical src/test/java");
     }
 
     /**
@@ -563,7 +826,7 @@ void testMixedSourcesModularMainClassicTest() throws 
Exception {
      * <p>
      * This verifies:
      * - An ERROR is reported when both modular and non-modular sources exist 
in {@code <sources>}
-     * - sourceDirectory is ignored because {@code <source scope="main" 
lang="java">} exists
+     * - sourceDirectory is not used because {@code <sources>} exists
      * <p>
      * Acceptance Criteria:
      * - AC1 (boolean flags eliminated - uses hasSources() for source 
detection)
diff --git 
a/impl/maven-core/src/test/projects/project-builder/classic-sources-with-explicit-legacy/pom.xml
 
b/impl/maven-core/src/test/projects/project-builder/classic-sources-with-explicit-legacy/pom.xml
new file mode 100644
index 0000000000..0c5726393a
--- /dev/null
+++ 
b/impl/maven-core/src/test/projects/project-builder/classic-sources-with-explicit-legacy/pom.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.1.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 
https://maven.apache.org/xsd/maven-4.1.0.xsd";>
+    <modelVersion>4.1.0</modelVersion>
+
+    <groupId>org.apache.maven.tests</groupId>
+    <artifactId>classic-sources-explicit-legacy-test</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <build>
+        <sources>
+            <!-- Classic (non-modular) source - no module attribute -->
+            <source>
+                <scope>main</scope>
+                <lang>java</lang>
+                <directory>src/main/java</directory>
+            </source>
+            <source>
+                <scope>test</scope>
+                <lang>java</lang>
+                <directory>src/test/java</directory>
+            </source>
+        </sources>
+        <!-- Explicit legacy directories that conflict with <sources> - should 
trigger ERROR (AC9) -->
+        <sourceDirectory>src/legacy/main/java</sourceDirectory>
+        <testSourceDirectory>src/legacy/test/java</testSourceDirectory>
+    </build>
+</project>
diff --git 
a/impl/maven-core/src/test/projects/project-builder/mixed-sources/pom.xml 
b/impl/maven-core/src/test/projects/project-builder/mixed-sources/pom.xml
deleted file mode 100644
index caa10d9885..0000000000
--- a/impl/maven-core/src/test/projects/project-builder/mixed-sources/pom.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-  Tests that legacy directories are ignored in modular projects.
-
-  Expected behavior:
-  - Modular sources for main Java are processed
-  - Both sourceDirectory and testSourceDirectory are unconditionally ignored
-  - WARNINGs are emitted because both are explicitly set (differ from defaults)
--->
-<project xmlns="http://maven.apache.org/POM/4.1.0";
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
-         xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 
https://maven.apache.org/xsd/maven-4.1.0.xsd";>
-    <modelVersion>4.1.0</modelVersion>
-
-    <groupId>org.apache.maven.tests</groupId>
-    <artifactId>mixed-sources-test</artifactId>
-    <version>1.0-SNAPSHOT</version>
-    <packaging>jar</packaging>
-
-    <build>
-        <!-- Classic sourceDirectory - ignored with WARNING because project is 
modular -->
-        <sourceDirectory>src/classic/main/java</sourceDirectory>
-        <!-- Classic testSourceDirectory - ignored with WARNING because 
project is modular -->
-        <testSourceDirectory>src/classic/test/java</testSourceDirectory>
-
-        <sources>
-            <!-- Modular main java sources - these should override 
sourceDirectory -->
-            <source>
-                <scope>main</scope>
-                <lang>java</lang>
-                <module>org.foo.moduleA</module>
-            </source>
-            <source>
-                <scope>main</scope>
-                <lang>java</lang>
-                <module>org.foo.moduleB</module>
-            </source>
-            <!-- No test sources defined in <sources> -->
-        </sources>
-    </build>
-</project>
diff --git 
a/impl/maven-core/src/test/projects/project-builder/modular-java-with-explicit-source-dir/pom.xml
 
b/impl/maven-core/src/test/projects/project-builder/modular-java-with-explicit-source-dir/pom.xml
new file mode 100644
index 0000000000..dfab4a6b3f
--- /dev/null
+++ 
b/impl/maven-core/src/test/projects/project-builder/modular-java-with-explicit-source-dir/pom.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Tests AC8 scenario 2: Modular project with Java in <sources> for MAIN scope
+  and explicit legacy <sourceDirectory> that differs from default.
+
+  Expected behavior:
+  - ERROR for <sourceDirectory> (modular project, <sources> has Java for MAIN)
+  - Modular Java sources are used, not legacy
+-->
+<project xmlns="http://maven.apache.org/POM/4.1.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 
https://maven.apache.org/xsd/maven-4.1.0.xsd";>
+    <modelVersion>4.1.0</modelVersion>
+
+    <groupId>org.apache.maven.tests</groupId>
+    <artifactId>modular-java-with-explicit-source-dir-test</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <build>
+        <!-- Explicit legacy sourceDirectory - triggers ERROR (AC8) -->
+        <sourceDirectory>src/custom/main/java</sourceDirectory>
+
+        <sources>
+            <!-- Modular main Java source -->
+            <source>
+                <scope>main</scope>
+                <lang>java</lang>
+                <module>org.foo.app</module>
+            </source>
+        </sources>
+    </build>
+</project>
diff --git 
a/impl/maven-core/src/test/projects/project-builder/modular-no-test-java-with-explicit-test-source-dir/pom.xml
 
b/impl/maven-core/src/test/projects/project-builder/modular-no-test-java-with-explicit-test-source-dir/pom.xml
new file mode 100644
index 0000000000..dd27dac586
--- /dev/null
+++ 
b/impl/maven-core/src/test/projects/project-builder/modular-no-test-java-with-explicit-test-source-dir/pom.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Tests AC8 scenario 5: Modular project with NO Java in <sources> for TEST 
scope
+  and explicit legacy <testSourceDirectory> that differs from default.
+
+  Expected behavior:
+  - ERROR for <testSourceDirectory> (modular project, no AC9 fallback)
+  - No test Java sources (legacy rejected, none in <sources>)
+-->
+<project xmlns="http://maven.apache.org/POM/4.1.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 
https://maven.apache.org/xsd/maven-4.1.0.xsd";>
+    <modelVersion>4.1.0</modelVersion>
+
+    <groupId>org.apache.maven.tests</groupId>
+    
<artifactId>modular-no-test-java-with-explicit-test-source-dir-test</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <build>
+        <!-- Explicit legacy testSourceDirectory - triggers ERROR (AC8, no AC9 
fallback for modular) -->
+        <testSourceDirectory>src/custom/test/java</testSourceDirectory>
+
+        <sources>
+            <!-- Modular main Java sources only - no test Java -->
+            <source>
+                <scope>main</scope>
+                <lang>java</lang>
+                <module>org.foo.app</module>
+            </source>
+            <!-- No test Java sources in <sources> -->
+        </sources>
+    </build>
+</project>
diff --git 
a/impl/maven-core/src/test/projects/project-builder/modular-resources-only-with-physical-legacy/pom.xml
 
b/impl/maven-core/src/test/projects/project-builder/modular-resources-only-with-physical-legacy/pom.xml
new file mode 100644
index 0000000000..92f8cb52a4
--- /dev/null
+++ 
b/impl/maven-core/src/test/projects/project-builder/modular-resources-only-with-physical-legacy/pom.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Tests AC8: Modular project with only resources in <sources> and physical 
default legacy directory.
+
+  Expected behavior:
+  - Modular resources from <sources> are processed
+  - Physical src/main/java and src/test/java directories exist but would be 
ignored
+  - ERROR is raised because physical default directories exist (AC8 physical 
presence check)
+  - Unlike non-modular projects (AC9), modular projects cannot use legacy 
directories as fallback
+
+  Note: The src/main/java and src/test/java directories are created empty to 
trigger
+  the physical presence check.
+-->
+<project xmlns="http://maven.apache.org/POM/4.1.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 
https://maven.apache.org/xsd/maven-4.1.0.xsd";>
+    <modelVersion>4.1.0</modelVersion>
+
+    <groupId>org.apache.maven.tests</groupId>
+    <artifactId>modular-resources-only-with-physical-legacy-test</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <build>
+        <!-- No explicit sourceDirectory/testSourceDirectory - uses Super POM 
defaults -->
+        <!-- But physical src/main/java and src/test/java directories exist -->
+
+        <sources>
+            <!-- Only modular resources - no Java sources -->
+            <source>
+                <scope>main</scope>
+                <lang>resources</lang>
+                <module>org.example.app</module>
+            </source>
+            <source>
+                <scope>test</scope>
+                <lang>resources</lang>
+                <module>org.example.app</module>
+            </source>
+        </sources>
+    </build>
+</project>
diff --git 
a/impl/maven-core/src/test/projects/project-builder/modular-resources-only-with-physical-legacy/src/main/java/.gitkeep
 
b/impl/maven-core/src/test/projects/project-builder/modular-resources-only-with-physical-legacy/src/main/java/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git 
a/impl/maven-core/src/test/projects/project-builder/modular-resources-only-with-physical-legacy/src/test/java/.gitkeep
 
b/impl/maven-core/src/test/projects/project-builder/modular-resources-only-with-physical-legacy/src/test/java/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git 
a/impl/maven-core/src/test/projects/project-builder/modular-with-physical-legacy/pom.xml
 
b/impl/maven-core/src/test/projects/project-builder/modular-with-physical-legacy/pom.xml
new file mode 100644
index 0000000000..27267c0bc2
--- /dev/null
+++ 
b/impl/maven-core/src/test/projects/project-builder/modular-with-physical-legacy/pom.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Tests AC8: Modular project with physical default legacy directory.
+
+  Expected behavior:
+  - Modular Java sources from <sources> are processed
+  - Physical src/main/java and src/test/java directories exist but would be 
ignored
+  - ERROR is raised because physical default directories exist (AC8 physical 
presence check)
+
+  Note: The src/main/java and src/test/java directories are created empty to 
trigger
+  the physical presence check.
+-->
+<project xmlns="http://maven.apache.org/POM/4.1.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 
https://maven.apache.org/xsd/maven-4.1.0.xsd";>
+    <modelVersion>4.1.0</modelVersion>
+
+    <groupId>org.apache.maven.tests</groupId>
+    <artifactId>modular-with-physical-legacy-test</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <build>
+        <!-- No explicit sourceDirectory/testSourceDirectory - uses Super POM 
defaults -->
+        <!-- But physical src/main/java and src/test/java directories exist -->
+
+        <sources>
+            <!-- Modular Java sources -->
+            <source>
+                <scope>main</scope>
+                <lang>java</lang>
+                <module>org.example.app</module>
+            </source>
+            <source>
+                <scope>test</scope>
+                <lang>java</lang>
+                <module>org.example.app</module>
+            </source>
+        </sources>
+    </build>
+</project>
diff --git 
a/impl/maven-core/src/test/projects/project-builder/modular-with-physical-legacy/src/main/java/.gitkeep
 
b/impl/maven-core/src/test/projects/project-builder/modular-with-physical-legacy/src/main/java/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git 
a/impl/maven-core/src/test/projects/project-builder/modular-with-physical-legacy/src/test/java/.gitkeep
 
b/impl/maven-core/src/test/projects/project-builder/modular-with-physical-legacy/src/test/java/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git 
a/impl/maven-core/src/test/projects/project-builder/non-modular-resources-only-explicit-legacy/pom.xml
 
b/impl/maven-core/src/test/projects/project-builder/non-modular-resources-only-explicit-legacy/pom.xml
new file mode 100644
index 0000000000..2bb12cd7a6
--- /dev/null
+++ 
b/impl/maven-core/src/test/projects/project-builder/non-modular-resources-only-explicit-legacy/pom.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Tests AC9 violation: Non-modular project with only resources in <sources> 
and explicit legacy directories.
+
+  AC9 allows implicit fallback to legacy directories (when they match 
defaults).
+  This test verifies that explicit configuration (differs from default) is 
rejected.
+
+  Expected behavior:
+  - Resources from <sources> are used
+  - ERROR for sourceDirectory and testSourceDirectory because they differ from 
defaults
+  - No Java source roots are added (legacy rejected, none in <sources>)
+-->
+<project xmlns="http://maven.apache.org/POM/4.1.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 
https://maven.apache.org/xsd/maven-4.1.0.xsd";>
+    <modelVersion>4.1.0</modelVersion>
+
+    <groupId>org.apache.maven.tests</groupId>
+    <artifactId>non-modular-resources-only-explicit-legacy-test</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <build>
+        <sources>
+            <!-- Only resources configured - no Java sources -->
+            <source>
+                <scope>main</scope>
+                <lang>resources</lang>
+                <directory>src/main/custom-resources</directory>
+            </source>
+            <source>
+                <scope>test</scope>
+                <lang>resources</lang>
+                <directory>src/test/custom-resources</directory>
+            </source>
+            <!-- No <source lang="java"> elements -->
+        </sources>
+        <!-- Explicit legacy directories - triggers ERROR (differ from 
default) -->
+        <sourceDirectory>src/custom/main/java</sourceDirectory>
+        <testSourceDirectory>src/custom/test/java</testSourceDirectory>
+    </build>
+</project>
diff --git 
a/impl/maven-core/src/test/projects/project-builder/non-modular-resources-only/pom.xml
 
b/impl/maven-core/src/test/projects/project-builder/non-modular-resources-only/pom.xml
new file mode 100644
index 0000000000..12eee4001d
--- /dev/null
+++ 
b/impl/maven-core/src/test/projects/project-builder/non-modular-resources-only/pom.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Tests AC9: Non-modular project with only resources in <sources>.
+
+  Expected behavior:
+  - Resources from <sources> are used
+  - Legacy sourceDirectory/testSourceDirectory are used as implicit fallback 
(AC9)
+  - No errors since <sources> doesn't have Java for these scopes
+-->
+<project xmlns="http://maven.apache.org/POM/4.1.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.1.0 
https://maven.apache.org/xsd/maven-4.1.0.xsd";>
+    <modelVersion>4.1.0</modelVersion>
+
+    <groupId>org.apache.maven.tests</groupId>
+    <artifactId>non-modular-resources-only-test</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+
+    <build>
+        <sources>
+            <!-- Only resources configured - no Java sources -->
+            <source>
+                <scope>main</scope>
+                <lang>resources</lang>
+                <directory>src/main/custom-resources</directory>
+            </source>
+            <source>
+                <scope>test</scope>
+                <lang>resources</lang>
+                <directory>src/test/custom-resources</directory>
+            </source>
+            <!-- No <source lang="java"> elements -->
+        </sources>
+        <!-- Legacy directories should be used as fallback (AC9) -->
+        <!-- Using Super POM defaults: src/main/java and src/test/java -->
+    </build>
+</project>

Reply via email to