Skip to content

Fix the Visual Studio preview: assembly binding and packaged apps - #1282

Merged
Alexandre Zollinger Chohfi (azchohfi) merged 16 commits into
mainfrom
azchohfi-fix-preview-exception
Sep 25, 2026
Merged

Alexandre Zollinger Chohfi (azchohfi) merged 16 commits into
mainfrom
azchohfi-fix-preview-exception

Conversation

@azchohfi

@azchohfi Alexandre Zollinger Chohfi (azchohfi) commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Two independent bugs that each made the Visual Studio Reactor Preview unusable, found while dogfooding it.

  1. Every preview session failed to start with FileNotFoundException: Could not load file or assembly 'System.Text.Json, Version=10.0.0.12'.
  2. Packaged (MSIX) projects never previewed - which is what dotnet new reactor generates. The app started and rendered, then the preview silently waited out its handshake timeout with nothing in stderr to explain it.

1. System.Text.Json past VS's binding-redirect ceiling

Visual Studio owns the identity of System.Text.Json inside devenv.exe: it ships one copy under Common7\IDE\SharedAssemblies and binds every extension to it through a devenv.exe.config <bindingRedirect> with a hard upper bound. VS 18 declares:

<assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.10" newVersion="10.0.0.10" />

Redirects unify upward, and only within their oldVersion range. When the repo-wide CPM pin rolled to 10.0.12 the extension began requesting AssemblyVersion 10.0.0.12, past that ceiling. No redirect matched; the VSIX carries no private copy (VS strips assemblies it provides); the package registers no BindingPath. The CLR probed devenv's base directory, found nothing, and threw at EmbedClient.StatusAsync - the first call every session makes.

The extension now pins System.Text.Json with VersionOverride="9.0.0", the version Microsoft.VisualStudio.SDK itself depends on, instead of following the repo-wide version.

Review surfaced a consequence worth calling out: the VSIX advertised Visual Studio 17.8+, but the SDK baselines System.Text.Json per host - 17.8 → 7.0.3, 17.9 → 8.0.0, 17.14 → 9.0.0. A 9.0.0.0 reference is outside the redirect range of any host below 17.14, so that claim was unsound. All 13 InstallationTarget ranges and the CoreEditor prerequisite now read [17.14,19.0), matching the SDK the extension compiles against, and spec 056 was updated to the same baseline.

2. Packaged projects had no stdout

The preview starts the target with dotnet watch run. For a packaged project that goes through the launcher Microsoft.Windows.SDK.BuildTools.WinApp installs, which the Reactor templates reference. That launcher activates the app by AUMID, which is brokered and has no stdout at all - and stdout is how the child reports CAPTURE_PORT / CAPTURE_TOKEN.

The launcher now sets WinAppRunUseExecutionAlias=true in the child environment, switching that launch to an execution alias that inherits stdout.

Two deliberate choices, both measured:

  • Through the environment, not -p:. dotnet watch rejects -p alongside --project as ambiguous, and an environment property is the lowest-precedence MSBuild property, so a project that sets the value explicitly still wins. Verified both ways: env-only evaluates to true, an explicit project false overrides it.
  • Unconditionally, not behind a packaged/unpackaged probe. The WinApp targets only consume it when their run support is active, so it is inert for an unpackaged project, and probing would cost an MSBuild evaluation per launch.

A project converted to MSIX by hand, following the MSIX section of the packaging guide, adds only MSIX properties and so still needs the run-support package itself. The guide says so.

Linked issue / spec

N/A - both reported directly while using the extension. No issues were filed.

Test plan

Packaged preview. Two apps generated from dotnet new reactor, which resolves to Microsoft.WindowsAppSDK.WinUI.CSharp.Templates 0.0.7-alpha - the pack WinAppSdkTemplates.PackageId names, now that #1277 retired the in-repo template pack. Its Reactor template declares no WindowsPackageType (so, packaged) and already references Microsoft.Windows.SDK.BuildTools.WinApp. Both apps were driven with the exact command the extension issues:

App Before After
Control - only edit is WindowsPackageType=None full handshake full handshake, byte-identical
Packaged, untouched no handshake at all full handshake
  • "After" runs emit CAPTURE_PORT, CAPTURE_TOKEN, MCP serving and devtools-ready; both endpoints probed live over HTTP (401 = up, token required).
  • Control proves the variable is inert when unpackaged - no alias registration, identical output.
  • Project-level opt-out verified via -getProperty:: env-only → true, explicit project false → false.
  • Confirmed working in the Visual Studio tool window itself, which the command-line runs could not exercise (HWND reparent + embed ack).

System.Text.Json:

  • Read the reference out of the installed Reactor.VsExtension.dll via System.Reflection.Metadata: 10.0.0.12, against a declared ceiling of 10.0.0.10.
  • Confirmed the installed extension folder contains no System.Text.Json.dll, so there was nothing local to bind to.
  • Rebuilt reference is 9.0.0.0, inside the range. All of the extension's references cross-checked against the ranges parsed out of devenv.exe.config; all in range.

Gates, each mutation-checked so a pass is not a tautology:

  • Ceiling gate reddens when lowered to 8.0.0.0; every gated assembly must be a direct reference, so a ceiling entry nothing evaluates fails rather than sitting dormant (re-adding transitive System.Text.Encodings.Web reddens it).
  • Manifest host gate reddens on [17.8.1,19.0) - a patch-component range the original regex skipped silently - and on bumping the SDK to 18.0. Its floor is derived from the central Microsoft.VisualStudio.SDK PackageVersion, so the pin and the manifest cannot drift apart.
  • ReactorChildLauncherEnvironmentTests reddens when the env-var call is removed.
  • Reactor.VsExtension.Tests 87, Reactor.VsExtension.SdkTests 6, Reactor.DocPipeline.Tests 472 - all passing on the merged tree.
  • dotnet restore Reactor.slnx then dotnet build Reactor.slnx --no-restore -c Release - 0 errors. (NU1900 warnings are this machine's blocked nuget.org feed.)

Risk / breaking changes

Low, and scoped to the VS extension plus guide/spec/changelog text. No product code or public API changed.

  • The VSIX minimum host moves from 17.8 to 17.14. This drops advertised support for 17.8-17.13. Those hosts could not have loaded the extension anyway once it references Microsoft.VisualStudio.SDK 17.14, so this makes an existing constraint honest rather than imposing a new one.
  • The extension is deliberately excluded from the repo-wide System.Text.Json version, so Dependabot bumps no longer reach it. The host gate derives its floor from the SDK PackageVersion, so an SDK bump fails the build until the manifest is raised deliberately.
  • The binding bug had already regressed silently once: 10.0.11 was also past the ceiling, so the extension was broken from that bump onward, not just the latest.

Visual Studio ships System.Text.Json as a shared assembly and binds every
extension to its own copy through a devenv.exe.config <bindingRedirect> with a
hard upper bound (VS 18: oldVersion="0.0.0.0-10.0.0.10"). Redirects unify
upward only, so when the repo-wide Central Package Management pin rolled to
10.0.12 the extension began requesting AssemblyVersion 10.0.0.12 -- past that
ceiling. No redirect applied; the VSIX carries no private copy because VS
strips assemblies it provides, and the package registers no BindingPath, so the
CLR probed devenv's base directory, found nothing, and threw:

  FileNotFoundException: Could not load file or assembly
  'System.Text.Json, Version=10.0.0.12, Culture=neutral,
   PublicKeyToken=cc7b13ffcd2ddd51'

That aborted every preview session at EmbedClient.StatusAsync, the first call a
session makes. The build stayed green throughout -- nothing at compile time
sees the ceiling.

Pin System.Text.Json with VersionOverride="9.0.0" (the version
Microsoft.VisualStudio.SDK 17.14 itself depends on) instead of following the
repo-wide version. Every VS release that satisfies the SDK redirects at least
that high, and building low is always safe because redirects unify upward.

Add VsSharedAssemblyBindingTests, which reads the compiled assembly references
and fails if any VS shared assembly exceeds the ceiling again. Verified that
all 13 of the extension's references now fall inside VS 18's declared redirect
ranges.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2
@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

📦 Build metrics

Artifact sizes for b23e413 vs the base branch (0a04370).

Packages (compressed .nupkg)

Artifact base PR Δ
Microsoft.UI.Reactor.nupkg 1.72 MB 1.72 MB +5 B (0.00%) ≈
Microsoft.UI.Reactor.Advanced.nupkg 487.5 KB 487.5 KB +7 B (0.00%) ≈
Microsoft.UI.Reactor.Devtools.nupkg 284.1 KB 284.1 KB +3 B (0.00%) ≈

Assemblies in Microsoft.UI.Reactor

Artifact base PR Δ
Reactor.Analyzers.dll 373.0 KB 373.0 KB +0 B (0.00%) ≈
Reactor.dll 2.44 MB 2.44 MB +0 B (0.00%) ≈
Reactor.Localization.Generator.dll 16.0 KB 16.0 KB +0 B (0.00%) ≈
Reactor.Wrappers.Abstractions.dll 10.5 KB 10.5 KB +0 B (0.00%) ≈
Reactor.Wrappers.Generator.dll 99.5 KB 99.5 KB +0 B (0.00%) ≈

Assemblies in Microsoft.UI.Reactor.Advanced

Artifact base PR Δ
Reactor.Advanced.dll 1021.5 KB 1021.5 KB +0 B (0.00%) ≈

Assemblies in Microsoft.UI.Reactor.Devtools

Artifact base PR Δ
Microsoft.UI.Reactor.Devtools.dll 784.5 KB 784.5 KB +0 B (0.00%) ≈

No size change beyond the noise floor. ✅

✅ smaller / ⚠️ larger / ≈ within noise. Sizes come from a Release dotnet pack on the CI runner: packages are the compressed .nupkg download size, assemblies the uncompressed DLL inside it.
workflow run.

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

🧪 Merged coverage

Coverage for b23e413 vs the base branch (8cc49ce) — unit + selftest merged.

Metric base PR Δ
Line 85.79% 85.79% 0.00 pp ≈
Branch 77.58% (962/1240) 77.66% (963/1240) +0.08 pp ≈

No coverage change beyond the noise floor. ✅

✅ higher / ⚠️ lower / ≈ within noise. Δ is in percentage points; coverage is unit + selftest merged (Debug x64) on the CI runner. Cobertura reports attached to the workflow run as artifacts.

…g opaquely

The preview starts the target with `dotnet watch run`, which launches the built
.exe directly. That works for the unpackaged default but leaves a packaged
project dead two different ways, and the extension reported neither.

Package identity: a bare .exe launch has no package graph, so the Windows App
SDK deployment auto-initializer cannot activate its WinRT types and the process
dies in .cctor() with COMException (0x80040154) REGDB_E_CLASSNOTREG before any
Reactor code runs. Microsoft.Windows.SDK.BuildTools.WinApp fixes this by
overriding ComputeRunArguments so RunCommand points at a launcher that registers
a debug identity.

Inherited stdout: that launcher defaults to AUMID activation, which is brokered
and has no stdout at all. The devtools handshake reports CAPTURE_PORT on stdout,
so the app would run while the preview could never attach. Setting
WinAppRunUseExecutionAlias=true makes the launcher use a generated execution
alias, which inherits stdout. Verified end to end against the exact command the
extension issues: the packaged app completes the full handshake, emitting
CAPTURE_PORT / CAPTURE_TOKEN and devtools-ready, and both servers answer over
HTTP.

Either change alone leaves the preview broken, so the handshake-timeout path now
recognises the stderr signature and answers with both, replacing a generic list
of guesses. The matcher requires a CLASSNOTREG marker AND a Windows App SDK
frame: REGDB_E_CLASSNOTREG on its own is a generic COM error, and matching it
alone would mislabel unrelated failures. A test asserts exactly that, and
mutation-checking it (dropping the second half of the signature) reddens it.

The VS extension guide gains a "Packaged (MSIX) projects" section, edited in the
pipeline template and recompiled.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2
@azchohfi Alexandre Zollinger Chohfi (azchohfi) changed the title Pin the VS extension's System.Text.Json to the VS SDK baseline Fix the Visual Studio preview: assembly binding and packaged projects Sep 25, 2026
The Reactor templates generate a packaged app by default, and previewing one
did not work.

The preview starts the target with `dotnet watch run`. For a packaged project
that goes through the launcher Microsoft.Windows.SDK.BuildTools.WinApp installs,
which the templates already reference. That launcher activates the app by AUMID,
which is brokered and has no stdout, and stdout is how the child reports
CAPTURE_PORT / CAPTURE_TOKEN. The app started and rendered while the preview
waited out its handshake timeout with nothing in stderr to explain it.

The launcher now sets WinAppRunUseExecutionAlias=true in the child environment,
which switches that launch to an execution alias that inherits stdout. A
freshly generated packaged app now previews with no project changes.

Set through the environment rather than -p: for two reasons. `dotnet watch`
rejects -p alongside --project as ambiguous, and an environment property is the
lowest-precedence MSBuild property, so a project that sets the value explicitly
still wins. Verified both: env-only evaluates to true, and an explicit project
value of false overrides it.

Set unconditionally rather than behind a packaged/unpackaged probe. The WinApp
targets only consume it when their run support is active, so it is inert for an
unpackaged project, and probing would cost an MSBuild evaluation per launch.

Verified against two apps generated from the current template, driven with the
exact command the extension issues. Unpackaged control (WindowsPackageType=None,
the only edit): full handshake, byte-identical with and without the variable.
Packaged default template, unchanged: no handshake before, full handshake after.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2
@azchohfi Alexandre Zollinger Chohfi (azchohfi) changed the title Fix the Visual Studio preview: assembly binding and packaged projects Fix the Visual Studio preview: assembly binding and packaged apps Sep 25, 2026

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.

Copilot review overview

🟡 Changes recommended

The 9.0 JSON pin remains incompatible with the VSIX’s advertised Visual Studio 17.8 minimum, and changelog references are missing.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity · 1 Low severity

Open (2)
What changed in this PR

Fixes Visual Studio preview startup failures caused by assembly binding and missing MSIX stdout handshakes.

Changes:

  • Pins System.Text.Json and adds binding regression tests.
  • Launches packaged apps through execution aliases and tests launcher configuration.
  • Updates documentation and changelog.
File Description
VsSharedAssemblyBindingTests.cs Tests shared-assembly ceilings.
ReactorChildLauncherEnvironmentTests.cs Tests packaged launch environment.
Reactor.VsExtension.Tests.csproj Aligns test JSON dependency.
Reactor.VsExtension.csproj Pins the extension’s JSON dependency.
ReactorChildLauncher.cs Enables execution-alias launching.
docs/​guide/​vs-extension.md Documents packaged preview support.
docs/​_pipeline/​templates/​vs-extension.md.dt Updates the guide source template.
CHANGELOG.md Records both fixes.

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/vs-reactor/Reactor.VsExtension/Reactor.VsExtension.csproj
Comment thread CHANGELOG.md Outdated
The extension compiles against Microsoft.VisualStudio.SDK 17.14, which baselines
System.Text.Json 9.0.0, but source.extension.vsixmanifest still advertised
Visual Studio 17.8. Measured against the SDK's own dependency graph, 17.8
baselines System.Text.Json 7.0.3 and 17.9 baselines 8.0.0, so a 9.0.0.0
reference falls outside those hosts' binding redirect range and the extension
would fail to load there with exactly the FileNotFoundException this PR fixes
for VS 18. Advertising 17.8 support was therefore unsound before this PR too.

Raise all 13 InstallationTarget ranges and the CoreEditor prerequisite to
[17.14,19.0), and say 17.14 in the guide and the VSIX README.

Add VsixManifest_DoesNotAdvertiseHostsOlderThanThePinnedBaseline so the pin and
the manifest cannot drift apart again: it parses every advertised range out of
the manifest and fails on any below the baseline, with a positive control so an
unparsed manifest cannot read as "no violations" and a hard failure if the file
cannot be located rather than a vacuous pass. Mutation-checked by restoring
17.8, which reddens it.

Also cite the originating PR on both changelog entries, per the cross-reference
convention in CHANGELOG.md.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2

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.

Copilot review overview

🟡 Changes recommended

The manifest regression gate misses unsupported host ranges containing patch-version components.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (2)

Comment thread src/vs-reactor/Tests/Reactor.VsExtension.Tests/VsSharedAssemblyBindingTests.cs Outdated
The regex form matched only two-component minimums, so a range carrying a patch
component -- [17.8.1,19.0) -- matched nothing and was dropped silently, while
Assert.NotEmpty stayed satisfied by the remaining ranges. A skipped range read
exactly like a compliant one, which is the failure mode the gate exists to
prevent.

Read the manifest with XDocument and scope to InstallationTarget and
Prerequisite elements instead. Every Version attribute on those elements is now
parsed, and an unrecognised range shape throws rather than being skipped, so the
gate cannot silently narrow again. Error text names the offending element and
Id.

Mutation-checked with the exact reported case: rewriting one range to
[17.8.1,19.0) reddens the test and names it. The previous regex passed it.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2

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.

Copilot review overview

🔵 Needs a closer look

Compatibility documentation remains inconsistent, and two release statements incorrectly claim Reactor templates are packaged by default.

Review effort: Balanced
Findings: None

Resolved since last review (1)
Previously missed (3)

In code that hasn't changed since last review

Low severity Remove inaccurate packaged app default claim from release notes

CHANGELOG.md:80

This release note says the templates generate packaged apps by default, but the current reactorapp template explicitly sets <WindowsPackageType>None</WindowsPackageType> at tools/Templates/templates/WinUIApp-CSharp/Company.ReactorApp1.csproj:15. Remove that claim so the changelog accurately describes the fix as enabling preview for projects that are packaged.

Low severity Update guide index compatibility to Visual Studio 17.14+

docs/​_pipeline/​templates/​vs-extension.md.dt:15

The extension's minimum is updated here, but the guide index still advertises this page as supporting Visual Studio 2022 17.8+ (docs/_pipeline/templates/index.md.dt:142, propagated to docs/guide/index.md:168 and docs/guide/README.md:168). Update the index template to 17.14+ and regenerate its outputs so users do not receive contradictory compatibility information.

Low severity Correct packaged app claim in Reactor template guide

docs/​_pipeline/​templates/​vs-extension.md.dt:41

The repository's reactorapp template currently sets <WindowsPackageType>None</WindowsPackageType> (tools/Templates/templates/WinUIApp-CSharp/Company.ReactorApp1.csproj:15), so the statement that Reactor templates generate packaged apps by default is false. Please describe this as support for existing packaged projects, then regenerate the guide output.

Three follow-ups from review, all factual accuracy rather than behaviour.

The claim that the Reactor templates generate a packaged app by default is not
true of this repository's template: tools/Templates/templates/WinUIApp-CSharp
sets WindowsPackageType=None. The packaged-by-default behaviour belongs to the
separately distributed Microsoft.WindowsAppSDK.WinUI.CSharp.Templates pack,
which is what was used while reproducing the bug. Rather than disambiguate two
templates in a changelog line, describe the fix by what it does: previewing a
packaged project now works with no project changes.

The guide index still advertised the VS extension page as Visual Studio 2022
17.8+, contradicting the raised minimum. Update index.md.dt and regenerate, so
index.md and README.md agree with the extension page and the manifest.

Regenerating the index also rewrote four unrelated readme screenshots; those are
reverted.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2
@azchohfi

Copy link
Copy Markdown
Collaborator Author

Addressed the three findings in the collapsed Previously missed block of the last review (they created no inline threads, so replying here).

Packaged-by-default claim — correct, and I verified it rather than taking it on faith. This repository's template, tools/Templates/templates/WinUIApp-CSharp/Company.ReactorApp1.csproj, does set <WindowsPackageType>None</WindowsPackageType>. The packaged-by-default behaviour I observed belongs to the separately distributed Microsoft.WindowsAppSDK.WinUI.CSharp.Templates pack, which is what dotnet new reactor resolved to on this machine while reproducing the bug. Two different templates, and the changelog is the wrong place to disambiguate them, so both the changelog entry and the guide now describe the fix by what it does: previewing a packaged project works with no project changes. The WindowsPackageType=None note for unpackaged previewing is unchanged.

Guide index compatibility. docs/_pipeline/templates/index.md.dt updated to 17.14+ and regenerated, so docs/guide/index.md and docs/guide/README.md now agree with the extension page and the manifest. Regenerating the index also rewrote four unrelated readme screenshots; those were reverted so the diff stays scoped.

Fixed in ac75bb2. Doc pipeline suite 472/472, extension suite 87/87.

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.

Copilot review overview

🔵 Needs a closer look

The host-version regression gate can remain green after a future Visual Studio SDK baseline bump because its minimum is independently hard-coded.

Review effort: Balanced
Findings: None

Previously missed (1)

In code that hasn't changed since last review

Medium severity Derive host minimum from central Visual Studio SDK version

src/​vs-reactor/​Tests/​Reactor.VsExtension.Tests/​VsSharedAssemblyBindingTests.cs:69

This minimum is still independent of the dependency it claims to gate. When Directory.Packages.props:82 bumps Microsoft.VisualStudio.SDK—the update path called out above—updating the JSON pin and Ceilings while leaving this literal at 17.14 keeps all three tests green, yet the VSIX can still advertise hosts below the new SDK baseline. Derive the required host major/minor from the central SDK PackageVersion, or otherwise give the pin and host minimum one source of truth, so the gate fails on that dependency bump.

The host minimum was a second hard-coded literal, independent of the dependency
it gates. Bumping Microsoft.VisualStudio.SDK in Directory.Packages.props while
updating the System.Text.Json pin and the ceiling would leave that literal
behind, keeping all three tests green while the VSIX still advertised hosts the
new pin cannot load on -- the same class of silent breakage this PR started
from.

Read the Microsoft.VisualStudio.SDK PackageVersion instead and take its
major.minor as the required host floor. The extension cannot support a host
older than the SDK it compiles against, and that SDK is what fixes the
System.Text.Json baseline, so the two now move together by construction. A
missing or unparseable PackageVersion throws rather than degrading to a default.

Mutation-checked with the reported scenario: bumping the SDK to 18.0 reddens the
manifest gate and names all thirteen declarations. It stayed green before.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2
@azchohfi

Copy link
Copy Markdown
Collaborator Author

Addressed the finding in the collapsed Previously missed block (no inline thread was created, so replying here).

Correct — and the scenario you describe was exactly reachable. MinimumSupportedVsHost was a second hard-coded literal, so bumping Microsoft.VisualStudio.SDK in Directory.Packages.props while updating the pin and the ceiling would have left it at 17.14, keeping all three tests green while the VSIX still advertised hosts the new pin cannot load on. That is the same class of silent breakage this PR started from.

Fixed in 8cb79af. The gate now reads the Microsoft.VisualStudio.SDK PackageVersion and takes its major/minor as the required host floor. The extension cannot support a host older than the SDK it compiles against, and that SDK is what fixes the System.Text.Json baseline, so the two move together by construction rather than by convention. A missing or unparseable PackageVersion throws rather than degrading to a default, so the gate cannot lose its source of truth silently.

Mutation-checked with your scenario — bumping the SDK to 18.0.40265 and changing nothing else:

source.extension.vsixmanifest advertises Visual Studio hosts older than 18.0, the
Microsoft.VisualStudio.SDK version the extension compiles against.
Offending declarations: InstallationTarget Microsoft.VisualStudio.Community [17.14,19.0); ... (13 total)

Before this change that same bump left the suite green.

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.

Copilot review overview

🔵 Needs a closer look

Packaged preview support remains ineffective for projects that do not import the optional WinApp targets that consume the new environment property.

Review effort: Balanced
Findings: None

Previously missed (4)

In code that hasn't changed since last review

Medium severity Packaged projects lack WinApp targets for execution alias support

src/​vs-reactor/​Reactor.VsExtension/​Embed/​ReactorChildLauncher.cs:183

This environment variable is inert unless the target project imports Microsoft.Windows.SDK.BuildTools.WinApp, whose targets are the component that consumes WinAppRunUseExecutionAlias. The supported project shapes in this repo do not add that package: tools/Templates/templates/WinUIApp-CSharp/Company.ReactorApp1.csproj:60-77 references only Reactor/Devtools, and the documented MSIX conversion in docs/_pipeline/templates/packaging.md.dt:95-111 adds only MSIX properties and a manifest. Such packaged projects still launch without this alias/stdout path, so the advertised packaged-preview fix is incomplete. Add/wire the WinApp run package for every supported packaged shape, or detect its absence and avoid claiming no-edit support.

Medium severity Test does not verify packaged projects import WinApp targets

src/​vs-reactor/​Tests/​Reactor.VsExtension.Tests/​ReactorChildLauncherEnvironmentTests.cs:33

This test only proves that a string was added to ProcessStartInfo; it does not prove that a supported packaged project imports the MSBuild targets that consume it. It therefore stays green for the repository's documented packaged shape, which does not reference Microsoft.Windows.SDK.BuildTools.WinApp, while the preview still lacks the alias/stdout handshake. Add a test that ties the packaged project/template shape to the WinApp targets (or an integration test that observes the packaged handshake).

Low severity No-project-changes claim fails for packaged projects

CHANGELOG.md:79

The “no project changes” claim is not true for packaged projects that do not already reference Microsoft.Windows.SDK.BuildTools.WinApp; without those targets, the newly set environment property has no consumer and the stdout handshake remains unavailable. The repository's documented MSIX shape does not add that package. Update the implementation/project wiring or narrow this release note to the supported package-enabled shape.

Low severity Documentation omits required WinApp package prerequisite

docs/​_pipeline/​templates/​vs-extension.md.dt:41

This promise is broader than the implementation. WinAppRunUseExecutionAlias is consumed only when the target imports Microsoft.Windows.SDK.BuildTools.WinApp, but the packaged project shape documented in docs/_pipeline/templates/packaging.md.dt:95-111 does not include that package. Users following the guide still need a project change before the preview can inherit stdout. Either wire the package into supported packaged projects or state this prerequisite here.

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.

Copilot review overview

🟢 Approval recommended

The runtime fixes are narrowly scoped and tested; the remaining finding only corrects non-blocking documentation wording.

Review effort: Balanced
Findings: 1 Low severity

Open (1)

Comment thread src/vs-reactor/Reactor.VsExtension/Embed/ReactorChildLauncher.cs Outdated
The XML doc still claimed the fix works "without any project edit" and that the
Reactor templates reference the WinApp run-support package by default. Neither
holds for this repository: the in-repo template is WindowsPackageType=None and
references only Reactor and Devtools. The packaged-by-default shape belongs to
the separately distributed WinUI template pack.

Restate the summary as what it does -- lets a packaged project complete the
handshake when the project uses the Windows App SDK run support -- and say
plainly that a project without that package must add it first. Matches the guide
and the PR scope; no behaviour change.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2
Path.Combine silently discards earlier arguments when a later one is rooted, so
a rooted relativePath would make every iteration of the walk probe the same
absolute path -- the search would still "run" while testing nothing, which is
the vacuous-pass failure this helper exists to prevent.

Guard the parameter and throw, and pass the manifest path as a single
repo-relative literal instead of assembling it with Path.Combine.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2

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.

Copilot review overview

🔵 Needs a closer look

The binding gate does not inspect its declared transitive shared-assembly ceiling.

Review effort: Balanced
Findings: None

Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Dependency ceiling check misses transitive assembly references

src/​vs-reactor/​Tests/​Reactor.VsExtension.Tests/​VsSharedAssemblyBindingTests.cs:53

GetReferencedAssemblies() inspects only Reactor.VsExtension.dll's direct metadata references. System.Text.Encodings.Web is transitive through System.Text.Json, and EmbedClient does not reference its types directly, so that ceiling entry is not exercised; a future incompatible transitive resolution would leave this gate green. Either traverse the resolved dependency closure or add a positive control for every entry in Ceilings (and remove entries the probe intentionally cannot cover).

Ceilings carried System.Text.Encodings.Web, but the gate reads this assembly's
direct manifest references and that dependency is transitive through
System.Text.Json -- EmbedClient never touches its types. The entry was therefore
never evaluated: any version could have resolved and the gate would have stayed
green while appearing to cover it.

Remove it, and turn the positive control into a stronger invariant. Instead of
asserting one known reference is visible, assert every key in Ceilings is a
direct reference. A ceiling nothing checks is false confidence, and this makes
adding one fail immediately rather than sit dormant.

Mutation-checked: re-adding System.Text.Encodings.Web reddens the control and
names it.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2
@azchohfi

Copy link
Copy Markdown
Collaborator Author

Addressed the finding in the collapsed Previously missed block (no inline thread, so replying here).

Correct, and it was exactly the false-confidence case. GetReferencedAssemblies() reads this assembly's own manifest references, and System.Text.Encodings.Web is transitive through System.Text.Json — EmbedClient never touches its types. So that Ceilings entry was never evaluated: any version could have resolved and the gate would have stayed green while appearing to cover it.

Fixed in 1fb97ca. I took your second option and went slightly further:

  • Removed the System.Text.Encodings.Web entry.
  • Replaced the single-reference positive control with a stronger invariant: every key in Ceilings must be a direct assembly reference. Adding an entry the probe cannot cover now fails immediately instead of sitting dormant, so this class of gap cannot reappear.

Mutation-checked by re-adding the entry:

These entries in Ceilings are not direct assembly references of Reactor.VsExtension, so the
ceiling gate never evaluates them and would stay green no matter what version resolved:
System.Text.Encodings.Web.

I did not traverse the resolved dependency closure. The failure this gate exists to catch is the CLR resolving this assembly's manifest reference against devenv.exe.config's redirects — which is a direct-reference concern — and a closure walk would assert over assemblies VS itself owns and unifies. The gate now states that scope explicitly rather than implying coverage it does not have.

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.

Copilot review overview

🔵 Needs a closer look

The changelog currently promises packaged-app support beyond the WinApp-enabled project shapes actually fixed.

Review effort: Balanced
Findings: None

Previously missed (1)

In code that hasn't changed since last review

Low severity Qualify changelog claim about supported packaged app project shapes

CHANGELOG.md:79

The changelog overstates the supported project shapes: this fix only applies when the project uses Microsoft.Windows.SDK.BuildTools.WinApp; hand-converted MSIX projects without that package still cannot attach, as the guide and PR scope explain. Qualify the release note so it does not promise support for every packaged app.

The note promised packaged-app support generally, but the fix only reaches
projects that use the Windows App SDK run support; a hand-converted MSIX project
without that package still cannot attach. Name the prerequisite inline so the
release note matches the guide and the PR scope.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2
@azchohfi

Copy link
Copy Markdown
Collaborator Author

Addressed the finding in the collapsed Previously missed block (no inline thread, so replying here).

Fair — the note said "supports packaged (MSIX) apps" without the qualifier the guide and PR scope both carry. It now names the prerequisite inline:

The Visual Studio preview now supports packaged (MSIX) apps that use the Windows App SDK run support (Microsoft.Windows.SDK.BuildTools.WinApp). Previewing one previously failed. (PR #1282)

That is the last place the unqualified claim survived; the guide, the launcher doc comment and the PR description were already scoped in earlier commits.

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.

Copilot review overview

🔵 Needs a closer look

The active design and implementation specifications still advertise unsupported Visual Studio 17.8 compatibility.

Review effort: Balanced
Findings: None

Previously missed (1)

In code that hasn't changed since last review

Low severity Update Visual Studio design references to the 17.14 baseline

docs/​_pipeline/​templates/​vs-extension.md.dt:15

The minimum-host update leaves the active Visual Studio preview design documents contradictory: docs/specs/056-visual-studio-embedded-preview.md:643-645 still specifies SDK/BuildTools 17.8+, and docs/specs/tasks/056-visual-studio-embedded-preview-implementation.md:467-473 still requires a VSIX declaring Visual Studio 17.8+. Update those source-of-design references to the 17.14 baseline so future implementation work does not restore the unsupported range.

The minimum-host change left the design and implementation specs specifying
Microsoft.VisualStudio.SDK / VSSDK.BuildTools 17.8+ and a VSIX declaring VS 2022
17.8+, which would invite future work to restore the unsupported range. Seven
references across both documents now read 17.14+.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2
@azchohfi

Copy link
Copy Markdown
Collaborator Author

Addressed the finding in the collapsed Previously missed block (no inline thread, so replying here).

Good catch on the design source. Updated in 80d73e5 — seven references across both documents now read 17.14+:

  • docs/specs/056-visual-studio-embedded-preview.md — Microsoft.VisualStudio.SDK and Microsoft.VSSDK.BuildTools prerequisites.
  • docs/specs/tasks/056-visual-studio-embedded-preview-implementation.md — the same two prerequisites in the project-shape section, and the VSIX manifest task's "VS 2022 17.8+" dependency requirement.

I swept the extension's documentation surface afterwards and no 17.8 claim remains in any vs-reactor, vs-extension or spec-056 file, so the manifest, the guide, the guide index, the VSIX README and both specs now agree on the same baseline.

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.

Copilot review overview

🟢 Approval recommended

The fixes are scoped, documented, and protected by targeted non-vacuous regression tests.

Review effort: Balanced
Findings: None

…ault

Merge origin/main, which removed the in-repo template pack (#1277) in favour of
the Windows App SDK dotnet new templates. WinAppSdkTemplates.PackageId is now
Microsoft.WindowsAppSDK.WinUI.CSharp.Templates -- the same pack `dotnet new
reactor` resolves to, and the same pack the packaged validation in this PR was
run against.

That pack's Reactor template declares no WindowsPackageType (so, packaged) and
already references Microsoft.Windows.SDK.BuildTools.WinApp. Earlier commits in
this PR hedged the claim down to "projects that use the Windows App SDK run
support" on the strength of tools/Templates/.../Company.ReactorApp1.csproj
setting WindowsPackageType=None -- a file this branch was still carrying from a
merge base that predates its deletion, and which no longer exists on main.

Restore the accurate statement: `dotnet new reactor` generates a packaged app
and previewing it needs no project changes. Keep the one caveat that remains
true, since packaging.md still documents a hand MSIX conversion that adds only
MSIX properties: such a project must add the run-support package itself.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2
@azchohfi

Copy link
Copy Markdown
Collaborator Author

Heads-up for the next review pass: this branch was carrying a stale file, and several earlier comments were reasoned from it.

tools/Templates/templates/WinUIApp-CSharp/Company.ReactorApp1.csproj — cited in rounds 3, 5, 6, 7 and 9 as evidence that "the in-repo Reactor template is unpackaged" — no longer exists on main. It was deleted by #1277, Use the Windows App SDK dotnet new templates and remove our own. This branch still had it only because its merge base predated that commit. I have now merged origin/main, so it is gone here too.

The current state, from src/Reactor.Cli/Templates/WinAppSdkTemplates.cs:

public const string PackageId = "Microsoft.WindowsAppSDK.WinUI.CSharp.Templates";

That is the pack dotnet new reactor resolves to, and it is the same pack this PR's packaged validation ran against. Its Reactor template declares no WindowsPackageType (so, packaged) and already references Microsoft.Windows.SDK.BuildTools.WinApp.

So the original claim was accurate and the hedging added in response to those rounds was not. Restored in b23e413: dotnet new reactor generates a packaged app, and previewing it needs no project changes. The one caveat that genuinely survives is kept — packaging.md documents a hand MSIX conversion that adds only MSIX properties, and such a project must add the run-support package itself.

Merged tree re-verified: Release build 0 errors, Reactor.VsExtension.Tests 87, SdkTests 6, DocPipeline 472.

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.

Copilot review overview

🟢 Approval recommended

The two regressions are addressed consistently across implementation, packaging, tests, specifications, and generated documentation.

Review effort: Balanced
Findings: None

@azchohfi
Alexandre Zollinger Chohfi (azchohfi) merged commit 99a6798 into main Sep 25, 2026
55 of 56 checks passed
@azchohfi
Alexandre Zollinger Chohfi (azchohfi) deleted the azchohfi-fix-preview-exception branch September 25, 2026 22:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants