Summary
The plugin is not compatible with Gradle's Project Isolation. This issue tracks making it Isolation-capable. The enabling refactor is tracked separately in #848 (migration to an explicit aggregator setup); this issue defines the goal and the acceptance bar.
Why we're incompatible today
Project Isolation forbids a project's build logic from reading or mutating another project's state at configuration time. Our multi-project support does exactly that: the plugin is applied once at the root, and the root then iterates its subprojects, configures tasks on them, and wires their outputs back together. That cross-project configuration is the core violation. It is also — less obviously — the mechanism that currently establishes the dependency edges from the aggregate root component to each subproject, so any redesign has to consciously preserve that behavior rather than inherit it for free.
What has to change (architecturally)
- No cross-project access at configuration time. Each project must configure only itself. The plugin therefore moves to a per-project application model every project that should contribute a BOM applies the plugin and, in its own configuration, publishes its Direct BOM as a consumable artifact (a variant).
- Aggregation stays variant/artifact-based. The aggregate task consumes member BOMs through normal variant-aware dependency resolution over a resolvable configuration. This is the path Gradle's configuration cache is designed to serialize, and it carries real task dependencies, so members are guaranteed built before aggregation runs.
- The aggregate task owns the root -> member dependency edges, synthesized from the members it actually merged — not inherited from cross-project traversal.
- A shared mutable BuildService is explicitly NOT the mechanism. A registry that projects write to at configuration time and the aggregator reads back fails on two counts: its accumulated state is not part of the service parameters, so it is not restored on a configuration-cache hit; and it assumes every subproject has been configured before the task graph is built — precisely the guarantee Isolation and configuration-on-demand remove.
Scope boundary
A structure-blind "apply to everything and aggregate the whole tree" workflow (e.g. a CI init script that iterates all projects) is inherently cross-project and therefore incompatible with Isolation by construction. That convenience remains a user-side, Isolation-disabled concern; the plugin itself targets Isolation-correctness for the explicit, per-project setup.
Acceptance criteria
Notes
Summary
The plugin is not compatible with Gradle's Project Isolation. This issue tracks making it Isolation-capable. The enabling refactor is tracked separately in #848 (migration to an explicit aggregator setup); this issue defines the goal and the acceptance bar.
Why we're incompatible today
Project Isolation forbids a project's build logic from reading or mutating another project's state at configuration time. Our multi-project support does exactly that: the plugin is applied once at the root, and the root then iterates its subprojects, configures tasks on them, and wires their outputs back together. That cross-project configuration is the core violation. It is also — less obviously — the mechanism that currently establishes the dependency edges from the aggregate root component to each subproject, so any redesign has to consciously preserve that behavior rather than inherit it for free.
What has to change (architecturally)
Scope boundary
A structure-blind "apply to everything and aggregate the whole tree" workflow (e.g. a CI init script that iterates all projects) is inherently cross-project and therefore incompatible with Isolation by construction. That convenience remains a user-side, Isolation-disabled concern; the plugin itself targets Isolation-correctness for the explicit, per-project setup.
Acceptance criteria
-Dorg.gradle.unsafe.isolated-projects=true), applies the plugin per project, declares aggregation members explicitly, runs the aggregate task, and asserts: build success, all members present, and the root->member edges. Version-gated to Gradle versions where Isolation is stable.--configuration-cachecoverage stays green.Notes