Skip to content

Add "watch" sub-mode for execution context#18863

Open
karolz-ms wants to merge 3 commits into
mainfrom
dev/karolz/project-v2-iswatch-mode
Open

Add "watch" sub-mode for execution context#18863
karolz-ms wants to merge 3 commits into
mainfrom
dev/karolz/project-v2-iswatch-mode

Conversation

@karolz-ms

Copy link
Copy Markdown
Contributor

Description

Adds a run sub-mode concept (and a watch sub-mode) so we can differentiate between the two and change how resources are launched.

Part of "Project v2" work stream, #15710 specifically.

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected (but the change is shippable as-is in the sense that it does not hurt anything and the new API is experimental)
  • 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

Copilot AI review requested due to automatic review settings July 22, 2026 21:29
@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 -- 18863

Or

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

@github-actions github-actions Bot added the area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication label Jul 22, 2026
@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

Adds an experimental run sub-mode model so integrations can distinguish normal and watch execution.

Changes:

  • Adds RunSubMode to hosting execution context APIs.
  • Reads the watch sub-mode from AppHost configuration.
  • Adds tests and updates polyglot snapshots and the Project v2 plan.

Reviewed changes

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

Show a summary per file
File Description
src/Aspire.Hosting/RunSubMode.cs Defines normal and watch sub-modes.
src/Aspire.Hosting/DistributedApplicationExecutionContextOptions.cs Adds sub-mode configuration.
src/Aspire.Hosting/DistributedApplicationExecutionContext.cs Exposes the selected sub-mode.
src/Aspire.Hosting/DistributedApplicationBuilder.cs Parses sub-mode configuration.
tests/Aspire.Hosting.Tests/OperationModesTests.cs Tests configuration and operation behavior.
tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts Updates TypeScript output.
tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs Updates Rust output.
tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py Updates Python output.
tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java Updates Java output.
tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go Updates Go output.
docs/plans/project-v2-csharpprogram-watch.md Records Session 3 completion.

Comment thread src/Aspire.Hosting/DistributedApplicationBuilder.cs Outdated
Comment thread src/Aspire.Hosting/DistributedApplicationExecutionContext.cs Outdated
Comment thread docs/plans/project-v2-csharpprogram-watch.md Outdated

@karolz-ms karolz-ms left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Found 3 issues: 1 failing generated snapshot and 2 correctness issues.

Comment thread src/Aspire.Hosting/DistributedApplicationBuilder.cs Outdated
Comment thread src/Aspire.Hosting/DistributedApplicationExecutionContext.cs Outdated
@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.

karolz-ms and others added 2 commits July 22, 2026 15:15
- Harden RunSubMode parsing: match the declared enum names case-insensitively
  instead of Enum.TryParse, which also accepted numeric ("42") and
  comma-separated ("Normal,Watch") inputs and bypassed the documented Normal
  fallback.
- Normalize RunSubMode to Normal for non-run operations in the
  DistributedApplicationExecutionContext options-constructor so a Publish
  context can never report Watch.
- Regenerate the TypeScript TwoPassScanning codegen snapshot so it matches the
  generated output (fixes the failing codegen test); the previously hand-edited
  wording was not reproducible.
- Fix unbalanced inline-code backticks in the Session 3 plan notes and correct
  the now-stale Enum.TryParse description.
- Add regression tests: numeric and compound config values fall back to Normal,
  and direct Publish construction reports Normal.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 22, 2026 22:15
@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.

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 11 out of 11 changed files in this pull request and generated no new comments.

@karolz-ms

Copy link
Copy Markdown
Contributor Author

/azp run

@karolz-ms karolz-ms closed this Jul 22, 2026
@karolz-ms karolz-ms reopened this Jul 22, 2026
@microsoft-github-policy-service microsoft-github-policy-service Bot added this to the 13.5 milestone Jul 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Tests selector (audit mode)

The full test matrix and all jobs still run in audit mode. The tests and jobs below are what selective CI would run under enforcement.

48 / 100 test projects · 4 jobs, from 10 changed files.

Selected test projects (48 / 100)

Aspire.EndToEnd.Tests, Aspire.Hosting.Analyzers.Tests, Aspire.Hosting.Azure.Kubernetes.Tests, Aspire.Hosting.Azure.Kusto.Tests, Aspire.Hosting.Azure.Tests, Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Browsers.Tests, Aspire.Hosting.CodeGeneration.Go.Tests, Aspire.Hosting.CodeGeneration.Java.Tests, Aspire.Hosting.CodeGeneration.Python.Tests, Aspire.Hosting.CodeGeneration.Rust.Tests, Aspire.Hosting.CodeGeneration.TypeScript.Tests, Aspire.Hosting.Containers.Tests, Aspire.Hosting.DevTunnels.Tests, Aspire.Hosting.Docker.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.DotnetTool.Tests, Aspire.Hosting.EntityFrameworkCore.Tests, Aspire.Hosting.Foundry.Tests, Aspire.Hosting.Garnet.Tests, Aspire.Hosting.GitHub.Models.Tests, Aspire.Hosting.Go.Tests, Aspire.Hosting.JavaScript.Tests, Aspire.Hosting.Kafka.Tests, Aspire.Hosting.Keycloak.Tests, Aspire.Hosting.Kubernetes.Tests, Aspire.Hosting.Maui.Tests, Aspire.Hosting.Milvus.Tests, Aspire.Hosting.MongoDB.Tests, Aspire.Hosting.MySql.Tests, Aspire.Hosting.Nats.Tests, Aspire.Hosting.OpenAI.Tests, Aspire.Hosting.Oracle.Tests, Aspire.Hosting.Orleans.Tests, Aspire.Hosting.PostgreSQL.Tests, Aspire.Hosting.Python.Tests, Aspire.Hosting.Qdrant.Tests, Aspire.Hosting.RabbitMQ.Tests, Aspire.Hosting.Radius.Tests, Aspire.Hosting.Redis.Tests, Aspire.Hosting.RemoteHost.Tests, Aspire.Hosting.Seq.Tests, Aspire.Hosting.SqlServer.Tests, Aspire.Hosting.Testing.Tests, Aspire.Hosting.Tests, Aspire.Hosting.Valkey.Tests, Aspire.Hosting.Yarp.Tests, Aspire.Playground.Tests

Selected jobs (4)

deployment-e2e, extension-e2e, polyglot, typescript-api-compat


How these were chosen — grouped by what changed

⚠️ 38 of the 48 selected test projects come from a single change — src/Aspire.Hosting/DistributedApplicationBuilder.cs.

🔧 src/Aspire.Hosting/DistributedApplicationBuilder.cs (changed source)
38 via the project graph

show 38

Aspire.Hosting.Analyzers.Tests (2 hops), Aspire.Hosting.Azure.Kubernetes.Tests (2 hops), Aspire.Hosting.Azure.Kusto.Tests (2 hops), Aspire.Hosting.Azure.Tests, Aspire.Hosting.Browsers.Tests (2 hops), Aspire.Hosting.Containers.Tests (2 hops), Aspire.Hosting.DevTunnels.Tests (2 hops), Aspire.Hosting.Docker.Tests (2 hops), Aspire.Hosting.DotnetTool.Tests (2 hops), Aspire.Hosting.EntityFrameworkCore.Tests (2 hops), Aspire.Hosting.Foundry.Tests (2 hops), Aspire.Hosting.Garnet.Tests (2 hops), Aspire.Hosting.GitHub.Models.Tests (2 hops), Aspire.Hosting.Go.Tests (2 hops), Aspire.Hosting.JavaScript.Tests (2 hops), Aspire.Hosting.Kafka.Tests (2 hops), Aspire.Hosting.Keycloak.Tests (2 hops), Aspire.Hosting.Kubernetes.Tests (2 hops), Aspire.Hosting.Maui.Tests, Aspire.Hosting.Milvus.Tests (2 hops), Aspire.Hosting.MongoDB.Tests (2 hops), Aspire.Hosting.MySql.Tests (2 hops), Aspire.Hosting.Nats.Tests (2 hops), Aspire.Hosting.OpenAI.Tests (2 hops), Aspire.Hosting.Oracle.Tests (2 hops), Aspire.Hosting.Orleans.Tests (2 hops), Aspire.Hosting.PostgreSQL.Tests (2 hops), Aspire.Hosting.Python.Tests (2 hops), Aspire.Hosting.Qdrant.Tests (2 hops), Aspire.Hosting.RabbitMQ.Tests (2 hops), Aspire.Hosting.Redis.Tests (2 hops), Aspire.Hosting.RemoteHost.Tests, Aspire.Hosting.Seq.Tests (2 hops), Aspire.Hosting.SqlServer.Tests (2 hops), Aspire.Hosting.Testing.Tests (2 hops), Aspire.Hosting.Valkey.Tests (2 hops), Aspire.Hosting.Yarp.Tests (2 hops), Aspire.Playground.Tests

🧪 tests/Aspire.Hosting.Tests/OperationModesTests.cs (changed test)
1 directly: Aspire.Hosting.Tests
3 via the project graph: Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.Radius.Tests

📦 affected project Aspire.Hosting
1 test: Aspire.EndToEnd.Tests

🧪 tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go (changed test)
1 directly: Aspire.Hosting.CodeGeneration.Go.Tests

🧪 tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java (changed test)
1 directly: Aspire.Hosting.CodeGeneration.Java.Tests

🧪 tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py (changed test)
1 directly: Aspire.Hosting.CodeGeneration.Python.Tests

🧪 tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs (changed test)
1 directly: Aspire.Hosting.CodeGeneration.Rust.Tests

🧪 tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts (changed test)
1 directly: Aspire.Hosting.CodeGeneration.TypeScript.Tests

Job reasons

Job Triggered by
deployment-e2e affected project Aspire.Hosting.Azure
extension-e2e src/Aspire.Hosting/DistributedApplicationBuilder.cs, src/Aspire.Hosting/DistributedApplicationExecutionContext.cs, src/Aspire.Hosting/DistributedApplicationExecutionContextOptions.cs, src/Aspire.Hosting/RunSubMode.cs
• affected project Aspire.Hosting
polyglot affected project Aspire.Hosting.Python
typescript-api-compat affected project Aspire.Hosting

Selection computed for commit fa81d7e.

@karolz-ms

Copy link
Copy Markdown
Contributor Author

PR Testing Report

PR Information

  • PR Number: Add "watch" sub-mode for execution context #18863
  • Title: Add "watch" sub-mode for execution context
  • Head Commit: fa81d7ef37ccd8379d425837aefdb8beaca559de
  • Tested At: 2026-07-22T23:37:03Z
  • Execution Target: Local macOS arm64 temporary directory

Artifact Version Verification

Changes Analyzed

Files Changed

  • docs/plans/project-v2-csharpprogram-watch.md
  • src/Aspire.Hosting/DistributedApplicationBuilder.cs
  • src/Aspire.Hosting/DistributedApplicationExecutionContext.cs
  • src/Aspire.Hosting/DistributedApplicationExecutionContextOptions.cs
  • src/Aspire.Hosting/RunSubMode.cs
  • tests/Aspire.Hosting.CodeGeneration.Go.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.go
  • tests/Aspire.Hosting.CodeGeneration.Java.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.java
  • tests/Aspire.Hosting.CodeGeneration.Python.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.py
  • tests/Aspire.Hosting.CodeGeneration.Rust.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.rs
  • tests/Aspire.Hosting.CodeGeneration.TypeScript.Tests/Snapshots/TwoPassScanningGeneratedAspire.verified.ts
  • tests/Aspire.Hosting.Tests/OperationModesTests.cs

Change Categories

  • CLI changes
  • Hosting core changes
  • Dashboard changes
  • Template changes
  • Client/component changes
  • VS Code extension changes
  • Test and polyglot code-generation snapshot changes
  • CI infrastructure changes

Test Scenarios Executed

Scenario 1: Execution-context source tests

Objective: Verify normal/watch parsing, case insensitivity, invalid-value fallback, and publish isolation in the hosting implementation.

Coverage Type: Unit, happy path, unhappy path, boundary

Status: ✅ Passed

Command:

dotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj \
  --no-launch-profile -- \
  --filter-class '*.OperationModesTests' \
  --filter-not-trait 'quarantined=true' \
  --filter-not-trait 'outerloop=true'

Result: 13 passed, 0 failed, 0 skipped.

Evidence: operation-modes-tests.log

Scenario 2: Polyglot API generation

Objective: Verify the additive RunSubMode enum and execution-context getter remain represented by all changed generated-language snapshots.

Coverage Type: Snapshot/code generation

Status: ✅ Passed

Method: TwoPassScanning_GeneratesWithEnvironmentOnTestRedisBuilder

Results:

Generator Passed Failed
Go 1 0
Java 1 0
Python 1 0
Rust 1 0
TypeScript 1 0

Evidence: codegen-go.log, codegen-java.log, codegen-python.log, codegen-rust.log, codegen-typescript.log

Scenario 3: Dogfood artifact and template

Objective: Verify the downloadable PR artifact matches the PR head and can create a fresh C# AppHost from the PR package hive.

Coverage Type: Artifact/template smoke

Status: ✅ Passed

Steps:

  1. Installed the PR CLI to an isolated temporary directory.
  2. Verified version 13.5.0-pr.18863.gfa81d7ef.
  3. Created fresh aspire-empty projects with --source <PR hive>, --version 13.5.0-pr.18863.gfa81d7ef, and --language csharp.
  4. Compiled the new experimental API from the generated single-file AppHosts.

Evidence: install.log, version.txt, new-app.log

Scenario 4: Default run mode

Objective: Verify an unconfigured AppHost reports the backward-compatible normal sub-mode.

Coverage Type: Happy path

Status: ✅ Passed

Observed:

EXECUTION_CONTEXT Operation=Run; RunSubMode=Normal

The detached AppHost started successfully, its dashboard login endpoint returned HTTP 302, and aspire stop terminated it cleanly.

Evidence: runtime-default-context.txt, runtime-default-health.txt, runtime-default-start.json, runtime-default-stop.log

Scenario 5: Watch run mode

Objective: Verify the inherited ASPIRE_AppHost__RunSubMode=watch configuration selects Watch, including case-insensitive parsing.

Coverage Type: Happy path and casing boundary

Status: ✅ Passed

Observed:

EXECUTION_CONTEXT Operation=Run; RunSubMode=Watch

The detached AppHost started successfully, its dashboard login endpoint returned HTTP 302, and aspire stop terminated it cleanly.

Evidence: runtime-watch-context.txt, runtime-watch-health.txt, runtime-watch-start.json, runtime-watch-stop.log

Scenario 6: Invalid numeric sub-mode

Objective: Verify an undeclared numeric value cannot leak through Enum.TryParse behavior and safely falls back to Normal.

Coverage Type: Unhappy path

Status: ✅ Passed

Input: ASPIRE_AppHost__RunSubMode=42

Expected Outcome: The AppHost starts normally and reports RunSubMode.Normal.

Observed:

EXECUTION_CONTEXT Operation=Run; RunSubMode=Normal

The detached AppHost started successfully, its dashboard login endpoint returned HTTP 302, and aspire stop terminated it cleanly.

Evidence: runtime-invalid-context.txt, runtime-invalid-health.txt, runtime-invalid-start.json, runtime-invalid-stop.log

Scenario 7: Publish-mode isolation

Objective: Verify a watch configuration cannot put a publish invocation into watch sub-mode.

Coverage Type: Boundary

Status: ✅ Passed

Input: ASPIRE_AppHost__RunSubMode=Watch with aspire publish

Expected Outcome: The publish invocation reports Operation=Publish, forces RunSubMode.Normal, and completes successfully.

Observed:

EXECUTION_CONTEXT Operation=Publish; RunSubMode=Normal

The publish pipeline completed all 4 steps successfully.

Evidence: runtime-publish-context.txt, runtime-publish.log, cli_20260722T233627_f4cdaf6b.log

Summary

Scenario Status Notes
Execution-context source tests ✅ Passed 13/13
Polyglot API generation ✅ Passed 5/5 generators
Dogfood artifact and template ✅ Passed Artifact matches PR head
Default run mode ✅ Passed RunSubMode.Normal
Watch run mode ✅ Passed Lowercase watch resolved to Watch
Invalid numeric sub-mode ✅ Passed Safely fell back to Normal
Publish-mode isolation ✅ Passed Publish forced Normal; 4/4 steps

Overall Result

✅ PR VERIFIED

The tested hosting-core behavior, public experimental API consumption, generated-language snapshots, downloadable artifact, and runtime configuration paths all behaved as intended. This PR intentionally adds only the core run sub-mode state; aspire run --watch mechanics are outside its scope.

@karolz-ms
karolz-ms marked this pull request as ready for review July 23, 2026 00:41
@karolz-ms
karolz-ms requested a review from DamianEdwards July 23, 2026 00:43
@karolz-ms

Copy link
Copy Markdown
Contributor Author

AI code review done earlier #18863 (review) all issues addressed

@davidfowl

Copy link
Copy Markdown
Contributor

Why is this top level on the execution context? Just a formal API for exposing how the apphost itself was launched? I would expect we keep watching of the apphost and watching of the individual resource types separate.

@karolz-ms

Copy link
Copy Markdown
Contributor Author

@davidfowl in my mind the "watch mode" is primarily about how individual resources are launched (enabling quick iteration during service development). It affects decisions about how resources are configured and launched that are made pretty deeply inside Aspire hosting package(s). That is why I have chosen (via this PR) to put that information in the application execution context.

The app host watch mode/hot reload is interesting and useful, but I expect it to be used less frequently (as in, close-loop iteration over app host code). Technically app host watch mode could be separated from resource watch mode, but I am not clear what a compelling scenario for doing this would be... ?

@danegsta

Copy link
Copy Markdown
Member

A few plan related thoughts. I'd love to see an extra section in the markdown discussing at least possible future patterns for another language or two (i.e. what would supporting this for to optionally enable watch for JS or Go projects look like?). Especially that some services already default to running with the equivalent of watch (Vite projects, for example). I worry that we'll end up with an inconsistent experience where aspire watch only really means dotnet watch while other languages only support the mode inconsistently.

@davidfowl

Copy link
Copy Markdown
Contributor

@karolz-ms See https://aspire.dev/app-host/hot-reload-and-watch/

I don't think we should expose an API for watch mode on the execution context. Watch for an individual resource can be configured on that resource in whatever way makes sense. If we want to build something for any resource then it needs to be outside of this project resource v2 scope.

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

Labels

area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants