Repository: gradle/gradle
Pull request #32288: Always desugar all attributes for local variants
Author: login: tresat
Labels: name: a:bug
description: This doesn't work as expected
name: in:dependency-declarations
description: variant notation attributes capability substitution
name: re:comprehensibility
description: reasonable errors and warnings, clear dsl, mental overload
Created: 2025-02-03T22:59:50Z | Merged: 2025-03-19T20:05:46Z

This avoids potential classloader issues where identically named types are not `equals()`, preventing confusion when retrieving attributes.   This is done by ensuring that attributes retrieved from the `ResolvedVariantResult` for both local and external dependencies are treated similarly, by always desugaring attributes, to match the status of external deps.

This is stacked on top of https://github.com/gradle/gradle/pull/32277, which should be merged first.



Diff:
diff --git a/platforms/core-configuration/configuration-cache/src/main/kotlin/org/gradle/internal/cc/impl/DefaultConfigurationCacheIO.kt b/platforms/core-configuration/configuration-cache/src/main/kotlin/org/gradle/internal/cc/impl/DefaultConfigurationCacheIO.kt
index ca56a994c5cf2..34b104d5b6435 100644
--- a/platforms/core-configuration/configuration-cache/src/main/kotlin/org/gradle/internal/cc/impl/DefaultConfigurationCacheIO.kt
+++ b/platforms/core-configuration/configuration-cache/src/main/kotlin/org/gradle/internal/cc/impl/DefaultConfigurationCacheIO.kt
@@ -669,6 +669,7 @@ class DefaultConfigurationCacheIO internal constructor(
             parallelStore = startParameter.isParallelStore,
             parallelLoad = startParameter.isParallelLoad,
             problems = service(),
+            attributeDesugaring = service(),
         )
 
     private
diff --git a/platforms/core-configuration/configuration-cache/src/main/kotlin/org/gradle/internal/cc/impl/serialize/Codecs.kt b/platforms/core-configuration/configuration-cache/src/main/kotlin/org/gradle/internal/cc/impl/serialize/Codecs.kt
index 220a43de67cf9..7b04747abf542 100644
--- a/platforms/core-configuration/configuration-cache/src/main/kotlin/org/gradle/internal/cc/impl/serialize/Codecs.kt
+++ b/platforms/core-configuration/configuration-cache/src/main/kotlin/org/gradle/internal/cc/impl/serialize/Codecs.kt
@@ -26,6 +26,7 @@ import org.gradle.api.internal.artifacts.transform.TransformActionScheme
 import org.gradle.api.internal.artifacts.transform.TransformParameterScheme
 import org.gradle.api.internal.artifacts.transform.TransformStepNode
 import org.gradle.api.internal.artifacts.transform.TransformStepNodeFactory
+import org.gradle.api.internal.attributes.AttributeDesugaring
 import org.gradle.api.internal.attributes.AttributesFactory
 import org.gradle.api.internal.file.FileCollectionFactory
 import org.gradle.api.internal.file.FileFactory
@@ -163,6 +164,7 @@ class Codecs(
     parameterScheme: TransformParameterScheme,
     actionScheme: TransformActionScheme,
     attributesFactory: AttributesFactory,
+    attributeDesugaring: AttributeDesugaring,
     valueSourceProviderFactory: ValueSourceProviderFactory,
     calculatedValueContainerFactory: CalculatedValueContainerFactory,
     patternSetFactory: Factory<PatternSet>,
@@ -207,7 +209,7 @@ class Codecs(
             bind(SerializedLambdaParametersCheckingCodec)
 
             // Dependency management types
-            bind(ArtifactCollectionCodec(calculatedValueContainerFactory, artifactSetConverter))
+            bind(ArtifactCollectionCodec(calculatedValueContainerFactory, artifactSetConverter, attributeDesugaring))
             bind(ImmutableAttributesCodec(attributesFactory, managedFactoryRegistry))
             bind(AttributeContainerCodec(attributesFactory, managedFactoryRegistry))
             bind(ComponentVariantIdentifierCodec)
diff --git a/platforms/core-configuration/configuration-cache/src/test/kotlin/org/gradle/internal/cc/impl/serialization/codecs/AbstractUserTypeCodecTest.kt b/platforms/core-configuration/configuration-cache/src/test/kotlin/org/gradle/internal/cc/impl/serialization/codecs/AbstractUserTypeCodecTest.kt
index fce21c16ccf57..71fe936fc6fd0 100644
--- a/platforms/core-configuration/configuration-cache/src/test/kotlin/org/gradle/internal/cc/impl/serialization/codecs/AbstractUserTypeCodecTest.kt
+++ b/platforms/core-configuration/configuration-cache/src/test/kotlin/org/gradle/internal/cc/impl/serialization/codecs/AbstractUserTypeCodecTest.kt
@@ -189,6 +189,7 @@ abstract class AbstractUserTypeCodecTest {
         flowProviders = mock(),
         transformStepNodeFactory = mock(),
         problems = mock(),
+        attributeDesugaring = mock(),
     )
 }
 
diff --git a/platforms/core-configuration/dependency-management-serialization-codecs/src/main/kotlin/org/gradle/internal/serialize/codecs/dm/ArtifactCollectionCodec.kt b/platforms/core-configuration/dependency-management-serialization-codecs/src/main/kotlin/org/gradle/internal/serialize/codecs/dm/ArtifactCollectionCodec.kt
index 1362d062f49f6..4c9a76f471ca3 100644
--- a/platforms/core-configuration/dependency-management-serialization-codecs/src/main/kotlin/org/gradle/internal/serialize/codecs/dm/ArtifactCollectionCodec.kt
+++ b/platforms/core-configuration/dependency-management-serialization-codecs/src/main/kotlin/org/gradle/internal/serialize/codecs/dm/ArtifactCollectionCodec.kt
@@ -17,7 +17,6 @@
 package org.gradle.internal.serialize.codecs.dm
 
 import org.gradle.api.artifacts.component.ComponentArtifactIdentifier
-import org.gradle.api.attributes.AttributeContainer
 import org.gradle.api.internal.artifacts.configurations.ArtifactCollectionInternal
 import org.gradle.api.internal.artifacts.configurations.DefaultArtifactCollection
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSetToFileCollectionFactory
@@ -25,23 +24,26 @@ import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.Artif
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedArtifactSet
 import org.gradle.api.internal.artifacts.transform.TransformedArtifactSet
+import org.gradle.api.internal.attributes.AttributeDesugaring
+import org.gradle.api.internal.attributes.ImmutableAttributes
 import org.gradle.api.internal.file.FileCollectionInternal
 import org.gradle.api.internal.file.FileCollectionStructureVisitor
+import org.gradle.internal.DisplayName
+import org.gradle.internal.component.external.model.ImmutableCapabilities
 import org.gradle.internal.extensions.stdlib.uncheckedCast
+import org.gradle.internal.model.CalculatedValueContainerFactory
 import org.gradle.internal.serialize.graph.Codec
 import org.gradle.internal.serialize.graph.ReadContext
 import org.gradle.internal.serialize.graph.WriteContext
 import org.gradle.internal.serialize.graph.readList
 import org.gradle.internal.serialize.graph.writeCollection
-import org.gradle.internal.DisplayName
-import org.gradle.internal.component.external.model.ImmutableCapabilities
-import org.gradle.internal.model.CalculatedValueContainerFactory
 import java.io.File
 
 
 class ArtifactCollectionCodec(
     private val calculatedValueContainerFactory: CalculatedValueContainerFactory,
-    private val artifactSetConverter: ArtifactSetToFileCollectionFactory
+    private val artifactSetConverter: ArtifactSetToFileCollectionFactory,
+    private val attributeDesugaring: AttributeDesugaring,
 ) : Codec<ArtifactCollectionInternal> {
 
     override suspend fun WriteContext.encode(value: ArtifactCollectionInternal) {
@@ -67,7 +69,7 @@ class ArtifactCollectionCodec(
                 }
             }
         )
-        return DefaultArtifactCollection(files, lenient, artifactSetConverter.resolutionHost(displayName), calculatedValueContainerFactory)
+        return DefaultArtifactCollection(files, lenient, artifactSetConverter.resolutionHost(displayName), calculatedValueContainerFactory, attributeDesugaring)
     }
 }
 
@@ -75,7 +77,7 @@ class ArtifactCollectionCodec(
 private
 data class FixedFileArtifactSpec(
     val id: ComponentArtifactIdentifier,
-    val variantAttributes: AttributeContainer,
+    val variantAttributes: ImmutableAttributes,
     val capabilities: ImmutableCapabilities,
     val variantDisplayName: DisplayName,
     val file: File
@@ -108,7 +110,7 @@ class CollectingArtifactVisitor : ArtifactVisitor {
         elements.add(failure)
     }
 
-    override fun visitArtifact(variantName: DisplayName, variantAttributes: AttributeContainer, capabilities: ImmutableCapabilities, artifact: ResolvableArtifact) {
+    override fun visitArtifact(variantName: DisplayName, variantAttributes: ImmutableAttributes, capabilities: ImmutableCapabilities, artifact: ResolvableArtifact) {
         if (artifacts.add(artifact)) {
             elements.add(FixedFileArtifactSpec(artifact.id, variantAttributes, capabilities, variantName, artifact.file))
         }
diff --git a/platforms/ide/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/EclipseDependenciesCreator.java b/platforms/ide/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/EclipseDependenciesCreator.java
index 14d3668e704a5..063658e71edc9 100644
--- a/platforms/ide/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/EclipseDependenciesCreator.java
+++ b/platforms/ide/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/EclipseDependenciesCreator.java
@@ -115,8 +115,7 @@ public void visitProjectDependency(ResolvedArtifactResult artifact, boolean test
             if (componentIdentifier.equals(currentProjectId)) {
                 return;
             }
-            LibraryElements libraryElements = artifact.getVariant().getAttributes().getAttribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE);
-            if (libraryElements == null || !libraryElements.getName().equals(LibraryElements.JAR)) {
+            if (isNotJar(artifact)) {
                 return;
             }
             ComponentArtifactIdentifier artifactId = artifact.getId();
@@ -256,4 +255,10 @@ private AbstractLibrary createLibraryEntry(File binary, File source, File javado
             return out;
         }
     }
+
+    private static boolean isNotJar(ResolvedArtifactResult artifact) {
+        LibraryElements libraryElements = artifact.getVariant().getAttributes().getAttribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE);
+        return libraryElements == null
+            || !libraryElements.getName().equals(LibraryElements.JAR);
+    }
 }
diff --git a/platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactSelectionIntegrationTest.groovy b/platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactSelectionIntegrationTest.groovy
index ec6d9cadedfa8..f90a9cafb3a9f 100644
--- a/platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactSelectionIntegrationTest.groovy
+++ b/platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactSelectionIntegrationTest.groovy
@@ -53,11 +53,11 @@ class ArtifactSelectionIntegrationTest extends AbstractHttpDependencyResolutionT
     def "selects artifacts and files whose format matches the requested"() {
         given:
         def m1 = ivyHttpRepo.module('org', 'test', '1.0')
-                    .artifact(name: 'some-jar', type: 'jar')
-                    .publish()
+            .artifact(name: 'some-jar', type: 'jar')
+            .publish()
         def m2 = ivyHttpRepo.module('org', 'test2', '1.0')
-                    .artifact(name: 'some-classes', type: 'classes')
-                    .publish()
+            .artifact(name: 'some-classes', type: 'classes')
+            .publish()
 
         settingsFile << """
             include 'lib'
@@ -185,11 +185,11 @@ class ArtifactSelectionIntegrationTest extends AbstractHttpDependencyResolutionT
     def "can create a view that selects different artifacts from the same dependency graph"() {
         given:
         def m1 = ivyHttpRepo.module('org', 'test', '1.0')
-                    .artifact(name: 'some-jar', type: 'jar')
-                    .publish()
+            .artifact(name: 'some-jar', type: 'jar')
+            .publish()
         def m2 = ivyHttpRepo.module('org', 'test2', '1.0')
-                    .artifact(name: 'some-classes', type: 'classes')
-                    .publish()
+            .artifact(name: 'some-classes', type: 'classes')
+            .publish()
 
         settingsFile << """
             include 'lib'
@@ -751,11 +751,11 @@ class ArtifactSelectionIntegrationTest extends AbstractHttpDependencyResolutionT
     def "can query the content of view before task graph is calculated"() {
         given:
         def m1 = ivyHttpRepo.module('org', 'test', '1.0')
-                    .artifact(name: 'some-jar', type: 'jar')
-                    .publish()
+            .artifact(name: 'some-jar', type: 'jar')
+            .publish()
         def m2 = ivyHttpRepo.module('org', 'test2', '1.0')
-                    .artifact(name: 'some-classes', type: 'classes')
-                    .publish()
+            .artifact(name: 'some-classes', type: 'classes')
+            .publish()
 
         settingsFile << """
             include 'lib'
diff --git a/platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolutionResultApiIntegrationTest.groovy b/platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolutionResultApiIntegrationTest.groovy
index 698d5e04825fe..7af989591a995 100644
--- a/platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolutionResultApiIntegrationTest.groovy
+++ b/platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolutionResultApiIntegrationTest.groovy
@@ -1076,4 +1076,42 @@ testRuntimeClasspath
         expect:
         succeeds("traverse")
     }
+
+    def "attributes on root variant can be requested using Stringly or strongly-typed values"() {
+        settingsFile << """
+            include("producer")
+        """
+
+        file("producer/build.gradle") << """
+            plugins {
+                id("java-library")
+            }
+        """
+
+        buildFile << """
+            plugins {
+                id("java-library")
+            }
+
+            dependencies {
+                implementation project(":producer")
+            }
+
+            task resolve {
+                def root = configurations.runtimeClasspath.incoming.resolutionResult.rootVariant
+
+                doLast {
+                    def usage = root.get().attributes.getAttribute(Usage.USAGE_ATTRIBUTE)
+                    assert Usage.class.isAssignableFrom(usage.class)
+                    assert usage.name == "java-runtime"
+
+                    def usageAsString = root.get().attributes.getAttribute(Attribute.of(Usage.USAGE_ATTRIBUTE.name, String.class))
+                    assert usageAsString == "java-runtime"
+                }
+            }
+        """
+
+        expect:
+        succeeds("resolve")
+    }
 }
diff --git a/platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedArtifactsApiIntegrationTest.groovy b/platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedArtifactsApiIntegrationTest.groovy
index c905f6586f380..e6e52f26e6b1b 100644
--- a/platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedArtifactsApiIntegrationTest.groovy
+++ b/platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolvedArtifactsApiIntegrationTest.groovy
@@ -1176,6 +1176,82 @@ Searched in the following locations:
         result.assertTasksExecuted(":c:jar1", ":resolveLenient")
     }
 
+    def "attributes on incoming artifact variants can be requested using Stringly or strongly-typed values"() {
+        settingsFile << """
+            include("producer")
+        """
+
+        file("producer/build.gradle") << """
+            plugins {
+                id("java-library")
+            }
+        """
+
+        buildFile << """
+            plugins {
+                id("java-library")
+            }
+
+            dependencies {
+                implementation project(":producer")
+            }
+
+            task resolve {
+                def artifacts = configurations.runtimeClasspath.incoming.artifacts
+
+                doLast {
+                    def usage = artifacts.artifacts[0].variant.attributes.getAttribute(Usage.USAGE_ATTRIBUTE)
+                    assert Usage.class.isAssignableFrom(usage.class)
+                    assert usage.name == "java-runtime"
+
+                    def usageAsString = artifacts.artifacts[0].variant.attributes.getAttribute(Attribute.of(Usage.USAGE_ATTRIBUTE.name, String.class))
+                    assert usageAsString == "java-runtime"
+                }
+            }
+        """
+
+        expect:
+        succeeds("resolve")
+    }
+
+    def "attributes on incoming artifact resolved artifacts variants can be requested using Stringly or strongly-typed values"() {
+        settingsFile << """
+            include("producer")
+        """
+
+        file("producer/build.gradle") << """
+            plugins {
+                id("java-library")
+            }
+        """
+
+        buildFile << """
+            plugins {
+                id("java-library")
+            }
+
+            dependencies {
+                implementation project(":producer")
+            }
+
+            task resolve {
+                def artifacts = configurations.runtimeClasspath.incoming.artifacts
+
+                doLast {
+                    def usage = artifacts.resolvedArtifacts.get()[0].variant.attributes.getAttribute(Usage.USAGE_ATTRIBUTE)
+                    assert Usage.class.isAssignableFrom(usage.class)
+                    assert usage.name == "java-runtime"
+
+                    def usageAsString = artifacts.artifacts[0].variant.attributes.getAttribute(Attribute.of(Usage.USAGE_ATTRIBUTE.name, String.class))
+                    assert usageAsString == "java-runtime"
+                }
+            }
+        """
+
+        expect:
+        succeeds("resolve")
+    }
+
     def showFailuresTask(expression) {
         """
             task show {
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultArtifactCollection.java b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultArtifactCollection.java
index c4a7a2b47dda3..61f4e6db31b03 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultArtifactCollection.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultArtifactCollection.java
@@ -21,6 +21,7 @@
 import org.gradle.api.file.FileCollection;
 import org.gradle.api.internal.artifacts.ivyservice.ResolvedArtifactCollectingVisitor;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor;
+import org.gradle.api.internal.attributes.AttributeDesugaring;
 import org.gradle.api.internal.file.FileCollectionInternal;
 import org.gradle.api.internal.provider.BuildableBackedProvider;
 import org.gradle.api.provider.Provider;
@@ -38,11 +39,11 @@ public class DefaultArtifactCollection implements ArtifactCollectionInternal {
     private final boolean lenient;
     private final CalculatedValue<ArtifactSetResult> result;
 
-    public DefaultArtifactCollection(ResolutionBackedFileCollection files, boolean lenient, ResolutionHost resolutionHost, CalculatedValueFactory calculatedValueFactory) {
+    public DefaultArtifactCollection(ResolutionBackedFileCollection files, boolean lenient, ResolutionHost resolutionHost, CalculatedValueFactory calculatedValueFactory, AttributeDesugaring attributeDesugaring) {
         this.fileCollection = files;
         this.lenient = lenient;
         this.result = calculatedValueFactory.create(resolutionHost.displayName("files"), () -> {
-            ResolvedArtifactCollectingVisitor visitor = new ResolvedArtifactCollectingVisitor();
+            ResolvedArtifactCollectingVisitor visitor = new ResolvedArtifactCollectingVisitor(attributeDesugaring);
             fileCollection.getArtifacts().visitArtifacts(visitor, lenient);
 
             Set<ResolvedArtifactResult> artifactResults = visitor.getArtifacts();
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
index 9ec5715b714f7..229179a579835 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/configurations/DefaultConfiguration.java
@@ -655,7 +655,7 @@ public ResolvedConfiguration getResolvedConfiguration() {
     }
 
     @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
-    private static Boolean isFullyResoled(Optional<ResolverResults> currentState) {
+    private static Boolean isFullyResolved(Optional<ResolverResults> currentState) {
         return currentState.map(ResolverResults::isFullyResolved).orElse(false);
     }
 
@@ -689,6 +689,7 @@ public ResolutionOutputsInternal getPublicView() {
                 taskDependencyFactory,
                 calculatedValueContainerFactory,
                 attributesFactory,
+                attributeDesugaring,
                 instantiator
             );
         }
@@ -721,7 +722,7 @@ private ResolverResults resolveGraphIfRequired() {
         maybeEmitResolutionDeprecation();
 
         Optional<ResolverResults> currentState = currentResolveState.get();
-        if (isFullyResoled(currentState)) {
+        if (isFullyResolved(currentState)) {
             return currentState.get();
         }
 
@@ -746,7 +747,7 @@ private ResolverResults resolveGraphIfRequired() {
 
     private ResolverResults resolveExclusivelyIfRequired() {
         return currentResolveState.update(currentState -> {
-            if (isFullyResoled(currentState)) {
+            if (isFullyResolved(currentState)) {
                 return currentState;
             }
 
@@ -914,7 +915,7 @@ private ResolverResults resolveGraphForBuildDependenciesIfRequired() {
 
                 CalculatedValue<ResolverResults> futureCompleteResults = calculatedValueContainerFactory.create(Describables.of("Full results for", getName()), context -> {
                     Optional<ResolverResults> currentState = currentResolveState.get();
-                    if (!isFullyResoled(currentState)) {
+                    if (!isFullyResolved(currentState)) {
                         // Do not validate that the current thread holds the project lock.
                         // TODO: Should instead assert that the results are available and fail if not.
                         return resolveExclusivelyIfRequired();
@@ -1397,7 +1398,7 @@ private void preventIllegalParentMutation(MutationType type) {
             return;
         }
 
-        if (isFullyResoled(currentResolveState.get())) {
+        if (isFullyResolved(currentResolveState.get())) {
             throw new InvalidUserDataException(String.format("Cannot change %s of parent of %s after it has been resolved", type, getDisplayName()));
         }
     }
@@ -1409,7 +1410,7 @@ private void preventIllegalMutation(MutationType type) {
             return;
         }
 
-        if (isFullyResoled(currentResolveState.get())) {
+        if (isFullyResolved(currentResolveState.get())) {
             // The public result for the configuration has been calculated.
             // It is an error to change anything that would change the dependencies or artifacts
             throw new InvalidUserDataException(String.format("Cannot change %s of dependency %s after it has been resolved.", type, getDisplayName()));
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ArtifactCollectingVisitor.java b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ArtifactCollectingVisitor.java
index a85236ac95586..af60316d535c9 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ArtifactCollectingVisitor.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ArtifactCollectingVisitor.java
@@ -17,10 +17,10 @@
 package org.gradle.api.internal.artifacts.ivyservice;
 
 import org.gradle.api.artifacts.ResolvedArtifact;
-import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.LocalDependencyFiles;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.file.FileCollectionInternal;
 import org.gradle.api.internal.file.FileCollectionStructureVisitor;
 import org.gradle.internal.DisplayName;
@@ -45,7 +45,7 @@ public ArtifactCollectingVisitor(Set<ResolvedArtifact> artifacts) {
     }
 
     @Override
-    public void visitArtifact(DisplayName variantName, AttributeContainer variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact) {
+    public void visitArtifact(DisplayName variantName, ImmutableAttributes variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact) {
         this.artifacts.add(artifact.toPublicView());
     }
 
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java
index 9eff0502cbbaf..498cde3881e52 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/DefaultLenientConfiguration.java
@@ -21,7 +21,6 @@
 import org.gradle.api.artifacts.ResolvedArtifact;
 import org.gradle.api.artifacts.ResolvedDependency;
 import org.gradle.api.artifacts.UnresolvedDependency;
-import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.internal.artifacts.DependencyGraphNodeResult;
 import org.gradle.api.internal.artifacts.configurations.ResolutionHost;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactSelectionSpec;
@@ -39,6 +38,7 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.results.VisitedGraphResults;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.TransientConfigurationResults;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.oldresult.TransientConfigurationResultsLoader;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.file.FileCollectionInternal;
 import org.gradle.api.internal.file.FileCollectionStructureVisitor;
 import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
@@ -264,7 +264,7 @@ private static class LenientArtifactCollectingVisitor implements ArtifactVisitor
         List<Throwable> failures;
 
         @Override
-        public void visitArtifact(DisplayName variantName, AttributeContainer variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact) {
+        public void visitArtifact(DisplayName variantName, ImmutableAttributes variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact) {
             try {
                 ResolvedArtifact resolvedArtifact = artifact.toPublicView();
                 files.add(resolvedArtifact.getFile());
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ResolvedArtifactCollectingVisitor.java b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ResolvedArtifactCollectingVisitor.java
index d125aaf0dda39..050bc3b0f3743 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ResolvedArtifactCollectingVisitor.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ResolvedArtifactCollectingVisitor.java
@@ -18,11 +18,12 @@
 
 import org.gradle.api.artifacts.component.ComponentArtifactIdentifier;
 import org.gradle.api.artifacts.result.ResolvedArtifactResult;
-import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.component.Artifact;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
 import org.gradle.api.internal.artifacts.result.DefaultResolvedArtifactResult;
+import org.gradle.api.internal.attributes.AttributeDesugaring;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.internal.DisplayName;
 import org.gradle.internal.component.external.model.ImmutableCapabilities;
 
@@ -35,6 +36,11 @@ public class ResolvedArtifactCollectingVisitor implements ArtifactVisitor {
     private final Set<ResolvedArtifactResult> artifacts = new LinkedHashSet<>();
     private final Set<Throwable> failures = new LinkedHashSet<>();
     private final Set<ComponentArtifactIdentifier> seenArtifacts = new HashSet<>();
+    private final AttributeDesugaring attributeDesugaring;
+
+    public ResolvedArtifactCollectingVisitor(AttributeDesugaring attributeDesugaring) {
+        this.attributeDesugaring = attributeDesugaring;
+    }
 
     @Override
     public void visitFailure(Throwable failure) {
@@ -42,11 +48,11 @@ public void visitFailure(Throwable failure) {
     }
 
     @Override
-    public void visitArtifact(DisplayName variantName, AttributeContainer variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact) {
+    public void visitArtifact(DisplayName variantName, ImmutableAttributes variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact) {
         try {
             if (seenArtifacts.add(artifact.getId())) {
                 File file = artifact.getFile();
-                this.artifacts.add(new DefaultResolvedArtifactResult(artifact.getId(), variantAttributes, capabilities, variantName, Artifact.class, file));
+                this.artifacts.add(new DefaultResolvedArtifactResult(artifact.getId(), attributeDesugaring.desugar(variantAttributes), capabilities, variantName, Artifact.class, file));
             }
         } catch (Exception t) {
             failures.add(t);
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ResolvedFilesCollectingVisitor.java b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ResolvedFilesCollectingVisitor.java
index d17438a1f31a3..5a3af712dd5ec 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ResolvedFilesCollectingVisitor.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/ResolvedFilesCollectingVisitor.java
@@ -16,9 +16,9 @@
 
 package org.gradle.api.internal.artifacts.ivyservice;
 
-import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.internal.DisplayName;
 import org.gradle.internal.component.external.model.ImmutableCapabilities;
 
@@ -32,7 +32,7 @@ public class ResolvedFilesCollectingVisitor implements ArtifactVisitor {
     private final Set<Throwable> failures = new LinkedHashSet<>();
 
     @Override
-    public void visitArtifact(DisplayName variantName, AttributeContainer variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact) {
+    public void visitArtifact(DisplayName variantName, ImmutableAttributes variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact) {
         try {
             File file = artifact.getFile();
             this.files.add(file);
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactBackedResolvedVariant.java b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactBackedResolvedVariant.java
index 225d1d13898f3..6bb7ff99b8c33 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactBackedResolvedVariant.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactBackedResolvedVariant.java
@@ -18,7 +18,6 @@
 
 import org.gradle.api.Action;
 import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
-import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.internal.artifacts.DownloadArtifactBuildOperationType;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.file.FileCollectionInternal;
@@ -113,11 +112,11 @@ public ImmutableCapabilities getCapabilities() {
 
     private static class SingleArtifactSet implements ResolvedArtifactSet, ResolvedArtifactSet.Artifacts {
         private final DisplayName variantName;
-        private final AttributeContainer variantAttributes;
+        private final ImmutableAttributes variantAttributes;
         private final ImmutableCapabilities capabilities;
         private final ResolvableArtifact artifact;
 
-        SingleArtifactSet(DisplayName variantName, AttributeContainer variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact) {
+        SingleArtifactSet(DisplayName variantName, ImmutableAttributes variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact) {
             this.variantName = variantName;
             this.variantAttributes = variantAttributes;
             this.capabilities = capabilities;
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactSetToFileCollectionFactory.java b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactSetToFileCollectionFactory.java
index 9d329fed5bd92..1be6e87bb577c 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactSetToFileCollectionFactory.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactSetToFileCollectionFactory.java
@@ -20,10 +20,10 @@
 import org.gradle.api.Action;
 import org.gradle.api.artifacts.ResolvedArtifact;
 import org.gradle.api.artifacts.component.ComponentArtifactIdentifier;
-import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.internal.artifacts.configurations.ResolutionBackedFileCollection;
 import org.gradle.api.internal.artifacts.configurations.ResolutionHost;
 import org.gradle.api.internal.artifacts.ivyservice.TypedResolveException;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.file.FileCollectionInternal;
 import org.gradle.api.internal.file.FileCollectionStructureVisitor;
 import org.gradle.api.internal.tasks.TaskDependencyFactory;
@@ -78,7 +78,7 @@ public ResolvedArtifactSet asResolvedArtifactSet(Throwable failure) {
 
     public ResolvedArtifactSet asResolvedArtifactSet(
         ComponentArtifactIdentifier id,
-        AttributeContainer variantAttributes,
+        ImmutableAttributes variantAttributes,
         ImmutableCapabilities capabilities,
         DisplayName variantDisplayName,
         File file
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactVisitor.java b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactVisitor.java
index c11212f8eb717..da22d0f75baab 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactVisitor.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactVisitor.java
@@ -16,7 +16,7 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact;
 
-import org.gradle.api.attributes.AttributeContainer;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.file.FileCollectionInternal;
 import org.gradle.api.internal.file.FileCollectionStructureVisitor;
 import org.gradle.internal.DisplayName;
@@ -38,10 +38,10 @@ default FileCollectionStructureVisitor.VisitType prepareForVisit(FileCollectionI
      *
      * <p>Note that a given artifact may be visited multiple times. The implementation is required to filter out duplicates.</p>
      */
-    void visitArtifact(DisplayName variantName, AttributeContainer variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact);
+    void visitArtifact(DisplayName variantName, ImmutableAttributes variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact);
 
     /**
-     * Should the file for each artifact be made available prior to calling {@link #visitArtifact(DisplayName, AttributeContainer, ImmutableCapabilities, ResolvableArtifact)}?
+     * Should the file for each artifact be made available prior to calling {@link #visitArtifact(DisplayName, ImmutableAttributes, ImmutableCapabilities, ResolvableArtifact)}?
      *
      * Returns true here allows the collection to preemptively resolve the files in parallel.
      */
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactVisitorToResolvedFileVisitorAdapter.java b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactVisitorToResolvedFileVisitorAdapter.java
index 8894377ab222e..cf40791febb24 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactVisitorToResolvedFileVisitorAdapter.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/ivyservice/resolveengine/artifact/ArtifactVisitorToResolvedFileVisitorAdapter.java
@@ -16,7 +16,7 @@
 
 package org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact;
 
-import org.gradle.api.attributes.AttributeContainer;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.file.FileCollectionInternal;
 import org.gradle.api.internal.file.FileCollectionStructureVisitor;
 import org.gradle.internal.DisplayName;
@@ -41,7 +41,7 @@ public void visitFile(File file) {
     }
 
     @Override
-    public void visitArtifact(DisplayName variantName, AttributeContainer variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact) {
+    public void visitArtifact(DisplayName variantName, ImmutableAttributes variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact) {
         visitor.visitFile(artifact.getFile());
     }
 
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/resolver/DefaultResolutionOutputs.java b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/resolver/DefaultResolutionOutputs.java
index 3b4fa3eb96309..c7fca97b40632 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/resolver/DefaultResolutionOutputs.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/resolver/DefaultResolutionOutputs.java
@@ -32,6 +32,7 @@
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.SelectedArtifactSet;
 import org.gradle.api.internal.artifacts.result.MinimalResolutionResult;
 import org.gradle.api.internal.attributes.AttributeContainerInternal;
+import org.gradle.api.internal.attributes.AttributeDesugaring;
 import org.gradle.api.internal.attributes.AttributesFactory;
 import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.file.FileCollectionInternal;
@@ -62,6 +63,7 @@ public class DefaultResolutionOutputs implements ResolutionOutputsInternal {
     private final TaskDependencyFactory taskDependencyFactory;
     private final CalculatedValueContainerFactory calculatedValueContainerFactory;
     private final AttributesFactory attributesFactory;
+    private final AttributeDesugaring attributeDesugaring;
     private final Instantiator instantiator;
 
     public DefaultResolutionOutputs(
@@ -69,12 +71,14 @@ public DefaultResolutionOutputs(
         TaskDependencyFactory taskDependencyFactory,
         CalculatedValueContainerFactory calculatedValueContainerFactory,
         AttributesFactory attributesFactory,
+        AttributeDesugaring attributeDesugaring,
         Instantiator instantiator
     ) {
         this.resolutionAccess = resolutionAccess;
         this.taskDependencyFactory = taskDependencyFactory;
         this.calculatedValueContainerFactory = calculatedValueContainerFactory;
         this.attributesFactory = attributesFactory;
+        this.attributeDesugaring = attributeDesugaring;
         this.instantiator = instantiator;
     }
 
@@ -124,7 +128,8 @@ private DefaultArtifactView doGetArtifactView(Action<? super ArtifactView.ViewCo
             resolutionAccess,
             taskDependencyFactory,
             calculatedValueContainerFactory,
-            attributesFactory
+            attributesFactory,
+            attributeDesugaring
         );
     }
 
@@ -142,6 +147,7 @@ public static class DefaultArtifactView implements ArtifactView {
         private final TaskDependencyFactory taskDependencyFactory;
         private final CalculatedValueContainerFactory calculatedValueContainerFactory;
         private final AttributesFactory attributesFactory;
+        private final AttributeDesugaring attributeDesugaring;
 
         public DefaultArtifactView(
             boolean lenient,
@@ -152,7 +158,8 @@ public DefaultArtifactView(
             ResolutionAccess resolutionAccess,
             TaskDependencyFactory taskDependencyFactory,
             CalculatedValueContainerFactory calculatedValueContainerFactory,
-            AttributesFactory attributesFactory
+            AttributesFactory attributesFactory,
+            AttributeDesugaring attributeDesugaring
         ) {
             this.lenient = lenient;
             this.componentFilter = componentFilter;
@@ -163,6 +170,7 @@ public DefaultArtifactView(
             this.taskDependencyFactory = taskDependencyFactory;
             this.calculatedValueContainerFactory = calculatedValueContainerFactory;
             this.attributesFactory = attributesFactory;
+            this.attributeDesugaring = attributeDesugaring;
         }
 
         @Override
@@ -171,7 +179,8 @@ public ArtifactCollectionInternal getArtifacts() {
                 getFiles(),
                 lenient,
                 resolutionAccess.getHost(),
-                calculatedValueContainerFactory
+                calculatedValueContainerFactory,
+                attributeDesugaring
             );
         }
 
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ComponentVariantIdentifier.java b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ComponentVariantIdentifier.java
index 930a68bccc7e9..1762e3f816b55 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ComponentVariantIdentifier.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/ComponentVariantIdentifier.java
@@ -17,7 +17,7 @@
 package org.gradle.api.internal.artifacts.transform;
 
 import org.gradle.api.artifacts.component.ComponentIdentifier;
-import org.gradle.api.attributes.AttributeContainer;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.internal.component.external.model.ImmutableCapabilities;
 
 /**
@@ -26,10 +26,10 @@
 public class ComponentVariantIdentifier {
 
     private final ComponentIdentifier componentId;
-    private final AttributeContainer attributes;
+    private final ImmutableAttributes attributes;
     private final ImmutableCapabilities capabilities;
 
-    public ComponentVariantIdentifier(ComponentIdentifier componentId, AttributeContainer attributes, ImmutableCapabilities capabilities) {
+    public ComponentVariantIdentifier(ComponentIdentifier componentId, ImmutableAttributes attributes, ImmutableCapabilities capabilities) {
         this.componentId = componentId;
         this.attributes = attributes;
         this.capabilities = capabilities;
@@ -39,7 +39,7 @@ public ComponentIdentifier getComponentId() {
         return componentId;
     }
 
-    public AttributeContainer getAttributes() {
+    public ImmutableAttributes getAttributes() {
         return attributes;
     }
 
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformingAsyncArtifactListener.java b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformingAsyncArtifactListener.java
index 55e2f6da274c5..d07b344fcadc1 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformingAsyncArtifactListener.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/transform/TransformingAsyncArtifactListener.java
@@ -17,7 +17,6 @@
 package org.gradle.api.internal.artifacts.transform;
 
 import com.google.common.collect.ImmutableList;
-import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.BrokenArtifacts;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
@@ -60,7 +59,7 @@ public TransformingAsyncArtifactListener(
     public void visitArtifacts(ResolvedArtifactSet.Artifacts artifacts) {
         artifacts.visit(new ArtifactVisitor() {
             @Override
-            public void visitArtifact(DisplayName variantName, AttributeContainer variantAttributes, ImmutableCapabilities variantCapabilities, ResolvableArtifact artifact) {
+            public void visitArtifact(DisplayName variantName, ImmutableAttributes variantAttributes, ImmutableCapabilities variantCapabilities, ResolvableArtifact artifact) {
                 TransformedArtifact transformedArtifact = new TransformedArtifact(variantName, target, capabilities, artifact, transformSteps);
                 result.add(transformedArtifact);
             }
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DefaultVariantArtifactResolver.java b/platforms/software/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DefaultVariantArtifactResolver.java
index b104d3b42aaff..dda623be25f8b 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DefaultVariantArtifactResolver.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DefaultVariantArtifactResolver.java
@@ -37,7 +37,11 @@ public class DefaultVariantArtifactResolver implements VariantArtifactResolver {
     private final ArtifactResolver artifactResolver;
     private final ResolvedVariantCache resolvedVariantCache;
 
-    public DefaultVariantArtifactResolver(ArtifactResolver artifactResolver, ImmutableArtifactTypeRegistry artifactTypeRegistry, ResolvedVariantCache resolvedVariantCache) {
+    public DefaultVariantArtifactResolver(
+        ArtifactResolver artifactResolver,
+        ImmutableArtifactTypeRegistry artifactTypeRegistry,
+        ResolvedVariantCache resolvedVariantCache
+    ) {
         this.artifactTypeRegistry = artifactTypeRegistry;
         this.artifactResolver = artifactResolver;
         this.resolvedVariantCache = resolvedVariantCache;
@@ -100,8 +104,8 @@ private ResolvedVariant createResolvedVariant(
         ImmutableArtifactTypeRegistry artifactTypeRegistry,
         ImmutableList<? extends ComponentArtifactMetadata> artifacts
     ) {
-        ImmutableAttributes attributes = artifactTypeRegistry.mapAttributesFor(artifactVariant.getAttributes(), artifacts);
 
+        ImmutableAttributes attributes = artifactTypeRegistry.mapAttributesFor(artifactVariant.getAttributes(), artifacts);
         ImmutableCapabilities capabilities = withImplicitCapability(artifactVariant.getCapabilities(), component);
 
         // TODO: This value gets cached in a build-tree-scoped cache. It captures a project-scoped `artifactResolver`, which
diff --git a/platforms/software/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/FilteringResolvedArtifactSet.java b/platforms/software/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/FilteringResolvedArtifactSet.java
index 6e6246f5998a7..2f9189f9af0c8 100644
--- a/platforms/software/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/FilteringResolvedArtifactSet.java
+++ b/platforms/software/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/FilteringResolvedArtifactSet.java
@@ -17,11 +17,11 @@
 package org.gradle.internal.resolve.resolver;
 
 import org.gradle.api.Action;
-import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedArtifactSet;
 import org.gradle.api.internal.artifacts.transform.TransformStepNode;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.file.FileCollectionInternal;
 import org.gradle.api.internal.file.FileCollectionStructureVisitor;
 import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
@@ -88,7 +88,7 @@ public FileCollectionStructureVisitor.VisitType prepareForVisit(FileCollectionIn
         }
 
         @Override
-        public void visitArtifact(DisplayName variantName, AttributeContainer variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact) {
+        public void visitArtifact(DisplayName variantName, ImmutableAttributes variantAttributes, ImmutableCapabilities capabilities, ResolvableArtifact artifact) {
             if (filter.test(artifact)) {
                 visitor.visitArtifact(variantName, variantAttributes, capabilities, artifact);
             }
diff --git a/platforms/software/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/TestArtifactSet.java b/platforms/software/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/TestArtifactSet.java
index 86b2dee1ba369..905fb0da1ba8b 100644
--- a/platforms/software/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/TestArtifactSet.java
+++ b/platforms/software/dependency-management/src/test/groovy/org/gradle/api/internal/artifacts/TestArtifactSet.java
@@ -20,10 +20,10 @@
 import org.gradle.api.Action;
 import org.gradle.api.artifacts.ResolvedArtifact;
 import org.gradle.api.artifacts.component.ComponentArtifactIdentifier;
-import org.gradle.api.attributes.AttributeContainer;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ArtifactVisitor;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact;
 import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvedArtifactSet;
+import org.gradle.api.internal.attributes.ImmutableAttributes;
 import org.gradle.api.internal.tasks.TaskDependencyResolveContext;
 import org.gradle.internal.Describables;
 import org.gradle.internal.DisplayName;
@@ -39,20 +39,20 @@
 public class TestArtifactSet implements ResolvedArtifactSet, ResolvedArtifactSet.Artifacts {
     public static final String DEFAULT_TEST_VARIANT = "test variant";
     private final DisplayName variantName;
-    private final AttributeContainer variant;
+    private final ImmutableAttributes variant;
     private final ImmutableSet<ResolvedArtifact> artifacts;
 
-    private TestArtifactSet(String variantName, AttributeContainer variant, Collection<? extends ResolvedArtifact> artifacts) {
+    private TestArtifactSet(String variantName, ImmutableAttributes variant, Collection<? extends ResolvedArtifact> artifacts) {
         this.variantName = Describables.of(variantName);
         this.variant = variant;
         this.artifacts = ImmutableSet.copyOf(artifacts);
     }
 
-    public static ResolvedArtifactSet create(String variantName, AttributeContainer variantAttributes, Collection<? extends ResolvedArtifact> artifacts) {
+    public static ResolvedArtifactSet create(String variantName, ImmutableAttributes variantAttributes, Collection<? extends ResolvedArtifact> artifacts) {
         return new TestArtifactSet(variantName, variantAttributes, artifacts);
     }
 
-    public static ResolvedArtifactSet create(AttributeContainer variantAttributes, Collection<? extends ResolvedArtifact> artifacts) {
+    public static ResolvedArtifactSet create(ImmutableAttributes variantAttributes, Collection<? extends ResolvedArtifact> artifacts) {
         return new TestArtifactSet(DEFAULT_TEST_VARIANT, variantAttributes, artifacts);
     }
 
diff --git a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildConfigurationAttributesResolveIntegrationTest.groovy b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildConfigurationAttributesResolveIntegrationTest.groovy
index 5705c03ea60ff..6af52482bfc4a 100644
--- a/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildConfigurationAttributesResolveIntegrationTest.groovy
+++ b/subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildConfigurationAttributesResolveIntegrationTest.groovy
@@ -478,6 +478,25 @@ class CompositeBuildConfigurationAttributesResolveIntegrationTest extends Abstra
         type         | freeValue                      | paidValue
         'SomeEnum'   | 'SomeEnum.free'                | 'SomeEnum.paid'
         'Thing'      | 'objects.named(Thing, "free")' | 'objects.named(Thing, "paid")'
+
+        /*
+          Note that OtherThing represents IMPROPER usage.  Named implementations should only be created by
+          Gradle, NOT by calling their constructor directly.  They shouldn't contain fields,
+          and should be abstract classes.
+
+          If you do try to create this instance via Gradle (as with Thing above), you will get the following error:
+
+           >  Could not create an instance of type OtherThing.
+             > Type OtherThing is not a valid Named implementation class:
+               - Field name is not valid: A Named implementation class must not define any instance fields.
+
+           Ideally, Gradle would verify that Named implementations are implemented correctly and/or
+           were created by Gradle as managed types (perhaps when used to create an Attribute) and would
+           fail-fast at that point.
+
+           So this test is left to document existing behavior, and to serve as a canary, but should not be used as a model for
+           how to use Named implementations.  If this test case causes problems later, it should just be removed.
+         */
         'OtherThing' | 'new OtherThing(name: "free")' | 'new OtherThing(name: "paid")'
     }
 
diff --git a/subprojects/core/src/main/java/org/gradle/api/internal/attributes/DefaultImmutableAttributesContainer.java b/subprojects/core/src/main/java/org/gradle/api/internal/attributes/DefaultImmutableAttributesContainer.java
index 083df98ad4fd3..54447175666ae 100644
--- a/subprojects/core/src/main/java/org/gradle/api/internal/attributes/DefaultImmutableAttributesContainer.java
+++ b/subprojects/core/src/main/java/org/gradle/api/internal/attributes/DefaultImmutableAttributesContainer.java
@@ -160,7 +160,7 @@ public <T> T getAttribute(Attribute<T> key) {
      * that method, where when asked for a {@code <T>} we must return one.
      * <p>
      * One interesting potential complication is caused by different classloaders
-     * loading the same type when an typed attribute is put into a container vs. when it is retrieved.
+     * loading the same type when a typed attribute is put into a container vs. when it is retrieved.
      * <p>
      * When a request for a key with type X1
      * is being made in a container holding an attribute with type X2, where X1 and X2 are


Reviews:
user: login: tresat
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-05T13:00:19Z
user: login: jvandort
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T17:35:11Z
user: login: jvandort
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T17:41:05Z
user: login: jvandort
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T17:45:34Z
user: login: tresat
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T14:44:10Z
user: login: tresat
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T14:44:38Z
user: login: jvandort
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T16:56:46Z
user: login: jvandort
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T16:58:10Z
user: login: jvandort
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T17:07:52Z
user: login: tresat
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T21:01:41Z
user: login: jvandort
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-19T18:24:16Z

Review comments:
diff_hunk: @@ -1170,31 +1169,6 @@ class ArtifactSelectionIntegrationTest extends AbstractHttpDependencyResolutionT
             }
         """
 
-        file("ui/build.gradle") << """
-            $header
-            def attr = Attribute.of('attr', Number)
path: platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/ArtifactSelectionIntegrationTest.groovy
user: login: tresat
body: 💭 This project is now successfully found, as Number can be coerced to String when the local metadata is created.
created_at: 2025-02-05T12:59:41Z
author_association: MEMBER
diff_hunk: @@ -478,7 +478,6 @@ class CompositeBuildConfigurationAttributesResolveIntegrationTest extends Abstra
         type         | freeValue                      | paidValue
         'SomeEnum'   | 'SomeEnum.free'                | 'SomeEnum.paid'
         'Thing'      | 'objects.named(Thing, "free")' | 'objects.named(Thing, "paid")'
-        'OtherThing' | 'new OtherThing(name: "free")' | 'new OtherThing(name: "paid")'
path: subprojects/composite-builds/src/integTest/groovy/org/gradle/integtests/composite/CompositeBuildConfigurationAttributesResolveIntegrationTest.groovy
user: login: tresat
body: 💭 Having a field in a Named implementation class is not allowed, not sure why this worked previously.

```
* What went wrong:
Could not determine the dependencies of task ':a:checkFree'.
> Could not resolve all dependencies for configuration ':a:_compileFree'.
   > Could not select a variant of project :includedBuild that matches the consumer attributes.
      > Could not create an instance of type OtherThing.
         > Type OtherThing is not a valid Named implementation class:
           - Field name is not valid: A Named implementation class must not define any instance fields.
```
created_at: 2025-02-05T13:00:16Z
author_association: MEMBER
diff_hunk: @@ -100,8 +108,7 @@ private ResolvedVariant createResolvedVariant(
         ImmutableArtifactTypeRegistry artifactTypeRegistry,
         ImmutableList<? extends ComponentArtifactMetadata> artifacts
     ) {
-        ImmutableAttributes attributes = artifactTypeRegistry.mapAttributesFor(artifactVariant.getAttributes(), artifacts);
-
+        ImmutableAttributes attributes = attributeDesugaring.desugar(artifactTypeRegistry.mapAttributesFor(artifactVariant.getAttributes(), artifacts));
path: platforms/software/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DefaultVariantArtifactResolver.java
user: login: jvandort
body: I think we should do the desugaring in `ResolvedArtifactCollectingVisitor`, after any internal processing, and just before we present the artifact to the user.
created_at: 2025-03-13T17:35:11Z
author_association: MEMBER
diff_hunk: @@ -1076,4 +1076,39 @@ testRuntimeClasspath
         expect:
         succeeds("traverse")
     }
+
+    def "attributes on resolved variants can be requested using strongly-typed values"() {
+        settingsFile << """
+            include("producer")
+        """
+
+        file("producer/build.gradle") << """
+            plugins {
+                id("java-library")
+            }
+        """
+
+        buildFile << """
+            plugins {
+                id("java-library")
+            }
+
+            dependencies {
+                implementation project(":producer")
+            }
+
+            task resolve {
+                def root = configurations.runtimeClasspath.incoming.resolutionResult.rootVariant
path: platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolutionResultApiIntegrationTest.groovy
user: login: jvandort
body: I don't think this test is verifying the changes we made here. Resolution result variant attributes should already be desugared. 

We want to verify the attributes of something like:
```
    configuration.getIncoming().getArtifacts().getArtifacts().getFirst().getVariant()

```

I would be curious to run this test without any of the production code changes. Does it fail then?
created_at: 2025-03-13T17:41:05Z
author_association: MEMBER
diff_hunk: @@ -1076,4 +1076,39 @@ testRuntimeClasspath
         expect:
         succeeds("traverse")
     }
+
+    def "attributes on resolved variants can be requested using strongly-typed values"() {
+        settingsFile << """
+            include("producer")
+        """
+
+        file("producer/build.gradle") << """
+            plugins {
+                id("java-library")
+            }
+        """
+
+        buildFile << """
+            plugins {
+                id("java-library")
+            }
+
+            dependencies {
+                implementation project(":producer")
+            }
+
+            task resolve {
+                def root = configurations.runtimeClasspath.incoming.resolutionResult.rootVariant
+
+                doLast {
path: platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolutionResultApiIntegrationTest.groovy
user: login: jvandort
body: I would like to see a test that verifies this scenario:
```
/*
task foo {
    Provider<Set<ResolvedArtifactResult>> artifacts = configuration.getIncoming().getArtifacts().getResolvedArtifacts();
    doLast {
        artifacts.get().forEach { artifact -> 
        
            // In the past, these were "sugared"
            def attributes = artifact.getVariant().getAttributes() // These are the attributes I would expect to changes
            
            // This would pass before these changes
            attributes.getAttribute(Usage.USAGE_ATTRIBUTE)
            
            // Without coersion, only this would work:
            // The user would have to look up attribute in desugared form
            attributes.getAttribute(Attribute.of("org.gradle.usage", String.class))
        }
    }
}
 */
```
created_at: 2025-03-13T17:45:34Z
author_association: MEMBER
diff_hunk: @@ -100,8 +108,7 @@ private ResolvedVariant createResolvedVariant(
         ImmutableArtifactTypeRegistry artifactTypeRegistry,
         ImmutableList<? extends ComponentArtifactMetadata> artifacts
     ) {
-        ImmutableAttributes attributes = artifactTypeRegistry.mapAttributesFor(artifactVariant.getAttributes(), artifacts);
-
+        ImmutableAttributes attributes = attributeDesugaring.desugar(artifactTypeRegistry.mapAttributesFor(artifactVariant.getAttributes(), artifacts));
path: platforms/software/dependency-management/src/main/java/org/gradle/internal/resolve/resolver/DefaultVariantArtifactResolver.java
user: login: tresat
body: Made this change.
created_at: 2025-03-18T14:44:09Z
author_association: MEMBER
diff_hunk: @@ -1076,4 +1076,39 @@ testRuntimeClasspath
         expect:
         succeeds("traverse")
     }
+
+    def "attributes on resolved variants can be requested using strongly-typed values"() {
+        settingsFile << """
+            include("producer")
+        """
+
+        file("producer/build.gradle") << """
+            plugins {
+                id("java-library")
+            }
+        """
+
+        buildFile << """
+            plugins {
+                id("java-library")
+            }
+
+            dependencies {
+                implementation project(":producer")
+            }
+
+            task resolve {
+                def root = configurations.runtimeClasspath.incoming.resolutionResult.rootVariant
path: platforms/software/dependency-management/src/integTest/groovy/org/gradle/integtests/resolve/api/ResolutionResultApiIntegrationTest.groovy
user: login: tresat
body: Added and expanded tests to get at various sets of resolved attributes in various ways and confirm both strongly and strongly typed attributes can be retrieved.
created_at: 2025-03-18T14:44:38Z
author_association: MEMBER
diff_hunk: @@ -171,7 +172,8 @@ public ArtifactCollectionInternal getArtifacts() {
                 getFiles(),
                 lenient,
                 resolutionAccess.getHost(),
-                calculatedValueContainerFactory
+                calculatedValueContainerFactory,
+                new AttributeDesugaring(attributesFactory)
path: platforms/software/dependency-management/src/main/java/org/gradle/api/internal/artifacts/resolver/DefaultResolutionOutputs.java
user: login: jvandort
body: AttibuteDesugaring contains a [cache](https://github.com/gradle/gradle/blob/35bd9ab866a9721114fd3dd85e7237c4b8e42153/platforms/software/dependency-management/src/main/java/org/gradle/api/internal/attributes/AttributeDesugaring.java#L34) of desugared containers. We should therefore use an injected `AttributeDesugaring` instance instead of creating a new one every time. 

Unfortunately, this does mean we need a bit more wiring, but I think we need to wire the common instance of `AttributeDesugaring` here
created_at: 2025-03-18T16:56:45Z
author_association: MEMBER
diff_hunk: @@ -207,7 +208,7 @@ class Codecs(
             bind(SerializedLambdaParametersCheckingCodec)
 
             // Dependency management types
-            bind(ArtifactCollectionCodec(calculatedValueContainerFactory, artifactSetConverter))
+            bind(ArtifactCollectionCodec(calculatedValueContainerFactory, artifactSetConverter, AttributeDesugaring(attributesFactory)))
path: platforms/core-configuration/configuration-cache/src/main/kotlin/org/gradle/internal/cc/impl/serialize/Codecs.kt
user: login: jvandort
body: Same goes here I think we need to inject this instance instead of creating a new one
created_at: 2025-03-18T16:58:10Z
author_association: MEMBER
diff_hunk: @@ -256,4 +255,12 @@ private AbstractLibrary createLibraryEntry(File binary, File source, File javado
             return out;
         }
     }
+
+    @SuppressWarnings("ConstantValue")
+    private static boolean isNotJar(ResolvedArtifactResult artifact) {
+        Object libraryElements = artifact.getVariant().getAttributes().getAttribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE);
+        return libraryElements == null
+            || (libraryElements instanceof String && !libraryElements.equals(LibraryElements.JAR))
path: platforms/ide/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/EclipseDependenciesCreator.java
user: login: jvandort
body: ❌ This seems fishy to me. is `libraryElements instanceof String` ever true? If so, this seems like a correctness bug in our coersion logic, as the type returned from `getAttribute` should always be the type of the attribute passed in the query paramter. 

I would expect `libraryElements instanceof LibraryElements` to always be true. 

If we revert the change to this code, do any tests fail? If so which?
created_at: 2025-03-18T17:07:51Z
author_association: MEMBER
diff_hunk: @@ -256,4 +255,12 @@ private AbstractLibrary createLibraryEntry(File binary, File source, File javado
             return out;
         }
     }
+
+    @SuppressWarnings("ConstantValue")
+    private static boolean isNotJar(ResolvedArtifactResult artifact) {
+        Object libraryElements = artifact.getVariant().getAttributes().getAttribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE);
+        return libraryElements == null
+            || (libraryElements instanceof String && !libraryElements.equals(LibraryElements.JAR))
path: platforms/ide/ide/src/main/java/org/gradle/plugins/ide/eclipse/model/internal/EclipseDependenciesCreator.java
user: login: tresat
body: I think you're right, and this is always true now/again.  I'll let the CI tests verify that beyond just `dependency-management`.
created_at: 2025-03-18T21:01:41Z
author_association: MEMBER

PR comments:
user: login: tresat
created_at: 2025-02-03T22:59:58Z
body: @bot-gradle test this
author_association: MEMBER
user: login: bot-gradle
created_at: 2025-02-03T23:00:04Z
body: <!-- {"replyTargetCommentId":2632370649,"triggeredTeamCityBuilds":[{"buildConfigurationId":"Gradle_Master_Check_Stage_PullRequestFeedback_Trigger","buildId":"94363195","headRef":"2b1750079132928958882a7f6a4d4ae955eeba18"}]} -->
I've triggered the following builds:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/build/94363195)
  - [Build Scan](https://ge.gradle.org/scans?search.tags=CI&amp;search.names=gitCommitId,PartOf&amp;search.values=2b1750079132928958882a7f6a4d4ae955eeba18,PullRequestFeedback&amp;)

author_association: COLLABORATOR
user: login: bot-gradle
created_at: 2025-02-03T23:58:25Z
body: The following builds have failed:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/buildConfiguration/Gradle_Master_Check_Stage_PullRequestFeedback_Trigger/94363195)
  - [Build Scan](https://ge.gradle.org/scans?search.buildOutcome=failure&amp;search.tags=CI&amp;search.names=gitCommitId&amp;search.values=2b1750079132928958882a7f6a4d4ae955eeba18&amp;)

author_association: COLLABORATOR
user: login: tresat
created_at: 2025-02-04T20:57:32Z
body: @bot-gradle test this
author_association: MEMBER
user: login: bot-gradle
created_at: 2025-02-04T20:57:36Z
body: <!-- {"replyTargetCommentId":2635059888,"triggeredTeamCityBuilds":[{"buildConfigurationId":"Gradle_Master_Check_Stage_PullRequestFeedback_Trigger","buildId":"94410095","headRef":"ad422e622c402bfabe2618897cf356b1d9bdbf29"}]} -->
I've triggered the following builds:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/build/94410095)
  - [Build Scan](https://ge.gradle.org/scans?search.tags=CI&amp;search.names=gitCommitId,PartOf&amp;search.values=ad422e622c402bfabe2618897cf356b1d9bdbf29,PullRequestFeedback&amp;)

author_association: COLLABORATOR
user: login: bot-gradle
created_at: 2025-02-04T21:56:30Z
body: The following builds have failed:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/buildConfiguration/Gradle_Master_Check_Stage_PullRequestFeedback_Trigger/94410095)
  - [Build Scan](https://ge.gradle.org/scans?search.buildOutcome=failure&amp;search.tags=CI&amp;search.names=gitCommitId&amp;search.values=ad422e622c402bfabe2618897cf356b1d9bdbf29&amp;)

author_association: COLLABORATOR
user: login: tresat
created_at: 2025-02-05T12:58:35Z
body: @bot-gradle test this
author_association: MEMBER
user: login: bot-gradle
created_at: 2025-02-05T12:58:53Z
body: <!-- {"replyTargetCommentId":2636790989,"triggeredTeamCityBuilds":[{"buildConfigurationId":"Gradle_Master_Check_Stage_PullRequestFeedback_Trigger","buildId":"94439575","headRef":"63f734f99bfba595bc00fae6627eaa939a014e97"}]} -->
I've triggered the following builds:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/build/94439575)
  - [Build Scan](https://ge.gradle.org/scans?search.tags=CI&amp;search.names=gitCommitId,PartOf&amp;search.values=63f734f99bfba595bc00fae6627eaa939a014e97,PullRequestFeedback&amp;)

author_association: COLLABORATOR
user: login: bot-gradle
created_at: 2025-02-05T13:28:29Z
body: The following builds have passed:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/buildConfiguration/Gradle_Master_Check_Stage_PullRequestFeedback_Trigger/94439575)
  - [Build Scan](https://ge.gradle.org/scans?search.tags=CI&amp;search.names=gitCommitId,PartOf&amp;search.values=63f734f99bfba595bc00fae6627eaa939a014e97,PullRequestFeedback&amp;)

author_association: COLLABORATOR
user: login: tresat
created_at: 2025-02-06T22:00:41Z
body: @bot-gradle test this
author_association: MEMBER
user: login: bot-gradle
created_at: 2025-02-06T22:00:58Z
body: <!-- {"replyTargetCommentId":2641156002,"triggeredTeamCityBuilds":[{"buildConfigurationId":"Gradle_Master_Check_Stage_PullRequestFeedback_Trigger","buildId":"94499655","headRef":"802499dd93b40d43846017af6b8ec38baf8e1a54"}]} -->
I've triggered the following builds:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/build/94499655)
  - [Build Scan](https://ge.gradle.org/scans?search.tags=CI&amp;search.names=gitCommitId,PartOf&amp;search.values=802499dd93b40d43846017af6b8ec38baf8e1a54,PullRequestFeedback&amp;)

author_association: COLLABORATOR
user: login: bot-gradle
created_at: 2025-02-06T23:01:03Z
body: The following builds have failed:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/buildConfiguration/Gradle_Master_Check_Stage_PullRequestFeedback_Trigger/94499655)
  - [Build Scan](https://ge.gradle.org/scans?search.buildOutcome=failure&amp;search.tags=CI&amp;search.names=gitCommitId&amp;search.values=802499dd93b40d43846017af6b8ec38baf8e1a54&amp;)

author_association: COLLABORATOR
user: login: tresat
created_at: 2025-02-07T21:38:36Z
body: @bot-gradle test this
author_association: MEMBER
user: login: bot-gradle
created_at: 2025-02-07T21:38:47Z
body: <!-- {"replyTargetCommentId":2644177156,"triggeredTeamCityBuilds":[{"buildConfigurationId":"Gradle_Master_Check_Stage_PullRequestFeedback_Trigger","buildId":"94542142","headRef":"d3060f2b17ed05abc4b3127a337847b721592809"}]} -->
I've triggered the following builds:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/build/94542142)
  - [Build Scan](https://ge.gradle.org/scans?search.tags=CI&amp;search.names=gitCommitId,PartOf&amp;search.values=d3060f2b17ed05abc4b3127a337847b721592809,PullRequestFeedback&amp;)

author_association: COLLABORATOR
user: login: bot-gradle
created_at: 2025-02-07T23:46:02Z
body: The following builds have failed:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/buildConfiguration/Gradle_Master_Check_Stage_PullRequestFeedback_Trigger/94542142)
  - [Build Scan](https://ge.gradle.org/scans?search.buildOutcome=failure&amp;search.tags=CI&amp;search.names=gitCommitId&amp;search.values=d3060f2b17ed05abc4b3127a337847b721592809&amp;)

author_association: COLLABORATOR
user: login: tresat
created_at: 2025-02-10T16:01:15Z
body: @bot-gradle test this
author_association: MEMBER
user: login: bot-gradle
created_at: 2025-02-10T16:01:36Z
body: <!-- {"replyTargetCommentId":2648497908,"triggeredTeamCityBuilds":[{"buildConfigurationId":"Gradle_Master_Check_Stage_PullRequestFeedback_Trigger","buildId":"94603947","headRef":"9a2687fed7b724e9d6736ce3616daa5f960ed97e"}]} -->
I've triggered the following builds:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/build/94603947)
  - [Build Scan](https://ge.gradle.org/scans?search.tags=CI&amp;search.names=gitCommitId,PartOf&amp;search.values=9a2687fed7b724e9d6736ce3616daa5f960ed97e,PullRequestFeedback&amp;)

author_association: COLLABORATOR
user: login: bot-gradle
created_at: 2025-02-10T17:01:29Z
body: The following builds have failed:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/buildConfiguration/Gradle_Master_Check_Stage_PullRequestFeedback_Trigger/94603947)
  - [Build Scan](https://ge.gradle.org/scans?search.buildOutcome=failure&amp;search.tags=CI&amp;search.names=gitCommitId&amp;search.values=9a2687fed7b724e9d6736ce3616daa5f960ed97e&amp;)

author_association: COLLABORATOR
user: login: tresat
created_at: 2025-03-06T01:22:11Z
body: @bot-gradle test this
author_association: MEMBER
user: login: bot-gradle
created_at: 2025-03-06T01:22:24Z
body: <!-- {"replyTargetCommentId":2702483174,"triggeredTeamCityBuilds":[{"buildConfigurationId":"Gradle_Master_Check_Stage_PullRequestFeedback_Trigger","buildId":"95443717","headRef":"2158c86713aef7032600aa715d50e7ebdfdc1be7"}]} -->
I've triggered the following builds:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/build/95443717)
  - [Build Scan](https://ge.gradle.org/scans?search.tags=CI&amp;search.names=gitCommitId,PartOf&amp;search.values=2158c86713aef7032600aa715d50e7ebdfdc1be7,PullRequestFeedback&amp;)

author_association: COLLABORATOR
user: login: bot-gradle
created_at: 2025-03-06T02:33:46Z
body: The following builds have failed:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/buildConfiguration/Gradle_Master_Check_Stage_PullRequestFeedback_Trigger/95443717)
  - [Build Scan](https://ge.gradle.org/scans?search.buildOutcome=failure&amp;search.tags=CI&amp;search.names=gitCommitId&amp;search.values=2158c86713aef7032600aa715d50e7ebdfdc1be7&amp;)

author_association: COLLABORATOR
user: login: tresat
created_at: 2025-03-06T20:44:56Z
body: @bot-gradle test this
author_association: MEMBER
user: login: bot-gradle
created_at: 2025-03-06T20:45:11Z
body: <!-- {"replyTargetCommentId":2704900745,"triggeredTeamCityBuilds":[{"buildConfigurationId":"Gradle_Master_Check_Stage_PullRequestFeedback_Trigger","buildId":"95480619","headRef":"4f2e7642fdc29d408bd6586a6940d7c5a9193704"}]} -->
I've triggered the following builds:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/build/95480619)
  - [Build Scan](https://ge.gradle.org/scans?search.tags=CI&amp;search.names=gitCommitId,PartOf&amp;search.values=4f2e7642fdc29d408bd6586a6940d7c5a9193704,PullRequestFeedback&amp;)

author_association: COLLABORATOR
user: login: bot-gradle
created_at: 2025-03-06T21:33:54Z
body: The following builds have passed:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/buildConfiguration/Gradle_Master_Check_Stage_PullRequestFeedback_Trigger/95480619)
  - [Build Scan](https://ge.gradle.org/scans?search.tags=CI&amp;search.names=gitCommitId,PartOf&amp;search.values=4f2e7642fdc29d408bd6586a6940d7c5a9193704,PullRequestFeedback&amp;)

author_association: COLLABORATOR
user: login: tresat
created_at: 2025-03-18T14:44:53Z
body: @bot-gradle test this
author_association: MEMBER
user: login: bot-gradle
created_at: 2025-03-18T14:45:15Z
body: <!-- {"replyTargetCommentId":2733517098,"triggeredTeamCityBuilds":[{"buildConfigurationId":"Gradle_Master_Check_Stage_PullRequestFeedback_Trigger","buildId":"95935033","headRef":"6fe3825111b0b03123fa514cb454ac4eb27a1ee5"}]} -->
I've triggered the following builds:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/build/95935033)
  - [Build Scan](https://ge.gradle.org/scans?search.tags=CI&amp;search.names=gitCommitId,PartOf&amp;search.values=6fe3825111b0b03123fa514cb454ac4eb27a1ee5,PullRequestFeedback&amp;)

author_association: COLLABORATOR
user: login: tresat
created_at: 2025-03-18T21:02:40Z
body: @bot-gradle test this
author_association: MEMBER
user: login: bot-gradle
created_at: 2025-03-18T21:03:12Z
body: <!-- {"replyTargetCommentId":2734724069,"triggeredTeamCityBuilds":[{"buildConfigurationId":"Gradle_Master_Check_Stage_PullRequestFeedback_Trigger","buildId":"95969345","headRef":"a11c297c0d034b249997a9ea8664aa3a9a3d0f56"}]} -->
I've triggered the following builds:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/build/95969345)
  - [Build Scan](https://ge.gradle.org/scans?search.tags=CI&amp;search.names=gitCommitId,PartOf&amp;search.values=a11c297c0d034b249997a9ea8664aa3a9a3d0f56,PullRequestFeedback&amp;)

author_association: COLLABORATOR
user: login: bot-gradle
created_at: 2025-03-18T23:31:21Z
body: The following builds have failed:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/buildConfiguration/Gradle_Master_Check_Stage_PullRequestFeedback_Trigger/95935033)
  - [Build Scan](https://ge.gradle.org/scans?search.buildOutcome=failure&amp;search.tags=CI&amp;search.names=gitCommitId&amp;search.values=6fe3825111b0b03123fa514cb454ac4eb27a1ee5&amp;)

author_association: COLLABORATOR
user: login: bot-gradle
created_at: 2025-03-19T00:12:49Z
body: The following builds have failed:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/buildConfiguration/Gradle_Master_Check_Stage_PullRequestFeedback_Trigger/95969345)
  - [Build Scan](https://ge.gradle.org/scans?search.buildOutcome=failure&amp;search.tags=CI&amp;search.names=gitCommitId&amp;search.values=a11c297c0d034b249997a9ea8664aa3a9a3d0f56&amp;)

author_association: COLLABORATOR
user: login: tresat
created_at: 2025-03-19T12:53:59Z
body: @bot-gradle test this
author_association: MEMBER
user: login: bot-gradle
created_at: 2025-03-19T12:54:19Z
body: <!-- {"replyTargetCommentId":2736532484,"triggeredTeamCityBuilds":[{"buildConfigurationId":"Gradle_Release_Check_Stage_PullRequestFeedback_Trigger","buildId":"95997608","headRef":"e346f327428b681efafc4e68a78cd7dc5c1d128c"}]} -->
I've triggered the following builds:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/build/95997608)
  - [Build Scan](https://ge.gradle.org/scans?search.tags=CI&amp;search.names=gitCommitId,PartOf&amp;search.values=e346f327428b681efafc4e68a78cd7dc5c1d128c,PullRequestFeedback&amp;)

author_association: COLLABORATOR
user: login: tresat
created_at: 2025-03-19T13:56:43Z
body: @bot-gradle test and merge
author_association: MEMBER
user: login: bot-gradle
created_at: 2025-03-19T13:57:15Z
body: <!-- {"replyTargetCommentId":2736743526,"triggeredTeamCityBuilds":[]} -->
Sorry, we can't process your request because of the following error:

```
Pull request Required status check "check_wrapper" is expected.
```
author_association: COLLABORATOR
user: login: bot-gradle
created_at: 2025-03-19T14:28:49Z
body: The following builds have passed:
- [Pull Request Feedback (Trigger) (Check)](https://builds.gradle.org/buildConfiguration/Gradle_Release_Check_Stage_PullRequestFeedback_Trigger/95997608)
  - [Build Scan](https://ge.gradle.org/scans?search.tags=CI&amp;search.names=gitCommitId,PartOf&amp;search.values=e346f327428b681efafc4e68a78cd7dc5c1d128c,PullRequestFeedback&amp;)

author_association: COLLABORATOR
user: login: tresat
created_at: 2025-03-19T14:34:12Z
body: @bot-gradle test and merge
author_association: MEMBER
user: login: bot-gradle
created_at: 2025-03-19T14:34:52Z
body: <!-- {"replyTargetCommentId":2736871050,"triggeredTeamCityBuilds":[]} -->
Sorry, we can't process your request because of the following error:

```
Pull request Required status check "check_wrapper" is expected.
```
author_association: COLLABORATOR
user: login: tresat
created_at: 2025-03-19T17:09:01Z
body: @bot-gradle test and merge
author_association: MEMBER