Enlighten AssignProjectConfiguration task for multithreaded mode
Parent: #11834
Context
AssignProjectConfiguration is the highest-execution unmigrated task (22.9B total executions) listed in the migration epic (#11834) under "Other (either simple or with unknown issues)". It maps solution configuration/platform pairs onto project references, optionally adding synthetic project references for solution-level dependencies.
The task has no cwd-dependent operations:
- No
Environment.CurrentDirectory, Path.GetFullPath, or File.*/Directory.* usage
- All path resolution uses MSBuild's
GetMetadata("FullPath") which returns absolute paths resolved by the engine
- Platform mapping dictionaries are instance-scoped lazy-init — no cross-thread sharing
- The only static method (
SetBuildInProjectAndReferenceOutputAssemblyMetadata) is pure logic with no mutable state
- Base class
ResolveProjectBase uses reference.GetMetadata("FullPath") — engine-resolved absolute paths
This is an attribute-only migration: the task needs [MSBuildMultiThreadableTask] but does NOT need IMultiThreadableTask or TaskEnvironment because it performs no file system operations, environment variable access, or process launches.
Approach
Apply the PR #13045 (attribute-only) pattern: add [MSBuildMultiThreadableTask] to the class declaration. No IMultiThreadableTask implementation is needed since the task has no cwd-dependent operations.
- Decorate
AssignProjectConfiguration with [MSBuildMultiThreadableTask].
- No other code changes required — the task is already thread-safe by design.
ChangeWave consideration
No behavioral change — the attribute simply declares the task safe for multithreaded scheduling. Observable behavior (return value, [Output] properties, error/warning messages) is byte-identical. No ChangeWave needed.
Test coverage assessment
Existing unit tests (src/Tasks.UnitTests/AssignProjectConfiguration_Tests.cs)
Existing tests cover configuration assignment, platform mapping, synthetic project references, and edge cases. Coverage is good for the functional behavior.
Gaps to fill in this PR
- G1 — Concurrency test. Two
AssignProjectConfiguration instances with different SolutionConfigurationContents and ProjectReferences execute concurrently, asserting each produces correct, independent output. Verifies no shared mutable state interference.
- G2 — Baseline parity. Verify all existing tests continue to pass with no behavioral changes.
[Output] AssignedProjects items have identical metadata (Configuration, Platform, SetConfiguration, SetPlatform, etc.) before and after the attribute addition.
Acceptance criteria
Implementation order
- Apply
[MSBuildMultiThreadableTask] to AssignProjectConfiguration.
- Add G1 (concurrency) test.
- Run
Tasks.UnitTests; verify clean.
- Run repo build with
-v quiet to ensure no new warnings.
Risks / open questions
- Base class
ResolveProjectBase is also used by ResolveNonMSBuildProjectOutput. The attribute has Inherited = false, so each concrete class must be independently decorated — no implicit inheritance of the attribute from the base class. This is correct and intentional.
AddSyntheticProjectReferences mutates the _projectReferences array via ResolveProjectBase. This is instance state, not static — safe under the per-instance-per-thread model.
References
Enlighten AssignProjectConfiguration task for multithreaded mode
Parent: #11834
Context
AssignProjectConfigurationis the highest-execution unmigrated task (22.9B total executions) listed in the migration epic (#11834) under "Other (either simple or with unknown issues)". It maps solution configuration/platform pairs onto project references, optionally adding synthetic project references for solution-level dependencies.The task has no cwd-dependent operations:
Environment.CurrentDirectory,Path.GetFullPath, orFile.*/Directory.*usageGetMetadata("FullPath")which returns absolute paths resolved by the engineSetBuildInProjectAndReferenceOutputAssemblyMetadata) is pure logic with no mutable stateResolveProjectBaseusesreference.GetMetadata("FullPath")— engine-resolved absolute pathsThis is an attribute-only migration: the task needs
[MSBuildMultiThreadableTask]but does NOT needIMultiThreadableTaskorTaskEnvironmentbecause it performs no file system operations, environment variable access, or process launches.Approach
Apply the PR #13045 (attribute-only) pattern: add
[MSBuildMultiThreadableTask]to the class declaration. NoIMultiThreadableTaskimplementation is needed since the task has no cwd-dependent operations.AssignProjectConfigurationwith[MSBuildMultiThreadableTask].ChangeWave consideration
No behavioral change — the attribute simply declares the task safe for multithreaded scheduling. Observable behavior (return value,
[Output]properties, error/warning messages) is byte-identical. No ChangeWave needed.Test coverage assessment
Existing unit tests (
src/Tasks.UnitTests/AssignProjectConfiguration_Tests.cs)Existing tests cover configuration assignment, platform mapping, synthetic project references, and edge cases. Coverage is good for the functional behavior.
Gaps to fill in this PR
AssignProjectConfigurationinstances with differentSolutionConfigurationContentsandProjectReferencesexecute concurrently, asserting each produces correct, independent output. Verifies no shared mutable state interference.[Output] AssignedProjectsitems have identical metadata (Configuration, Platform, SetConfiguration, SetPlatform, etc.) before and after the attribute addition.Acceptance criteria
AssignProjectConfigurationdecorated with[MSBuildMultiThreadableTask].IMultiThreadableTaskimplementation (not needed — no cwd-dependent operations).AssignProjectConfiguration_Testspass onnet472andnet10.0.[Output] AssignedProjectsitems have byte-identicalItemSpecand metadata.multithreaded-task-migrationSKILL sign-off checklist walked and passes.Implementation order
[MSBuildMultiThreadableTask]toAssignProjectConfiguration.Tasks.UnitTests; verify clean.-v quietto ensure no new warnings.Risks / open questions
ResolveProjectBaseis also used byResolveNonMSBuildProjectOutput. The attribute hasInherited = false, so each concrete class must be independently decorated — no implicit inheritance of the attribute from the base class. This is correct and intentional.AddSyntheticProjectReferencesmutates the_projectReferencesarray viaResolveProjectBase. This is instance state, not static — safe under the per-instance-per-thread model.References
.github/skills/multithreaded-task-migration/SKILL.md