You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@maven.apache.org by "Tarun4201 (via GitHub)" <gi...@apache.org> on 2026/10/04 02:27:35 UTC

[PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Tarun4201 opened a new pull request, #13330:
URL: https://github.com/apache/maven/pull/13330

   …cification (#8718)
   
   - Add comprehensive documentation and architectural design specification for aggregator Mojos and reactor lifecycle in src/site/markdown/aggregator-goals.md
   - Add 'Aggregator Mojos & Lifecycles' reference link in src/site/site.xml
   - Expand Javadoc in Mojo.java and MojoDescriptor.java detailing CLI vs lifecycle execution differences and thread safety locking
   - Update plugin.mdo aggregator field description with multi-module execution semantics
   
   <!--
   Please open pull requests against one of the following branches:
   
   - master — default target for all changes (new features + bug fixes).  
     If the change should also go to maven-4.0.x, request a backport in the PR.
   
   - maven-3.10.x — target for 3.x changes (new features + bug fixes).  
     If the change should also go to maven-3.9.x, request a backport in the PR.
     maven-3.9.x should receive only serious bug fixes, as it is approaching EOL.
   
   Backports are typically handled by maintainers; 
   however, after agreement you may open a separate backport PR to the target maintenance branch.
   -->
   
   <!-- Add your description here... what, why and how you resolve the issue -->
   
   ---
   
   To make clear that you license your contribution under
   the [Apache License Version 2.0, January 2004](https://www.apache.org/licenses/LICENSE-2.0)
   check one of the following:
   
   - [ ] I hereby declare this contribution to be licensed under the [Apache License Version 2.0, January 2004](https://www.apache.org/licenses/LICENSE-2.0)
   - [ ] I hereby declare that I have filed an [Apache Individual Contributor License Agreement](https://www.apache.org/licenses/icla.pdf).
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "Tarun4201 (via GitHub)" <gi...@apache.org>.
Tarun4201 commented on PR #13330:
URL: https://github.com/apache/maven/pull/13330#issuecomment-5975877254

   Thanks for the review @gnodet! 
   
   I've addressed all the feedback in commit [`020503a`](https://github.com/apache/maven/pull/13330/commits/020503ad67ae2d1921350a4ea355db90c9b0e27f):
   
   1. **`aggregator-goals.md`**: Corrected the attribution for root-project-only filtering to `BuildListCalculator` and `BuildPlanExecutor`.
   2. **`Mojo.java`**: Standardized the Javadoc terminology to use "subproject" instead of "module" per MNG-8210.
   3. **`MojoDescriptor.java`**: Aligned `isAggregator()` Javadoc with `setAggregator` to consistently use "subprojects".
   
   Please let me know if any further adjustments are needed!
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "gnodet (via GitHub)" <gi...@apache.org>.
gnodet commented on PR #13330:
URL: https://github.com/apache/maven/pull/13330#issuecomment-6209502844

   Correction to my previous comment: the maven-plugin-tools gap I mentioned doesn't actually exist.
   
   **`@After` annotation scanning is already fully implemented** in maven-plugin-tools 4.0.0-beta-3 (commit `ce239e95b58e`, 2026-09-06). The full chain works end-to-end:
   
   1. `DefaultMojoAnnotationsScanner` scans `@After` / `@Afters` via ASM bytecode visitors
   2. `JavaAnnotationsMojoDescriptorExtractor` collects from class hierarchy
   3. `PluginDescriptorFilesGenerator` writes `<afterLink>` elements into `plugin.xml`
   4. Maven core's `BuildPlanExecutor.applyAfterLinks()` creates DAG edges from them
   
   All three pointer types (PROJECT, DEPENDENCIES, CHILDREN) are covered with IT tests in both plugin-tools (`v4api-3x`) and maven-core (`MavenITmng12534AfterAnnotationTest`).
   
   This means the suggested section 3.4 ("Decomposing `aggregator = true`") can be stronger — all v4 replacement mechanisms are **available today**, not just planned:
   
   - Execution scope → `all` phases ✓
   - Dependency resolution → `@Resolution` ✓
   - Ordering constraints → `@After` ✓ (plugin-tools beta-3 generates descriptors)
   - Exclusive locking → DAG edges ✓
   
   The comment in the Maven core IT test mojo (`TouchMojo.java` line 41: *"once maven-plugin-tools learns to scan @After..."*) is stale and should be cleaned up.
   
   _Claude Code on behalf of Guillaume Nodet_


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "gnodet-bot (via GitHub)" <gi...@apache.org>.
gnodet-bot commented on code in PR #13330:
URL: https://github.com/apache/maven/pull/13330#discussion_r4260304447


##########
src/site/markdown/aggregator-goals.md:
##########
@@ -0,0 +1,177 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+# Aggregator Mojos and Reactor Lifecycle Specification
+
+## Overview
+
+Aggregation was introduced early in Maven 2 ([MNG-250](https://github.com/apache/maven/issues/2896)) to enable plugins to operate across an entire multi-module build reactor rather than on a single isolated module. It is exposed to plugin developers via the `@Mojo(aggregator = true)` annotation (or `@aggregator` in JavaDoc tag format).
+
+This document analyzes the current behavior, outlines historical shortcomings, and establishes a target design specification for refactoring aggregator goals in Maven ([MNG-7991](https://github.com/apache/maven/issues/8718)).
+
+---
+
+## 1. Current Aggregator Behavior
+
+Maven treats aggregator Mojos differently depending on whether they are invoked directly via the Command Line Interface (CLI) or bound to a build lifecycle phase in a POM.
+
+### 1.1 CLI Invocation (`mvn plugin:goal`)
+When an aggregating goal is invoked from the command line:
+1. **Task Segment Classification**: `DefaultLifecycleTaskSegmentCalculator` inspects the Mojo descriptor:
+   ```java
+   boolean aggregating = mojoDescriptor.isAggregator() || !mojoDescriptor.isProjectRequired();
+   ```
+2. **Aggregating Task Segment**: A distinct aggregating `TaskSegment` is created.
+3. **Execution on Root Project Only**: Aggregating task segments are restricted to the top-level project (`session.getTopLevelProject()`). Submodules in the reactor are skipped for this goal.
+   - In the **sequential builder**, this filtering is performed by `BuildListCalculator`.
+   - In the **concurrent builder** (`-T`), it is performed by `BuildPlanExecutor` inside `buildInitialPlan()`:
+     ```java
+     Map<MavenProject, List<MavenProject>> projects = taskSegment.isAggregating()
+         ? Collections.singletonMap(rootProject, allProjects.get(rootProject))
+         : allProjects;
+     ```
+4. **Ordering**: If the CLI invocation specifies both a lifecycle phase and an aggregator goal (e.g. `mvn clean install site:stage`), the normal lifecycle runs across all modules first, and the aggregator goal executes once at the end on the root module.
+
+### 1.2 Lifecycle-Bound Execution (`<phase>...</phase>`)
+When an aggregator Mojo is bound to a phase in a `pom.xml`:
+1. **Lifecycle Segment Classification**: Lifecycle phases (e.g. `package`, `verify`) produce standard non-aggregating `TaskSegment`s.
+2. **Per-Module Execution**: The goal is injected into the execution plan of **every module** in the reactor that inherits the plugin configuration.
+3. **Redundant Executions**: Unless the project author explicitly configures `<inherited>false</inherited>` on the plugin execution in the parent POM, the aggregator Mojo executes once for each module in the reactor.
+4. **Thread Locking (sequential builder only)**: In sequential parallel builds (`-T`) using the classic builder, `MojoExecutor` acquires an exclusive reactor-wide write lock (`aggregatorLock.writeLock()`) whenever an aggregator executes:
+   ```java
+   acquiredAggregatorLock = aggregator ? aggregatorLock.writeLock() : aggregatorLock.readLock();
+   ```
+   This means that executing an aggregator Mojo across multiple submodules repeatedly halts parallel build concurrency across all threads.
+
+   In the **concurrent builder** (Maven 4's default `-T` implementation), project-level locking is disabled entirely — the concurrent `MojoExecutor` overrides `useProjectLock()` to return `false`. The concurrent builder instead relies on the build plan DAG to enforce ordering between steps.
+
+### 1.3 Dependency Resolution
+When executing an aggregator Mojo:
+- `MojoExecutor.ensureDependenciesAreResolved` checks `mojoDescriptor.isAggregator()`.
+- If `true`, Maven resolves dependencies not only for the current project, but also across all projects in `session.getProjects()` matching the scopes requested by the Mojo.
+
+### 1.4 Forked Lifecycles
+When an aggregator Mojo declares `@Execute(phase = ...)` or `@Execute(goal = ...)`:
+- `BuildPlanExecutor` creates a forked plan encompassing the current project and all collected sub-projects (`step.project.getCollectedProjects()`).
+- This forks execution across the entire reactor tree, leading to duplicated builds, redundant tests, and nested reactor re-executions.
+
+---
+
+## 2. Identified Shortcomings & Problem Space
+
+Over successive Maven releases, multiple discrepancies and pain points have been identified:
+
+1. **CLI vs. Lifecycle Discrepancy ([MNG-6336](https://github.com/apache/maven/issues/7529))**:
+   - Plugin authors annotate a goal with `@Mojo(aggregator = true)` with the expectation that Maven will execute it once per reactor.
+   - While CLI invocation honors this expectation (running only on root), lifecycle binding runs it on every module.
+   - Forcing users to configure `<inherited>false</inherited>` is error-prone, unintuitive, and breaks down in nested multi-module structures.
+
+2. **Execution Timing: Aggregate at Start vs. Aggregate at End**:
+   - In hierarchical builds, the root project is typically evaluated first in reactor order.
+   - Consequently, an aggregator bound to `package` or `verify` on the root module executes *before* child modules have compiled or packaged their artifacts.
+   - When the purpose of the aggregator is to combine artifacts or reports produced by child modules (e.g. aggregated Javadoc, Jacoco code coverage, distribution zip), executing on the root project at the start fails because child outputs do not yet exist.
+   - Projects often resort to artificial "distribution" child modules placed last in reactor order to work around this limitation.
+
+3. **Submodule Forking Issues ([MNG-7672](https://github.com/apache/maven/issues/8780), [MNG-7163](https://github.com/apache/maven/issues/8291), [MNG-2184](https://github.com/apache/maven/issues/4453))**:
+   - When an aggregator goal is executed within a submodule, forking triggers a build of the entire reactor from within that submodule, repeating already-completed goals.
+
+4. **Reporting and Site Generation Edge Cases**:
+   - The `maven-site-plugin` and `maven-reporting-exec` maintain separate aggregation abstractions for generating multi-module documentation and stage reports, leading to inconsistencies between CLI reporting and lifecycle documentation goals.
+
+---
+
+## 3. Maven 4 Concurrent Builder: `before:`/`after:` Phase System
+
+The Maven 4 concurrent builder introduces a first-class solution to several of the shortcomings above through
+the `before:` and `after:` phase prefixes and two special reactor-scoped phases: `all` and `each`.
+
+> **Note**: The `before:`/`after:` phase system and the `all`/`each` phases require **model version 4.1.0**
+> (`<modelVersion>4.1.0</modelVersion>`) and the **concurrent builder** (`ConcurrentLifecycleStarter`,
+> activated via `-b concurrent`). They are not supported by the legacy sequential builder
+> (`DefaultLifecycleStarter`, the default when no `-b` flag is passed).
+
+### 3.1 The `all` and `each` Phases
+
+The default Maven 4 lifecycle defines a top-level `all` phase that wraps the entire reactor, with `each`
+representing a single subproject's full build:
+
+```
+all                    ← reactor-scoped wrapper (children link)
+└── each               ← per-subproject scope
+    ├── validate / initialize
+    ├── build (sources → compile → ready → package)
+    ├── verify (unit-test, integration-test)
+    ├── install
+    └── deploy
+```
+
+The `all` phase carries a `children(all)` link, which the concurrent builder translates into DAG edges:
+- Every child project's `before:all` must complete before the root project's `before:all` fires.
+- The root project's `after:all` fires only after every child's `after:all` has completed.

Review Comment:
   ⚠️ **Technical inaccuracy — `before:all` edge direction is inverted.**
   
   `BuildPlanExecutor.java` lines 1092–1093 read:
   ```java
   // 1. Child's phase start waits for parent's phase start
   plan.step(p, BEFORE + n2).ifPresent(before::executeBefore);
   ```
   
   This means: parent's `before:all` fires **first**; each child's `before:all` waits for it. The `children(all)` link creates a "parent wraps children" pattern — parent starts first, children run in between, parent finishes last. The first bullet has this backwards.
   
   ```suggestion
   The `all` phase carries a `children(all)` link, which the concurrent builder translates into DAG edges:
   - The root project's `before:all` fires first; every child project's `before:all` waits for it to complete.
   - The root project's `after:all` fires only after every child's `after:all` has completed.
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "Tarun4201 (via GitHub)" <gi...@apache.org>.
Tarun4201 commented on PR #13330:
URL: https://github.com/apache/maven/pull/13330#issuecomment-6193726862

   Addressed in commit b9d8fce6d1 — removed the parenthetical from Mojo.java and folded the CLI execution order statement into item 4 of section 1.1 in aggregator-goals.md.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "gnodet-bot (via GitHub)" <gi...@apache.org>.
gnodet-bot commented on code in PR #13330:
URL: https://github.com/apache/maven/pull/13330#discussion_r4279035843


##########
api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java:
##########
@@ -76,8 +76,17 @@
     boolean projectRequired() default true;
 
     /**
-     * if the Mojo uses the Maven project and its subprojects.
-     * @return uses the Maven project and its subprojects
+     * Indicates whether the Mojo operates as an aggregator across the Maven reactor and its subprojects.
+     * <p>
+     * <b>Execution semantics:</b>
+     * <ul>
+     *   <li>When invoked directly via CLI (e.g. {@code mvn plugin:goal}), an aggregator Mojo executes
+     *       only once on the top-level project of the reactor (tasks are executed in CLI argument order).</li>

Review Comment:
   💡 **Nit:** The parenthetical `(tasks are executed in CLI argument order)` is contextually awkward here — the bullet is about a single aggregator Mojo executing once, but the parenthetical makes a general statement about all CLI tasks. Consider removing it from this line and folding it into item 4 of section 1.1 in `aggregator-goals.md` where task ordering is already documented in detail.
   
   ```suggestion
        *   <li>When invoked directly via CLI (e.g. {@code mvn plugin:goal}), an aggregator Mojo executes
        *       only once on the top-level project of the reactor.</li>
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "gnodet (via GitHub) via issues" <is...@maven.apache.org>.
gnodet merged PR #13330:
URL: https://github.com/apache/maven/pull/13330


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "slawekjaranowski (via GitHub)" <gi...@apache.org>.
slawekjaranowski commented on code in PR #13330:
URL: https://github.com/apache/maven/pull/13330#discussion_r4178310251


##########
src/site/markdown/aggregator-goals.md:
##########
@@ -0,0 +1,124 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+# Aggregator Mojos and Reactor Lifecycle Specification
+
+## Overview
+
+Aggregation was introduced early in Maven 2 ([MNG-250](https://issues.apache.org/jira/browse/MNG-250)) to enable plugins to operate across an entire multi-module build reactor rather than on a single isolated module. It is exposed to plugin developers via the `@Mojo(aggregator = true)` annotation (or `@aggregator` in JavaDoc tag format).
+
+This document analyzes the current behavior, outlines historical shortcomings, and establishes a target design specification for refactoring aggregator goals in Maven ([MNG-7991](https://issues.apache.org/jira/browse/MNG-7991)).
+
+---
+
+## 1. Current Aggregator Behavior
+
+Maven treats aggregator Mojos differently depending on whether they are invoked directly via the Command Line Interface (CLI) or bound to a build lifecycle phase in a POM.
+
+### 1.1 CLI Invocation (`mvn plugin:goal`)
+When an aggregating goal is invoked from the command line:
+1. **Task Segment Classification**: `DefaultLifecycleTaskSegmentCalculator` inspects the Mojo descriptor:
+   ```java
+   boolean aggregating = mojoDescriptor.isAggregator() || !mojoDescriptor.isProjectRequired();
+   ```
+2. **Aggregating Task Segment**: A distinct aggregating `TaskSegment` is created.
+3. **Execution on Root Project Only**: `BuildListCalculator` (and `BuildPlanExecutor` in the concurrent path) restricts aggregating task segments to the top-level project (`session.getTopLevelProject()`). Submodules in the reactor are skipped for this goal.
+4. **Ordering**: If the CLI invocation specifies both a lifecycle phase and an aggregator goal (e.g. `mvn clean install site:stage`), the normal lifecycle runs across all modules first, and the aggregator goal executes once at the end on the root module.
+
+### 1.2 Lifecycle-Bound Execution (`<phase>...</phase>`)
+When an aggregator Mojo is bound to a phase in a `pom.xml`:
+1. **Lifecycle Segment Classification**: Lifecycle phases (e.g. `package`, `verify`) produce standard non-aggregating `TaskSegment`s.
+2. **Per-Module Execution**: The goal is injected into the execution plan of **every module** in the reactor that inherits the plugin configuration.
+3. **Redundant Executions**: Unless the project author explicitly configures `<inherited>false</inherited>` on the plugin execution in the parent POM, the aggregator Mojo executes once for each module in the reactor.
+4. **Thread Locking**: In parallel builds (`-T`), `MojoExecutor` acquires an exclusive reactor-wide write lock (`aggregatorLock.writeLock()`) whenever an aggregator executes:
+   ```java
+   acquiredAggregatorLock = aggregator ? aggregatorLock.writeLock() : aggregatorLock.readLock();
+   ```
+   Executing an aggregator Mojo across multiple submodules repeatedly halts parallel build concurrency across all threads.
+
+### 1.3 Dependency Resolution
+When executing an aggregator Mojo:
+- `MojoExecutor.ensureDependenciesAreResolved` checks `mojoDescriptor.isAggregator()`.
+- If `true`, Maven resolves dependencies not only for the current project, but also across all projects in `session.getProjects()` matching the scopes requested by the Mojo.
+
+### 1.4 Forked Lifecycles
+When an aggregator Mojo declares `@Execute(phase = ...)` or `@Execute(goal = ...)`:
+- `BuildPlanExecutor` creates a forked plan encompassing the current project and all collected sub-projects (`step.project.getCollectedProjects()`).
+- This forks execution across the entire reactor tree, leading to duplicated builds, redundant tests, and nested reactor re-executions.
+
+---
+
+## 2. Identified Shortcomings & Problem Space
+
+Over successive Maven releases, multiple discrepancies and pain points have been identified:
+
+1. **CLI vs. Lifecycle Discrepancy ([MNG-6336](https://issues.apache.org/jira/browse/MNG-6336))**:

Review Comment:
   As we migrated issues to GH, I would like to point to current at GH.
   
   In JIRA all issues are read only.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "gnodet-bot (via GitHub)" <gi...@apache.org>.
gnodet-bot commented on code in PR #13330:
URL: https://github.com/apache/maven/pull/13330#discussion_r4175849937


##########
compat/maven-plugin-api/src/main/java/org/apache/maven/plugin/descriptor/MojoDescriptor.java:
##########
@@ -539,16 +539,18 @@ public void setExecuteLifecycle(String executeLifecycle) {
     }
 
     /**
-     * @param aggregator <code>true</code> if the Mojo uses the Maven project and its child modules,
-     * <code>false</code> otherwise.
+     * @param aggregator <code>true</code> if the Mojo operates across the reactor project and its subprojects,
+     *                   <code>false</code> otherwise.
      */
     public void setAggregator(boolean aggregator) {
         this.aggregator = aggregator;
     }
 
     /**
-     * @return <code>true</code> if the Mojo uses the Maven project and its child modules,
-     * <code>false</code> otherwise.
+     * Indicates whether the Mojo operates as an aggregator across the Maven reactor and its child modules.
+     *
+     * @return <code>true</code> if the Mojo operates across the Maven reactor and its child modules,
+     *         <code>false</code> otherwise.

Review Comment:
   💡 `setAggregator` (line 542) uses "subprojects" but `isAggregator` uses "child modules" — inconsistent within the same class in the same PR.
   
   ```suggestion
        * Indicates whether the Mojo operates as an aggregator across the Maven reactor and its subprojects.
        *
        * @return <code>true</code> if the Mojo operates across the Maven reactor and its subprojects,
        *         <code>false</code> otherwise.
   ```



##########
src/site/markdown/aggregator-goals.md:
##########
@@ -0,0 +1,124 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+# Aggregator Mojos and Reactor Lifecycle Specification
+
+## Overview
+
+Aggregation was introduced early in Maven 2 ([MNG-250](https://issues.apache.org/jira/browse/MNG-250)) to enable plugins to operate across an entire multi-module build reactor rather than on a single isolated module. It is exposed to plugin developers via the `@Mojo(aggregator = true)` annotation (or `@aggregator` in JavaDoc tag format).
+
+This document analyzes the current behavior, outlines historical shortcomings, and establishes a target design specification for refactoring aggregator goals in Maven ([MNG-7991](https://issues.apache.org/jira/browse/MNG-7991)).
+
+---
+
+## 1. Current Aggregator Behavior
+
+Maven treats aggregator Mojos differently depending on whether they are invoked directly via the Command Line Interface (CLI) or bound to a build lifecycle phase in a POM.
+
+### 1.1 CLI Invocation (`mvn plugin:goal`)
+When an aggregating goal is invoked from the command line:
+1. **Task Segment Classification**: `DefaultLifecycleTaskSegmentCalculator` inspects the Mojo descriptor:
+   ```java
+   boolean aggregating = mojoDescriptor.isAggregator() || !mojoDescriptor.isProjectRequired();
+   ```
+2. **Aggregating Task Segment**: A distinct aggregating `TaskSegment` is created.
+3. **Execution on Root Project Only**: The lifecycle execution engine (`LifecycleStarter` / `ConcurrentLifecycleStarter`) executes this task segment exclusively on the top-level project (`session.getTopLevelProject()`). Submodules in the reactor are skipped for this goal.

Review Comment:
   ⚠️ **Inaccurate class attribution.** The root-project-only filtering is handled by `BuildListCalculator` (line 48: `if (taskSegment.isAggregating()) { projects = Collections.singletonList(rootProject); }`) and `BuildPlanExecutor` (line 246-248, same logic). `LifecycleStarter` / `ConcurrentLifecycleStarter` are entry points that delegate downward — they don't perform the aggregation filtering themselves.
   
   ```suggestion
   3. **Execution on Root Project Only**: `BuildListCalculator` (and `BuildPlanExecutor` in the concurrent path) restricts aggregating task segments to the top-level project (`session.getTopLevelProject()`). Submodules in the reactor are skipped for this goal.
   ```



##########
api/maven-api-core/src/main/java/org/apache/maven/api/plugin/annotations/Mojo.java:
##########
@@ -76,8 +76,17 @@
     boolean projectRequired() default true;
 
     /**
-     * if the Mojo uses the Maven project and its subprojects.
-     * @return uses the Maven project and its subprojects
+     * Indicates whether the Mojo operates as an aggregator across the Maven reactor and its subprojects.
+     * <p>
+     * <b>Execution semantics:</b>
+     * <ul>
+     *   <li>When invoked directly via CLI (e.g. {@code mvn plugin:goal}), an aggregator Mojo executes
+     *       only once on the top-level project of the reactor, after preceding lifecycle phases complete.</li>
+     *   <li>When bound to a lifecycle phase, the goal executes during that phase for each module in the
+     *       reactor unless explicitly configured with {@code <inherited>false</inherited>}.</li>

Review Comment:
   💡 "module" here is inconsistent with the rest of this Javadoc (lines 79, 89) which uses "subprojects" per MNG-8210.
   
   ```suggestion
        *   <li>When bound to a lifecycle phase, the goal executes during that phase for each subproject in the
        *       reactor unless explicitly configured with {@code <inherited>false</inherited>}.</li>
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "gnodet (via GitHub)" <gi...@apache.org>.
gnodet commented on PR #13330:
URL: https://github.com/apache/maven/pull/13330#issuecomment-6203287317

   Thanks for the thorough documentation @Tarun4201! The current behavior sections (1 and 2) are excellent and should be kept as-is.
   
   Following the merge of #13532 (concurrent builder merge fixes) and a deeper analysis of what `aggregator` actually controls at runtime, I think sections 3.4 and 3.5 should be updated to frame `before:`/`after:` phases as the **replacement path** for `aggregator`, not just an alternative. The analysis also shows that forked lifecycles (`@Execute`) can be deprecated entirely — no existing plugin needs fork isolation, only ordering guarantees.
   
   Here's a suggested rewrite for sections 3.4 and 3.5, plus a new section 3.6:
   
   ---
   
   ### 3.4 Decomposing `aggregator = true`
   
   The `aggregator` flag is a **compound attribute** that bundles four distinct runtime behaviors. Maven 4 already provides orthogonal replacements for each:
   
   | Behavior | What `aggregator=true` does | Maven 4 replacement | Status |
   | :--- | :--- | :--- | :--- |
   | **Execution scope** | Run once on the root project, not per-module | Bind to `all` phase (`before:all`, `after:all`) | Available |
   | **Dependency resolution scope** | Resolve dependencies for all reactor projects | `@Resolution` annotation (per-field, per-project injection in `DefaultMavenPluginManager`) | Available |
   | **Exclusive locking** | Acquire reactor-wide write lock in `-T` builder | DAG edges enforce ordering in the concurrent builder — no lock needed | Available |
   | **Fork scope** | Forked lifecycles include all subprojects | Deprecated — see section 3.5 | Planned |
   
   This decomposition means new Maven 4 plugins should **not** use `@Mojo(aggregator = true)`. Instead:
   
   - Bind the goal to `after:all` (or `before:all`) for reactor-scoped execution.
   - Use `@Resolution` fields for dependency injection, which is already orthogonal to aggregator.
   - Express ordering constraints via `@After` / `AfterLink` with `PROJECT`, `DEPENDENCIES`, or `CHILDREN` pointers.
   
   For backward compatibility, existing plugin descriptors declaring `aggregator=true` will continue to work — the runtime maps the flag to the equivalent DAG constraints internally.
   
   ### 3.5 Deprecating Forked Lifecycles (`@Execute`)
   
   Forked lifecycles (`@Execute(phase = ...)`) let a mojo fork a lifecycle phase in a separate execution context before running. Analysis of all Apache Maven plugins using `@Execute` shows that **none require fork isolation** — every use is purely an ordering guarantee:
   
   | Plugin | `@Execute` target | Actual need |
   | :--- | :--- | :--- |
   | maven-javadoc-plugin | `@Execute(phase = COMPILE)` | Compiled classes must exist |
   | maven-source-plugin | `@Execute(phase = GENERATE_SOURCES)` | Generated sources must exist |
   | maven-dependency-plugin | `@Execute(phase = TEST_COMPILE)` | Classes compiled for analysis |
   | maven-pmd-plugin | `@Execute(phase = TEST_COMPILE)`, `@Execute(goal = "pmd")` | Classes compiled; PMD report generated |
   | maven-surefire (report) | `@Execute(lifecycle = "surefire", phase = TEST)` | Test results must exist |
   
   Every case reduces to "phase X must have completed before I run" — a DAG edge, not an isolated execution context. The `-no-fork` mojo variants (`javadoc-no-fork`, `aggregate-no-fork`, `jxr-no-fork`) exist only to opt out of unwanted forking, confirming that the fork is the wrong abstraction.
   
   The Maven 4 replacement is `@After` constraints:
   
   ```java
   // Instead of @Execute(phase = LifecyclePhase.COMPILE)
   @After(phase = "compile", scope = AfterLink.Pointer.PROJECT)
   
   // Instead of @Execute(goal = "pmd")
   @After(goal = "pmd:pmd")
   ```
   
   The `computeForkPlan()` machinery, `$plan$` sentinel detection, and `getExecutionProject()` API can be deprecated for v4 mojos. Maven 3 plugin descriptors declaring `@Execute` will be translated into equivalent `@After` constraints at descriptor load time.
   
   ### 3.6 Migration Roadmap
   
   | Phase | Action |
   | :--- | :--- |
   | **Maven 4.0** | `before:`/`after:` phases available; `aggregator` and `@Execute` still supported |
   | **Maven 4.x** | Document `aggregator` and `@Execute` as deprecated for new plugins; provide migration guide |
   | **Maven 5.x** | Remove forked lifecycle machinery; `aggregator` mapped to phase binding internally |
   
   ---
   
   The related issues table (section 4) should also add:
   - [#13529](https://github.com/apache/maven/issues/13529) — Concurrent builder drops later lifecycle phases when merging overlapping task segments (fixed in #13532)
   
   _Claude Code on behalf of Guillaume Nodet_


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "Tarun4201 (via GitHub)" <gi...@apache.org>.
Tarun4201 commented on PR #13330:
URL: https://github.com/apache/maven/pull/13330#issuecomment-5975914914

   Thanks @gnodet! 
   
   I've addressed the remaining nit in commit [`cfd4083`](https://github.com/apache/maven/pull/13330/commits/cfd40836e9d892d19487b99c73e0a2948eb7f2d4):
   - Updated `plugin.mdo` to use "each subproject" instead of "each module" to ensure complete terminology consistency per MNG-8210.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "Tarun4201 (via GitHub)" <gi...@apache.org>.
Tarun4201 commented on PR #13330:
URL: https://github.com/apache/maven/pull/13330#issuecomment-6265403070

   Hi @slachiewicz, all review feedback from @gnodet and @gnodet-bot has been addressed and approved. The branch is clean, all checks are passing. Could you please take a look when you have a moment? 
   Thank you!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "Tarun4201 (via GitHub) via issues" <is...@maven.apache.org>.
Tarun4201 commented on PR #13330:
URL: https://github.com/apache/maven/pull/13330#issuecomment-6662614837

   Hi @slachiewicz,
   
   Friendly ping when you have a moment to take a look at this PR! 
   
   All feedback from @gnodet has been addressed and incorporated into the document via commit 482145d. The PR currently has 3 approvals (including 2 maintainers with write access) and all checks are passing cleanly. 
   
   Could you please review and share your thoughts/approval when convenient so we can move this forward? 
   
   Thank you!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "gnodet-bot (via GitHub)" <gi...@apache.org>.
gnodet-bot commented on code in PR #13330:
URL: https://github.com/apache/maven/pull/13330#discussion_r4308173931


##########
src/site/markdown/aggregator-goals.md:
##########
@@ -0,0 +1,221 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+# Aggregator Mojos and Reactor Lifecycle Specification
+
+## Overview
+
+Aggregation was introduced early in Maven 2 ([MNG-250](https://github.com/apache/maven/issues/2896)) to enable plugins to operate across an entire multi-module build reactor rather than on a single isolated module. It is exposed to plugin developers via the `@Mojo(aggregator = true)` annotation (or `@aggregator` in JavaDoc tag format).
+
+This document analyzes the current behavior, outlines historical shortcomings, and establishes a target design specification for refactoring aggregator goals in Maven ([MNG-7991](https://github.com/apache/maven/issues/8718)).
+
+---
+
+## 1. Current Aggregator Behavior
+
+Maven treats aggregator Mojos differently depending on whether they are invoked directly via the Command Line Interface (CLI) or bound to a build lifecycle phase in a POM.
+
+### 1.1 CLI Invocation (`mvn plugin:goal`)
+When an aggregating goal is invoked from the command line:
+1. **Task Segment Classification**: `DefaultLifecycleTaskSegmentCalculator` inspects the Mojo descriptor:
+   ```java
+   boolean aggregating = mojoDescriptor.isAggregator() || !mojoDescriptor.isProjectRequired();
+   ```
+2. **Aggregating Task Segment**: A distinct aggregating `TaskSegment` is created.
+3. **Execution on Root Project Only**: Aggregating task segments are restricted to the top-level project (`session.getTopLevelProject()`). Submodules in the reactor are skipped for this goal.
+   - In the **singlethreaded** and **multithreaded** builders, this filtering is performed by `BuildListCalculator`.
+   - In the **concurrent builder** (`-b concurrent`), it is performed by `BuildPlanExecutor` inside `buildInitialPlan()`:
+     ```java
+     Map<MavenProject, List<MavenProject>> projects = taskSegment.isAggregating()
+         ? Collections.singletonMap(rootProject, allProjects.get(rootProject))
+         : allProjects;
+     ```
+4. **Ordering**: Tasks are executed in CLI argument order — task segments are produced in CLI argument order with no automatic re-sorting. In `mvn clean install site:stage`, the non-aggregating `clean install` segment runs first (across all modules), then the aggregating `site:stage` segment runs once on the root. If the user writes `mvn site:stage install`, the aggregating segment runs first.
+
+### 1.2 Lifecycle-Bound Execution (`<phase>...</phase>`)
+When an aggregator Mojo is bound to a phase in a `pom.xml`:
+1. **Lifecycle Segment Classification**: Lifecycle phases (e.g. `package`, `verify`) produce standard non-aggregating `TaskSegment`s.
+2. **Per-Module Execution**: The goal is injected into the execution plan of **every module** in the reactor that inherits the plugin configuration.
+3. **Redundant Executions**: Unless the project author explicitly configures `<inherited>false</inherited>` on the plugin execution in the parent POM, the aggregator Mojo executes once for each module in the reactor.
+4. **Thread Locking (multithreaded builder only)**: When using the multithreaded builder (`-T`), `MojoExecutor` acquires an exclusive reactor-wide write lock (`aggregatorLock.writeLock()`) whenever an aggregator executes:
+   ```java
+   acquiredAggregatorLock = aggregator ? aggregatorLock.writeLock() : aggregatorLock.readLock();
+   ```
+   This means that executing an aggregator Mojo across multiple submodules repeatedly halts parallel build concurrency across all threads.
+
+   In the **concurrent builder** (`-b concurrent`), project-level locking is disabled entirely — the concurrent `MojoExecutor` overrides `useProjectLock()` to return `false`. The concurrent builder instead relies on the build plan DAG to enforce ordering between steps.
+
+### 1.3 Dependency Resolution
+When executing an aggregator Mojo:
+- `MojoExecutor.ensureDependenciesAreResolved` checks `mojoDescriptor.isAggregator()`.
+- If `true`, Maven resolves dependencies not only for the current project, but also across all projects in `session.getProjects()` matching the scopes requested by the Mojo.
+
+### 1.4 Forked Lifecycles
+When an aggregator Mojo declares `@Execute(phase = ...)` or `@Execute(goal = ...)`, the concurrent builder (`BuildPlanExecutor`) handles each case differently:
+- **`@Execute(phase = ...)`**: A forked plan is computed for the **current project only** (`Collections.singletonMap(step.project, ...)`), up to the specified phase. The main plan is appended after the forked plan as a DAG dependency.
+- **`@Execute(goal = ...)`**: A forked plan is computed for the current project, plus `step.project.getCollectedProjects()` if the mojo is also an aggregator. This means forking a non-aggregator goal targets only the current project; forking an aggregator goal spans the reactor tree rooted at the current project, which can lead to duplicated builds, redundant tests, and nested reactor re-executions.
+
+---
+
+## 2. Identified Shortcomings & Problem Space
+
+Over successive Maven releases, multiple discrepancies and pain points have been identified:
+
+1. **CLI vs. Lifecycle Discrepancy ([MNG-6336](https://github.com/apache/maven/issues/7529))**:
+   - Plugin authors annotate a goal with `@Mojo(aggregator = true)` with the expectation that Maven will execute it once per reactor.
+   - While CLI invocation honors this expectation (running only on root), lifecycle binding runs it on every module.
+   - Forcing users to configure `<inherited>false</inherited>` is error-prone, unintuitive, and breaks down in nested multi-module structures.
+
+2. **Execution Timing: Aggregate at Start vs. Aggregate at End**:
+   - In hierarchical builds, the root project is typically evaluated first in reactor order.
+   - Consequently, an aggregator bound to `package` or `verify` on the root module executes *before* child modules have compiled or packaged their artifacts.
+   - When the purpose of the aggregator is to combine artifacts or reports produced by child modules (e.g. aggregated Javadoc, Jacoco code coverage, distribution zip), executing on the root project at the start fails because child outputs do not yet exist.
+   - Projects often resort to artificial "distribution" child modules placed last in reactor order to work around this limitation.
+
+3. **Submodule Forking Issues ([MNG-7672](https://github.com/apache/maven/issues/8780), [MNG-7163](https://github.com/apache/maven/issues/8291), [MNG-2184](https://github.com/apache/maven/issues/4453))**:
+   - When an aggregator goal is executed within a submodule, forking triggers a build of the entire reactor from within that submodule, repeating already-completed goals.
+
+4. **Reporting and Site Generation Edge Cases**:
+   - The `maven-site-plugin` and `maven-reporting-exec` maintain separate aggregation abstractions for generating multi-module documentation and stage reports, leading to inconsistencies between CLI reporting and lifecycle documentation goals.
+
+---
+
+## 3. Maven 4 Concurrent Builder: `before:`/`after:` Phase System
+
+The Maven 4 concurrent builder introduces a first-class solution to several of the shortcomings above through
+the `before:` and `after:` phase prefixes and two special reactor-scoped phases: `all` and `each`.
+
+> **Note**: The `before:`/`after:` phase system and the `all`/`each` phases require the **concurrent builder**
+> (`ConcurrentLifecycleStarter`, activated via `-b concurrent`). They work with any POM model version
+> (4.0.0 or 4.1.0) — no model version upgrade is required. They are not supported by the legacy
+> singlethreaded or multithreaded builders (`DefaultLifecycleStarter`, the default when no `-b` flag is passed).
+
+### 3.1 The `all` and `each` Phases
+
+The default Maven 4 lifecycle defines a top-level `all` phase that wraps the entire reactor, with `each`
+representing a single subproject's full build:
+
+```
+all                    ← reactor-scoped wrapper (children link)
+└── each               ← per-subproject scope
+    ├── validate / initialize
+    ├── build (sources → compile → ready → package)
+    ├── verify (unit-test, integration-test)
+    ├── install
+    └── deploy
+```
+
+The `all` phase carries a `children(all)` link, which the concurrent builder translates into DAG edges:
+- The root project's `before:all` fires first; every child project's `before:all` waits for it to complete.
+- The root project's `after:all` fires only after every child's `after:all` has completed.
+
+This means a plugin goal bound to `after:all` on the root project is **guaranteed to run once, after all
+reactor subprojects have finished their full build** — enforced by the DAG, not by a global lock.
+
+### 3.2 Solving the Aggregation Problems
+
+| Problem (section 2) | Concurrent builder solution |
+| :--- | :--- |
+| **CLI vs. lifecycle discrepancy** ([MNG-6336](https://github.com/apache/maven/issues/7529)) | Bind to `after:all` on the root project — runs once after the full reactor. The timing guarantee is enforced by the DAG regardless of whether the goal was invoked via CLI or lifecycle. Note: `<inherited>false</inherited>` is still required if the execution is declared in a parent POM, since the `before:`/`after:` system controls ordering, not inheritance. |
+| **Execution timing** (aggregator runs before children are built) | `after:all` fires after all children complete `all`, which includes `build`/`verify`. Artifact outputs are available. |
+| **`before:each` / `after:each`** | Goals bound to these phases run once per subproject, bracketing each module's full build. Useful for per-module setup/teardown. |
+
+### 3.3 Example: Aggregated Javadoc after the Full Reactor
+
+```xml
+<!-- requires -b concurrent; works with any model version -->
+<execution>
+  <id>aggregate-javadoc</id>
+  <goals><goal>aggregate</goal></goals>
+  <phase>after:all</phase>
+</execution>
+```
+
+This executes `maven-javadoc-plugin:aggregate` exactly once on the root project, after every subproject has
+completed its `all` phase (which includes compilation and packaging). No distribution submodule workaround
+required.
+
+### 3.4 Decomposing `aggregator = true`
+
+The `aggregator` flag is a **compound attribute** that bundles four distinct runtime behaviors. Maven 4 already provides orthogonal replacements for each:
+
+| Behavior | What `aggregator=true` does | Maven 4 replacement | Status |
+| :--- | :--- | :--- | :--- |
+| **Execution scope** | Run once on the root project, not per-module | Bind to `all` phase (`before:all`, `after:all`) | Available |
+| **Dependency resolution scope** | Resolve dependencies for all reactor projects | `@Resolution` annotation (per-field, per-project injection in `DefaultMavenPluginManager`) | Available |
+| **Exclusive locking** | Acquire reactor-wide write lock in `-T` builder | DAG edges enforce ordering in the concurrent builder — no lock needed | Available |
+| **Fork scope** | Forked lifecycles include all subprojects | Ordering constraints via `@After` (forks deprecated — see section 3.5) | Available |
+
+This decomposition means new Maven 4 plugins should **not** use `@Mojo(aggregator = true)`. Instead:
+
+- Bind the goal to `after:all` (or `before:all`) for reactor-scoped execution.
+- Use `@Resolution` fields for dependency injection, which is already orthogonal to aggregator.
+- Express ordering constraints via `@After` / `AfterLink` with `PROJECT`, `DEPENDENCIES`, or `CHILDREN` pointers.
+
+For backward compatibility, existing plugin descriptors declaring `aggregator=true` will continue to work — the runtime maps the flag to the equivalent DAG constraints internally.

Review Comment:
   ⚠️ **Present-tense claim for future/unimplemented behavior**: "the runtime maps the flag to the equivalent DAG constraints internally" — is this already implemented, or is it the target design for Maven 5.x (as the roadmap table at §3.6 suggests)?
   
   If this is future work, the phrasing should reflect that:
   ```suggestion
   For backward compatibility, existing plugin descriptors declaring `aggregator=true` will continue to work in Maven 4.x; the target for Maven 5.x is to map the flag to equivalent DAG constraints internally (see §3.6 roadmap).
   ```



##########
its/core-it-suite/src/test/resources/mng-12534-after-annotation/plugin/src/main/java/org/apache/maven/its/mng12534/TouchMojo.java:
##########
@@ -37,8 +37,7 @@
  * {@code @After} link, compile could start before resources completes.
  * <p>
  * The handcrafted V2 plugin descriptor mirrors this as an
- * {@code <afterLink>} element — once maven-plugin-tools learns to
- * scan {@code @After}, the descriptor will be generated automatically.
+ * {@code <afterLink>} element.

Review Comment:
   ⚠️ **Premature removal of accurate caveat**: The removed sentence ("once maven-plugin-tools learns to scan `@After`, the descriptor will be generated automatically") accurately describes the current state of the toolchain — the V2 plugin descriptor is still handcrafted because maven-plugin-tools does not yet generate `<afterLink>` elements from `@After`. Removing this caveat implies the tooling gap has been resolved when it hasn't.
   
   The caveat should be preserved until maven-plugin-tools actually gains this capability.



##########
src/site/markdown/aggregator-goals.md:
##########
@@ -0,0 +1,221 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+# Aggregator Mojos and Reactor Lifecycle Specification
+
+## Overview
+
+Aggregation was introduced early in Maven 2 ([MNG-250](https://github.com/apache/maven/issues/2896)) to enable plugins to operate across an entire multi-module build reactor rather than on a single isolated module. It is exposed to plugin developers via the `@Mojo(aggregator = true)` annotation (or `@aggregator` in JavaDoc tag format).
+
+This document analyzes the current behavior, outlines historical shortcomings, and establishes a target design specification for refactoring aggregator goals in Maven ([MNG-7991](https://github.com/apache/maven/issues/8718)).
+
+---
+
+## 1. Current Aggregator Behavior
+
+Maven treats aggregator Mojos differently depending on whether they are invoked directly via the Command Line Interface (CLI) or bound to a build lifecycle phase in a POM.
+
+### 1.1 CLI Invocation (`mvn plugin:goal`)
+When an aggregating goal is invoked from the command line:
+1. **Task Segment Classification**: `DefaultLifecycleTaskSegmentCalculator` inspects the Mojo descriptor:
+   ```java
+   boolean aggregating = mojoDescriptor.isAggregator() || !mojoDescriptor.isProjectRequired();
+   ```
+2. **Aggregating Task Segment**: A distinct aggregating `TaskSegment` is created.
+3. **Execution on Root Project Only**: Aggregating task segments are restricted to the top-level project (`session.getTopLevelProject()`). Submodules in the reactor are skipped for this goal.
+   - In the **singlethreaded** and **multithreaded** builders, this filtering is performed by `BuildListCalculator`.
+   - In the **concurrent builder** (`-b concurrent`), it is performed by `BuildPlanExecutor` inside `buildInitialPlan()`:
+     ```java
+     Map<MavenProject, List<MavenProject>> projects = taskSegment.isAggregating()
+         ? Collections.singletonMap(rootProject, allProjects.get(rootProject))
+         : allProjects;
+     ```
+4. **Ordering**: Tasks are executed in CLI argument order — task segments are produced in CLI argument order with no automatic re-sorting. In `mvn clean install site:stage`, the non-aggregating `clean install` segment runs first (across all modules), then the aggregating `site:stage` segment runs once on the root. If the user writes `mvn site:stage install`, the aggregating segment runs first.
+
+### 1.2 Lifecycle-Bound Execution (`<phase>...</phase>`)
+When an aggregator Mojo is bound to a phase in a `pom.xml`:
+1. **Lifecycle Segment Classification**: Lifecycle phases (e.g. `package`, `verify`) produce standard non-aggregating `TaskSegment`s.
+2. **Per-Module Execution**: The goal is injected into the execution plan of **every module** in the reactor that inherits the plugin configuration.
+3. **Redundant Executions**: Unless the project author explicitly configures `<inherited>false</inherited>` on the plugin execution in the parent POM, the aggregator Mojo executes once for each module in the reactor.
+4. **Thread Locking (multithreaded builder only)**: When using the multithreaded builder (`-T`), `MojoExecutor` acquires an exclusive reactor-wide write lock (`aggregatorLock.writeLock()`) whenever an aggregator executes:
+   ```java
+   acquiredAggregatorLock = aggregator ? aggregatorLock.writeLock() : aggregatorLock.readLock();
+   ```
+   This means that executing an aggregator Mojo across multiple submodules repeatedly halts parallel build concurrency across all threads.
+
+   In the **concurrent builder** (`-b concurrent`), project-level locking is disabled entirely — the concurrent `MojoExecutor` overrides `useProjectLock()` to return `false`. The concurrent builder instead relies on the build plan DAG to enforce ordering between steps.
+
+### 1.3 Dependency Resolution
+When executing an aggregator Mojo:
+- `MojoExecutor.ensureDependenciesAreResolved` checks `mojoDescriptor.isAggregator()`.
+- If `true`, Maven resolves dependencies not only for the current project, but also across all projects in `session.getProjects()` matching the scopes requested by the Mojo.
+
+### 1.4 Forked Lifecycles
+When an aggregator Mojo declares `@Execute(phase = ...)` or `@Execute(goal = ...)`, the concurrent builder (`BuildPlanExecutor`) handles each case differently:
+- **`@Execute(phase = ...)`**: A forked plan is computed for the **current project only** (`Collections.singletonMap(step.project, ...)`), up to the specified phase. The main plan is appended after the forked plan as a DAG dependency.
+- **`@Execute(goal = ...)`**: A forked plan is computed for the current project, plus `step.project.getCollectedProjects()` if the mojo is also an aggregator. This means forking a non-aggregator goal targets only the current project; forking an aggregator goal spans the reactor tree rooted at the current project, which can lead to duplicated builds, redundant tests, and nested reactor re-executions.
+
+---
+
+## 2. Identified Shortcomings & Problem Space
+
+Over successive Maven releases, multiple discrepancies and pain points have been identified:
+
+1. **CLI vs. Lifecycle Discrepancy ([MNG-6336](https://github.com/apache/maven/issues/7529))**:
+   - Plugin authors annotate a goal with `@Mojo(aggregator = true)` with the expectation that Maven will execute it once per reactor.
+   - While CLI invocation honors this expectation (running only on root), lifecycle binding runs it on every module.
+   - Forcing users to configure `<inherited>false</inherited>` is error-prone, unintuitive, and breaks down in nested multi-module structures.
+
+2. **Execution Timing: Aggregate at Start vs. Aggregate at End**:
+   - In hierarchical builds, the root project is typically evaluated first in reactor order.
+   - Consequently, an aggregator bound to `package` or `verify` on the root module executes *before* child modules have compiled or packaged their artifacts.
+   - When the purpose of the aggregator is to combine artifacts or reports produced by child modules (e.g. aggregated Javadoc, Jacoco code coverage, distribution zip), executing on the root project at the start fails because child outputs do not yet exist.
+   - Projects often resort to artificial "distribution" child modules placed last in reactor order to work around this limitation.
+
+3. **Submodule Forking Issues ([MNG-7672](https://github.com/apache/maven/issues/8780), [MNG-7163](https://github.com/apache/maven/issues/8291), [MNG-2184](https://github.com/apache/maven/issues/4453))**:
+   - When an aggregator goal is executed within a submodule, forking triggers a build of the entire reactor from within that submodule, repeating already-completed goals.
+
+4. **Reporting and Site Generation Edge Cases**:
+   - The `maven-site-plugin` and `maven-reporting-exec` maintain separate aggregation abstractions for generating multi-module documentation and stage reports, leading to inconsistencies between CLI reporting and lifecycle documentation goals.
+
+---
+
+## 3. Maven 4 Concurrent Builder: `before:`/`after:` Phase System
+
+The Maven 4 concurrent builder introduces a first-class solution to several of the shortcomings above through
+the `before:` and `after:` phase prefixes and two special reactor-scoped phases: `all` and `each`.
+
+> **Note**: The `before:`/`after:` phase system and the `all`/`each` phases require the **concurrent builder**
+> (`ConcurrentLifecycleStarter`, activated via `-b concurrent`). They work with any POM model version
+> (4.0.0 or 4.1.0) — no model version upgrade is required. They are not supported by the legacy
+> singlethreaded or multithreaded builders (`DefaultLifecycleStarter`, the default when no `-b` flag is passed).
+
+### 3.1 The `all` and `each` Phases
+
+The default Maven 4 lifecycle defines a top-level `all` phase that wraps the entire reactor, with `each`
+representing a single subproject's full build:
+
+```
+all                    ← reactor-scoped wrapper (children link)
+└── each               ← per-subproject scope
+    ├── validate / initialize
+    ├── build (sources → compile → ready → package)
+    ├── verify (unit-test, integration-test)
+    ├── install
+    └── deploy
+```
+
+The `all` phase carries a `children(all)` link, which the concurrent builder translates into DAG edges:
+- The root project's `before:all` fires first; every child project's `before:all` waits for it to complete.
+- The root project's `after:all` fires only after every child's `after:all` has completed.
+
+This means a plugin goal bound to `after:all` on the root project is **guaranteed to run once, after all
+reactor subprojects have finished their full build** — enforced by the DAG, not by a global lock.
+
+### 3.2 Solving the Aggregation Problems
+
+| Problem (section 2) | Concurrent builder solution |
+| :--- | :--- |
+| **CLI vs. lifecycle discrepancy** ([MNG-6336](https://github.com/apache/maven/issues/7529)) | Bind to `after:all` on the root project — runs once after the full reactor. The timing guarantee is enforced by the DAG regardless of whether the goal was invoked via CLI or lifecycle. Note: `<inherited>false</inherited>` is still required if the execution is declared in a parent POM, since the `before:`/`after:` system controls ordering, not inheritance. |
+| **Execution timing** (aggregator runs before children are built) | `after:all` fires after all children complete `all`, which includes `build`/`verify`. Artifact outputs are available. |
+| **`before:each` / `after:each`** | Goals bound to these phases run once per subproject, bracketing each module's full build. Useful for per-module setup/teardown. |
+
+### 3.3 Example: Aggregated Javadoc after the Full Reactor
+
+```xml
+<!-- requires -b concurrent; works with any model version -->
+<execution>
+  <id>aggregate-javadoc</id>
+  <goals><goal>aggregate</goal></goals>
+  <phase>after:all</phase>
+</execution>
+```
+
+This executes `maven-javadoc-plugin:aggregate` exactly once on the root project, after every subproject has
+completed its `all` phase (which includes compilation and packaging). No distribution submodule workaround
+required.
+
+### 3.4 Decomposing `aggregator = true`
+
+The `aggregator` flag is a **compound attribute** that bundles four distinct runtime behaviors. Maven 4 already provides orthogonal replacements for each:
+
+| Behavior | What `aggregator=true` does | Maven 4 replacement | Status |
+| :--- | :--- | :--- | :--- |
+| **Execution scope** | Run once on the root project, not per-module | Bind to `all` phase (`before:all`, `after:all`) | Available |
+| **Dependency resolution scope** | Resolve dependencies for all reactor projects | `@Resolution` annotation (per-field, per-project injection in `DefaultMavenPluginManager`) | Available |
+| **Exclusive locking** | Acquire reactor-wide write lock in `-T` builder | DAG edges enforce ordering in the concurrent builder — no lock needed | Available |
+| **Fork scope** | Forked lifecycles include all subprojects | Ordering constraints via `@After` (forks deprecated — see section 3.5) | Available |
+
+This decomposition means new Maven 4 plugins should **not** use `@Mojo(aggregator = true)`. Instead:
+
+- Bind the goal to `after:all` (or `before:all`) for reactor-scoped execution.
+- Use `@Resolution` fields for dependency injection, which is already orthogonal to aggregator.
+- Express ordering constraints via `@After` / `AfterLink` with `PROJECT`, `DEPENDENCIES`, or `CHILDREN` pointers.

Review Comment:
   💡 **`AfterLink` is a descriptor model class, not a plugin-author annotation**: Listing it alongside `@After` as "`@After` / `AfterLink`" suggests they are peer concepts that plugin authors use together. In reality, `AfterLink` is a Modello-generated descriptor model class — plugin authors only interact with `@After`. Consider removing the `/ AfterLink` part here to avoid confusion:
   
   ```suggestion
   - Express ordering constraints via `@After` with `PROJECT`, `DEPENDENCIES`, or `CHILDREN` type values.
   ```



##########
src/site/markdown/aggregator-goals.md:
##########
@@ -0,0 +1,221 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+# Aggregator Mojos and Reactor Lifecycle Specification
+
+## Overview
+
+Aggregation was introduced early in Maven 2 ([MNG-250](https://github.com/apache/maven/issues/2896)) to enable plugins to operate across an entire multi-module build reactor rather than on a single isolated module. It is exposed to plugin developers via the `@Mojo(aggregator = true)` annotation (or `@aggregator` in JavaDoc tag format).
+
+This document analyzes the current behavior, outlines historical shortcomings, and establishes a target design specification for refactoring aggregator goals in Maven ([MNG-7991](https://github.com/apache/maven/issues/8718)).
+
+---
+
+## 1. Current Aggregator Behavior
+
+Maven treats aggregator Mojos differently depending on whether they are invoked directly via the Command Line Interface (CLI) or bound to a build lifecycle phase in a POM.
+
+### 1.1 CLI Invocation (`mvn plugin:goal`)
+When an aggregating goal is invoked from the command line:
+1. **Task Segment Classification**: `DefaultLifecycleTaskSegmentCalculator` inspects the Mojo descriptor:
+   ```java
+   boolean aggregating = mojoDescriptor.isAggregator() || !mojoDescriptor.isProjectRequired();
+   ```
+2. **Aggregating Task Segment**: A distinct aggregating `TaskSegment` is created.
+3. **Execution on Root Project Only**: Aggregating task segments are restricted to the top-level project (`session.getTopLevelProject()`). Submodules in the reactor are skipped for this goal.
+   - In the **singlethreaded** and **multithreaded** builders, this filtering is performed by `BuildListCalculator`.
+   - In the **concurrent builder** (`-b concurrent`), it is performed by `BuildPlanExecutor` inside `buildInitialPlan()`:
+     ```java
+     Map<MavenProject, List<MavenProject>> projects = taskSegment.isAggregating()
+         ? Collections.singletonMap(rootProject, allProjects.get(rootProject))
+         : allProjects;
+     ```
+4. **Ordering**: Tasks are executed in CLI argument order — task segments are produced in CLI argument order with no automatic re-sorting. In `mvn clean install site:stage`, the non-aggregating `clean install` segment runs first (across all modules), then the aggregating `site:stage` segment runs once on the root. If the user writes `mvn site:stage install`, the aggregating segment runs first.
+
+### 1.2 Lifecycle-Bound Execution (`<phase>...</phase>`)
+When an aggregator Mojo is bound to a phase in a `pom.xml`:
+1. **Lifecycle Segment Classification**: Lifecycle phases (e.g. `package`, `verify`) produce standard non-aggregating `TaskSegment`s.
+2. **Per-Module Execution**: The goal is injected into the execution plan of **every module** in the reactor that inherits the plugin configuration.
+3. **Redundant Executions**: Unless the project author explicitly configures `<inherited>false</inherited>` on the plugin execution in the parent POM, the aggregator Mojo executes once for each module in the reactor.
+4. **Thread Locking (multithreaded builder only)**: When using the multithreaded builder (`-T`), `MojoExecutor` acquires an exclusive reactor-wide write lock (`aggregatorLock.writeLock()`) whenever an aggregator executes:
+   ```java
+   acquiredAggregatorLock = aggregator ? aggregatorLock.writeLock() : aggregatorLock.readLock();
+   ```
+   This means that executing an aggregator Mojo across multiple submodules repeatedly halts parallel build concurrency across all threads.
+
+   In the **concurrent builder** (`-b concurrent`), project-level locking is disabled entirely — the concurrent `MojoExecutor` overrides `useProjectLock()` to return `false`. The concurrent builder instead relies on the build plan DAG to enforce ordering between steps.
+
+### 1.3 Dependency Resolution
+When executing an aggregator Mojo:
+- `MojoExecutor.ensureDependenciesAreResolved` checks `mojoDescriptor.isAggregator()`.
+- If `true`, Maven resolves dependencies not only for the current project, but also across all projects in `session.getProjects()` matching the scopes requested by the Mojo.
+
+### 1.4 Forked Lifecycles
+When an aggregator Mojo declares `@Execute(phase = ...)` or `@Execute(goal = ...)`, the concurrent builder (`BuildPlanExecutor`) handles each case differently:
+- **`@Execute(phase = ...)`**: A forked plan is computed for the **current project only** (`Collections.singletonMap(step.project, ...)`), up to the specified phase. The main plan is appended after the forked plan as a DAG dependency.
+- **`@Execute(goal = ...)`**: A forked plan is computed for the current project, plus `step.project.getCollectedProjects()` if the mojo is also an aggregator. This means forking a non-aggregator goal targets only the current project; forking an aggregator goal spans the reactor tree rooted at the current project, which can lead to duplicated builds, redundant tests, and nested reactor re-executions.
+
+---
+
+## 2. Identified Shortcomings & Problem Space
+
+Over successive Maven releases, multiple discrepancies and pain points have been identified:
+
+1. **CLI vs. Lifecycle Discrepancy ([MNG-6336](https://github.com/apache/maven/issues/7529))**:
+   - Plugin authors annotate a goal with `@Mojo(aggregator = true)` with the expectation that Maven will execute it once per reactor.
+   - While CLI invocation honors this expectation (running only on root), lifecycle binding runs it on every module.
+   - Forcing users to configure `<inherited>false</inherited>` is error-prone, unintuitive, and breaks down in nested multi-module structures.
+
+2. **Execution Timing: Aggregate at Start vs. Aggregate at End**:
+   - In hierarchical builds, the root project is typically evaluated first in reactor order.
+   - Consequently, an aggregator bound to `package` or `verify` on the root module executes *before* child modules have compiled or packaged their artifacts.
+   - When the purpose of the aggregator is to combine artifacts or reports produced by child modules (e.g. aggregated Javadoc, Jacoco code coverage, distribution zip), executing on the root project at the start fails because child outputs do not yet exist.
+   - Projects often resort to artificial "distribution" child modules placed last in reactor order to work around this limitation.
+
+3. **Submodule Forking Issues ([MNG-7672](https://github.com/apache/maven/issues/8780), [MNG-7163](https://github.com/apache/maven/issues/8291), [MNG-2184](https://github.com/apache/maven/issues/4453))**:
+   - When an aggregator goal is executed within a submodule, forking triggers a build of the entire reactor from within that submodule, repeating already-completed goals.
+
+4. **Reporting and Site Generation Edge Cases**:
+   - The `maven-site-plugin` and `maven-reporting-exec` maintain separate aggregation abstractions for generating multi-module documentation and stage reports, leading to inconsistencies between CLI reporting and lifecycle documentation goals.
+
+---
+
+## 3. Maven 4 Concurrent Builder: `before:`/`after:` Phase System
+
+The Maven 4 concurrent builder introduces a first-class solution to several of the shortcomings above through
+the `before:` and `after:` phase prefixes and two special reactor-scoped phases: `all` and `each`.
+
+> **Note**: The `before:`/`after:` phase system and the `all`/`each` phases require the **concurrent builder**
+> (`ConcurrentLifecycleStarter`, activated via `-b concurrent`). They work with any POM model version
+> (4.0.0 or 4.1.0) — no model version upgrade is required. They are not supported by the legacy
+> singlethreaded or multithreaded builders (`DefaultLifecycleStarter`, the default when no `-b` flag is passed).
+
+### 3.1 The `all` and `each` Phases
+
+The default Maven 4 lifecycle defines a top-level `all` phase that wraps the entire reactor, with `each`
+representing a single subproject's full build:
+
+```
+all                    ← reactor-scoped wrapper (children link)
+└── each               ← per-subproject scope
+    ├── validate / initialize
+    ├── build (sources → compile → ready → package)
+    ├── verify (unit-test, integration-test)
+    ├── install
+    └── deploy
+```
+
+The `all` phase carries a `children(all)` link, which the concurrent builder translates into DAG edges:
+- The root project's `before:all` fires first; every child project's `before:all` waits for it to complete.
+- The root project's `after:all` fires only after every child's `after:all` has completed.
+
+This means a plugin goal bound to `after:all` on the root project is **guaranteed to run once, after all
+reactor subprojects have finished their full build** — enforced by the DAG, not by a global lock.
+
+### 3.2 Solving the Aggregation Problems
+
+| Problem (section 2) | Concurrent builder solution |
+| :--- | :--- |
+| **CLI vs. lifecycle discrepancy** ([MNG-6336](https://github.com/apache/maven/issues/7529)) | Bind to `after:all` on the root project — runs once after the full reactor. The timing guarantee is enforced by the DAG regardless of whether the goal was invoked via CLI or lifecycle. Note: `<inherited>false</inherited>` is still required if the execution is declared in a parent POM, since the `before:`/`after:` system controls ordering, not inheritance. |
+| **Execution timing** (aggregator runs before children are built) | `after:all` fires after all children complete `all`, which includes `build`/`verify`. Artifact outputs are available. |
+| **`before:each` / `after:each`** | Goals bound to these phases run once per subproject, bracketing each module's full build. Useful for per-module setup/teardown. |
+
+### 3.3 Example: Aggregated Javadoc after the Full Reactor
+
+```xml
+<!-- requires -b concurrent; works with any model version -->
+<execution>
+  <id>aggregate-javadoc</id>
+  <goals><goal>aggregate</goal></goals>
+  <phase>after:all</phase>
+</execution>
+```
+
+This executes `maven-javadoc-plugin:aggregate` exactly once on the root project, after every subproject has
+completed its `all` phase (which includes compilation and packaging). No distribution submodule workaround
+required.
+
+### 3.4 Decomposing `aggregator = true`
+
+The `aggregator` flag is a **compound attribute** that bundles four distinct runtime behaviors. Maven 4 already provides orthogonal replacements for each:
+
+| Behavior | What `aggregator=true` does | Maven 4 replacement | Status |
+| :--- | :--- | :--- | :--- |
+| **Execution scope** | Run once on the root project, not per-module | Bind to `all` phase (`before:all`, `after:all`) | Available |
+| **Dependency resolution scope** | Resolve dependencies for all reactor projects | `@Resolution` annotation (per-field, per-project injection in `DefaultMavenPluginManager`) | Available |
+| **Exclusive locking** | Acquire reactor-wide write lock in `-T` builder | DAG edges enforce ordering in the concurrent builder — no lock needed | Available |
+| **Fork scope** | Forked lifecycles include all subprojects | Ordering constraints via `@After` (forks deprecated — see section 3.5) | Available |
+
+This decomposition means new Maven 4 plugins should **not** use `@Mojo(aggregator = true)`. Instead:
+
+- Bind the goal to `after:all` (or `before:all`) for reactor-scoped execution.
+- Use `@Resolution` fields for dependency injection, which is already orthogonal to aggregator.
+- Express ordering constraints via `@After` / `AfterLink` with `PROJECT`, `DEPENDENCIES`, or `CHILDREN` pointers.
+
+For backward compatibility, existing plugin descriptors declaring `aggregator=true` will continue to work — the runtime maps the flag to the equivalent DAG constraints internally.
+
+### 3.5 Deprecating Forked Lifecycles (`@Execute`)
+
+Forked lifecycles (`@Execute(phase = ...)`) let a mojo fork a lifecycle phase in a separate execution context before running. Analysis of all Apache Maven plugins using `@Execute` shows that **none require fork isolation** — every use is purely an ordering guarantee:
+
+| Plugin | `@Execute` target | Actual need |
+| :--- | :--- | :--- |
+| maven-javadoc-plugin | `@Execute(phase = COMPILE)` | Compiled classes must exist |
+| maven-source-plugin | `@Execute(phase = GENERATE_SOURCES)` | Generated sources must exist |
+| maven-dependency-plugin | `@Execute(phase = TEST_COMPILE)` | Classes compiled for analysis |
+| maven-pmd-plugin | `@Execute(phase = TEST_COMPILE)`, `@Execute(goal = "pmd")` | Classes compiled; PMD report generated |
+| maven-surefire (report) | `@Execute(lifecycle = "surefire", phase = TEST)` | Test results must exist |
+
+Every case reduces to "phase X must have completed before I run" — a DAG edge, not an isolated execution context. The `-no-fork` mojo variants (`javadoc-no-fork`, `aggregate-no-fork`, `jxr-no-fork`) exist only to opt out of unwanted forking, confirming that the fork is the wrong abstraction.
+
+The Maven 4 replacement is `@After` constraints:
+
+```java
+// Instead of @Execute(phase = LifecyclePhase.COMPILE)
+@After(phase = "compile", scope = AfterLink.Pointer.PROJECT)
+
+// Instead of @Execute(goal = "pmd")
+@After(goal = "pmd:pmd")

Review Comment:
   ⚠️ **Non-compilable API usage — `goal` attribute does not exist**: `@After` has no `goal` attribute. The annotation only has `phase()`, `type()`, and `scope()`. This example won't compile.
   
   What was the intended equivalent here? If the goal is "PMD report must exist before I run", the ordering constraint would be expressed differently (e.g. `@After(phase = "test-compile", type = After.Type.PROJECT)` — or the intent may need a different API entirely).



##########
src/site/markdown/aggregator-goals.md:
##########
@@ -0,0 +1,221 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+# Aggregator Mojos and Reactor Lifecycle Specification
+
+## Overview
+
+Aggregation was introduced early in Maven 2 ([MNG-250](https://github.com/apache/maven/issues/2896)) to enable plugins to operate across an entire multi-module build reactor rather than on a single isolated module. It is exposed to plugin developers via the `@Mojo(aggregator = true)` annotation (or `@aggregator` in JavaDoc tag format).
+
+This document analyzes the current behavior, outlines historical shortcomings, and establishes a target design specification for refactoring aggregator goals in Maven ([MNG-7991](https://github.com/apache/maven/issues/8718)).
+
+---
+
+## 1. Current Aggregator Behavior
+
+Maven treats aggregator Mojos differently depending on whether they are invoked directly via the Command Line Interface (CLI) or bound to a build lifecycle phase in a POM.
+
+### 1.1 CLI Invocation (`mvn plugin:goal`)
+When an aggregating goal is invoked from the command line:
+1. **Task Segment Classification**: `DefaultLifecycleTaskSegmentCalculator` inspects the Mojo descriptor:
+   ```java
+   boolean aggregating = mojoDescriptor.isAggregator() || !mojoDescriptor.isProjectRequired();
+   ```
+2. **Aggregating Task Segment**: A distinct aggregating `TaskSegment` is created.
+3. **Execution on Root Project Only**: Aggregating task segments are restricted to the top-level project (`session.getTopLevelProject()`). Submodules in the reactor are skipped for this goal.
+   - In the **singlethreaded** and **multithreaded** builders, this filtering is performed by `BuildListCalculator`.
+   - In the **concurrent builder** (`-b concurrent`), it is performed by `BuildPlanExecutor` inside `buildInitialPlan()`:
+     ```java
+     Map<MavenProject, List<MavenProject>> projects = taskSegment.isAggregating()
+         ? Collections.singletonMap(rootProject, allProjects.get(rootProject))
+         : allProjects;
+     ```
+4. **Ordering**: Tasks are executed in CLI argument order — task segments are produced in CLI argument order with no automatic re-sorting. In `mvn clean install site:stage`, the non-aggregating `clean install` segment runs first (across all modules), then the aggregating `site:stage` segment runs once on the root. If the user writes `mvn site:stage install`, the aggregating segment runs first.
+
+### 1.2 Lifecycle-Bound Execution (`<phase>...</phase>`)
+When an aggregator Mojo is bound to a phase in a `pom.xml`:
+1. **Lifecycle Segment Classification**: Lifecycle phases (e.g. `package`, `verify`) produce standard non-aggregating `TaskSegment`s.
+2. **Per-Module Execution**: The goal is injected into the execution plan of **every module** in the reactor that inherits the plugin configuration.
+3. **Redundant Executions**: Unless the project author explicitly configures `<inherited>false</inherited>` on the plugin execution in the parent POM, the aggregator Mojo executes once for each module in the reactor.
+4. **Thread Locking (multithreaded builder only)**: When using the multithreaded builder (`-T`), `MojoExecutor` acquires an exclusive reactor-wide write lock (`aggregatorLock.writeLock()`) whenever an aggregator executes:
+   ```java
+   acquiredAggregatorLock = aggregator ? aggregatorLock.writeLock() : aggregatorLock.readLock();
+   ```
+   This means that executing an aggregator Mojo across multiple submodules repeatedly halts parallel build concurrency across all threads.
+
+   In the **concurrent builder** (`-b concurrent`), project-level locking is disabled entirely — the concurrent `MojoExecutor` overrides `useProjectLock()` to return `false`. The concurrent builder instead relies on the build plan DAG to enforce ordering between steps.
+
+### 1.3 Dependency Resolution
+When executing an aggregator Mojo:
+- `MojoExecutor.ensureDependenciesAreResolved` checks `mojoDescriptor.isAggregator()`.
+- If `true`, Maven resolves dependencies not only for the current project, but also across all projects in `session.getProjects()` matching the scopes requested by the Mojo.
+
+### 1.4 Forked Lifecycles
+When an aggregator Mojo declares `@Execute(phase = ...)` or `@Execute(goal = ...)`, the concurrent builder (`BuildPlanExecutor`) handles each case differently:
+- **`@Execute(phase = ...)`**: A forked plan is computed for the **current project only** (`Collections.singletonMap(step.project, ...)`), up to the specified phase. The main plan is appended after the forked plan as a DAG dependency.
+- **`@Execute(goal = ...)`**: A forked plan is computed for the current project, plus `step.project.getCollectedProjects()` if the mojo is also an aggregator. This means forking a non-aggregator goal targets only the current project; forking an aggregator goal spans the reactor tree rooted at the current project, which can lead to duplicated builds, redundant tests, and nested reactor re-executions.
+
+---
+
+## 2. Identified Shortcomings & Problem Space
+
+Over successive Maven releases, multiple discrepancies and pain points have been identified:
+
+1. **CLI vs. Lifecycle Discrepancy ([MNG-6336](https://github.com/apache/maven/issues/7529))**:
+   - Plugin authors annotate a goal with `@Mojo(aggregator = true)` with the expectation that Maven will execute it once per reactor.
+   - While CLI invocation honors this expectation (running only on root), lifecycle binding runs it on every module.
+   - Forcing users to configure `<inherited>false</inherited>` is error-prone, unintuitive, and breaks down in nested multi-module structures.
+
+2. **Execution Timing: Aggregate at Start vs. Aggregate at End**:
+   - In hierarchical builds, the root project is typically evaluated first in reactor order.
+   - Consequently, an aggregator bound to `package` or `verify` on the root module executes *before* child modules have compiled or packaged their artifacts.
+   - When the purpose of the aggregator is to combine artifacts or reports produced by child modules (e.g. aggregated Javadoc, Jacoco code coverage, distribution zip), executing on the root project at the start fails because child outputs do not yet exist.
+   - Projects often resort to artificial "distribution" child modules placed last in reactor order to work around this limitation.
+
+3. **Submodule Forking Issues ([MNG-7672](https://github.com/apache/maven/issues/8780), [MNG-7163](https://github.com/apache/maven/issues/8291), [MNG-2184](https://github.com/apache/maven/issues/4453))**:
+   - When an aggregator goal is executed within a submodule, forking triggers a build of the entire reactor from within that submodule, repeating already-completed goals.
+
+4. **Reporting and Site Generation Edge Cases**:
+   - The `maven-site-plugin` and `maven-reporting-exec` maintain separate aggregation abstractions for generating multi-module documentation and stage reports, leading to inconsistencies between CLI reporting and lifecycle documentation goals.
+
+---
+
+## 3. Maven 4 Concurrent Builder: `before:`/`after:` Phase System
+
+The Maven 4 concurrent builder introduces a first-class solution to several of the shortcomings above through
+the `before:` and `after:` phase prefixes and two special reactor-scoped phases: `all` and `each`.
+
+> **Note**: The `before:`/`after:` phase system and the `all`/`each` phases require the **concurrent builder**
+> (`ConcurrentLifecycleStarter`, activated via `-b concurrent`). They work with any POM model version
+> (4.0.0 or 4.1.0) — no model version upgrade is required. They are not supported by the legacy
+> singlethreaded or multithreaded builders (`DefaultLifecycleStarter`, the default when no `-b` flag is passed).
+
+### 3.1 The `all` and `each` Phases
+
+The default Maven 4 lifecycle defines a top-level `all` phase that wraps the entire reactor, with `each`
+representing a single subproject's full build:
+
+```
+all                    ← reactor-scoped wrapper (children link)
+└── each               ← per-subproject scope
+    ├── validate / initialize
+    ├── build (sources → compile → ready → package)
+    ├── verify (unit-test, integration-test)
+    ├── install
+    └── deploy
+```
+
+The `all` phase carries a `children(all)` link, which the concurrent builder translates into DAG edges:
+- The root project's `before:all` fires first; every child project's `before:all` waits for it to complete.
+- The root project's `after:all` fires only after every child's `after:all` has completed.
+
+This means a plugin goal bound to `after:all` on the root project is **guaranteed to run once, after all
+reactor subprojects have finished their full build** — enforced by the DAG, not by a global lock.
+
+### 3.2 Solving the Aggregation Problems
+
+| Problem (section 2) | Concurrent builder solution |
+| :--- | :--- |
+| **CLI vs. lifecycle discrepancy** ([MNG-6336](https://github.com/apache/maven/issues/7529)) | Bind to `after:all` on the root project — runs once after the full reactor. The timing guarantee is enforced by the DAG regardless of whether the goal was invoked via CLI or lifecycle. Note: `<inherited>false</inherited>` is still required if the execution is declared in a parent POM, since the `before:`/`after:` system controls ordering, not inheritance. |
+| **Execution timing** (aggregator runs before children are built) | `after:all` fires after all children complete `all`, which includes `build`/`verify`. Artifact outputs are available. |
+| **`before:each` / `after:each`** | Goals bound to these phases run once per subproject, bracketing each module's full build. Useful for per-module setup/teardown. |
+
+### 3.3 Example: Aggregated Javadoc after the Full Reactor
+
+```xml
+<!-- requires -b concurrent; works with any model version -->
+<execution>
+  <id>aggregate-javadoc</id>
+  <goals><goal>aggregate</goal></goals>
+  <phase>after:all</phase>
+</execution>
+```
+
+This executes `maven-javadoc-plugin:aggregate` exactly once on the root project, after every subproject has
+completed its `all` phase (which includes compilation and packaging). No distribution submodule workaround
+required.
+
+### 3.4 Decomposing `aggregator = true`
+
+The `aggregator` flag is a **compound attribute** that bundles four distinct runtime behaviors. Maven 4 already provides orthogonal replacements for each:
+
+| Behavior | What `aggregator=true` does | Maven 4 replacement | Status |
+| :--- | :--- | :--- | :--- |
+| **Execution scope** | Run once on the root project, not per-module | Bind to `all` phase (`before:all`, `after:all`) | Available |
+| **Dependency resolution scope** | Resolve dependencies for all reactor projects | `@Resolution` annotation (per-field, per-project injection in `DefaultMavenPluginManager`) | Available |
+| **Exclusive locking** | Acquire reactor-wide write lock in `-T` builder | DAG edges enforce ordering in the concurrent builder — no lock needed | Available |
+| **Fork scope** | Forked lifecycles include all subprojects | Ordering constraints via `@After` (forks deprecated — see section 3.5) | Available |
+
+This decomposition means new Maven 4 plugins should **not** use `@Mojo(aggregator = true)`. Instead:
+
+- Bind the goal to `after:all` (or `before:all`) for reactor-scoped execution.
+- Use `@Resolution` fields for dependency injection, which is already orthogonal to aggregator.
+- Express ordering constraints via `@After` / `AfterLink` with `PROJECT`, `DEPENDENCIES`, or `CHILDREN` pointers.
+
+For backward compatibility, existing plugin descriptors declaring `aggregator=true` will continue to work — the runtime maps the flag to the equivalent DAG constraints internally.
+
+### 3.5 Deprecating Forked Lifecycles (`@Execute`)
+
+Forked lifecycles (`@Execute(phase = ...)`) let a mojo fork a lifecycle phase in a separate execution context before running. Analysis of all Apache Maven plugins using `@Execute` shows that **none require fork isolation** — every use is purely an ordering guarantee:
+
+| Plugin | `@Execute` target | Actual need |
+| :--- | :--- | :--- |
+| maven-javadoc-plugin | `@Execute(phase = COMPILE)` | Compiled classes must exist |
+| maven-source-plugin | `@Execute(phase = GENERATE_SOURCES)` | Generated sources must exist |
+| maven-dependency-plugin | `@Execute(phase = TEST_COMPILE)` | Classes compiled for analysis |
+| maven-pmd-plugin | `@Execute(phase = TEST_COMPILE)`, `@Execute(goal = "pmd")` | Classes compiled; PMD report generated |
+| maven-surefire (report) | `@Execute(lifecycle = "surefire", phase = TEST)` | Test results must exist |
+
+Every case reduces to "phase X must have completed before I run" — a DAG edge, not an isolated execution context. The `-no-fork` mojo variants (`javadoc-no-fork`, `aggregate-no-fork`, `jxr-no-fork`) exist only to opt out of unwanted forking, confirming that the fork is the wrong abstraction.
+
+The Maven 4 replacement is `@After` constraints:
+
+```java
+// Instead of @Execute(phase = LifecyclePhase.COMPILE)
+@After(phase = "compile", scope = AfterLink.Pointer.PROJECT)

Review Comment:
   ⚠️ **Non-compilable API usage — wrong attribute and wrong enum**: The `@After` annotation does not have a `scope` attribute, and `AfterLink.Pointer` is not a valid enum. The correct attribute is `type` and the correct enum is `After.Type`. This example won't compile as written:
   
   ```java
   // WRONG — does not compile
   @After(phase = "compile", scope = AfterLink.Pointer.PROJECT)
   ```
   
   Should be:
   
   ```suggestion
   @After(phase = "compile", type = After.Type.PROJECT)
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "Tarun4201 (via GitHub)" <gi...@apache.org>.
Tarun4201 commented on PR #13330:
URL: https://github.com/apache/maven/pull/13330#issuecomment-6253403425

   Thanks [@gnodet](https://github.com/gnodet)! Updated in commit 5d0ea5956f:
   
   Updated §3.4 to frame before:/after: phases and @After as the replacement path for aggregator, showing all v4 replacements as available today.
   Added §3.5 documenting the rationale and @After replacements for deprecating forked lifecycles (@Execute).
   Added §3.6 migration roadmap spanning Maven 4.0 to 5.x.
   Added issue reference #13529 to §4.
   Cleaned up the stale comment in TouchMojo.java.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "Tarun4201 (via GitHub)" <gi...@apache.org>.
Tarun4201 commented on PR #13330:
URL: https://github.com/apache/maven/pull/13330#issuecomment-6231159816

   Hi @slachiewicz, all review feedback from @gnodet and @gnodet-bot has been addressed and approved. The branch is clean, all checks are passing, and the PR is ready to merge. Could you please take a look when you have a moment? Thank you!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "slachiewicz (via GitHub)" <gi...@apache.org>.
slachiewicz commented on PR #13330:
URL: https://github.com/apache/maven/pull/13330#issuecomment-6231749637

   Please apply above suggestions 


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "gnodet-bot (via GitHub)" <gi...@apache.org>.
gnodet-bot commented on code in PR #13330:
URL: https://github.com/apache/maven/pull/13330#discussion_r4175878390


##########
api/maven-api-plugin/src/main/mdo/plugin.mdo:
##########
@@ -308,8 +308,9 @@ under the License.
           <version>1.0.0+</version>
           <type>boolean</type>
           <description>
-            Flags this Mojo to run it in a multi-module way, i.e. aggregate the build with the set of projects
-            listed as modules.
+            Flags this Mojo to run in a multi-module aggregating manner across the reactor.
+            When invoked directly via CLI, it executes only once on the top-level project.
+            When bound to a lifecycle phase, it executes on each module unless inheritance is explicitly disabled.

Review Comment:
   💡 The rest of this PR standardizes on "subproject" per MNG-8210, but this line still says "each module". Align for consistency.
   
   ```suggestion
               When bound to a lifecycle phase, it executes on each subproject unless inheritance is explicitly disabled.
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "Tarun4201 (via GitHub)" <gi...@apache.org>.
Tarun4201 commented on PR #13330:
URL: https://github.com/apache/maven/pull/13330#issuecomment-5981832595

   Updated all issue references across `aggregator-goals.md` to point to the active GitHub Issues instead of Jira in commit [`6405b79`](https://github.com/apache/maven/pull/13330/commits/6405b799fb1f81d52033bc6f3df3b8fe70eb9226). Thanks for the note!
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [MNG-7991] Document aggregator goal lifecycle behavior and design spe… [maven]

Posted by "Tarun4201 (via GitHub)" <gi...@apache.org>.
Tarun4201 commented on PR #13330:
URL: https://github.com/apache/maven/pull/13330#issuecomment-6253602086

   Thanks @gnodet and @gnodet-bot! All findings have been addressed in commit `d418eca533`:
   
   - **§3.5 `@After` Code Examples**: Updated to valid Maven 4 API (`type = After.Type.PROJECT` and phase-based `@After(phase = "test-compile", type = After.Type.PROJECT)`).
   - **§3.4 Backward Compatibility**: Reframed to state that `aggregator=true` remains supported in Maven 4.x with the target for internal DAG mapping in Maven 5.x (aligned with §3.6 roadmap).
   - **§3.4 Ordering Constraints**: Clarified to mention only `@After` with `PROJECT`, `DEPENDENCIES`, or `CHILDREN` type values.
   - **`TouchMojo.java`**: Restored the comment caveat regarding handcrafted descriptor until tooling generation is available.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@maven.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org