You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/07/31 14:18:41 UTC

(maven) branch fix/12643-structured-problems updated: Fix #12643: Migrate PluginValidationManager to native BuilderProblem API

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

gnodet pushed a commit to branch fix/12643-structured-problems
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/fix/12643-structured-problems by this push:
     new e3cc24a756 Fix #12643: Migrate PluginValidationManager to native BuilderProblem API
e3cc24a756 is described below

commit e3cc24a756b59c4ed6685c2c52bc480630c5bfb3
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Fri Jul 31 16:18:31 2026 +0200

    Fix #12643: Migrate PluginValidationManager to native BuilderProblem API
    
    Change the PluginValidationManager interface from String-based to
    BuilderProblem-based, enabling structured problem reporting with keys,
    severity, and suggestions throughout the plugin validation pipeline.
    
    - Change 3 abstract report methods from String issue to BuilderProblem
    - Add deprecated String-based default methods for backward compat
    - Update all 9 call sites (7 validators + 1 plugin manager) to create
      BuilderProblem with structured key, severity, and suggestion
    - Inject DefaultDiagnosticCollector into DefaultPluginValidationManager
      to pipe problems into the diagnostic pipeline
    - Add japicmp exclusion for the intentional API break
    - Add DefaultPluginValidationManager to EXCLUDED_LOGGERS in
      BuildReportCollector to prevent double-counting
    
    Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
 impl/maven-core/pom.xml                            |  2 +
 .../maven/internal/build/BuildReportCollector.java |  3 +-
 .../maven/plugin/PluginValidationManager.java      | 74 +++++++++++++++++++++-
 .../AbstractMavenPluginParametersValidator.java    | 14 ++++
 .../plugin/internal/DefaultMavenPluginManager.java |  9 ++-
 .../internal/DefaultPluginValidationManager.java   | 34 +++++++---
 .../DeprecatedCoreExpressionValidator.java         | 15 ++++-
 .../plugin/internal/DeprecatedPluginValidator.java | 14 +++-
 .../internal/Maven2DependenciesValidator.java      |  8 ++-
 .../Maven3CompatDependenciesValidator.java         |  9 ++-
 .../internal/MavenMixedDependenciesValidator.java  |  8 ++-
 .../internal/MavenScopeDependenciesValidator.java  | 12 +++-
 ...lexusContainerDefaultDependenciesValidator.java |  8 ++-
 .../ReadOnlyPluginParametersValidator.java         |  7 +-
 14 files changed, 191 insertions(+), 26 deletions(-)

diff --git a/impl/maven-core/pom.xml b/impl/maven-core/pom.xml
index 07ffa6662b..efec4c8c09 100644
--- a/impl/maven-core/pom.xml
+++ b/impl/maven-core/pom.xml
@@ -396,6 +396,8 @@ under the License.
               <exclude>org.apache.maven.toolchain.ToolchainManagerPrivate</exclude>
               <exclude>org.apache.maven.toolchain.ToolchainPrivate</exclude>
               <exclude>org.apache.maven.toolchain.ToolchainsBuilder</exclude>
+              <!-- PluginValidationManager: String issue → BuilderProblem problem (4.1.0) -->
+              <exclude>org.apache.maven.plugin.PluginValidationManager</exclude>
             </excludes>
           </parameter>
         </configuration>
diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/build/BuildReportCollector.java b/impl/maven-core/src/main/java/org/apache/maven/internal/build/BuildReportCollector.java
index 9425cc15ee..6d85b8393e 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/internal/build/BuildReportCollector.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/internal/build/BuildReportCollector.java
@@ -102,7 +102,8 @@ public final class BuildReportCollector extends AbstractEventSpy {
     private static final Set<String> EXCLUDED_LOGGERS = Set.of(
             BuildReportCollector.class.getName(),
             "org.apache.maven.DefaultMaven",
-            "org.apache.maven.project.collector.DefaultProjectsSelector");
+            "org.apache.maven.project.collector.DefaultProjectsSelector",
+            "org.apache.maven.plugin.internal.DefaultPluginValidationManager");
 
     private final DefaultDiagnosticCollector diagnosticCollector;
 
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginValidationManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginValidationManager.java
index d65f8dad68..6c2cc943ad 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginValidationManager.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/PluginValidationManager.java
@@ -18,6 +18,7 @@
  */
 package org.apache.maven.plugin;
 
+import org.apache.maven.api.services.BuilderProblem;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
 import org.eclipse.aether.RepositorySystemSession;
@@ -54,27 +55,94 @@ enum IssueLocality {
      * This method should be used in "early" phase of plugin execution, possibly even when plugin or mojo descriptor
      * does not exist yet. In turn, this method will not record extra information like plugin occurrence or declaration
      * location as those are not yet available.
+     *
+     * @since 4.1.0
      */
     void reportPluginValidationIssue(
-            IssueLocality locality, RepositorySystemSession session, Artifact pluginArtifact, String issue);
+            IssueLocality locality, RepositorySystemSession session, Artifact pluginArtifact, BuilderProblem problem);
 
     /**
      * Reports plugin issues applicable to the plugin as a whole.
      * <p>
      * This method will record extra information as well, like plugin occurrence or declaration location.
+     *
+     * @since 4.1.0
      */
     void reportPluginValidationIssue(
-            IssueLocality locality, MavenSession mavenSession, MojoDescriptor mojoDescriptor, String issue);
+            IssueLocality locality, MavenSession mavenSession, MojoDescriptor mojoDescriptor, BuilderProblem problem);
 
     /**
      * Reports plugin Mojo issues applicable to the Mojo itself.
      * <p>
      * This method will record extra information as well, like plugin occurrence or declaration location.
+     *
+     * @since 4.1.0
      */
     void reportPluginMojoValidationIssue(
             IssueLocality locality,
             MavenSession mavenSession,
             MojoDescriptor mojoDescriptor,
             Class<?> mojoClass,
-            String issue);
+            BuilderProblem problem);
+
+    // ---- Deprecated String-based adapters for external callers ----
+
+    /**
+     * @deprecated Use {@link #reportPluginValidationIssue(IssueLocality, RepositorySystemSession, Artifact,
+     *     BuilderProblem)} instead.
+     */
+    @Deprecated(since = "4.1.0", forRemoval = true)
+    default void reportPluginValidationIssue(
+            IssueLocality locality, RepositorySystemSession session, Artifact pluginArtifact, String issue) {
+        reportPluginValidationIssue(
+                locality,
+                session,
+                pluginArtifact,
+                BuilderProblem.builder()
+                        .message(issue)
+                        .severity(BuilderProblem.Severity.WARNING)
+                        .key("plugin-validation:" + issue.hashCode())
+                        .build());
+    }
+
+    /**
+     * @deprecated Use {@link #reportPluginValidationIssue(IssueLocality, MavenSession, MojoDescriptor,
+     *     BuilderProblem)} instead.
+     */
+    @Deprecated(since = "4.1.0", forRemoval = true)
+    default void reportPluginValidationIssue(
+            IssueLocality locality, MavenSession mavenSession, MojoDescriptor mojoDescriptor, String issue) {
+        reportPluginValidationIssue(
+                locality,
+                mavenSession,
+                mojoDescriptor,
+                BuilderProblem.builder()
+                        .message(issue)
+                        .severity(BuilderProblem.Severity.WARNING)
+                        .key("plugin-validation:" + issue.hashCode())
+                        .build());
+    }
+
+    /**
+     * @deprecated Use {@link #reportPluginMojoValidationIssue(IssueLocality, MavenSession, MojoDescriptor, Class,
+     *     BuilderProblem)} instead.
+     */
+    @Deprecated(since = "4.1.0", forRemoval = true)
+    default void reportPluginMojoValidationIssue(
+            IssueLocality locality,
+            MavenSession mavenSession,
+            MojoDescriptor mojoDescriptor,
+            Class<?> mojoClass,
+            String issue) {
+        reportPluginMojoValidationIssue(
+                locality,
+                mavenSession,
+                mojoDescriptor,
+                mojoClass,
+                BuilderProblem.builder()
+                        .message(issue)
+                        .severity(BuilderProblem.Severity.WARNING)
+                        .key("plugin-validation:" + issue.hashCode())
+                        .build());
+    }
 }
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/AbstractMavenPluginParametersValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/AbstractMavenPluginParametersValidator.java
index 9e7426b168..47a772809e 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/AbstractMavenPluginParametersValidator.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/AbstractMavenPluginParametersValidator.java
@@ -18,6 +18,7 @@
  */
 package org.apache.maven.plugin.internal;
 
+import org.apache.maven.api.services.BuilderProblem;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.plugin.PluginValidationManager;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
@@ -97,6 +98,11 @@ protected boolean isIgnoredProperty(String strValue) {
 
     protected abstract String getParameterLogReason(Parameter parameter);
 
+    /**
+     * Returns the validation key prefix for this validator (e.g. "deprecated-param", "readonly-param").
+     */
+    protected abstract String getValidationKeyPrefix();
+
     protected String formatParameter(Parameter parameter) {
         StringBuilder stringBuilder = new StringBuilder()
                 .append("Parameter '")
@@ -112,4 +118,12 @@ protected String formatParameter(Parameter parameter) {
 
         return stringBuilder.toString();
     }
+
+    protected BuilderProblem buildParameterProblem(Parameter parameter) {
+        return BuilderProblem.builder()
+                .message(formatParameter(parameter))
+                .severity(BuilderProblem.Severity.WARNING)
+                .key("plugin-validation:" + getValidationKeyPrefix() + ":" + parameter.getName())
+                .build();
+    }
 }
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
index 15f3d8df7b..a95cf34fff 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultMavenPluginManager.java
@@ -51,6 +51,7 @@
 import org.apache.maven.api.Service;
 import org.apache.maven.api.Session;
 import org.apache.maven.api.plugin.descriptor.Resolution;
+import org.apache.maven.api.services.BuilderProblem;
 import org.apache.maven.api.services.DependencyResolver;
 import org.apache.maven.api.services.DependencyResolverResult;
 import org.apache.maven.api.services.PathScopeRegistry;
@@ -751,7 +752,13 @@ private <T> T loadV3Mojo(
                     session,
                     mojoDescriptor,
                     mojo.getClass(),
-                    "Mojo implements `Contextualizable` interface from Plexus Container, which is EOL.");
+                    BuilderProblem.builder()
+                            .message(
+                                    "Mojo implements `Contextualizable` interface from Plexus Container, which is EOL.")
+                            .severity(BuilderProblem.Severity.WARNING)
+                            .key("plugin-validation:contextualizable")
+                            .suggestion("Migrate from Contextualizable to javax.inject dependency injection")
+                            .build());
         }
 
         XmlNode dom = mojoExecution.getConfiguration() != null
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginValidationManager.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginValidationManager.java
index 0bd14d1635..e445b2978e 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginValidationManager.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginValidationManager.java
@@ -18,6 +18,7 @@
  */
 package org.apache.maven.plugin.internal;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
@@ -39,9 +40,11 @@
 import java.util.stream.Collectors;
 
 import org.apache.maven.api.Constants;
+import org.apache.maven.api.services.BuilderProblem;
 import org.apache.maven.eventspy.AbstractEventSpy;
 import org.apache.maven.execution.ExecutionEvent;
 import org.apache.maven.execution.MavenSession;
+import org.apache.maven.internal.build.DefaultDiagnosticCollector;
 import org.apache.maven.model.InputLocation;
 import org.apache.maven.plugin.PluginValidationManager;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
@@ -83,6 +86,13 @@ private enum ValidationReportLevel {
 
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
+    private final DefaultDiagnosticCollector diagnosticCollector;
+
+    @Inject
+    DefaultPluginValidationManager(DefaultDiagnosticCollector diagnosticCollector) {
+        this.diagnosticCollector = diagnosticCollector;
+    }
+
     @Override
     public void onEvent(Object event) {
         if (event instanceof ExecutionEvent executionEvent) {
@@ -158,28 +168,30 @@ private void mayReportInline(RepositorySystemSession session, IssueLocality loca
 
     @Override
     public void reportPluginValidationIssue(
-            IssueLocality locality, RepositorySystemSession session, Artifact pluginArtifact, String issue) {
+            IssueLocality locality, RepositorySystemSession session, Artifact pluginArtifact, BuilderProblem problem) {
         String pluginKey = pluginKey(pluginArtifact);
         if (validationPluginExcludes(session).contains(pluginKey)) {
             return;
         }
         PluginValidationIssues pluginIssues =
                 pluginIssues(session).computeIfAbsent(pluginKey, k -> new PluginValidationIssues());
-        pluginIssues.reportPluginIssue(locality, null, issue);
-        mayReportInline(session, locality, issue);
+        pluginIssues.reportPluginIssue(locality, null, problem.getMessage());
+        diagnosticCollector.report(problem);
+        mayReportInline(session, locality, problem.getMessage());
     }
 
     @Override
     public void reportPluginValidationIssue(
-            IssueLocality locality, MavenSession mavenSession, MojoDescriptor mojoDescriptor, String issue) {
+            IssueLocality locality, MavenSession mavenSession, MojoDescriptor mojoDescriptor, BuilderProblem problem) {
         String pluginKey = pluginKey(mojoDescriptor);
         if (validationPluginExcludes(mavenSession.getRepositorySession()).contains(pluginKey)) {
             return;
         }
         PluginValidationIssues pluginIssues = pluginIssues(mavenSession.getRepositorySession())
                 .computeIfAbsent(pluginKey, k -> new PluginValidationIssues());
-        pluginIssues.reportPluginIssue(locality, pluginDeclaration(mavenSession, mojoDescriptor), issue);
-        mayReportInline(mavenSession.getRepositorySession(), locality, issue);
+        pluginIssues.reportPluginIssue(locality, pluginDeclaration(mavenSession, mojoDescriptor), problem.getMessage());
+        diagnosticCollector.report(problem);
+        mayReportInline(mavenSession.getRepositorySession(), locality, problem.getMessage());
     }
 
     @Override
@@ -188,7 +200,7 @@ public void reportPluginMojoValidationIssue(
             MavenSession mavenSession,
             MojoDescriptor mojoDescriptor,
             Class<?> mojoClass,
-            String issue) {
+            BuilderProblem problem) {
         String pluginKey = pluginKey(mojoDescriptor);
         if (validationPluginExcludes(mavenSession.getRepositorySession()).contains(pluginKey)) {
             return;
@@ -196,8 +208,12 @@ public void reportPluginMojoValidationIssue(
         PluginValidationIssues pluginIssues = pluginIssues(mavenSession.getRepositorySession())
                 .computeIfAbsent(pluginKey, k -> new PluginValidationIssues());
         pluginIssues.reportPluginMojoIssue(
-                locality, pluginDeclaration(mavenSession, mojoDescriptor), mojoInfo(mojoDescriptor, mojoClass), issue);
-        mayReportInline(mavenSession.getRepositorySession(), locality, issue);
+                locality,
+                pluginDeclaration(mavenSession, mojoDescriptor),
+                mojoInfo(mojoDescriptor, mojoClass),
+                problem.getMessage());
+        diagnosticCollector.report(problem);
+        mayReportInline(mavenSession.getRepositorySession(), locality, problem.getMessage());
     }
 
     private void reportSessionCollectedValidationIssues(MavenSession mavenSession) {
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedCoreExpressionValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedCoreExpressionValidator.java
index faaac5cf1c..81029df570 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedCoreExpressionValidator.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedCoreExpressionValidator.java
@@ -63,6 +63,11 @@ protected String getParameterLogReason(Parameter parameter) {
                 + DEPRECATED_CORE_PARAMETERS.get(parameter.getDefaultValue());
     }
 
+    @Override
+    protected String getValidationKeyPrefix() {
+        return "deprecated-expr";
+    }
+
     @Override
     protected void doValidate(
             MavenSession mavenSession,
@@ -76,9 +81,13 @@ protected void doValidate(
 
         mojoDescriptor.getParameters().stream()
                 .filter(this::isDeprecated)
-                .map(this::formatParameter)
-                .forEach(m -> pluginValidationManager.reportPluginMojoValidationIssue(
-                        PluginValidationManager.IssueLocality.EXTERNAL, mavenSession, mojoDescriptor, mojoClass, m));
+                .map(this::buildParameterProblem)
+                .forEach(problem -> pluginValidationManager.reportPluginMojoValidationIssue(
+                        PluginValidationManager.IssueLocality.EXTERNAL,
+                        mavenSession,
+                        mojoDescriptor,
+                        mojoClass,
+                        problem));
     }
 
     private boolean isDeprecated(Parameter parameter) {
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedPluginValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedPluginValidator.java
index a4d784c403..45c973fcd9 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedPluginValidator.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedPluginValidator.java
@@ -22,6 +22,7 @@
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import org.apache.maven.api.services.BuilderProblem;
 import org.apache.maven.api.services.MessageBuilderFactory;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.plugin.PluginValidationManager;
@@ -52,6 +53,11 @@ protected String getParameterLogReason(Parameter parameter) {
         return "is deprecated: " + parameter.getDeprecated();
     }
 
+    @Override
+    protected String getValidationKeyPrefix() {
+        return "deprecated-param";
+    }
+
     @Override
     protected void doValidate(
             MavenSession mavenSession,
@@ -65,7 +71,11 @@ protected void doValidate(
                     mavenSession,
                     mojoDescriptor,
                     mojoClass,
-                    logDeprecatedMojo(mojoDescriptor));
+                    BuilderProblem.builder()
+                            .message(logDeprecatedMojo(mojoDescriptor))
+                            .severity(BuilderProblem.Severity.WARNING)
+                            .key("plugin-validation:deprecated-goal:" + mojoDescriptor.getGoal())
+                            .build());
         }
 
         if (mojoDescriptor.getParameters() != null) {
@@ -92,7 +102,7 @@ private void checkParameter(
                     mavenSession,
                     mojoDescriptor,
                     mojoClass,
-                    formatParameter(parameter));
+                    buildParameterProblem(parameter));
         }
     }
 
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven2DependenciesValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven2DependenciesValidator.java
index d2f8dd89ce..05b206b97c 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven2DependenciesValidator.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven2DependenciesValidator.java
@@ -25,6 +25,7 @@
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import org.apache.maven.api.services.BuilderProblem;
 import org.apache.maven.plugin.PluginValidationManager;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.Artifact;
@@ -64,7 +65,12 @@ protected void doValidate(
                     PluginValidationManager.IssueLocality.EXTERNAL,
                     session,
                     pluginArtifact,
-                    "Plugin is a Maven 2.x plugin, which will be not supported in Maven 4.x");
+                    BuilderProblem.builder()
+                            .message("Plugin is a Maven 2.x plugin, which will be not supported in Maven 4.x")
+                            .severity(BuilderProblem.Severity.WARNING)
+                            .key("plugin-validation:maven2-plugin")
+                            .suggestion("Upgrade to a Maven 3.x/4.x compatible version of this plugin")
+                            .build());
         }
     }
 }
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven3CompatDependenciesValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven3CompatDependenciesValidator.java
index 0cf8fee6ad..559d3c1898 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven3CompatDependenciesValidator.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/Maven3CompatDependenciesValidator.java
@@ -23,6 +23,7 @@
 import javax.inject.Singleton;
 
 import org.apache.maven.api.DependencyScope;
+import org.apache.maven.api.services.BuilderProblem;
 import org.apache.maven.plugin.PluginValidationManager;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.Artifact;
@@ -55,7 +56,13 @@ protected void doValidate(
                         PluginValidationManager.IssueLocality.EXTERNAL,
                         session,
                         pluginArtifact,
-                        "Plugin depends on the deprecated Maven 2.x compatibility layer, which will be not supported in Maven 4.x");
+                        BuilderProblem.builder()
+                                .message(
+                                        "Plugin depends on the deprecated Maven 2.x compatibility layer, which will be not supported in Maven 4.x")
+                                .severity(BuilderProblem.Severity.WARNING)
+                                .key("plugin-validation:maven-compat-dep")
+                                .suggestion("Remove the maven-compat dependency from the plugin")
+                                .build());
             }
         }
     }
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenMixedDependenciesValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenMixedDependenciesValidator.java
index 27003836d3..3ae05d3a10 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenMixedDependenciesValidator.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenMixedDependenciesValidator.java
@@ -25,6 +25,7 @@
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import org.apache.maven.api.services.BuilderProblem;
 import org.apache.maven.plugin.PluginValidationManager;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.Artifact;
@@ -63,7 +64,12 @@ protected void doValidate(
                     PluginValidationManager.IssueLocality.EXTERNAL,
                     session,
                     pluginArtifact,
-                    "Plugin mixes multiple Maven versions: " + mavenVersions);
+                    BuilderProblem.builder()
+                            .message("Plugin mixes multiple Maven versions: " + mavenVersions)
+                            .severity(BuilderProblem.Severity.WARNING)
+                            .key("plugin-validation:mixed-maven-versions")
+                            .suggestion("Align all Maven dependencies to a single version to avoid classloading issues")
+                            .build());
         }
     }
 }
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenScopeDependenciesValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenScopeDependenciesValidator.java
index 460a91c6ec..48f78c9011 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenScopeDependenciesValidator.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/MavenScopeDependenciesValidator.java
@@ -26,6 +26,7 @@
 import java.util.stream.Collectors;
 
 import org.apache.maven.api.DependencyScope;
+import org.apache.maven.api.services.BuilderProblem;
 import org.apache.maven.plugin.PluginValidationManager;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.Artifact;
@@ -65,8 +66,15 @@ protected void doValidate(
                     PluginValidationManager.IssueLocality.EXTERNAL,
                     session,
                     pluginArtifact,
-                    "Plugin should declare Maven artifacts in `provided` scope. If the plugin already declares them in `provided` scope, update the maven-plugin-plugin to latest version. Artifacts found with wrong scope: "
-                            + mavenArtifacts);
+                    BuilderProblem.builder()
+                            .message(
+                                    "Plugin should declare Maven artifacts in `provided` scope. If the plugin already declares them in `provided` scope, update the maven-plugin-plugin to latest version. Artifacts found with wrong scope: "
+                                            + mavenArtifacts)
+                            .severity(BuilderProblem.Severity.WARNING)
+                            .key("plugin-validation:wrong-scope")
+                            .suggestion(
+                                    "Change Maven artifact dependencies to 'provided' scope or update maven-plugin-plugin")
+                            .build());
         }
     }
 }
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/PlexusContainerDefaultDependenciesValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/PlexusContainerDefaultDependenciesValidator.java
index 31638139a3..ba15ab4d80 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/PlexusContainerDefaultDependenciesValidator.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/PlexusContainerDefaultDependenciesValidator.java
@@ -22,6 +22,7 @@
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import org.apache.maven.api.services.BuilderProblem;
 import org.apache.maven.plugin.PluginValidationManager;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.Artifact;
@@ -55,7 +56,12 @@ protected void doValidate(
                     PluginValidationManager.IssueLocality.EXTERNAL,
                     session,
                     pluginArtifact,
-                    "Plugin depends on plexus-container-default, which is EOL");
+                    BuilderProblem.builder()
+                            .message("Plugin depends on plexus-container-default, which is EOL")
+                            .severity(BuilderProblem.Severity.WARNING)
+                            .key("plugin-validation:plexus-container-eol")
+                            .suggestion("Migrate from plexus-container-default to javax.inject / Eclipse Sisu")
+                            .build());
         }
     }
 }
diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/ReadOnlyPluginParametersValidator.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/ReadOnlyPluginParametersValidator.java
index 2989fb5166..69e4d9870a 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/ReadOnlyPluginParametersValidator.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/ReadOnlyPluginParametersValidator.java
@@ -47,6 +47,11 @@ protected String getParameterLogReason(Parameter parameter) {
         return "is read-only, must not be used in configuration";
     }
 
+    @Override
+    protected String getValidationKeyPrefix() {
+        return "readonly-param";
+    }
+
     @Override
     protected void doValidate(
             MavenSession mavenSession,
@@ -79,7 +84,7 @@ private void checkParameter(
                     mavenSession,
                     mojoDescriptor,
                     mojoClass,
-                    formatParameter(parameter));
+                    buildParameterProblem(parameter));
         }
     }
 }