Skip to content

Preserve ItemGroup evaluation order in template manifest normalization #20629

Closed

Description

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

Non-blocking follow-up to this review comment on #20574.

TemplateRestorePlan.Normalize in tools/GenerateTemplateManifest/TemplateRestorePlan.cs sorts unconditional ItemGroup children to canonicalize restore graphs. These rendered projects have not yet been evaluated by MSBuild, so sorting can change the meaning of order-sensitive item expressions.

For example:

<ItemGroup>
  <TemplatePackage Include="System.CommandLine" Version="2.0.12" />
  <PackageReference Include="@(TemplatePackage)" />
</ItemGroup>

Sorting moves PackageReference before TemplatePackage. The reviewer reports that an MSBuild/restore probe restored System.CommandLine/2.0.12 from the original project, while the normalized project restored successfully with no libraries or dependencies. The resulting manifest would silently omit the package.

No current template uses an @(...) item expression, so this is not evidence that the current manifest is incomplete. This issue should not block #20574.

Expected Behavior

Normalization should preserve ItemGroup document order, including order across combined groups, so evaluating the generated project selects the same dependencies as evaluating the original.

Prefer preserving item order rather than attempting to recognize every order-sensitive expression. Possible approaches include canonicalizing only safe PropertyGroup shapes, or combining item groups without sorting their children.

Add an MSBuild-evaluation/restore regression test that proves the package in the example remains present after normalization. Confirm that existing template graph deduplication and manifest output remain correct.

Steps To Reproduce

  1. Create a minimal SDK-style project targeting a supported framework and containing the ItemGroup above.
  2. Evaluate/restore the original project and record its package references and project.assets.json libraries.
  3. Pass the project XML through TemplateRestorePlan.Normalize, save the normalized project in an isolated directory, and evaluate/restore it.
  4. Compare the results. As reported in the linked review, sorting places the item consumer before its declaration, omitting System.CommandLine without causing restore to fail.

Exceptions (if any)

None reported: restore succeeds with the dependency missing.

Aspire doctor output

N/A — build-time template manifest normalization.

Anything else?

The reviewer also reports that removing ItemGroup sorting did not change the current plan size: 138 configurations / 518 project instances → 78 unique projects / 105 framework graphs. Recheck this when implementing the fix; preserving order may have no deduplication cost for today's templates.

Related: #20451. The existing guard against moving groups across top-level Choose/Import/Target elements addresses a different evaluation-order issue; this follow-up concerns sorting the item declarations themselves.

Activity

  1. joperezr commented on Sep 30, 2026

    @joperezr
    Member
    🤖 Aspire triage pass — 2026-09-30 22:43 UTC

    Area: area-engineering-systems
    Kind: bug
    Repro: yes-minimal — see lines 13–20 and 32–37 of the issue body
    Affected version(s): unreleased main after #20574 (latest: 13.6.0)
    Severity: S3 — normalization can silently omit a dependency, but no current template uses the affected item-expression shape

    Urgency: ⚪ backlog — no milestone
    Reason: valid and actionable follow-up, but there is no demonstrated impact to current templates or a released Aspire version

    Suggested owner(s): Ankit Jain (@radical) (score 55)
    Evidence:

    Confidence: high
    Needs human?: no

  2. added a commit that references this issue on Sep 30, 2026
    f9de3f7
  3. locked and limited conversation to collaborators on Oct 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions