Skip to content

Standardize NuGet restore and channel-update policy across AppHosts - #19763

Merged
David Negstad (danegsta) merged 69 commits into
mainfrom
danegsta/shared-integration-closure
Oct 15, 2026
Merged

David Negstad (danegsta) merged 69 commits into
mainfrom
danegsta/shared-integration-closure

Conversation

@danegsta

@danegsta David Negstad (danegsta) commented Aug 28, 2026 •

Copy link
Copy Markdown
Member

Description

Polyglot integration restore, package/template discovery, and C# channel updates currently compose NuGet policy in different ways. This can make the packages offered by discovery disagree with what can be restored, change configuration behavior when an integration project reference is added, or leave persisted AppHost configuration inconsistent with an update.

This PR shares NuGet configuration composition across those paths. Aspire-selected sources and mappings are applied alongside NuGet's native configuration hierarchy, reusing existing source aliases and inheriting their authentication and transport settings. Package and project integrations resolve together, discovery checks source eligibility, and explicit C# channel updates preview configuration changes before restoring and persisting them.

Polyglot integration restore

  • Package-only AppHosts restore all integration packages through the CLI's in-process NuGet client without requiring the .NET SDK.
  • AppHosts with project references use one generated IntegrationRestore.csproj containing the direct integration packages and projects. NuGet resolves their shared dependency graph and reports incompatible requirements during restore.
  • The generated root targets the current host runtime identifier. Referenced projects retain their own target and restore behavior. Package assets remain in NuGet package folders; project-built output is copied into a content-addressed snapshot.

Configuration discovery is anchored at the AppHost rather than the generated cache directory. Both restore implementations use the same selected SDK, channel, source override, and evaluated NuGet settings. Overlays express the selected source/mapping policy; operations requiring a separate package cache use one instead of the ordinary global-packages folder.

Referenced projects continue to discover configuration from their own directories. Opt-in MSBuild properties provide hosting-version and credential-free source hints without automatically replacing the projects' restore configuration.

Integration and template discovery

Bundled and SDK-driven discovery check effective package-source mappings before offering ordinary search or exact-version results. Bundled discovery filters before cross-source deduplication so an ineligible feed's higher version cannot hide an eligible result.

An explicit --source restricts discovery and template installation to that source while retaining an existing selected alias's NuGet settings. Dependency restore has a different scope: after polyglot aspire add selects a package, its package ID is mapped to the explicit source, but other eligible sources remain available for dependencies. The selected integration feed is not assumed to contain every dependency.

C# AppHost updates

Unqualified updates preserve the current native NuGet policy rather than selecting replacement feeds from CLI or Aspire channel metadata. Explicit channel updates compose a configuration candidate and show it through the existing NuGet preview and approval flow. Project/package edits are staged, the updated graph is restored through native MSBuild, and configuration and metadata changes are persisted only after restore succeeds. Handled failures roll back owned file edits.

An unresolved AppHost SDK can be repaired before strict restore-policy validation, without allowing unknown or overridden policy to be persisted. Fallback project parsing also normalizes MSBuild project-reference separators across platforms.

When an existing mapping policy is Aspire-only, the candidate can add * mappings for enabled, previously unmapped ambient feeds so unrelated dependencies remain eligible. These mapping changes appear in the same approval; inherited feed URLs and credentials are not copied into the local file. Unrelated mapping restrictions are retained. Explicit stable selection returns to existing ambient feeds without adding NuGet.org; an explicitly supplied service-index override remains a separate source selection.

Known trade-offs

  • aspire add does not persist per-integration channel or source provenance. A package selected from another channel or an explicit source can restore for that invocation but require a project-wide channel change or user-owned NuGet configuration for a later cold restore.
  • The project-reference path always runs implicit restore before building the generated integration project. It favors correctness over warm-build performance; package-only restores retain their reusable cache.
  • Package-only restores do not fully support NuGet fallback package folders from configuration or NUGET_FALLBACK_PACKAGES. Manifest asset resolution across those folders remains tracked by Support NuGet fallback package folders in polyglot package restores #20631.

NuGet configuration and diagnostics

NuGet remains responsible for evaluating configuration and handling authentication and trust settings. Overlays reuse existing source aliases and leave those settings in their original hierarchy rather than copying them into generated configuration.

Credential-bearing explicit --source values are rejected. For configured sources containing inline credentials, diagnostic handling suppresses raw invocation logging and applies best-effort redaction using known URL spellings and credential components. This is not a general-purpose credential scrubber. Generated NuGet artifacts are not scrubbed and can still contain configured source URLs; credential providers and credential sections remain preferred over inline URL credentials.

Related: #20631 tracks the remaining fallback-package-folder limitation.

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

@danegsta David Negstad (danegsta) changed the title Share generated integration closure infrastructure Make polyglot integration restores consistent and cache-safe Aug 28, 2026
@danegsta
David Negstad (danegsta) requested a balanced review from Copilot August 28, 2026 00:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Unifies polyglot integration restore behavior across generated AppHost projects, including source resolution, closure generation, cache layout, and runtime configuration.

Changes:

  • Adds shared C# project, restore-source, closure, and environment helpers.
  • Updates prebuilt and repository-mode AppHost generation.
  • Expands restore fingerprinting and project-reference tests.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/Aspire.Cli.Tests/Projects/PrebuiltAppHostServerTests.cs Tests restore configuration, closure outputs, and cache normalization.
src/Aspire.Cli/Utils/CliPathHelper.cs Adds path and staging-cache helpers.
src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs Adopts shared restore and closure infrastructure.
src/Aspire.Cli/Projects/IntegrationRestoreSourceResolver.cs Centralizes channel and source resolution.
src/Aspire.Cli/Projects/IntegrationClosureEnvironment.cs Centralizes runtime integration environment variables.
src/Aspire.Cli/Projects/IntegrationClosureBuilder.cs Centralizes closure project generation and manifest reading.
src/Aspire.Cli/Projects/DotNetBasedAppHostServerProject.cs Uses the shared C# project model.
src/Aspire.Cli/Projects/CSharpProjectFile.cs Introduces a generated C# project model.

Comment thread src/Aspire.Cli/Projects/DotNetBasedAppHostServerProject.cs Outdated
Comment thread src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs Outdated
Comment thread src/Aspire.Cli/Projects/IntegrationRestoreSourceResolver.cs Outdated
Copilot AI review requested due to automatic review settings August 28, 2026 01:47
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 19763

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 19763"

@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Suppressed comments (1)

src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs:750

  • This only checks PackageSourceMappings, but the no-channel path returns all explicit channel URLs in AdditionalSources while leaving PackageSourceMappings null. If one of those channels contains a SAS/user-info URL, it is written into the long-lived IntegrationRestore.csproj as RestoreAdditionalProjectSources, and restore-cache skipping remains enabled. Include all effective sources in the credential check and keep the generated project/config ephemeral (or reject the ambiguous no-channel case) so credentials are never persisted.
        var hasCredentialBearingRestoreSource = restoreSources.PackageSourceMappings?.Any(
            static mapping => PackageSourceOverrideMappings.HasCredentialMaterial(mapping.Source)) == true;

Comment thread src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs Outdated
Comment thread src/Aspire.Cli/Projects/CSharpProjectFile.cs
Copilot AI review requested due to automatic review settings August 28, 2026 02:05
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment thread src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs Outdated
Comment thread src/Aspire.Cli/Projects/PrebuiltAppHostServer.cs Outdated
Copilot AI review requested due to automatic review settings August 28, 2026 02:39
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/Aspire.Cli/Projects/DotNetBasedAppHostServerProject.cs:164

  • This set now stores project paths, but OrdinalIgnoreCase collapses distinct paths such as /src/Foo/Foo.csproj and /src/foo/foo.csproj on case-sensitive Linux/macOS filesystems, silently omitting one valid ProjectReference. Use ordinal path identity here; the restore graph uses the same rule specifically to avoid dropping distinct paths (PrebuiltAppHostServer.cs:378-380).
        var addedProjects = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

Comment thread src/Aspire.Cli/Packaging/NuGetConfigComposer.cs Outdated
Base automatically changed from danegsta/apphost-path-normalization to main September 2, 2026 02:28
Copilot AI review requested due to automatic review settings September 2, 2026 02:28
@danegsta
David Negstad (danegsta) force-pushed the danegsta/shared-integration-closure branch from c9c2ea5 to 7ea840a Compare September 2, 2026 02:28
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Case-sensitive identity comparisons can preserve overridden NuGet sources or credentials.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 12/12 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread src/Aspire.Cli/Packaging/NuGetConfigComposer.cs Outdated
Copilot AI review requested due to automatic review settings September 2, 2026 02:40
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

NuGet’s Unix $NAME variables are incorrectly converted into literal relative filesystem paths.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

src/Aspire.Cli/Packaging/NuGetConfigComposer.cs:251

  • On macOS/Linux, NuGet supports $NAME environment variables in config values, but Environment.ExpandEnvironmentVariables only expands %NAME%. A valid source such as $PACKAGEHOME/packages therefore remains relative here and is rewritten to <config-dir>/$PACKAGEHOME/packages, causing restore to use the wrong path. Expand NuGet's platform-specific syntax (or use NuGet.Configuration to obtain the effective value) before resolving relative paths.
  • Files reviewed: 12/12 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI review requested due to automatic review settings September 2, 2026 02:50
Update the JSON SDK version independently of channel-name persistence. Explicit stable updates clear previous channel pins while implicit updates preserve the existing selection. Cover successful updates, metadata-only repairs, decline, restore rollback, and channel-removal display.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d38799a7-a99c-48c5-b45b-9d6594dd1830
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The 189-file, restore- and security-sensitive change needs final human review and a successful current-head CI run.

0 open findings

Files not reviewed (2)
  • src/Aspire.Cli/Resources/AddCommandStrings.Designer.cs: Generated file
  • src/Aspire.Cli/Resources/UpdateCommandStrings.Designer.cs: Generated file

🧠 Review effort: Balanced

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

Give no-op fixtures matching SDK metadata and expect accepted stable updates to clear prior channel pins. Update C# E2E preview assertions and verify declining leaves the complete configuration unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d38799a7-a99c-48c5-b45b-9d6594dd1830

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The broad security-sensitive restore, credential, persistence, and rollback changes require final human validation.

0 open findings

Files not reviewed (2)
  • src/Aspire.Cli/Resources/AddCommandStrings.Designer.cs: Generated file
  • src/Aspire.Cli/Resources/UpdateCommandStrings.Designer.cs: Generated file

🧠 Review effort: Balanced

Merge origin/main at 517d566, preserving shared NuGet restore policy and AppHost workload identity while incorporating npm hosting integrations. Adapt package-entry and modern AppHost fixtures, and verify the combined npm, NuGet, and project-reference closure.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d38799a7-a99c-48c5-b45b-9d6594dd1830
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Relative template source overrides are resolved from the generated overlay directory, breaking local --source installation.

1 open finding
Files not reviewed (2)
  • src/Aspire.Cli/Resources/AddCommandStrings.Designer.cs: Generated file
  • src/Aspire.Cli/Resources/UpdateCommandStrings.Designer.cs: Generated file

🧠 Review effort: Balanced

Comment thread src/Aspire.Cli/Templating/TemplateNuGetConfigService.cs
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

Merge origin/main at 9cc6730. Preserve native restore-policy projection and transactional updates while integrating package-only AppHosts and imported or conditional SDK declarations. Track imported files for rollback, avoid acquiring the deprecated SDK for package-only AppHosts, and adapt upstream fixtures to source eligibility filtering.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d38799a7-a99c-48c5-b45b-9d6594dd1830

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Cross-cutting restore policy, configuration inheritance, credential handling, and update transactions require final human review.

0 open findings

1 resolved since last review
Files not reviewed (2)
  • src/Aspire.Cli/Resources/AddCommandStrings.Designer.cs: Generated file
  • src/Aspire.Cli/Resources/UpdateCommandStrings.Designer.cs: Generated file

🧠 Review effort: Balanced

Merge origin/main at 4096792. Preserve the repository guards and read-only workflow permissions while retaining actions:read across the specialized test workflow chain for artifact-backed acquisition tests. Add structural permission coverage for the reusable-workflow boundaries.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d38799a7-a99c-48c5-b45b-9d6594dd1830
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Broad changes to restore, configuration persistence, and authentication handling require final human review and approval.

0 open findings

Files not reviewed (2)
  • src/Aspire.Cli/Resources/AddCommandStrings.Designer.cs: Generated file
  • src/Aspire.Cli/Resources/UpdateCommandStrings.Designer.cs: Generated file

🧠 Review effort: Balanced

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@mitchdenny

Copy link
Copy Markdown
Member

/deployment-test

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Deployment tests starting on PR #19763...

This will deploy to real Azure infrastructure. Results will be posted here when complete.

View workflow run

@eerhardt

Copy link
Copy Markdown
Member

PR #19763 readiness assessment

Recommendation

Conditionally merge-ready for the PR-specific restore and channel-policy changes, pending completion of macOS CLI CI and required approval.

The targeted scenarios below found no new blocking regression in mixed polyglot restore, accepted channel transitions, or persisted-file rollback. This is a focused readiness assessment, not a complete line-by-line review or a blanket statement that every CLI scenario works.

Two real failures were observed: accepted file-based C# updates fail while trying to edit a synthetic project path, and failed TypeScript updates return exit code zero. Both behaviors follow logic already present in the local origin/main baseline; they are not established as regressions introduced by this PR. They should be tracked or fixed independently rather than hidden by the passing scenarios.

PR and artifact identity

  • PR: Standardize NuGet restore and channel-update policy across AppHosts #19763
  • Title: Standardize NuGet restore and channel-update policy across AppHosts
  • Tested date: 2026-10-14
  • Verified PR head and local checkout: 24e280b7d29a59075515a626fcacc46068417f40
  • Installed CLI: 17.0.0-pr.19763.g24e280b7
  • Artifact-producing run: https://github.com/microsoft/aspire/actions/runs/38631694503
  • Baseline used for source attribution: local origin/main, dbaaddc8cb4c71d9b6428d2bf07c2c438cb20d34
  • Environment: Windows; local temporary directories; Node.js; installed .NET SDKs including 10.0.402 and 11.0.100-rc.1.26425.128. Normal dotnet selection in the temporary workspace was the .NET 11 RC SDK.
  • Isolation: Separate fresh NUGET_PACKAGES directories for bootstrap, package-only restore, mixed restore, each channel update, each ordinary restore, each induced failure, and recovery.
  • Changes: No repository files were modified. Only disposable test fixtures and session artifacts were written.

The normal PR installer was used without changing HOME, USERPROFILE, APPDATA, or the user's normal CLI installation:

iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 19763 -InstallPath '$root' -SkipExtension -SkipPath"
& $cli --version
gh pr view 19763 --repo microsoft/aspire --json headRefOid --jq .headRefOid

Result: Installation passed; installed version matched the current PR commit. The head was rechecked after execution and remained unchanged.

Changes analyzed

Relevant production surfaces are package-only in-process NuGet restore, SDK-backed mixed package/project restore, source mappings and configuration hierarchy, generated polyglot SDKs, C# transactional channel updates, and polyglot channel metadata persistence.

The PR also changes CLI templates, tests, GitHub Actions test runners, and MSBuild CI test properties. It does not change the dashboard, client integrations, or hosting-library APIs in the reviewed diff.

Local scenarios

In the commands below, $cli denotes the exact installed PR binary, $root is the securely created temporary test directory, and $hive is its downloaded hives\pr-19763\packages directory. Every fixture began with aspire new; the project-based C# fixture was then adapted from its generated C# skeleton into an ordinary SDK-style project.

1. Cold package-only restore with custom mappings — PASSED

Created a fresh TypeScript AppHost:

& $cli new aspire-empty --language typescript --name MixedReadiness --output "$root\MixedReadiness" --source $hive --version 17.0.0-pr.19763.g24e280b7 --localhost-tld false --suppress-agent-init --non-interactive
& $cli add Aspire.Hosting.Redis --version 17.0.0-pr.19763.g24e280b7 --source $hive --apphost apphost.mts --non-interactive

The user-owned test NuGet configuration cleared inherited sources and mappings, mapped Aspire* to the PR hive using a relative path, and mapped other packages to the approved dotnet-public mirror.

Result: Both commands exited 0. The Redis reference was persisted and its API generated using a fresh package cache.

Evidence: readiness-evidence\create-mixed.log, mixed-package-add.log, and preserved fixture inputs.

2. Cold mixed package/project restore and runtime — PASSED

Added ReadinessIntegration.csproj to the TypeScript AppHost's package configuration alongside Redis. The project references Aspire.Hosting through $(AspireIntegrationHostingVersion) and inherits its own directory-scoped NuGet policy. Its exported AddReadinessWorker creates a Node executable resource.

& $cli restore --apphost apphost.mts --non-interactive
& $cli start --apphost apphost.mts --isolated --non-interactive --format Json
& $cli wait readiness-worker --status up --timeout 120 --apphost apphost.mts --non-interactive
& $cli describe --apphost apphost.mts --format Json --non-interactive
& $cli stop --apphost apphost.mts --non-interactive

Result: Restore, start, wait, and stop exited 0. Generated SDK contained both addRedis and addReadinessWorker. Structured resource output reported state: Running and healthStatus: Healthy for the custom worker. The ambient NuGet configuration was byte-for-byte unchanged.

This tests the SDK-backed integration graph, custom API generation, assembly loading, RPC invocation, resource execution, and exact-target shutdown—not just compilation.

Evidence: mixed-cold-restore.log, mixed-start.log with dashboard login token redacted, mixed-wait.log, mixed-describe.log, mixed-stop.log.

3. Accepted TypeScript PR → stable → PR round-trip — PASSED

Created a separate TypeScript AppHost pinned to pr-19763. Its ambient configuration contains only the approved mirror with a catch-all mapping, so the PR policy must provide PR packages while stable must return to ambient policy.

& $cli update --channel stable --yes --non-interactive --apphost apphost.mts
& $cli restore --apphost apphost.mts --non-interactive
& $cli update --channel pr-19763 --yes --non-interactive --apphost apphost.mts
& $cli restore --apphost apphost.mts --non-interactive

A different empty package directory was selected before every command.

Result: All four commands exited 0. Stable selected SDK 13.6.1 and removed the channel pin. Returning to PR selected 17.0.0-pr.19763.g24e280b7 and persisted pr-19763. Both ordinary cold restores succeeded. The user NuGet configuration was byte-for-byte unchanged across the complete round-trip.

Evidence: ts-channel-stable.log, ts-stable-ordinary-restore.log, ts-channel-back-pr.log, ts-pr-ordinary-restore.log.

4. Accepted project-based C# PR → stable → PR round-trip — PASSED

The fixture uses an ordinary Microsoft.NET.Sdk project with Aspire.Hosting.AppHost, plus Aspire metadata and the NuGet configuration produced by the CLI.

& $cli update --channel stable --yes --non-interactive --apphost ChannelReadiness.csproj
dotnet restore ChannelReadiness.csproj --verbosity quiet
& $cli update --channel pr-19763 --yes --non-interactive --nuget-config-dir . --apphost ChannelReadiness.csproj
dotnet restore ChannelReadiness.csproj --verbosity quiet

A different empty package directory was selected before every command.

Result: All four commands exited 0. Project package version, SDK metadata, channel metadata, and persisted NuGet policy moved together. Stable used 13.6.1 with no channel pin; PR restored 17.0.0-pr.19763.g24e280b7 with the PR pin. Ordinary native dotnet restore, without Aspire's candidate preview, succeeded after each transition.

Evidence: csproj-channel-stable.log, csproj-stable-ordinary-restore.log, csproj-channel-back-pr.log, csproj-pr-ordinary-restore.log.

5. C# candidate restore failure and rollback — PASSED

After returning to PR, added a fixture-only MSBuild target that rejects restore when the package version changes away from the PR version. This permits discovery and package editing, but fails validation of the updated stable candidate.

& $cli update --channel stable --yes --non-interactive --apphost ChannelReadiness.csproj

Expected: Nonzero exit; project, NuGet configuration, and Aspire metadata restored to their original bytes.

Result: Exit 13 with an explicit restore failure. All three owned files were byte-for-byte unchanged. After removing only the deliberately injected failure target, native restore with another empty cache succeeded.

Evidence: csproj-failed-update.log, rollback-before-* baseline files, csproj-recovery.log.

6. TypeScript regeneration failure — PERSISTENCE PASSED; EXIT CONTRACT FAILED

Added a more-specific ambient mapping for Aspire.Hosting.CodeGeneration.* pointing to an empty local source. This allows stable SDK version discovery but prevents the code-generator dependency from restoring. Used a fresh package cache.

& $cli update --channel stable --yes --non-interactive --apphost apphost.mts

Expected: Explicit failure, nonzero exit, and unchanged persisted SDK/channel configuration.

Result: The CLI reported failure to restore Aspire.Hosting.CodeGeneration.TypeScript and displayed Failed to prepare AppHost server. The previous PR SDK and channel configuration remained byte-for-byte unchanged. However, the process exited 0.

After removing only the deliberately blocking mapping, a cold ordinary restore succeeded.

Attribution: The existing UpdateCommand ignores the result from project.UpdatePackagesAsync; the guest update path returns UpdatesApplied = false for regeneration failure, and the command ultimately returns exit 0. The same control flow exists in the local origin/main baseline. This is an observed pre-existing defect, not a demonstrated new regression.

Code: src/Aspire.Cli/Commands/UpdateCommand.cs, src/Aspire.Cli/Projects/GuestAppHostProject.cs.

Evidence: ts-failed-update.log, ts-rollback-before.json, ts-recovery.log.

7. Mixed-graph add with an unavailable project dependency — PASSED

Added a fixture-only nonexistent package reference to the project integration, then attempted to add PostgreSQL from the PR hive using another empty cache:

& $cli add Aspire.Hosting.PostgreSQL --version 17.0.0-pr.19763.g24e280b7 --source $hive --apphost apphost.mts --non-interactive

Expected: Graph restore fails clearly; PostgreSQL is not partially persisted.

Result: Exit 5 with the missing dependency and applicable source-mapping diagnostic. The persisted package list was byte-for-byte unchanged. Removing the injected missing reference and restoring with another empty cache succeeded.

Evidence: mixed-failed-add.log, mixed-before-failed-add.json, mixed-recovery.log.

8. Accepted file-based C# update — FAILED, PRE-EXISTING LOGIC

The unmodified C# empty template generated by the PR CLI uses apphost.cs.

& $cli restore --apphost apphost.cs --non-interactive
& $cli update --apphost apphost.cs --channel stable --yes --non-interactive

Result: Initial restore exited 0. The accepted update exited 1:

Could not find file '...\ChannelReadiness\apphost.cs.csproj'.

MSBuild reports DefiningProjectFullPath as the synthetic apphost.cs.csproj. The updater treats this as an imported package declaration and attempts to load it as an on-disk XML file. The stack identifies ProjectUpdater.UpdateImportedPackageReferenceAsync. The original AppHost, Aspire configuration, and NuGet configuration remained byte-for-byte unchanged.

The synthetic metadata was reproduced with both the default .NET 11 RC CLI and a direct invocation of the installed .NET 10.0.402 CLI assembly. The defining-file selection and imported-file update logic are already present in the local origin/main baseline.

Impact: Successful accepted updates of file-based C# AppHosts were not validated. The project-based C# success above is not a substitute for that scenario.

Evidence: channel-initial-restore.log, channel-to-stable.log, original fixture snapshots, and the preserved CLI failure log.

Current-head CI evidence

Main run: https://github.com/microsoft/aspire/actions/runs/38631694503

Completed job logs were downloaded directly rather than relying only on green check names.

Coverage Observed execution evidence
Python validation 54 AppHosts passed, 0 failed
Go validation 53 AppHosts passed, 0 failed
Java validation 54 AppHosts passed, 0 failed
TypeScript validation 59 AppHosts passed, 0 failed
Rust validation Redis package added, Rust SDK generated, Redis container confirmed running
ProjectReferenceTests 1 test, 0 failed, 0 skipped
ChannelUpdateWorkflowTests 4 tests, 0 failed, 0 skipped; these preview/decline transitions rather than proving accepted transitions
EmulatedLocalReleaseBuildTests 7 total, 0 failed, 2 skipped; skipped rows are the C# and TypeScript starter scaffolding cases. Package-scoped-source append and four identity-channel rows executed
Infrastructure.Tests 1,607 tests, 0 failed, 0 skipped
CLI unit jobs Windows and Ubuntu jobs report success; macOS remains queued

The downloaded language and test logs are retained in the session artifacts directory.

CI infrastructure validation

  • Changes affect run-tests.yml, specialized-test-runner.yml, outerloop/quarantine callers, and CITestsProperties.props.
  • Shared runner changes preserve runsheet transport through environment variables and add read-only Actions permission for archive access. run-tests.yml supplies a GitHub token only for archive/token-requiring consumers.
  • Polyglot validation consumes the built Linux CLI archive and general/RID-specific NuGet artifacts. Current-head logs confirm actual package restore and generated-language validation, with Rust additionally confirming resource startup.
  • The existing tests/PolyglotAppHosts/** path rule routes fixture configuration changes to job:polyglot.
  • Current-head outerloop and quarantine workflows both passed their PR smoke runs. Each exercised the Acquisition project across Windows/Linux/macOS; native archive, package build, and CLI E2E image jobs were skipped. This does not validate the archive/token-requiring specialized lane or the complete scheduled project set.
  • No workflows were manually dispatched. Full scheduled/dispatch specialized-runner coverage remains unvalidated.
  • No gh-aw source/lock changes or Azure DevOps pipeline YAML changes are present in the reviewed diff; no corresponding compile or Azure DevOps run was performed.
  • macOS regular CLI tests remained queued at the final status check. GitHub still reported BLOCKED / REVIEW_REQUIRED.

Remaining hairy areas and limits

  • Per-integration source/channel provenance remains invocation-only: a successful add may require persistent user policy for a later cold restore.
  • Referenced projects retain their own NuGet hierarchy. The local mixed fixture verifies compatible inherited mappings; it does not prove every independent external-project mapping arrangement.
  • Fallback package folders remain unsupported for package-only integration restore, tracked by Support NuGet fallback package folders in polyglot package restores #20631.
  • Project-reference restores intentionally favor correctness over warm-build speed; no performance benchmark was run.
  • No private authenticated feed, interactive credential-provider, client-certificate, trust-store, concurrent-writer, cancellation, process-crash, or disk-full scenario was exercised locally.
  • The observed exit-code and file-based-update defects need separate attention even though their relevant control flow predates this PR.

Final disposition

No newly introduced merge-blocking regression was established by this targeted assessment. The strongest PR-specific boundaries passed: cold mixed restore plus execution; accepted TypeScript and project-based C# channel round-trips; ordinary cold restores after persistence; and rollback following candidate failure.

This supports merging the PR once required CI and review complete, while explicitly recording the two pre-existing failures. It does not support describing all local scenarios as passing.

Artifacts and cleanup

  • Report: pr-19763-readiness-report.md in this session's files directory.
  • Local logs, fixture inputs, and before-failure snapshots: readiness-evidence\.
  • CI logs: project-reference-ci.log, channel-update-ci.log, emulated-release-ci.log, infrastructure-ci.log, and the five *-validation-ci.log files.
  • Dashboard login token was redacted from the preserved local start log.
  • The test AppHost was explicitly stopped; a final exact-target describe confirmed that no AppHost was running. The temporary test directory, installations, and caches were removed after evidence preservation.
  • Posting this assessment requires explicit user confirmation; the previous October 8 blocked-testing comment is a separate report.

Map file-based MSBuild defining-project identities back to the source AppHost while preserving real imported declarations and rollback. Report SDK regeneration failures through the existing nonzero update error path before migrations or CLI self-update. Add native metadata, package editing, restore, rollback, and command-exit coverage with the agreed localized failure message.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: d38799a7-a99c-48c5-b45b-9d6594dd1830
@github-actions

Copy link
Copy Markdown
Contributor

Tests selector

Selects the full PR test matrix + all PR-gated jobs (ALL) — a rule matching '.github/workflows/run-tests.yml' selects ALL


Selection computed for commit 0ef87af.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Broad restore and configuration changes require human approval; validation was static, with builds and tests not run.

0 open findings

Files not reviewed (2)
  • src/Aspire.Cli/Resources/AddCommandStrings.Designer.cs: Generated file
  • src/Aspire.Cli/Resources/UpdateCommandStrings.Designer.cs: Generated file

🧠 Review effort: Balanced

@radical

Copy link
Copy Markdown
Member

CI confidence report

🔴 BLOCKED

The required validation gaps below still block confidence.

Failing PR/CI

🔴 Ordinary regression and platform coverage — Three attempt-1 test-job failures remain unclassified despite the successful retry.

  • Expected: Final-head ordinary CI passes, and every failed attempt is classified against the changed behavior.
  • Observed: Attempt 1 failed the Acquisition (Ubuntu), Hosting.DevTunnels (Windows), and Hosting-1 (Windows) test jobs. Their causes and relevance could not be established because the job-log requests failed (the direct Acquisition log response was empty). Attempt 2 completed successfully on the same pinned PR head.
  • Missing evidence: The Acquisition, DevTunnels, and Hosting attempt-1 failures remain unclassified because their logs were unavailable; the successful retry does not establish their relevance.
  • Required validation: Use the pr-testing skill to test PR Standardize NuGet restore and channel-update policy across AppHosts #19763. First retrieve and classify the three attempt-1 failures (Acquisition, DevTunnels, Hosting) against changed behavior; run a targeted check only if a failure is shown to affect it. This is a handoff, not authorization to dispatch.
  • Done when: All three attempt-1 failures have an evidence-backed relevance disposition, and any failure affecting changed behavior passes on the final tree. Attempt 2 already provides a successful final-head CI result.
  • Evidence: job status: receipt; job status: receipt; job status: receipt; workflow run: receipt
  • Consulted evidence: The attempt-1 job log endpoints returned stream errors; the direct Acquisition log response was empty. Failure cause is unknown, not classified as unrelated.

Coverage not demonstrated

🔴 Deployment E2E final-tree coverage — Those named deployment consumers directly exercise changed CLI template and AppHost behavior, but the available run is stale and does not validate the audited tree.

  • Expected: Changed TypeScript template and generated-AppHost behavior is exercised by supported deployment E2E on the final tree.
  • Observed: The available deployment run is for older head 24e280b; no final-head result or qualifying maintainer acceptance was found. TypeScriptExpressDeploymentTests and TypeScriptAzureContainerAppJobDeploymentTests exercise aspire new/init and aspire deploy against generated TypeScript AppHosts.
  • Missing evidence: Those named deployment consumers directly exercise changed CLI template and AppHost behavior, but the available run is stale and does not validate the audited tree.
  • Required validation: Establish authorized supported deployment-E2E validation for TypeScriptExpressDeploymentTests and TypeScriptAzureContainerAppJobDeploymentTests on the final tree, or record explicit non-author maintainer acceptance of this unrun coverage.
  • Done when: Both named scenarios pass on the final tree, or a linked qualifying maintainer acceptance names the unrun behavior, why it cannot safely run, and the alternative checks.
  • Safety: These scenarios provision real Azure resources; do not trigger them without explicit authorization and a confirmed cleanup path.
  • Evidence: consumer trace: no public receipt URL — tests/Aspire.Deployment.EndToEnd.Tests/TypeScriptExpressDeploymentTests.cs and TypeScriptAzureContainerAppJobDeploymentTests.cs use aspire new/init and aspire deploy with TypeScript AppHosts.; workflow run: no public receipt URL — Local run inventory: available deployment E2E used older head 24e280b; no final-head deployment result was found.

Audited commit: 0ef87afe
Independent review: completed — A late read-only challenge returned and its four findings were reconciled against pinned source and run evidence. The exact exposed reviewer runtime metadata is not retained, so model equivalence cannot be independently confirmed.
This is a partial CI-confidence review, not a complete code review.

Coverage catalog

The items below show the affected workflows, tests, artifacts and other validation evidence.

Workflows and pipelines

🔴 1. GitHub execution and coverage — A workflow or test-lane gap remains.
  • Evidence / missing boundary: Final-head ordinary CI, outerloop, and quarantine runs executed the changed definitions. | Acquisition ran on three OSes, with GH_TOKEN present in the test environment. | Acquisition ran on three OSes; the Ubuntu log recorded one passing quarantined test and masked GH_TOKEN. | TypeScriptExpressDeploymentTests and TypeScriptAzureContainerAppJobDeploymentTests are direct consumers; the available run used an older head.
🟢 1a. Affected workflow final-definition execution — Verified
  • Applicability: yes — All four changed workflow definitions executed at the pinned PR head.
  • Evidence / missing boundary: Final-head ordinary CI, outerloop, and quarantine runs executed the changed definitions.
🟢 run-tests.yml final-definition execution

  • Expected: The changed reusable workflow executes from the pinned final definition.
  • Observed: The exact-head outerloop and quarantine runs invoked the changed reusable workflow successfully.
  • Evidence: workflow run: receipt
🟢 specialized-test-runner.yml final-definition execution

  • Expected: The changed specialized runner executes from the pinned final definition.
  • Observed: The exact-head outerloop and quarantine runs invoked the changed specialized runner successfully.
  • Evidence: workflow run: receipt
🟢 tests-outerloop.yml final-definition execution

  • Expected: The touched outerloop workflow executes from its pinned final definition.
  • Observed: The exact-head outerloop run completed successfully.
  • Evidence: workflow run: receipt
🟢 tests-quarantine.yml final-definition execution

  • Expected: The touched quarantine workflow executes from its pinned final definition.
  • Observed: The exact-head quarantine run completed successfully.
  • Evidence: workflow run: receipt
🟢 1b. Outerloop scenario coverage — Verified
  • Applicability: yes — The exact-head outerloop run exercised the token-bearing Acquisition consumer; skipped resource jobs were not required by this runsheet.
  • Evidence / missing boundary: Acquisition ran on three OSes, with GH_TOKEN present in the test environment.
🟢 Outerloop consumer coverage

  • Expected: The affected outerloop path executes the final shared runner and receives the requested per-project GitHub token.
  • Observed: The final-head outerloop run generated and ran the Acquisition project on Ubuntu, macOS, and Windows. Its run log shows a masked GH_TOKEN in the test process and enumerates the four PRScriptIntegrationTests. NuGet, CLI-archive, package, and image setup was skipped because this selected project requires only the GitHub token.
  • Evidence: workflow run: receipt
🟢 1c. Quarantine scenario coverage — Verified
  • Applicability: yes — The exact-head quarantine run exercised the selected quarantined Acquisition test; skipped package/archive/image jobs were not required.
  • Evidence / missing boundary: Acquisition ran on three OSes; the Ubuntu log recorded one passing quarantined test and masked GH_TOKEN.
🟢 Quarantine consumer coverage

  • Expected: The affected quarantine path executes the final shared runner and passes the selected quarantined Acquisition test.
  • Observed: The final-head quarantine run generated and ran Acquisition on Ubuntu, macOS, and Windows; the Ubuntu log records one quarantined test passed, zero skipped, and a masked GH_TOKEN. Package/archive/image jobs were not required by this selected project.
  • Evidence: workflow run: receipt
🔴 1d. Deployment E2E coverage — Validation not demonstrated
  • Applicability: yes — Changed TypeScript template/AppHost behavior is consumed by deployment E2E scenarios, but no final-head run was found.
  • Evidence / missing boundary: TypeScriptExpressDeploymentTests and TypeScriptAzureContainerAppJobDeploymentTests are direct consumers; the available run used an older head.
  • Required validation: see finding for Deployment E2E final-tree coverage.
⚪ 2. Internal Azure pipeline/stage execution — Not applicable
  • Applicability: no — The runsheet generation path is gated on GitHub Actions; definition 1602 does not consume this changed GitHub-only runner behavior.
  • Evidence / missing boundary: eng/Testing.props derives IsGitHubActionsRunner from GITHUB_ACTIONS, and SpecializedTestRunsheetBuilderBase.targets requires the GitHub runner property; the official pipeline uses eng/pipelines/azure-pipelines.yml.
🟢 3. CI selection/result gates — Verified
  • Applicability: yes — Final-head workflows exercised generated test selection, per-project setup skips, and per-item GH_TOKEN delivery.
  • Evidence / missing boundary: The specialized run logs show the token-bearing Acquisition process and expected skipped NuGet/archive/image setup; regular CI passed CLI E2E checks.
🟢 Runsheet selection and result gates

  • Expected: Generated per-project metadata selects NuGet/archive setup only when required and supplies GH_TOKEN to token/archive consumers.
  • Observed: The exact-head ordinary CI, outerloop, and quarantine runs completed. The specialized runs executed the Acquisition project, whose project metadata requires a GitHub token; logs show the masked token in test environments and the NuGet/archive jobs skipped for this consumer. The regular CI run passed the changed CLI E2E checks for projects that use package/archive inputs. The check_github_token step output has no downstream consumer; run-tests.yml instead gates GH_TOKEN per matrix item.
  • Evidence: workflow run: receipt; workflow run: receipt; workflow run: receipt; consumer trace: no public receipt URL — Pinned source: .github/workflows/run-tests.yml (per-item GH_TOKEN condition); .github/workflows/specialized-test-runner.yml (NuGet/archive outputs and unused GitHub-token output); eng/testing/CITestsProperties.props (property emission).

Functional and platform coverage

🔴 4. Ordinary functional/regression/platform coverage — Validation not demonstrated
  • Applicability: yes — Attempt 2 passed, but three attempt-1 test-job failures remain unclassified because their logs were unavailable.
  • Evidence / missing boundary: CI attempt 1 failure receipts and attempt 2 success are recorded in the canonical finding.
  • Required validation: see finding for Ordinary regression and platform coverage.

Produced outputs and artifacts

⚪ 5. Artifact/release-consumer integrity — Not applicable
  • Applicability: no — The diff changes CLI product behavior and GitHub test plumbing, not shipped artifact layout, signing, release publishing, or downstream artifact contracts; ordinary product changes are covered by regular CI.
  • Evidence / missing boundary: No release/publish pipeline, package-format, signing, or artifact-layout change appears in the pinned changed-file inventory.

Static checks and dependency safety

🟢 6. Lint/static analysis/generated consistency — Verified
  • Applicability: yes — Targeted scans cover changed workflow YAML and embedded Bash/PowerShell; they are not represented as scans of all product code.
  • Evidence / missing boundary: ShellCheck 0.11.0, PSScriptAnalyzer 1.25.0, actionlint, and zizmor results were recorded with the changed-workflow scope.
🟢 ShellCheck

  • Expected: Changed embedded Bash is free of ShellCheck errors.
  • Observed: ShellCheck 0.11.0 completed successfully with no diagnostics.
  • Evidence: local static analysis: no public receipt URL — ShellCheck 0.11.0 run against changed embedded Bash; exit 0.
🟢 PSScriptAnalyzer

  • Expected: Changed embedded PowerShell is free of errors and warnings.
  • Observed: PSScriptAnalyzer 1.25.0 reported zero error/warning diagnostics for the exact changed PowerShell block.
  • Evidence: local static analysis: no public receipt URL — PSScriptAnalyzer 1.25.0 against the Configure CLI test NuGet service index block; zero diagnostics.
🟢 Actionlint

  • Expected: Handwritten workflow YAML passes actionlint.
  • Observed: The PR check for linting handwritten workflows succeeded.
  • Evidence: check summary: no public receipt URL — The current PR check summary reports the handwritten-workflow lint check successful.
🟢 Zizmor

  • Expected: Changed workflows pass the repository security workflow scan.
  • Observed: The PR Zizmor check succeeded; no changed action pins were found.
  • Evidence: check summary: no public receipt URL — The current PR check summary reports Zizmor successful; workflow diff contains no changed action pin.
⚪ 7. Dependency/action/task/tool version safety — Not applicable
  • Applicability: no — No action pin, Azure task version, SDK/toolchain, or external dependency version changed in the workflow diff.
  • Evidence / missing boundary: The four changed workflow files retain their pinned action references.

Security and agent behavior

🟢 8. Security/trust-boundary review — Verified
  • Applicability: yes — Changed CI token exposure and expression-to-shell handling were reviewed; no unsafe trust-boundary change was found.
  • Evidence / missing boundary: The per-item GH_TOKEN condition, limited workflow permissions, and environment-variable JSON transport were checked; zizmor passed.
🟢 Changed workflow trust boundaries

  • Expected: Changed workflow inputs and token permissions do not create an unsafe expression-to-shell path or excessive token scope.
  • Observed: Runsheet JSON is transported through environment variables before jq processing; the workflow adds actions:read alongside contents:read, and the service-index override is a fixed value. No direct expression interpolation into shell source was observed.
  • Evidence: source trust review: no public receipt URL — Reviewed the changed run-tests.yml and specialized-test-runner.yml hunks; expressions are passed via environment variables and the added token permission is limited to actions:read.
⚪ 9. Representative agent behavior — Not applicable
  • Applicability: no — Although AspireSkills temporary-cache code changed, it only acquires the sibling lease before creating a temporary directory to prevent a concurrent stale sweep race; no agent instructions, tool permissions, decisions, safety rules or output contracts changed, so representative agent execution is not applicable.
  • Evidence / missing boundary: Pinned diff: src/Aspire.Cli/Agents/AspireSkills/TemporaryCacheDirectory.cs changes filesystem lease/directory creation order only; agent prompts, tools, decision rules and outputs are unchanged.
⚠️ Assessment limits — high-impact limitations
  • Retrospective snapshot: PR Standardize NuGet restore and channel-update policy across AppHosts #19763 merged on 2026-10-15 as fff2b56. The report remains pinned to the PR head/base and does not claim a new post-merge test run.
  • The first CI attempt failed three test jobs. Attempt-1 logs were unavailable through GitHub log retrieval, so those failure causes remain unclassified despite the successful second attempt.

@danegsta

Copy link
Copy Markdown
Member Author

CI confidence report

🔴 BLOCKED

The required validation gaps below still block confidence.

Failing PR/CI

🔴 Ordinary regression and platform coverage — Three attempt-1 test-job failures remain unclassified despite the successful retry.

  • Expected: Final-head ordinary CI passes, and every failed attempt is classified against the changed behavior.
  • Observed: Attempt 1 failed the Acquisition (Ubuntu), Hosting.DevTunnels (Windows), and Hosting-1 (Windows) test jobs. Their causes and relevance could not be established because the job-log requests failed (the direct Acquisition log response was empty). Attempt 2 completed successfully on the same pinned PR head.
  • Missing evidence: The Acquisition, DevTunnels, and Hosting attempt-1 failures remain unclassified because their logs were unavailable; the successful retry does not establish their relevance.
  • Required validation: Use the pr-testing skill to test PR Standardize NuGet restore and channel-update policy across AppHosts #19763. First retrieve and classify the three attempt-1 failures (Acquisition, DevTunnels, Hosting) against changed behavior; run a targeted check only if a failure is shown to affect it. This is a handoff, not authorization to dispatch.
  • Done when: All three attempt-1 failures have an evidence-backed relevance disposition, and any failure affecting changed behavior passes on the final tree. Attempt 2 already provides a successful final-head CI result.
  • Evidence: job status: receipt; job status: receipt; job status: receipt; workflow run: receipt
  • Consulted evidence: The attempt-1 job log endpoints returned stream errors; the direct Acquisition log response was empty. Failure cause is unknown, not classified as unrelated.

Coverage not demonstrated

🔴 Deployment E2E final-tree coverage — Those named deployment consumers directly exercise changed CLI template and AppHost behavior, but the available run is stale and does not validate the audited tree.

  • Expected: Changed TypeScript template and generated-AppHost behavior is exercised by supported deployment E2E on the final tree.
  • Observed: The available deployment run is for older head 24e280b; no final-head result or qualifying maintainer acceptance was found. TypeScriptExpressDeploymentTests and TypeScriptAzureContainerAppJobDeploymentTests exercise aspire new/init and aspire deploy against generated TypeScript AppHosts.
  • Missing evidence: Those named deployment consumers directly exercise changed CLI template and AppHost behavior, but the available run is stale and does not validate the audited tree.
  • Required validation: Establish authorized supported deployment-E2E validation for TypeScriptExpressDeploymentTests and TypeScriptAzureContainerAppJobDeploymentTests on the final tree, or record explicit non-author maintainer acceptance of this unrun coverage.
  • Done when: Both named scenarios pass on the final tree, or a linked qualifying maintainer acceptance names the unrun behavior, why it cannot safely run, and the alternative checks.
  • Safety: These scenarios provision real Azure resources; do not trigger them without explicit authorization and a confirmed cleanup path.
  • Evidence: consumer trace: no public receipt URL — tests/Aspire.Deployment.EndToEnd.Tests/TypeScriptExpressDeploymentTests.cs and TypeScriptAzureContainerAppJobDeploymentTests.cs use aspire new/init and aspire deploy with TypeScript AppHosts.; workflow run: no public receipt URL — Local run inventory: available deployment E2E used older head 24e280b; no final-head deployment result was found.

Audited commit: 0ef87afe Independent review: completed — A late read-only challenge returned and its four findings were reconciled against pinned source and run evidence. The exact exposed reviewer runtime metadata is not retained, so model equivalence cannot be independently confirmed. This is a partial CI-confidence review, not a complete code review.

Coverage catalog

The items below show the affected workflows, tests, artifacts and other validation evidence.

Workflows and pipelines

🔴 1. GitHub execution and coverage — A workflow or test-lane gap remains.

  • Evidence / missing boundary: Final-head ordinary CI, outerloop, and quarantine runs executed the changed definitions. | Acquisition ran on three OSes, with GH_TOKEN present in the test environment. | Acquisition ran on three OSes; the Ubuntu log recorded one passing quarantined test and masked GH_TOKEN. | TypeScriptExpressDeploymentTests and TypeScriptAzureContainerAppJobDeploymentTests are direct consumers; the available run used an older head.

🟢 1a. Affected workflow final-definition execution — Verified

  • Applicability: yes — All four changed workflow definitions executed at the pinned PR head.
  • Evidence / missing boundary: Final-head ordinary CI, outerloop, and quarantine runs executed the changed definitions.

🟢 run-tests.yml final-definition execution

  • Expected: The changed reusable workflow executes from the pinned final definition.
  • Observed: The exact-head outerloop and quarantine runs invoked the changed reusable workflow successfully.
  • Evidence: workflow run: receipt

🟢 specialized-test-runner.yml final-definition execution

  • Expected: The changed specialized runner executes from the pinned final definition.
  • Observed: The exact-head outerloop and quarantine runs invoked the changed specialized runner successfully.
  • Evidence: workflow run: receipt

🟢 tests-outerloop.yml final-definition execution

  • Expected: The touched outerloop workflow executes from its pinned final definition.
  • Observed: The exact-head outerloop run completed successfully.
  • Evidence: workflow run: receipt

🟢 tests-quarantine.yml final-definition execution

  • Expected: The touched quarantine workflow executes from its pinned final definition.
  • Observed: The exact-head quarantine run completed successfully.
  • Evidence: workflow run: receipt

🟢 1b. Outerloop scenario coverage — Verified

  • Applicability: yes — The exact-head outerloop run exercised the token-bearing Acquisition consumer; skipped resource jobs were not required by this runsheet.
  • Evidence / missing boundary: Acquisition ran on three OSes, with GH_TOKEN present in the test environment.

🟢 Outerloop consumer coverage

  • Expected: The affected outerloop path executes the final shared runner and receives the requested per-project GitHub token.
  • Observed: The final-head outerloop run generated and ran the Acquisition project on Ubuntu, macOS, and Windows. Its run log shows a masked GH_TOKEN in the test process and enumerates the four PRScriptIntegrationTests. NuGet, CLI-archive, package, and image setup was skipped because this selected project requires only the GitHub token.
  • Evidence: workflow run: receipt

🟢 1c. Quarantine scenario coverage — Verified

  • Applicability: yes — The exact-head quarantine run exercised the selected quarantined Acquisition test; skipped package/archive/image jobs were not required.
  • Evidence / missing boundary: Acquisition ran on three OSes; the Ubuntu log recorded one passing quarantined test and masked GH_TOKEN.

🟢 Quarantine consumer coverage

  • Expected: The affected quarantine path executes the final shared runner and passes the selected quarantined Acquisition test.
  • Observed: The final-head quarantine run generated and ran Acquisition on Ubuntu, macOS, and Windows; the Ubuntu log records one quarantined test passed, zero skipped, and a masked GH_TOKEN. Package/archive/image jobs were not required by this selected project.
  • Evidence: workflow run: receipt

🔴 1d. Deployment E2E coverage — Validation not demonstrated

  • Applicability: yes — Changed TypeScript template/AppHost behavior is consumed by deployment E2E scenarios, but no final-head run was found.
  • Evidence / missing boundary: TypeScriptExpressDeploymentTests and TypeScriptAzureContainerAppJobDeploymentTests are direct consumers; the available run used an older head.
  • Required validation: see finding for Deployment E2E final-tree coverage.

⚪ 2. Internal Azure pipeline/stage execution — Not applicable

  • Applicability: no — The runsheet generation path is gated on GitHub Actions; definition 1602 does not consume this changed GitHub-only runner behavior.
  • Evidence / missing boundary: eng/Testing.props derives IsGitHubActionsRunner from GITHUB_ACTIONS, and SpecializedTestRunsheetBuilderBase.targets requires the GitHub runner property; the official pipeline uses eng/pipelines/azure-pipelines.yml.

🟢 3. CI selection/result gates — Verified

  • Applicability: yes — Final-head workflows exercised generated test selection, per-project setup skips, and per-item GH_TOKEN delivery.
  • Evidence / missing boundary: The specialized run logs show the token-bearing Acquisition process and expected skipped NuGet/archive/image setup; regular CI passed CLI E2E checks.

🟢 Runsheet selection and result gates

  • Expected: Generated per-project metadata selects NuGet/archive setup only when required and supplies GH_TOKEN to token/archive consumers.
  • Observed: The exact-head ordinary CI, outerloop, and quarantine runs completed. The specialized runs executed the Acquisition project, whose project metadata requires a GitHub token; logs show the masked token in test environments and the NuGet/archive jobs skipped for this consumer. The regular CI run passed the changed CLI E2E checks for projects that use package/archive inputs. The check_github_token step output has no downstream consumer; run-tests.yml instead gates GH_TOKEN per matrix item.
  • Evidence: workflow run: receipt; workflow run: receipt; workflow run: receipt; consumer trace: no public receipt URL — Pinned source: .github/workflows/run-tests.yml (per-item GH_TOKEN condition); .github/workflows/specialized-test-runner.yml (NuGet/archive outputs and unused GitHub-token output); eng/testing/CITestsProperties.props (property emission).

Functional and platform coverage

🔴 4. Ordinary functional/regression/platform coverage — Validation not demonstrated

  • Applicability: yes — Attempt 2 passed, but three attempt-1 test-job failures remain unclassified because their logs were unavailable.
  • Evidence / missing boundary: CI attempt 1 failure receipts and attempt 2 success are recorded in the canonical finding.
  • Required validation: see finding for Ordinary regression and platform coverage.

Produced outputs and artifacts

⚪ 5. Artifact/release-consumer integrity — Not applicable

  • Applicability: no — The diff changes CLI product behavior and GitHub test plumbing, not shipped artifact layout, signing, release publishing, or downstream artifact contracts; ordinary product changes are covered by regular CI.
  • Evidence / missing boundary: No release/publish pipeline, package-format, signing, or artifact-layout change appears in the pinned changed-file inventory.

Static checks and dependency safety

🟢 6. Lint/static analysis/generated consistency — Verified

  • Applicability: yes — Targeted scans cover changed workflow YAML and embedded Bash/PowerShell; they are not represented as scans of all product code.
  • Evidence / missing boundary: ShellCheck 0.11.0, PSScriptAnalyzer 1.25.0, actionlint, and zizmor results were recorded with the changed-workflow scope.

🟢 ShellCheck

  • Expected: Changed embedded Bash is free of ShellCheck errors.
  • Observed: ShellCheck 0.11.0 completed successfully with no diagnostics.
  • Evidence: local static analysis: no public receipt URL — ShellCheck 0.11.0 run against changed embedded Bash; exit 0.

🟢 PSScriptAnalyzer

  • Expected: Changed embedded PowerShell is free of errors and warnings.
  • Observed: PSScriptAnalyzer 1.25.0 reported zero error/warning diagnostics for the exact changed PowerShell block.
  • Evidence: local static analysis: no public receipt URL — PSScriptAnalyzer 1.25.0 against the Configure CLI test NuGet service index block; zero diagnostics.

🟢 Actionlint

  • Expected: Handwritten workflow YAML passes actionlint.
  • Observed: The PR check for linting handwritten workflows succeeded.
  • Evidence: check summary: no public receipt URL — The current PR check summary reports the handwritten-workflow lint check successful.

🟢 Zizmor

  • Expected: Changed workflows pass the repository security workflow scan.
  • Observed: The PR Zizmor check succeeded; no changed action pins were found.
  • Evidence: check summary: no public receipt URL — The current PR check summary reports Zizmor successful; workflow diff contains no changed action pin.

⚪ 7. Dependency/action/task/tool version safety — Not applicable

  • Applicability: no — No action pin, Azure task version, SDK/toolchain, or external dependency version changed in the workflow diff.
  • Evidence / missing boundary: The four changed workflow files retain their pinned action references.

Security and agent behavior

🟢 8. Security/trust-boundary review — Verified

  • Applicability: yes — Changed CI token exposure and expression-to-shell handling were reviewed; no unsafe trust-boundary change was found.
  • Evidence / missing boundary: The per-item GH_TOKEN condition, limited workflow permissions, and environment-variable JSON transport were checked; zizmor passed.

🟢 Changed workflow trust boundaries

  • Expected: Changed workflow inputs and token permissions do not create an unsafe expression-to-shell path or excessive token scope.
  • Observed: Runsheet JSON is transported through environment variables before jq processing; the workflow adds actions:read alongside contents:read, and the service-index override is a fixed value. No direct expression interpolation into shell source was observed.
  • Evidence: source trust review: no public receipt URL — Reviewed the changed run-tests.yml and specialized-test-runner.yml hunks; expressions are passed via environment variables and the added token permission is limited to actions:read.

⚪ 9. Representative agent behavior — Not applicable

  • Applicability: no — Although AspireSkills temporary-cache code changed, it only acquires the sibling lease before creating a temporary directory to prevent a concurrent stale sweep race; no agent instructions, tool permissions, decisions, safety rules or output contracts changed, so representative agent execution is not applicable.
  • Evidence / missing boundary: Pinned diff: src/Aspire.Cli/Agents/AspireSkills/TemporaryCacheDirectory.cs changes filesystem lease/directory creation order only; agent prompts, tools, decision rules and outputs are unchanged.

⚠️ Assessment limits — high-impact limitations

Ankit Jain (@radical) I think your analyzer is raising false negatives due to focusing on single commits. The deployment tests were run on the previous commit and didn't show any issues related to this PR (there were failures, but all due to deployment flakiness, not restore issues). The logs are also available for the three failed attempt 1 jobs; they were flakey tests unrelated to this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants