Skip to content

Merge release/13.4 to main after v13.4.0 release#17804

Closed
aspire-repo-bot[bot] wants to merge 43 commits into
mainfrom
release/13.4
Closed

Merge release/13.4 to main after v13.4.0 release#17804
aspire-repo-bot[bot] wants to merge 43 commits into
mainfrom
release/13.4

Conversation

@aspire-repo-bot
Copy link
Copy Markdown
Contributor

This PR merges the release/13.4 branch back to main after the v13.4.0 release.

Checklist

  • Verify all release-specific changes are appropriate for main
  • Resolve any merge conflicts
  • Ensure CI passes

Created automatically by the release workflow.

joperezr and others added 30 commits May 26, 2026 20:43
…7520)

Flip StabilizePackageVersion default to true in eng/Versions.props.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix remaining issue 17244 items

Preserve legacy auxiliary backchannel resource property serialization unless clients opt in to JSON-valued properties, and remove the unused dashboard start command filter.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address backchannel review feedback

Use the existing auxiliary backchannel V3 capability for typed resource properties and stop advertising a new V4 capability. Also limit the watch-resource capability test to the first streamed snapshot so it does not wait for a never-ending watch stream.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Adam Ratzman <adam@adamratzman.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…egration and fix found issues (#17531)

* Migrate playground/SqlServerEndToEnd to EF Hosting integration - Remove the `SqlServerEndToEnd.DbSetup` worker project. Its `EnsureCreatedAsync` schema bootstrap is replaced by the `AddEFMigrations` resource model. - Check in the initial migration set.

`Aspire.Hosting.EntityFrameworkCore` fixes
- Connection-string resolution for the generated `Dockerfile` / bundle env-var now prefers explicit `.WithReference(<db>)` over `.WaitFor(<db>)`. Falls back to wait-based inference only when no references are declared.
- `EFCoreOperationExecutor`: read exit code from the strongly-typed `snapshot.ExitCode` instead of parsing the `ExitCode` property string, and publish `ExitCode` on the tool resource's final snapshot update so consumers (including the executor itself) see it.
- Tighten `AddEFMigrationsCore` duplicate-registration error messages to talk about "registered for specific DbContext types" / "registered without a context type" instead of "auto-detected".
- XML doc cleanup on the public `AddEFMigrations` / `PublishAsMigrationBundle` / `WithMigrationsProject` / `WithMigrationOutputDirectory` / `WithMigrationNamespace` / `RunDatabaseUpdateOnStart` overloads (more accurate summaries, `<c>` instead of quotes, drop obsolete "auto-detected" language, add `<exception>` notes).

* Avoid concurrent tool execution

---------

Co-authored-by: Andriy Svyryd <Andriy.Svyryd@microsoft.com>
…g.md (#17541)

The Spectre.Console [Y/n] confirmation prompt is a single-character reader,
not a line reader. Sending TypeAsync("n") + EnterAsync() queues both bytes
in the TTY input buffer; when the CLI returns on "n" and tears down its
stdin handler before \n is consumed, bash receives the stray \n as an
empty command and bumps CMDCOUNT via PROMPT_COMMAND.

The test's SequenceCounter then ends up coincidentally agreeing with bash's
drifted CMDCOUNT. The next WaitForAspireAddSuccessAsync false-positives on
the typed-command prompt header line ([N OK] $ aspire add ...) that bash
printed when accepting the command, returning success before aspire add has
actually written aspire.config.json. The follow-on Assert reads the file
too early and fails.

This matches the existing convention in DeclineAgentInitPromptAsync which
documents the same race and avoids it by not sending Enter after the
single-character answer.

Also adds .github/skills/cli-e2e-testing/troubleshooting.md describing the
diagnostic recipe (right-artifact selection, cast reconstruction, prompt-
counter desync, Y/n race signature) so future agent sessions can recognize
the pattern, and references it from SKILL.md.

Co-authored-by: Mitch Denny <midenn@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address remaining PR feedback and fix Docker Compose publish

PR feedback from #15691:
- Add [ResourceName] attribute to all Add* API name parameters
- Replace null-forgiving operator with explicit throw in EndpointsManifestTransformer
- Use StringComparison.OrdinalIgnoreCase for route comparison
- Fix '.NET Aspire' phrasing in README files

Docker Compose publish fixes for Blazor gateway:
- Add GatewayOriginReference (IValueProvider + IManifestExpressionProvider)
- Use ReferenceExpression for ConfigResponse so publishers emit proper placeholders
- Use TextEncoderSettings to escape braces in JSON string values
- Simplify Gateway.cs.in (serve ConfigResponse directly, no Replace logic)
- Add System.Text.Unicode dependency for brace-escaping encoder

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add OTLP misconfiguration warning to hosted Blazor path

The standalone gateway path already logged a warning when telemetry proxying
was requested but no HTTP OTLP endpoint could be resolved. The hosted model
(ProxyBlazorTelemetry via BlazorHostedExtensions) was missing the equivalent
diagnostic.

- Add LogWarning in BlazorHostedExtensions.EnsureEnvironmentCallback
- Add tests verifying the warning fires (and does not fire) in both scenarios

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Rename ASPIRE_OTLP_PATH_BASE to OTEL_EXPORTER_OTLP_ENDPOINT

Use the standard OpenTelemetry environment variable name instead of a custom
Aspire-specific one. The value remains a relative path that the WASM client
resolves against its page origin to stay same-origin.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use TestSink/TestLogger instead of custom ListLogger

Replace private ListLogger and LogMessage with shared TestSink and
TestLogger from Microsoft.Extensions.Logging.Testing. Also moves the
OTLP warning in the standalone gateway path before the build step so
it is testable, and adds two tests for that path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Filter OTLP proxy traffic from tracing to prevent feedback loop

The gateway and hosted server YARP proxies forward /_otlp/* requests
to the dashboard. Without filtering, those forwarding requests are
themselves traced and exported, creating recursive telemetry entries.

- Gateway (Gateway.cs.in): Simplified filter to use Contains for
  /_otlp/ paths (handles prefix-mounted apps like /app/_otlp/...).
  Removed IsStaticAssetOrOtlpRequest helper since static asset
  requests don't go through YARP.
- Hosted server (Program.cs): Added PostConfigure to wrap existing
  OTEL filters with /_otlp/ and /v1/ exclusions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Javier Calvarro Nelson <jacalvar@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…step (#17543)

* Friendly error for 'aspire do --list-steps' without a step

'aspire do' is always step-targeted, so '--list-steps' with no step has no
meaningful scope. The validator previously allowed it, which caused the CLI
to launch the AppHost and race ahead executing the full pipeline before
the CLI could fetch steps and stop. That race surfaced as
'InvalidOperationException: Sequence contains more than one matching
element' from AzurePublishingContext (#17526).

Tighten the DoCommand validator to always require the step argument
(outside the extension host, which prompts interactively). When the user
specifies '--list-steps' without a step, emit a friendly, localized error
pointing at concrete examples: 'aspire do deploy --list-steps' or
'aspire do publish --list-steps'.

Adds StepArgumentRequired and ListStepsRequiresStep entries to
DoCommandStrings.resx + Designer.cs and refreshes all xlf translations
via UpdateXlf.

Tests:
 - DoCommandTests: new DoCommandWithListStepsAndNoStepArgumentShowsFriendlyError
   regression case; existing list-steps tests updated to pass a step.
 - ListStepsTests (E2E): single Docker-backed test now exercises
   'aspire do --list-steps' (asserts friendly error and the absence of
   the 'Sequence contains more than one matching element' crash) plus
   'aspire do deploy --list-steps', 'aspire publish --list-steps' and
   'aspire deploy --list-steps' against a freshly created starter app.

Fixes: #17526

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* List all well-known step names in the friendly error

Expand the 'aspire do --list-steps' validation error to enumerate every
well-known pipeline step instead of just naming 'deploy' and 'publish'.
The step names are hand-maintained in DoCommand alongside a comment
pointing at src/Aspire.Hosting/Pipelines/WellKnownPipelineSteps.cs (the
CLI does not reference Aspire.Hosting). Updated unit and E2E assertions
to match the new message shape.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Trim list to build/publish/deploy and add docs link

Replace the long enumeration of well-known step names with a short,
opinionated suggestion (build, publish, deploy) and a link to the
official 'aspire do' reference page on aspire.dev for the full list.
This removes the hand-maintained mirror of WellKnownPipelineSteps in
DoCommand and simplifies localization since the message no longer takes
a format parameter.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address review: fix extension-host bypass and harden E2E asserts

- DoCommand validator: fire the ListStepsRequiresStep friendly error
  for `aspire do --list-steps` even when running in the extension host,
  because --list-steps does not flow through GetRunArgumentsAsync's
  interactive step prompt and would otherwise still hit the original
  pipeline crash from #17526.
- ListStepsTests (E2E): replace long, wrap-sensitive substrings with
  short fragments ("required when using --list-steps", "aspire.dev/")
  so the assertion does not fail when the friendly error wraps in a
  narrow Docker terminal.
- DoCommandTests: add DoCommandWithListStepsAndNoStepArgumentInExtensionHostShowsFriendlyError
  to regression-test the extension-host path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Mitch Denny <midenn@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… deploy (#17542)

* Validate Helm CLI version (>= 4.2.0) before Kubernetes deploy

Aspire's Kubernetes deployment pipeline shells out to 'helm upgrade --install'
for the main application chart and for any AddHelmChart(...) resources on a
KubernetesEnvironmentResource. Previously we only checked that 'helm' was on
PATH; we never asserted the installed Helm version was new enough for the
flags and behaviors we depend on (e.g. '--server-side=true --force-conflicts'
in the Helm 4 form). Missing or older Helm produced confusing low-level
errors like 'unknown flag: --force-conflicts', 'Flag --force has been
deprecated', or raw process-spawn failures.

Changes:

* Add internal HelmVersionValidator that runs 'helm version --short --client',
  parses the SemVer, and asserts a minimum of Helm 4.2.0. Throws a clear
  actionable InvalidOperationException (detected vs required + link to
  https://helm.sh/docs/intro/install/) when the version is too old,
  unparseable, or the command fails.
* Wire the validator into the existing check-helm-prereqs-{env} pipeline
  step in HelmDeploymentEngine. One check per environment covers both the
  main chart deploy and AddHelmChart(...) flows since they all DependsOn this
  step.
* Update the 'Helm CLI not found' message to also mention the minimum
  version requirement.
* Remove the now-redundant ad-hoc 'helm version --short' probe at the top of
  HelmDeployAsync (the prereq step covers it with a much better error).
* Promote FakeHelmRunner to a file-scoped test helper that emits canned
  'helm version' stdout (defaults to v4.2.0+gfa15ec0) and supports a
  separate VersionExitCode, so any test exercising the deploy path
  automatically passes the prereq check.
* Add HelmVersionValidatorTests covering: SemVer parsing of v3/v4/v5 outputs
  with and without '+gitsha' build metadata, rejection of unparseable
  output, threshold behavior for too-old versions (v4.1.0, v4.0.0, v3.18.0,
  v3.14.4), and that error messages include the detected version, the
  required version, and the install docs URL.
* Document the Helm 4.2.0+ requirement in the Aspire.Hosting.Kubernetes and
  Aspire.Hosting.Azure.Kubernetes READMEs.

Fixes #16977

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Drop --client flag from helm version invocation

The validator was invoking 'helm version --short --client', but the
--client flag was removed in Helm 4 (it existed in Helm 2 for the
real client/server split, was kept as a no-op in Helm 3, and is
unknown in Helm 4). Since this validator's purpose is to enforce
Helm 4.2.0 or later, passing --client guarantees a failure against
the very minimum version we require, surfacing the exact kind of
confusing prereq error this step exists to prevent.

Caught by dogfood testing of PR #17491 against a local Helm 4.2.0
install, which produced:

  Step 'check-helm-prereqs-k8s' failed: 'helm version --short --client'
  failed (Error: unknown flag: --client). Aspire requires Helm 4.2.0
  or later.

Switch to 'helm version --short', which produces identical output
shape (e.g. v4.2.0+gfa15ec0) on Helm 3 and Helm 4. Add a regression
test that records the arguments passed to the runner and asserts
--client is never included.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR review feedback

Three review items from the automated PR reviewer:

1. Gate destroy/uninstall on the same Helm prereq check as deploy.
   Both 'destroy-helm-{env}' and 'helm-uninstall-{env}' invoke 'helm
   uninstall', so a missing or too-old Helm would surface as a raw
   process-spawn / unknown-flag error during teardown instead of the
   actionable validator message. Add a 'DependsOn(check-helm-prereqs-
   {env})' on both, and add a regression test that asserts the
   dependency edge exists.

2. Fix the misleading comment above HelmVersionRegex. The regex is
   intentionally unanchored so we tolerate banner/shim lines that
   some shells, oh-my-zsh plugins, or asdf-style shims can prepend
   to the version output. Update the comment to describe that
   intent instead of claiming a start anchor that isn't there.

3. Shorten the Helm prerequisite bullets in both Kubernetes README
   files. Keep the bullet to the requirement itself and move the
   'why we validate up front' narrative into a short paragraph
   below, matching the scannable style of the other hosting READMEs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Bump E2E Helm install version to v4.2.0 to match new floor

The 11 DeployK8s* CLI E2E tests failed on commit d03916c because the
container install scripts default HELM_VERSION to v3.17.3 — below the
new HelmVersionValidator.MinimumHelmVersion (v4.2.0) that the
check-helm-prereqs-{env} pipeline step now enforces.

Centralize the version constants in a new
tests/Aspire.Cli.EndToEnd.Tests/Helpers/KubernetesE2EVersions.cs so the
default lives in one place (and points at the validator's documented
minimum), then bump HelmVersion default v3.17.3 -> v4.2.0 (used by every
DeployK8s* test and by the quarantined KubernetesPublishTests).

HELM_VERSION / KIND_VERSION / KUBECTL_VERSION env-var overrides are
preserved so CI can still bump to a newer point release without a
code change.

The AKS deployment workflow (deployment-tests.yml) still pins
azure/setup-helm to v4.1.4 and needs the same bump to v4.2.0 to avoid
breaking AKS scenarios under the new validator; that workflow file edit
will land in a separate push that has 'workflow' OAuth scope.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Bump deployment-tests.yml Helm pin to v4.2.0

Match Aspire.Hosting.Kubernetes' new minimum supported Helm version
(HelmVersionValidator.MinimumHelmVersion). The check-helm-prereqs-{env}
pipeline step now fails fast on older Helm CLIs, so leaving the AKS
deployment workflow pinned to v4.1.4 would break every AKS deployment
scenario. Also refresh the surrounding rationale comment, which still
referred to the historical v3.18 server-side narrative.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Gate AddHelmChart uninstall on prereqs and unify Helm CLI check

Address self-review follow-ups on PR #17491:

- Per-chart `helm-uninstall-{name}` step (AddHelmChart(...).WithDestroy())
  now depends on `check-helm-prereqs-{env}`. Previously only the env-level
  destroy/uninstall steps were gated, so chart teardown could still hit the
  cryptic spawn / unknown-flag error the validator exists to prevent.
- Drop the standalone PathLookupHelper probe from the prereq step. The
  validator already wraps spawn failures with the same actionable hint, and
  routing everything through IHelmRunner lets tests inject a fake without
  needing real Helm on PATH (fixes 3 pre-existing K8s test failures in
  environments without helm installed).
- Refresh validator catch comment + error wording accordingly.
- Drop stale `--client` mention in FakeHelmRunner comment.
- Add regression test PerChartHelmUninstallStep_DependsOnCheckHelmPrereqs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Defer Helm version check in destroy-helm-{env} until state is found

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Mitch Denny <midenn@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add embedded Aspire skills fallback (#17537)

Embed a checked-in Aspire skills bundle snapshot for CLI fallback, make agent init warn instead of fail when bundle acquisition is unavailable, and add automation to refresh the snapshot via a draft PR.

Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix release backport resource strings

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Docker compose prepare test runtime dependency

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rce mapping (#17528)

* Bake AspireCliChannel=staging for release-branch builds even when stabilizing

The channel-compute step in build_sign_native.yml was checking
$versionKind -eq 'release' BEFORE the release-branch regex check. A
13.4 staging build runs from a release/* branch with
StabilizePackageVersion=true, which sets DotNetFinalVersionKind=release,
so the wrong arm fired and baked AspireCliChannel=stable into the
binary.

Downstream, aspire init reads CliExecutionContext.IdentityChannel to
pick the channel mappings it writes into the workspace nuget.config.
With identity=stable there's no Aspire.* → staging-feed mapping, so
aspire add tries to resolve packages from nuget.org and either gets
13.3.5 or fails outright (the apphost.cs template pins
#:sdk Aspire.AppHost.Sdk@13.4.0+<sha>, which isn't on nuget.org).

Swap the conditions so the release-branch check runs first. Release-
branch builds are always staging artifacts; only release-shaped
non-release-branch builds (effectively none in practice) get stable.

Fixes #17527

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add aspireCliChannelOverride pipeline parameter for GA ship builds

With release-branch builds now defaulting to 'staging' (so stabilizing
dogfood builds aren't mis-baked as 'stable'), there was no remaining
path to produce a real 'stable' GA ship binary — the same release/*
branch that produces the staging dogfood drops also produces the final
ship build, and the pipeline has no other signal to tell them apart.

Add a runtime pipeline parameter 'aspireCliChannelOverride' (auto |
stable | staging | daily, default auto) to azure-pipelines.yml and
thread it through every build_sign_native.yml invocation. When the
release manager kicks off the official GA ship build, they set this to
'stable' so the distributed binary bakes AspireCliChannel=stable and
aspire init writes the nuget.org-only nuget.config that matches the
promoted package set. Routine stabilizing builds leave it on 'auto'
and continue to bake 'staging'.

The override is validated against the same accepted-channel set that
IdentityChannelReader.IsValidChannel enforces at CLI startup so a typo
fails the pipeline step rather than producing a binary that refuses
to boot. pr-<N> is intentionally excluded from the override set since
PR builds always come from the PullRequest reason arm.

The unofficial pipeline doesn't get the parameter — its test builds
should always derive the channel from branch+reason, and the
template's default 'auto' achieves that.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Route stabilizing staging CLI to SHA-derived darc feed

The staging-channel synthesis in PackagingService defaulted to
PackageChannelQuality.Both for staging-identity CLIs. With Both,
useSharedFeed=true and Aspire.* gets routed to the shared dnceng/dotnet9
daily feed -- which only contains prerelease-tagged 13.4.0-preview.*
packages, not the stable-shaped 13.4.0 packages produced during release
stabilization (StabilizePackageVersion=true).

Net effect on the just-shipped staging build of 13.4: `aspire init`
drops a NuGet.config pointing Aspire.* at dotnet9, then `aspire add yarp`
fails to resolve Aspire.Hosting.Yarp 13.4.0 because dotnet9 doesn't
carry it. The packages actually live in the SHA-derived
darc-pub-microsoft-aspire-<hash> feed.

Fix: when the CLI's identity is staging, derive the synthesized
channel's default quality from the CLI build's version shape:
  - Stable-shaped (no semver prerelease tag) -> Stable, which makes
    useSharedFeed=false and routes Aspire.* to the SHA-derived darc
    feed where stabilizing packages actually live.
  - Prerelease-shaped -> Both (the historical default), since SHA-
    specific darc feeds are only created for stable release-branch
    builds and prerelease staging CLIs must use the shared feed.

The identity-staging branch runs before the requested/configured
branches in the if/else because `init` (and many other commands) calls
GetChannelsAsync(requestedChannelName: "staging") when the running
CLI's identity is staging -- short-circuiting on the requested branch
would re-introduce the bug.

The version-shape predicate is injected via constructor so unit tests
can deterministically exercise both paths regardless of the test-host
assembly's baked InformationalVersion.

Validated end-to-end with a locally-built NAOT `aspire` binary
(`/p:AspireCliChannel=staging`) + overrideStagingFeed pointing at
darc-pub-microsoft-aspire-0f514452: `aspire init` -> `aspire add yarp`
now resolves Aspire.Hosting.Yarp 13.4.0 instead of falling back to
13.3.5.

Refs #17527

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Pin PackagingService stable-shape predicate to false in CliTestHelper

The stabilization-check CI job builds the test host with
StabilizePackageVersion=true, which bakes a stable-shaped (no '-')
informational version into Aspire.Cli. PackagingService's new
identity-staging branch then defaults quality to Stable and requires
a SHA suffix in the assembly's InformationalVersion to compute the
darc-pub feed URL. Stabilized test-host assemblies don't carry a
+sha suffix, so CreateStagingChannel returned null and the
UpdateCommand_WhenStagingIdentityRegistersChannel_UsesStagingForUnpinnedProject
test fell back to the default channel instead of staging.

Default CliTestHelper.PackagingServiceFactory to inject
isStableShapedCliVersion: () => false so command-level tests get
deterministic prerelease-shaped behavior (quality=Both → shared
dotnet9 feed) regardless of how the test host was built. Tests
that specifically exercise the stable-shape branch (in
PackagingServiceTests) construct PackagingService directly and
already pass an explicit predicate.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Update eng/pipelines/templates/build_sign_native.yml

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jose Perez Rodriguez <joperezr@microsoft.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…17556)

* Enrich AppHost codegen TypeLoadException diagnostics (#16709)

When the installed Aspire CLI ships an �spire-managed server whose bundled Aspire.Hosting.dll is on a different build than the user-restored Aspire.Hosting.CodeGeneration.TypeScript / Aspire.TypeSystem DLLs, reflection-based codegen can throw an empty TypeLoadException that travels back to the CLI with no message and triggers a 60-second backchannel timeout.

This change adds three coordinated improvements:

1. Server-side: wrap reflection-load exceptions (TypeLoadException, MissingMethodException, MissingFieldException, BadImageFormatException, FileLoadException, ReflectionTypeLoadException) in a LocalRpcException with a safe, language-agnostic Message and a structured ErrorData payload (TypeName, MemberName, loaded ATS assemblies + informational versions, runtime Aspire.Hosting version, original exception type) carried via JSON-RPC error code -32050.

2. CLI-side: tiered output — emit a yellow pre-flight warning on detected CLI/SDK skew; render only the safe summary + remediation hint by default; reveal the full .NET diagnostic payload under --debug; always log the full payload via LogDebug. Also fault the BackchannelCompletionSource immediately on codegen failure so users no longer wait through the 60s timeout.

3. CLI-side: prune leftover cli.sock.* files older than 24 hours from ~/.aspire/cli/runtime/sockets/ on startup so stale entries don't accumulate from previous crashed runs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR review feedback for #16709

- Drop [JsonPropertyName(camelCase)] from CLI diagnostic DTO so the
  source-generated context deserializes the server's default PascalCase
  payload. Add a wire-contract test that round-trips the on-the-wire
  shape and the BackchannelJsonSerializerContext options.
- Use SemVersion.ComparePrecedence in IsKnownIncompatibleSkew so SemVer
  prerelease identifiers are compared (the #16709 case:
  13.4.0-preview.1.26218.1 vs 13.4.0-preview.1.26227.1). Update skew
  tests to cover prerelease and build-metadata cases.
- Resolve the runtime Aspire.Hosting version by walking
  AppDomain.CurrentDomain.GetAssemblies(); never fall back to
  Aspire.Hosting.RemoteHost (which is what typeof(AssemblyLoader)
  returned). Add a regression test.
- Only the diagnostic-section header keeps the microscope emoji; the
  continuation lines (Exception, Type, Member, runtime version, loaded
  assemblies) render as plain text indented under the header.
- Tests: use Directory.CreateTempSubdirectory() instead of manually
  combining Path.GetTempPath() + Guid for CliPathHelper janitor tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix stale CLI socket cleanup matching

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR feedback on codegen diagnostics

- Catch AppHostCodeGenerationException in sdk dump per-integration path so one failing integration does not abort the full Task.WhenAll batch.
- Log the full serialized AppHostCodeGenerationDiagnostic payload in RenderCodeGenerationFailure so debug logs match the XML doc contract.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Sebastien Ros <sebastienros@gmail.com>
* Prefer current CLI template version

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Pin aspire new template version to CLI

Ensure CLI-runtime templates selected from explicit package channels use the current bundled CLI/SDK version instead of floating to newer channel packages that can mismatch the bundled AppHost server.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Folder path (#17573)

* Stabilize PrebuiltAppHostServer staging globalPackagesFolder path

PackagingService creates the staging channel with
ConfigureGlobalPackagesFolder=true so each darc/override feed restore
lands in an isolated cache (two staging builds of the same release
branch ship as 13.4.0 but from different feeds, and NuGet keys by
(id,version) only). PrebuiltAppHostServer was wiring that flag into a
TemporaryNuGetConfig whose default globalPackagesFolder value is the
relative '.nugetpackages' -- NuGet resolved it under the temp config's
own directory, BundleNuGetService baked those temp paths into
integration-package-probe-manifest.json, and TemporaryNuGetConfig.Dispose
then recursively deleted the cache out from under the manifest. On macOS
osx-arm64 polyglot staging builds this surfaced as a hang during DI /
assembly loading in aspire-managed.

Preserve the per-feed cache isolation behavior and anchor the override
at a stable absolute path instead:

  <ASPIRE_HOME>/.nugetpackages/<first-8-of-CLI-commit-sha>

Keying by the truncated commit hash matches the existing
darc-pub-microsoft-aspire-<hash> feed URL convention in
PackagingService.GetStagingFeedUrl, so the cache key and feed key stay
aligned at 8 hex chars. 8 chars is short enough to avoid Windows
MAX_PATH blow-ups on deep integration cache trees while keeping SHA
collisions negligible. The cache lives under ASPIRE_HOME (not the
per-AppHost working directory) so multiple AppHosts on the same machine
running against the same staging build can share a single restore --
the unit of isolation here is the staging build, not the individual
restore command.

Mechanics:
- TemporaryNuGetConfig.CreateAsync now accepts an optional
  globalPackagesFolderValue and propagates it through
  AddGlobalPackagesFolderToConfigAsync into the merger.
- NuGetConfigMerger.AddGlobalPackagesFolderConfiguration takes the
  optional override and falls back to the workspace-relative default
  ('.nugetpackages') for the non-temp workspace-merge path.
- PrebuiltAppHostServer.ResolveStableGlobalPackagesFolder routes both
  temp config branches (channel and package-source-override) through
  the new helper.
- VersionHelper.TryGetCurrentCommitHashShort surfaces the truncated
  SHA from the running CLI's AssemblyInformationalVersion (returns
  null on clean release builds with no '+sha' suffix; callers fall
  back to 'default').
- CliPathHelper centralizes the '<ASPIRE_HOME>/.nugetpackages' path so
  the producer (PrebuiltAppHostServer) and consumer (CacheCommand)
  can't drift.
- CacheCommand.ClearCommand now wipes <ASPIRE_HOME>/.nugetpackages so
  a wedged staging restore is recoverable through the same UX as every
  other CLI cache.

Tests:
- Updated existing PrebuiltAppHostServerTests staging cases to assert
  the globalPackagesFolder value is absolute and lives outside the
  temp config directory.
- New PrebuiltAppHostServerTests case wires a real PackagingService
  with overrideStagingFeed on a stable-shaped CLI and pins the same
  invariant end-to-end.
- New TemporaryNuGetConfigTests for the override propagation and for
  the no-override-when-disabled invariant.
- New CacheCommandTests covering the staging cache wipe and the
  missing-cache no-op path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Key staging globalPackagesFolder cache by feed URL hash

The staging globalPackagesFolder fix in the previous commit keyed the
\`.nugetpackages\` subdirectory off the CLI's own commit SHA (first 8
hex chars of \`AssemblyInformationalVersionAttribute\`). That handles
two darc-shipped staging builds of the same release branch, but it
breaks the local-dev case where one CLI is repeatedly retargeted at
different \`overrideStagingFeed\` values: the URL changes but the SHA
doesn't, so every override silently shares one cache bucket and the
second restore reuses the first feed's now-stale \`13.4.0\` assemblies.

Switch the cache key to the first 8 hex chars of \`XxHash3\` over the
trimmed, lower-cased resolved feed URL:

- Override branch passes the explicit \`--source\` URL.
- Channel branch passes the channel's \`Aspire*\` mapping source (or
  the first mapping for forward compatibility).

Trim + lower-case before hashing so a stray whitespace from a config
file or a hostname-case change doesn't fragment the cache.
Non-cryptographic hashing is fine here — the key is a directory name,
not a security boundary — and 8 hex chars keep deep integration cache
paths well under Windows MAX_PATH while giving ~4 billion buckets, so
collisions are negligible across the handful of staging feeds any user
ever sees.

Removes the now-unused \`VersionHelper.TryGetCurrentCommitHashShort\`
helper (added in the previous commit, no remaining callers).

Tests:
- New \`CliPathHelperTests.ComputeStagingFeedCacheKey_*\` cover
  determinism, normalization, length defaults, and null/empty input.
- Existing \`PrebuiltAppHostServerTests\` strengthened to assert the
  emitted globalPackagesFolder equals \`<aspireHome>/.nugetpackages/<hash(feed)>\`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Narrow #17564 CLI-pin fallback to prerelease channels

PR #17564 introduced a new fallback in TryGetCurrentCliTemplateVersionPackage
that pins the template package to the running CLI's SDK version whenever the
selected channel is explicit and not local-build. That preserved the intended
fix for prerelease channels (daily, staging) where the channel feed filters
the running CLI's package out of search results even though the feed can
still restore it.

But it also fired for the stable channel, where the filter does not apply.
For a non-stable-shape CLI (PR build like '13.4.0-pr.X.gY' or a daily-shape
preview) invoked with '--channel stable', it forced an unpublishable version
into the generated apphost.cs, breaking the SmokeTests
LatestCliCanStartStableChannelAppHost and
LatestCliCanStartStableChannelTypeScriptAppHost.

Exclude the stable channel from the new fallback so that case falls through
to the OrderByDescending picker and the user gets the highest shipped stable
package they explicitly asked for. The original prerelease-channel motivation
('Aspire.TypeSystem version mismatch when 13.4 CLI floats templates to a
13.5 daily preview') is preserved unchanged.

Test updates:
 - NewCommandChannelResolutionTests.NewCommand_NoChannelArg_ResolvesTemplateFromIdentityChannel
   gets an expectedVersion theory parameter so the daily case still pins to
   the CLI version while the stable case asserts the highest shipped stable.
 - NewCommandChannelResolutionTests.NewCommand_ExplicitChannelArg_OverridesIdentityChannel
   asserts highest shipped stable (matching the SmokeTest contract).
 - New NewCommand_ExplicitStableChannel_NonStableCliVersion_FallsBackToHighestShippedStable
   regression test covers both daily-shape and PR-shape CLI identities.
 - NewCommandTests.NewCommandWithTypeScriptEmptyTemplatePassesResolvedVersionAndChannelToScaffolding
   reverts to its pre-#17564 assertion of '9.2.0' from the stable feed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… with legacy fallback (#17571)

* Use KnownConfigNames for resource service endpoint URL with legacy fallback

Remove hardcoded DOTNET_RESOURCE_SERVICE_ENDPOINT_URL constant from
DashboardServiceHost and use KnownConfigNames.ResourceServiceEndpointUrl
(ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL) with fallback to
KnownConfigNames.Legacy.ResourceServiceEndpointUrl.

* Add test for configured resource service endpoint URL

---------

Co-authored-by: James Newton-King <james@newtonking.com>
* Fix VS Code AppHost launch path resolution

* Use aspire ls for extension AppHost discovery

* Refresh AppHost discovery consumers on changes

* Use CLI language ids for AppHost discovery

* Handle AppHost discovery failures in editor commands



* Add TypeScript AppHost launch discovery coverage



* Harden AppHost discovery process handling



* Honor configured AppHosts in discovery



* Consolidate extension AppHost discovery



* Fix workspace test path separators



* Fix AppHost configured path selection

Ensure workspace AppHost selection checks all configured paths before falling back to the single discovered candidate, and add a regression test for multiple config files.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Keep extension-launched AppHost CLI alive

Keep the Aspire CLI process alive after VS Code launches an AppHost so the CLI-owned backchannel remains available for the lifetime of the extension-managed debug session. Propagate extension backchannel startup failures instead of returning success.

Also ignore generated .modules directories for polyglot AppHosts.



* Update extension build and CLI debug logging



* Address AppHost launch review feedback

Keep extension-managed CLI runs alive only until the AppHost backchannel disconnects, align configured AppHost discovery UX with scanned candidates, restore dynamic debug fallbacks, and return no disposable for async workspace prompts.

Restore Yarn 1 frozen install compatibility for extension CI and avoid the npx package-name/bin-name mismatch in localization export.



* Address extension discovery review feedback



* Stabilize pipeline unit tests without Docker

Use the existing fake container runtime resolver in the pipeline unit test context so the built-in container runtime check doesn't depend on Docker availability on CI runners.



* Use ordinal comparison in pipeline test provider

Keep the fake container runtime service-provider lookup null-safe and explicit.



* Revert pipeline test isolation changes



---------



(cherry picked from commit 00eff9d)

Co-authored-by: David Fowler <davidfowl@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…d field filter details (#17568)

* Update --search option description to include aka.ms link and field filter details

* Regenerate xlf files to match current resx values

---------

Co-authored-by: James Newton-King <james@newtonking.com>
Graduate the ATS/polyglot surface by removing ASPIREATS001 attributes and obsolete suppressions now that TypeScript/polyglot support is GA for 13.4.

Co-authored-by: Sebastien Ros <sebastienros@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…cribe` for resource streaming (#17579)

Backport of #17479 to release/13.4

Conflict resolved in extension/src/views/AppHostDataRepository.ts dispose():
kept both release/13.4's _getAppHostsProcess cleanup and the PR's
_stopAllGlobalDescribes() call.

Reverted the extension/package.json bump (1.0.9 -> 1.10.0) so the 13.4
extension line stays on 1.0.x.

Co-authored-by: adamint <adamint@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Revert the Corepack-specific Yarn invocation added to the extension build so internal Azure Pipelines builds use the configured Yarn executable instead of downloading Yarn from the public registry.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…17623)

* Fix aspire stop falsely reporting failure on Unix

The stop path treated the managing CLI PID as a success condition. On
Unix the CLI process can remain observable (for example as an unreaped
or briefly lingering process) after the AppHost has already stopped, so
aspire stop reported '\u274c Failed to stop apphost.cs' even when shutdown
completed successfully.

Use the AppHost PID as the success condition, and keep the CLI PID as a
shutdown handle that still gets force-killed when present so we never
leave a true zombie CLI running after the AppHost is gone.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Signal AppHost directly on Unix and tree-terminate on force-stop

The cascade path (signal launcher CLI, let it terminate 'dotnet run',
and rely on that to terminate the AppHost) is racy on Unix: if the
descendant walk inside 'dotnet run' misses the AppHost, the AppHost
is orphaned to PID 1 and HasExited polling can't distinguish a zombie
from a live process, so 'aspire stop' falsely reports failure after
the full SIGTERM + SIGKILL timeout (~40s).

On Unix we now:
* Send SIGTERM directly to the AppHost PID so it shuts down through
  its own IHostApplicationLifetime path. The launcher CLI and dotnet
  run process exit naturally when their child exits.
* Pass killEntireProcessTree:true when force-terminating on Unix. DCP
  is launched in a separate session there, so force-terminating the
  AppHost tree doesn't take DCP with it; DCP detects the parent gone
  and tears down its own children gracefully.

Windows behavior is preserved: we keep cascading through the launcher
CLI to DCP because DCP is an in-tree descendant of the AppHost on
Windows, and a full tree termination would break DCP's orderly
resource cleanup.

Validation in the repo's Linux container (Ubuntu 24.04 ARM64):
* Baseline: 5/25 failures (20%), failures hit the 40s timeout
* CLI-visibility-only fix: 1/25 failures (4%)
* This change: 0/30 failures, stop wall-time 0.7-2.3s

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: David Negstad <David.Negstad@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…templates (#17637)

* Add failing tests for aspire-starter channel resolution on release/13.4

Reproduces the bug where 'aspire new aspire-starter' (and
aspire-starter-csharp-typescript) silently resolve Aspire.ProjectTemplates
from the Implicit (nuget.org) channel on a daily / staging / release-branch
CLI, ignoring CliExecutionContext.IdentityChannel. Passing --channel works
because that value is the only thing forwarded into TemplateInputs.Channel
today.

The new tests pin the contract for both DotNet- and CLI-runtime templates:
NewCommand must forward IdentityChannel into inputs.Channel whenever
--channel is not passed, so DotNetTemplateFactory.ApplyTemplateAsync uses
the identity-matching channel when calling
TemplateNuGetConfigService.ResolveTemplatePackageAsync.

Coverage across the four shipping identities (pr-<N>, daily, staging,
stable) plus three edge cases (unregistered identity falls back to null,
explicit --channel overrides identity, --version still forwards identity
into inputs.Channel).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Forward IdentityChannel to TemplateInputs.Channel for dotnet-runtime templates

When 'aspire new' invoked a TemplateRuntime.DotNet template (e.g.
aspire-starter, aspire-starter-csharp-typescript) without an explicit
--channel argument, inputs.Channel was left null. That caused
TemplateNuGetConfigService.ResolveTemplatePackageAsync to fall back to
the Implicit (nuget.org) channel, ignoring the CLI's IdentityChannel.
Daily / staging / pr builds would therefore resolve
Aspire.ProjectTemplates from nuget.org instead of the channel matching
the running CLI.

This change mirrors the existing CLI-runtime contract: when --channel is
absent and no version-resolution channel was selected, look up the
identity channel in the packaging service and forward it to
inputs.Channel only when it matches a registered Explicit channel
(Implicit matches still resolve to null so the project inherits ambient
NuGet configuration).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Consolidate channel precedence into a single resolvedChannelName

Address review feedback: collapse the explicitChannelArg /
resolvedChannelName / identityChannelName three-way coalesce on
TemplateInputs.Channel into a single resolvedChannelName variable with a
clear precedence chain (explicit --channel > resolver result > identity).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Explain why identity-channel fallback lives at the call site

Document why ResolveIdentityChannelNameAsync is invoked at the
TemplateInputs assembly site rather than folded into
ResolveCliTemplateVersionAsync: the two paths that need the identity
hint (TemplateRuntime.DotNet templates and --version short-circuit) are
precisely the ones the resolver never runs on.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…7646)

The signed-build verifier ran 'aspire new aspire-starter' after extracting
the CLI archive, claiming to exercise 'template engine + bundle
self-extraction without requiring a NuGet restore'. That claim was wrong:
'aspire new' resolves template versions via
TemplateNuGetConfigService.ResolveTemplatePackageAsync, which always
queries a NuGet feed.

The step only ever succeeded on release branches because builds were
mis-baked with AspireCliChannel=stable, which routed the implicit
identity-channel lookup to nuget.org and found a previously-shipped
Aspire.ProjectTemplates version. Once #17528 corrected the release-branch
builds to bake AspireCliChannel=staging, the identity channel switched to
a staging feed that is not reachable from the 1ES signed-build agent, and
the step started failing with 'No template versions were found' in the
internal build pipeline.

This is the same egress reason the TypeScript starter check was already
removed for (#17274, tracked in #17345). Re-adding meaningful starter
coverage in the signed-build verifier requires either an internal NuGet
mirror or a no-network template path.

Drop the step (and the corresponding test assertion); the verifier still
covers archive layout, sidecar contract, and 'aspire --version'
execution.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Accept dev localhost resource service URLs

Allow DashboardServiceHost to treat RFC-reserved .localhost subdomains as local resource service endpoints so polyglot AppHosts scaffolded with *.dev.localhost can run.

Add unit coverage for local endpoint recognition and a CLI E2E regression test that scaffolds and runs an Express/React polyglot AppHost with dev-localhost URLs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR review follow-up comments

Co-authored-by: danegsta <50252651+danegsta@users.noreply.github.com>

* Use release E2E terminal lifecycle

Adapt the backported polyglot dev-localhost CLI E2E test to the release/13.4 test harness, which does not have CliE2ETestHelpers.StartRun.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: David Negstad <David.Negstad@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: danegsta <50252651+danegsta@users.noreply.github.com>
* Add TerminalRun IAsyncDisposable for consistent CLI E2E diagnostics capture (#17576)

* Fix CLI Ctrl+C/SIGTERM shutdown: responsive cancellation and double-signal bug (#17588)

* Fix CLI Ctrl+C shutdown taking too long during AppHost startup

- Make ConsoleCancellationManager.Cancel() non-blocking so Ctrl+C handler
  returns immediately
- Pass cancellation token through to WaitAsync in CancelAppHostStartupAsync
  so Ctrl+C exits promptly instead of waiting for the full 5s timeout
- Support second Ctrl+C for immediate force exit (Environment.Exit)
- Add logging support to ConsoleCancellationManager via SetLogger()
- Add comprehensive unit tests for ConsoleCancellationManager
- Add integration test for RunCommand cancellation during startup timeout

Fixes #17569

* Fix review comments: volatile logger, accurate comments, clearer warning

* Clean up

* Use WaitForSuccessPromptFailFastAsync in CLI E2E tests

Replace WaitForSuccessPromptAsync with WaitForSuccessPromptFailFastAsync
across all CLI E2E tests. The fail-fast variant detects error prompts
immediately and throws instead of hanging for up to 500s waiting for a
success prompt that will never arrive. This prevents 10+ minute CI
timeouts when a command fails with a non-zero exit code.

* Fix double-signal bug and consolidate test helpers

- Fix ConsoleCancellationManager double-signal bug: move Console.CancelKeyPress
  to else branch so it only registers on platforms without PosixSignalRegistration.
  Previously both handlers fired for the same SIGINT, causing immediate force-kill.
- Add SIGQUIT/Ctrl+Break registration for Windows parity.
- Remove old WaitForSuccessPromptAsync (no fail-fast) and rename
  WaitForSuccessPromptFailFastAsync to WaitForSuccessPromptAsync.
- Remove duplicate RunCommandFailFastAsync (identical to RunCommandAsync).

* Fix stale comment and restrict SIGQUIT to Windows only

* Remove unused legacy builder methods and update E2E skill docs

* Don't force when debugging

* More logging
* Improve dashboard summary log formatting

- Indent URL lines with 6 spaces and '- ' prefix so they align with
  the standard log output indentation
- Move container warning to a separate log message with clearer text
- Update tests to match new format

* Update FrontendBrowserTokenAuthTests to assert new standalone container log message

Co-authored-by: JamesNK <303201+JamesNK@users.noreply.github.com>

---------

Co-authored-by: James Newton-King <james@newtonking.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: JamesNK <303201+JamesNK@users.noreply.github.com>
Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Update Foundry hosted agent builder APIs

* Update Foundry hosted agent API surface

* Update Foundry hosted agent tests

* Remove commented-out health check URLs from hosted agent configuration

* Adjust Foundry hosted agent behavior

Consolidate AsHostedAgent usage, update Foundry icons and command rendering, and refresh playgrounds, samples, and tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Foundry hosted-agent run and command icon behavior

Conditionally require/provision Foundry project ACR only for hosted-agent publish or explicit registry override, add regression tests for run/publish paths, and align PromptAgent Send Message icon with ChatSparkle.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Avoid default Foundry ACR in run mode

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Cache JsonSerializerOptions in HostedAgentResourceBuilderExtensions

Avoid allocating a fresh JsonSerializerOptions per Send Message command
invocation, which also defeats JsonSerializer's per-options metadata cache.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Make project positional and add HostedAgentOptions for polyglot

- AsHostedAgent(project, HostedAgentOptions?) is now the polyglot-exported overload
- Action<HostedAgentConfiguration> overload kept as .NET-only for advanced use
- HostedAgentOptions exposes Description, Cpu, Memory, Metadata, EnvironmentVariables
- Polyglot users now get .asHostedAgent(project, { ... }) instead of
  .asHostedAgent({ project, configure: async cfg => ... })
- Updated TypeScript, Go, and Java polyglot apphost fixtures to new shape

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Make HostedAgentOptions and its overload internal (ATS-only)

HostedAgentOptions exists only to give polyglot SDK generators a clean
options-bag shape. .NET callers should keep using the richer
Action<HostedAgentConfiguration> overload, so both the DTO and the
overload that takes it are now internal.

To avoid C# overload ambiguity between '.AsHostedAgent(project)' (which
should bind to the public Action overload) and the internal options
overload, the internal method is renamed to AsHostedAgentForExport. The
polyglot-facing name stays as 'asHostedAgent' via [AspireExport(MethodName)].

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Clean up polyglot JSDoc for asHostedAgent

Remove <remarks> blocks from HostedAgentOptions and AsHostedAgentForExport
that were flowing into the generated TypeScript/Go/Java SDK JSDoc and
including C#-only implementation notes plus broken <see cref> renderings
(e.g. \`AsHostedAgent\`\`1). The polyglot codegen concatenates
<summary>+<remarks>, so any C#-implementation chatter pollutes the
generated SDK docs. Replace with plain // source comments that stay in C#.

Also fix the TypeScript polyglot fixture to match the actual generated
signature: when an extension method takes a single optional DTO parameter,
the codegen wraps it in an options bag, so the call shape is
asHostedAgent(project, { options: { ... } }) — not the flat
asHostedAgent(project, { ... }) shape.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Foundry hosted agent options DTO generation

Mark HostedAgentOptions as an ATS DTO so polyglot SDK generation treats it as a JSON value object instead of a live exported handle. Update the TypeScript validation fixture to use the generated flat options shape.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Foundry polyglot hosted agent fixtures

Update Go and Java Foundry polyglot AppHosts to match the generated HostedAgentOptions DTO and AsHostedAgent optional-parameter shapes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Foundry Java hosted agent fixture

Use Double literals for hosted agent CPU and memory options so the generated Java DTO setters compile.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Foundry API baselines

Regenerate the Foundry ATS and API baselines so the release branch backport exposes the hosted-agent options export and drops the stale withComputeEnvironmentExecutable surface.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: tommasodotnet <tstocchi@microsoft.com>
Co-authored-by: Maddy Montaquila <maleger@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Treat Win32Exception while inspecting process metadata as a stale or non-targetable process so stop monitoring does not fail when an AppHost exits during shutdown.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
joperezr and others added 11 commits May 29, 2026 15:11
* [release/13.4] Mark Aspire.Hosting.Blazor as preview

The 13.4.0 stable build ships gateway scripts only under buildTransitive/net8.0/Scripts/, while the TypeScript AppHost loader resolves Gateway.cs from lib/net8.0/Scripts/. This breaks the TS polyglot AppHost flow out of the box. Mark the package as preview-shaped via SuppressFinalPackageVersion so consumers don't pick it up as stable until the packaging/loader issue is fully addressed.

Fixes #17685

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Update src/Aspire.Hosting.Blazor/Aspire.Hosting.Blazor.csproj

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: James Newton-King <james@newtonking.com>
…7704)

* Reference Foundry project from hosted agent target

* Strengthen hosted agent Foundry reference test

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Foundry hosted agent CI tests

Seed Foundry project outputs in tests that directly invoke hosted-agent environment resolution, matching the provisioning state available during deployment.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Document Foundry project output seeding

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: tommasodotnet <tstocchi@microsoft.com>
Co-authored-by: David Fowler <davidfowl@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix five `aspire ls` bugs from #17620 (L1–L5)

Fixes #17615, #17620, #17621, #17624, #17626.

- L1 (#17615): Remove the eager-migration block in
  ConfigurationHelper.RegisterSettingsFiles. Read commands like
  `aspire ls` no longer silently materialize an aspire.config.json
  next to a user's legacy .aspire/settings.json. Migration now happens
  lazily/explicitly via the existing write paths.

- L2 (#17620): Drop the `silent` parameter from
  ProjectLocator.GetAppHostProjectFileFromSettingsAsync so the legacy
  branch unconditionally surfaces the migration warning, and surface
  the actual user-authored `.aspire/settings.json` path in the warning
  text rather than the auto-created `aspire.config.json` path.

- L3 (#17621): Remove the dead post-emission `appHosts.Sort()` in
  LsCommand.FindAppHostsWithJsonStreamAsync (--stream emits candidates
  as they are discovered, so the sort had no effect on already-emitted
  output). Update the --stream option description and
  docs/specs/cli-output-formats.md to declare the arrival-ordered
  contract.

- L4 (#17624): Add an IsValidConfiguredAppHostPath helper in
  ProjectLocator that rejects `\0` and Path.GetInvalidPathChars()
  before the path is passed to Path.IsPathRooted / Path.Combine.
  Wired into both the modern `aspire.config.json` (`appHost.path`)
  branch and the legacy `.aspire/settings.json` (`appHostPath`)
  branch. Validation is intentionally at the consumption point rather
  than in AspireConfigFile.Load, which has 12+ unrelated callers.
  Adds a new ConfiguredAppHostPathHasInvalidCharacters resource string
  and refreshes the xlf set via UpdateXlf.

- L5 (#17626): Add PathNormalizer.ResolveSymlinks in src/Shared, a
  recursive segment-walker that canonicalizes intermediate symlinks
  (Directory.ResolveLinkTarget only reads exactly the path it is given,
  and returns the link target as stored on disk — so a single call on
  /tmp/x/y.cs does not unwrap /tmp -> /private/tmp, and following a
  link whose stored target is /var/.../app keeps the un-canonical
  /var prefix). The recursion has a hard depth limit of 40 and falls
  back to the un-resolved input on broken or circular links. Use it in
  AddSettingsAppHostCandidateAsync as a comparison key only — the
  surfaced AppHostProjectCandidate keeps its original FileInfo so the
  displayed path matches what the user authored in settings.

Tests: 158 of 160 targeted tests pass (2 Windows-only skipped on
macOS). New tests cover L1 (no migration on read), L2 (legacy warning
references settings.json), L3 (arrival-order under --stream), L4
(NUL byte in modern and legacy branches), L5 (symlink dedupe via a
node_modules-hosted link the discovery walk excludes), plus 5 unit
tests on PathNormalizer.ResolveSymlinks itself.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address Aspire CLI PR feedback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use constants for AppHost config keys

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Deduplicate AppHost config casing on macOS

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Handle duplicate ATS capabilities

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Revert duplicate ATS compatibility fix

PR #17671 owns the Foundry ATS baseline update; this branch should only contain the AppHost and CLI fixes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: adamint <adamratzman1@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Adam Ratzman <adam@adamratzman.com>
…fuzzy auto-pick (#17724, #17725) (#17728)

* fix(cli): restore implicit-channel discovery + guard non-interactive fuzzy auto-pick (#17724, #17725)

## #17725 — Prerelease-only integrations invisible on polyglot apphosts

`IntegrationPackageSearchService.GetIntegrationPackagesWithChannelsAsync`
used to narrow the channel set to whatever `configuredChannel` resolved
to (from `aspire.config.json`'s `"channel"` field). For a polyglot apphost
pinned to a `Quality.Stable` channel this dropped the implicit channel
from discovery, so prerelease-only packages (e.g. `Aspire.Hosting.Foundry`,
`Aspire.Hosting.Kubernetes`) became invisible.

This narrowing was born 2026-01-13 in PR #13705 with a C#-only short-circuit
in `GetConfiguredChannel`. It stayed dormant until PR #17452 (2026-05-26)
started writing `"channel": "<identity>"` into the scaffolded
`aspire.config.json` during `aspire init`. After #17452 every newly-init'd
polyglot apphost in 13.4 had the field populated and tripped the narrowing.
13.3.5 users had no `"channel"` persisted, so the bug was invisible there
— this is a 13.4 regression introduced by the activator, not the
narrowing code itself.

The fix removes the narrowing. The configured channel is still forwarded
to `PackagingService.GetChannelsAsync` as `requestedChannelName` so
out-of-tree apphost staging-channel synthesis keeps working — it just no
longer constrains the post-retrieval filter pipeline. The filter pipeline
is now byte-identical for C# and polyglot apphosts.

## #17724 — `aspire add <fuzzy> --non-interactive` silently picks first match

`AddCommand` falls back to fuzzy search when there's no exact match. The
fuzzy candidates were passed to `GetPackageByInteractiveFlow`, which in
non-interactive mode auto-selected `distinctPackages.First()` and silently
installed it. Combined with #17725, `aspire add kube --non-interactive`
on a TS apphost silently installed `Aspire.Hosting.Azure`.

The existing guard at AddCommand.cs:181 already refused this when
`--version` was supplied. This change generalizes the guard: any
non-interactive invocation without an exact match now fails with a new
`NonInteractiveRequiresExactPackageMatch` resource message. Fuzzy
fallback remains available in interactive mode.

## Tests

- IntegrationSearchCommandFormatJsonWithTypeScriptAppHostPinnedToChannelAlsoSearchesImplicitChannel
- IntegrationSearchCommandFormatJsonWithTypeScriptAppHostPinnedToStagingChannelAlsoSearchesImplicitChannel
- IntegrationSearchCommandFormatJsonWithTypeScriptAppHostPinnedToStableChannelStillSurfacesPrereleaseOnlyPackages
  (primary regression test for #17725 — Foundry case)
- IntegrationSearchCommandTypeScriptAppHostProducesSameResultRegardlessOfPersistedChannel
  (durable structural guard: parameterized over with/without `"channel"`
  in aspire.config.json; asserts the result is identical, proving the
  narrowing is gone)
- AddCommand_NonInteractive_NoExactMatchWithoutVersion_FailsInsteadOfFuzzyAutoPick_Regression17724
  (primary regression test for #17724)
- AddCommand_NonInteractive_ExactMatchWithoutVersion_StillSucceeds
  (companion guard: exact-match happy path keeps working non-interactive)

Two pre-existing AddCommandFuzzySearchTests were testing the buggy
auto-pick behavior implicitly (they used `add postgre` / typo input under
the default non-interactive test host). Updated to opt into an interactive
host environment to assert the documented interactive-fuzzy-prompt behavior.

Full Aspire.Cli.Tests suite: 3900 passed, 21 skipped, 0 failed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(cli): restore explicit-channel inclusion when polyglot apphost pins a channel

Address review feedback on PR #17728.

The first revision dropped too much. Removing the
`|| !string.IsNullOrEmpty(configuredChannel)` half of the gate caused a
NEW regression: a TS apphost pinned to "daily" / "staging" / a custom
channel now searched only the implicit channel, losing access to
packages that live on the pinned feed.

Production change in IntegrationPackageSearchService:
  channels = hasHives || !string.IsNullOrEmpty(configuredChannel)
      ? allChannels
      : allChannels.Where(c => c.Type is PackageChannelType.Implicit);

This preserves the #17725 fix (narrowing is still gone, so the implicit
channel always participates and prerelease-only packages like Foundry
remain discoverable when pinned to a Stable-quality channel) while
keeping pinned explicit channels in the search.

Tests strengthened with per-channel invocation counters so that
"channel X was searched" is asserted directly rather than inferred from
the dedupe outcome. The Theory test is reframed: both arms agree on the
user-visible preferred result (Redis 1.0.0, implicit wins), but the
with-channel arm additionally hits the daily channel and the
without-channel arm does not.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* test(cli): pin staging-shipping behavior for #17724 + #17725

Adds proof-by-test that the IPSS gate behaves identically when the CLI
is shipped as staging (`IdentityChannel == "staging"`) as it does today
with the PR dogfood build (where the channel name was `pr-17728`):

1. Adds `[InlineData("\"staging\"", true)]` to the existing theory
   `…PersistedChannelExpandsDiscoveryWithoutChangingPreferredResult`.
   This proves the IPSS gate (`hasHives || !string.IsNullOrEmpty(
   configuredChannel)`) is channel-name-opaque — `"staging"` and
   `"daily"` produce identical gate behavior.

2. Adds a new fact
   `IntegrationSearchCommandStagingStampedCliWithPinnedStagingApphost
   QueriesBothImplicitAndStagingChannelsAndSurfacesPrereleaseOnlyPackages`
   that exercises the exact shipping shape:
     * Real PackagingService (not the fake TestPackagingService) — so
       the real staging-channel synthesis path is exercised.
     * `IdentityChannel = Staging` — the CLI binary is stamped as the
       staging release identity, which is how shipped staging CLIs run.
     * `aspire.config.json` pins `"channel": "staging"` — which is what
       `aspire new` writes into polyglot apphosts on a staging-stamped
       CLI (see CliTemplateFactory.TypeScriptStarterTemplate).
     * No PR hives — this is a real installed CLI, not a dogfood build.
   Asserts both invariants:
     (i)  Total cache call count >= 2, proving both implicit AND staging
          channels were queried. Pre-fix narrowing would have produced
          exactly 1 call.
     (ii) A prerelease-only package returned only when prerelease=true
          surfaces to the user — proving the #17725 fix holds on a real
          staging release, not just on a PR build.

Together with the existing
`…UsesConfiguredStagingChannelWithRealPackagingService` (apphost-pin
triggers staging synthesis under Stable identity) and
`…UnpinnedAppHostUsesImplicitChannelUnderStagingCli` (staging identity
without pin correctly falls back to implicit-only), the staging
quadrant is now fully covered.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix interactive fuzzy add confirmation

Prompt before adding a single fuzzy or no-match fallback candidate in interactive aspire add flows, while preserving exact-match auto-selection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: David Fowler <davidfowl@gmail.com>
* API review fixes for 13.4 (PR #17700)

Addresses several issues found during API surface review:

1. Rename NetworkID -> NetworkId (and networkID -> networkId) on
   AllocatedEndpoint, EndpointAnnotation, EndpointReference,
   EndpointReferenceAnnotation, NetworkEndpointSnapshot,
   NetworkEndpointSnapshotList, and related methods/parameters.
2. Add [Experimental("ASPIREAZURE003")] to AzureRoleAssignmentResource.
3. Change EndpointReferenceAnnotation.EndpointNames from HashSet<string>
   to ISet<string> (backing field stays HashSet).
4. Add 'sealed' to new public resource classes that are not subclassed
   in the repo: KubernetesHelmChartResource, BlazorWasmAppResource,
   BunAppResource, NextJsAppResource, ViteAppResource,
   AzureNatGatewayResource, AzureNetworkSecurityGroupResource,
   AzureNetworkSecurityPerimeterResource, AzurePrivateEndpointResource,
   AzurePublicIPAddressResource, AzureSubnetResource,
   AzureVirtualNetworkResource. (GoAppResource and NodeAppResource left
   non-sealed because they are used as generic type constraints in the
   same assembly.)
5. Disambiguate WithHiddenOnCompletion overloads by removing the
   '= 0' default from the int overload, so calls with no argument
   resolve to the params overload.

api/*.cs and api/*.ats.txt are intentionally not updated here - the API
surface PR will regenerate them.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Revert breaking API changes; bump baseline to 13.3.5

Reverts the subset of API changes from the previous commit that would be
binary-breaking against 13.3.5, and bumps PackageValidationBaselineVersion
from 13.2.2 to 13.3.5 so pack validation runs against the latest shipped
release.

Reverts (binary-breaking against 13.3.5):
- AllocatedEndpoint.NetworkID (kept ctor param 'networkId' - not breaking)
- EndpointAnnotation.DefaultNetworkID (kept ctor param 'networkId')
- EndpointReference.ContextNetworkID (kept ctor param 'contextNetworkId')
- NetworkEndpointSnapshot.NetworkID record positional param
- Removed 'sealed' from 9 shipped resource classes:
  - NextJsAppResource, ViteAppResource
  - AzureNatGatewayResource, AzureNetworkSecurityGroupResource,
    AzureNetworkSecurityPerimeterResource, AzurePrivateEndpointResource,
    AzurePublicIPAddressResource, AzureSubnetResource, AzureVirtualNetworkResource

Kept (not binary-breaking):
- All constructor/method parameter renames (networkID->networkId, etc.)
- EndpointReferenceAnnotation.ContextNetworkId (new in 13.4)
- EndpointNames type change (HashSet -> ISet)
- WithHiddenOnCompletion overload disambiguation
- [Experimental("ASPIREAZURE003")] on AzureRoleAssignmentResource
- 'sealed' on KubernetesHelmChartResource, BlazorWasmAppResource, BunAppResource (new in 13.4)

Package validation:
- Bumped PackageValidationBaselineVersion 13.2.2 -> 13.3.5
- Regenerated CompatibilitySuppressions.xml in 4 projects: most legacy
  entries against 13.2.2 are no longer needed because those APIs already
  shipped in 13.3.x. The remaining suppression is PublishAsNpmScript in
  Aspire.Hosting.JavaScript (documented removal from PR #17382).

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ed with dynamic ports (#17731)

When users set DisableDashboard = false on DistributedApplicationTestingBuilder,
the TransportOptionsValidator rejected the HTTP URLs that the testing builder
defaults (http://localhost:8080, http://localhost:4317) because
ASPIRE_ALLOW_UNSECURED_TRANSPORT was not set.

Fix: Set AllowUnsecuredTransport = true by default in PreConfigureBuilderOptions,
since all URLs are already defaulted to HTTP.

Add a regression test that enables the dashboard and verifies it becomes healthy.

Fixes #17622

Co-authored-by: James Newton-King <james@newtonking.com>
* Add Foundry hosted agent protocol selection

Support configuring Foundry hosted agent protocols for publish output and local run-mode dashboard commands, including TypeScript AppHost coverage for the exported DTO shape.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address Foundry hosted agent review feedback

Improve hosted agent protocol validation parameter names and add run-mode context when configuration callbacks fail during protocol inference.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Sync Foundry hosted agent endpoint protocols

Update hosted agent deployment to patch the Foundry agent endpoint protocols after creating a hosted-agent version, so endpoint routing matches the configured container protocol versions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Skip Foundry reserved hosted agent env vars

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…pe (#17743)

* Route staging-identity CLI to its darc feed regardless of version shape

The synthesized `staging` package channel derived its feed from the CLI
build's version shape: a prerelease-shaped staging build (e.g.
13.4.0-preview.1.26280.6) routed Aspire.* to the shared dnceng/dotnet9
daily feed instead of its SHA-specific darc-pub-microsoft-aspire-<commit>
feed. C# apphosts masked this because the darc feed is baked into their
nuget.config, but polyglot (TypeScript) apphosts resolve solely through the
channel's feed, so `aspire add <pkg>` offered the wrong versions.

Decouple feed provenance (identity) from version filtering (quality):

- Add `ShouldUseSharedStagingFeed(...)`: a staging-identity CLI always uses
  its own darc feed, any version shape. Override feeds and non-staging
  identities keep the prior quality-based routing.
- Add an injectable `cliInformationalVersionProvider` constructor seam so the
  derived darc feed URL is deterministic and assertable in tests.
- Correct the comments that incorrectly claimed darc feeds only exist for
  stable-shaped builds.

Add tests covering prerelease-shaped staging -> darc, stable-shaped staging
-> darc, and override-wins. The prerelease repro fails before the fix
(resolves dotnet9) and passes after (resolves darc).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Strengthen staging feed-routing tests and warn on underivable feed

Adds a decision-table theory across PR/daily/staging/stable channel
configurations, drops the override-feed crutch from the staging-identity
tests so they assert the real darc feed via an injected version seam, adds
coverage for the underivable-feed warning path, and adds symmetry asserts
for the stable-shaped staging case.

Also logs a one-time warning in CreateStagingChannel when a staging channel
is permitted but no staging feed URL can be derived, so the channel is
omitted visibly instead of silently.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add diagnostic overrides to validate staging feed routing locally

Adds two PackagingService-scoped diagnostic config overrides so a locally
built CLI (baked identity 'local', unstamped version) can simulate a staging
build and validate end-to-end that 'aspire add' resolves Aspire.* from the
correct SHA-specific darc-pub-microsoft-aspire-<sha> feed:

- overrideCliIdentityChannel: forces the identity used for staging-feed
  routing decisions only (validated via IdentityChannelReader.IsValidChannel;
  invalid values ignored). Does not change the global identity used for
  hive/packages-directory lookups, keeping blast radius limited.
- overrideCliInformationalVersion: forces the version that both the SHA
  derivation and the stable-shape/quality predicate read.

All staging-feed decision points now route through GetEffectiveIdentityChannel.
A one-time warning is emitted whenever either override is active. Adds
docs/cli-staging-validation.md with the local-validation recipe and nine
PackagingServiceTests covering the override permutations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add debug-staging/debug-stable scripts to simulate release-branch feed routing

Add eng/scripts/debug-staging.{sh,ps1} and debug-stable.{sh,ps1} (plus the
shared debug-aspire-channel core) that make an easy-to-get build (an installed
PR build or a local build) resolve Aspire.* packages exactly like an official
staging or stable release-branch build, so the feed-routing fix can be
validated end-to-end.

Each script targets identity 'staging' and the SHA-specific
darc-pub-microsoft-aspire-<sha8> feed, differing only in version shape/quality
(staging => prerelease/Both, #17744; stable => stable/Stable, #17527). Modes:

- default: one-shot 'aspire add --debug' that asserts the darc feed resolves.
- --print-env / -PrintEnv: emit export/$env lines to apply to the current shell.
- --shell / -Shell: interactive subshell with overrides applied and the target
  CLI first on PATH, for a full 'aspire new'/'add'/run flow; overrides vanish on exit.

Extend docs/cli-staging-validation.md with the helper-script usage, the
interactive PR-build recipe, and a validation matrix; link it from
docs/contributing.md.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Isolate NuGet package cache in debug-channel --shell mode

When dropping into the interactive subshell (--shell / -Shell), point
NUGET_PACKAGES at an isolated, per-sha directory so packages restored from
the simulated staging darc feed can never contaminate the developer's real
global package cache. Also commit the staging-override NOTE clarifying the
overrides route to but do not create a feed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix staging-identity update test for identity-driven darc feed routing

The staging identity now always routes to its build's SHA-specific
darc-pub-microsoft-aspire-<sha> feed regardless of version shape, so the
feed must be derivable from the CLI's +<commit> informational version. The
test host assembly has no commit metadata, so the staging channel could not
be synthesized and the test regressed. Stamp a staging-shaped informational
version via the overrideCliInformationalVersion config so the derivation
matches a real staging build.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ted agents (#17756)

* Resolve cross-compute-environment endpoint references to Foundry hosted agents

Fixes #17749. When a resource deployed to App Service, Azure Container
Apps, or Kubernetes used WithReference() to reference a Foundry hosted
agent, publishing failed because the publisher resolved the endpoint
against its own local endpoint map, which does not contain the agent
(deployed to the Foundry project compute environment).

Introduce a shared ComputeEnvironmentEndpointResolver that, when an
endpoint's owning resource is deployed to a different compute environment
than the current publisher, delegates resolution to that owning
environment's GetEndpointPropertyExpression. The three compute-environment
publishers now call it in both the EndpointReference and
EndpointReferenceExpression branches. Azure Front Door and the Foundry
hosted-agent resolver are refactored onto the same shared lookup.

AzureCognitiveServicesProjectResource gets a GetEndpointPropertyExpression
override because the agent address is already a full https URL; the
default scheme://host composition would produce a malformed double-scheme
value.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add branch tests for ComputeEnvironmentEndpointResolver and correct misleading comment

Add direct unit tests covering each branch of TryGetCrossEnvironmentEndpointExpression:
cross-environment delegation, same-environment deployment target, WithComputeEnvironment
binding backstop, no-compute-environment, bound multi-target (no throw), and unbound
multi-target (throws). Correct the comment on the fast-path loop which incorrectly claimed
it never throws on multi-target resources.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Remove redundant fast-path loop from ComputeEnvironmentEndpointResolver

The first foreach loop using GetDeploymentTargetAnnotation(current) was redundant with
TryGetEffectiveComputeEnvironment + the ReferenceEquals backstop for all well-formed inputs,
and did not provide multi-target throw-safety. Remove it and keep the simpler resolve-then-
compare flow. All six branch tests continue to pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Drop params and move out parameter last in cross-env endpoint resolver

Replace the params IComputeEnvironmentResource?[] with an explicit
IReadOnlyList parameter placed before the out, so the out parameter
comes last per convention. Kubernetes still passes two current
environments (its environment plus OwningComputeEnvironment) via a
collection expression; ACA and AppService pass a single-element list.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add EndpointReference overload for cross-env endpoint resolver

Add an overload taking EndpointReference that uses ep.Property(EndpointProperty.Url)
internally, so the three EndpointReference call sites (ACA, AppService, Kubernetes)
pass the endpoint directly instead of repeating the .Property(Url) projection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Use hosted-agent deployment name in cross-env Foundry agent URL

Hosted-agent deployment creates the Foundry agent version using the
wrapper AzureHostedAgentResource.Name (e.g. "agent-ha" for a target
named "agent"). The published cross-environment endpoint path was built
from the bare resource name, producing /agents/agent which does not match
the deployed /agents/agent-ha. Resolve the hosted-agent deployment target
and use its name when present, falling back to the resource name for
non-hosted agents.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Auto-wire Azure AI User role for hosted-agent consumers

When a compute resource references a Foundry hosted agent's node app via
WithReference, automatically grant the consumer the Azure AI User role on the
owning Foundry account and provision a managed identity, removing the two manual
post-deploy `az role assignment create` steps.

Introduces a public, experimental ReferenceRoleAssignmentAnnotation in
Aspire.Hosting.Azure. A resource that "fronts" an Azure resource (without being
an IAzureResource itself) carries this annotation; AzureResourcePreparer folds
its (Target, Roles) into the same role-assignment path used for direct Azure
references. Foundry's AsHostedAgent stamps the annotation on the agent's node
app granting only the least-privilege Azure AI User role; account defaults and
explicit WithRoleAssignments suppression remain owned by the preparer and are
not reintroduced. GetAllRoleAssignments now dedupes roles per target to avoid
colliding bicep role-assignment identifiers.

Adds preparer end-to-end tests (suppression preserved, defaults preserved,
dedup) and Foundry stamp tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Regenerate cross-env Foundry snapshots for RBAC auto-wiring

The cross-compute-environment Foundry hosted-agent snapshots were committed
before the RBAC auto-wiring change and never regenerated. With the consumer
now receiving a managed identity (web-identity) plus AZURE_CLIENT_ID and
AZURE_TOKEN_CREDENTIALS env vars, the generated bicep/json changed. CI failed
because the verified baselines were stale; local runs masked it because Verify
auto-accepts on developer machines.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 1, 2026 18:56
@aspire-repo-bot aspire-repo-bot Bot added the area-engineering-systems infrastructure helix infra engineering repo stuff label Jun 1, 2026
@aspire-repo-bot aspire-repo-bot Bot requested a review from danegsta as a code owner June 1, 2026 18:56
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 1, 2026

🚀 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 -- 17804

Or

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

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@joperezr
Copy link
Copy Markdown
Member

joperezr commented Jun 2, 2026

Closing in favor of #17817, which is the same merge re-opened from a non-protected branch so conflict resolutions and post-merge cleanups (revert StabilizePackageVersion, re-target generate-api-diffs.yml at main, bump backmerge-release.yml from release/13.3 to release/13.4) could be committed. #17817 is also tagged with a DO NOT SQUASH notice — the release-branch commit history is important to preserve on main.

@joperezr joperezr closed this Jun 2, 2026
@microsoft-github-policy-service microsoft-github-policy-service Bot added this to the 13.5 milestone Jun 2, 2026
pull Bot pushed a commit to tooniez/aspire that referenced this pull request Jun 2, 2026
Brings 43 release-branch commits forward onto main now that 13.4.0 has shipped.
This PR replaces the original automated merge (microsoft#17804) which had to be closed so
that conflict resolution and post-merge cleanups could be made on a non-protected
branch.

Conflict resolution summary (33 files):

* Equivalent backports (took main's commit identity): ChannelUpdateWorkflowTests,
  LoggingHelpersTests, the four extension test files, AspireEditorCommandProvider,
  appHostDiscovery.

* Release-only forwards (preserved): microsoft#17732 / microsoft#17756 Foundry hosted-agent protocol
  selection and cross-compute-environment endpoint references, microsoft#17573 stabilize
  PrebuiltAppHostServer staging globalPackagesFolder path, microsoft#17743 staging-identity
  CLI darc feed routing.

* Main-only forwards (preserved): microsoft#17506 Show discovered AppHosts in Aspire pane,
  microsoft#17547 Localize Aspire skills metadata errors, microsoft#17801 VS Code v1.12.0, microsoft#17297
  Aspire CLI npm package release integration, microsoft#17576 TerminalRun IAsyncDisposable,
  microsoft#17721 / microsoft#17723 VS Code telemetry, microsoft#17671 ATS baseline fix (re-applied manually
  on top of Foundry source taken from release).

* Hybrid (manually spliced): docs/contributing.md - kept main's restructured
  layout and inserted release's staging-validation paragraph; HostedAgentBuilder-
  Extension - took release base then re-applied microsoft#17671 asHostedAgent rename;
  UpdateCommandTests - took main and injected microsoft#17743's
  OverrideCliInformationalVersionConfigKey block.

Post-merge cleanups included in this PR:

* eng/Versions.props: revert StabilizePackageVersion to false (was flipped to
  true on release/13.4 by microsoft#17520 for shipping 13.4.0; main must stay in preview
  mode).

* .github/workflows/generate-api-diffs.yml: retarget back to main (was pointed
  at release/13.4 by microsoft#17696 release prep).

* .github/workflows/backmerge-release.yml: update from release/13.3 to
  release/13.4 (was stale - missed the 13.4 release-time bump).

* .github/workflows/milestone-assignment.yml: audited - already correct
  (main -> 13.5, release/13.4 -> 13.4.x); no change.

This merge commit must be preserved - do not squash on merge.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-engineering-systems infrastructure helix infra engineering repo stuff

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants