You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Two independent bugs that each made the Visual Studio Reactor Preview unusable, found while dogfooding it.
Every preview session failed to start with FileNotFoundException: Could not load file or assembly 'System.Text.Json, Version=10.0.0.12'.
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:
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.
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 installedReactor.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.SDKPackageVersion, 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
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.
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
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
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
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
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
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.
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.
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
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.
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
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.SDKPackageVersion 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.
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.
Test does not verify packaged projects import WinApp targets
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).
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.
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.
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
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
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.
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
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
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.
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
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
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.
…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
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:
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two independent bugs that each made the Visual Studio Reactor Preview unusable, found while dogfooding it.
FileNotFoundException: Could not load file or assembly 'System.Text.Json, Version=10.0.0.12'.dotnet new reactorgenerates. 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.Jsoninsidedevenv.exe: it ships one copy underCommon7\IDE\SharedAssembliesand binds every extension to it through adevenv.exe.config<bindingRedirect>with a hard upper bound. VS 18 declares:Redirects unify upward, and only within their
oldVersionrange. When the repo-wide CPM pin rolled to10.0.12the extension began requestingAssemblyVersion 10.0.0.12, past that ceiling. No redirect matched; the VSIX carries no private copy (VS strips assemblies it provides); the package registers noBindingPath. The CLR probed devenv's base directory, found nothing, and threw atEmbedClient.StatusAsync- the first call every session makes.The extension now pins
System.Text.JsonwithVersionOverride="9.0.0", the versionMicrosoft.VisualStudio.SDKitself 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 baselinesSystem.Text.Jsonper host - 17.8 → 7.0.3, 17.9 → 8.0.0, 17.14 → 9.0.0. A9.0.0.0reference is outside the redirect range of any host below 17.14, so that claim was unsound. All 13InstallationTargetranges and theCoreEditorprerequisite 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 launcherMicrosoft.Windows.SDK.BuildTools.WinAppinstalls, 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 reportsCAPTURE_PORT/CAPTURE_TOKEN.The launcher now sets
WinAppRunUseExecutionAlias=truein the child environment, switching that launch to an execution alias that inherits stdout.Two deliberate choices, both measured:
-p:.dotnet watchrejects-palongside--projectas 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 totrue, an explicit projectfalseoverrides it.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 toMicrosoft.WindowsAppSDK.WinUI.CSharp.Templates0.0.7-alpha - the packWinAppSdkTemplates.PackageIdnames, now that #1277 retired the in-repo template pack. Its Reactor template declares noWindowsPackageType(so, packaged) and already referencesMicrosoft.Windows.SDK.BuildTools.WinApp. Both apps were driven with the exact command the extension issues:WindowsPackageType=NoneCAPTURE_PORT,CAPTURE_TOKEN,MCP servinganddevtools-ready; both endpoints probed live over HTTP (401 = up, token required).-getProperty:: env-only →true, explicit projectfalse→false.System.Text.Json:
Reactor.VsExtension.dllviaSystem.Reflection.Metadata:10.0.0.12, against a declared ceiling of10.0.0.10.System.Text.Json.dll, so there was nothing local to bind to.9.0.0.0, inside the range. All of the extension's references cross-checked against the ranges parsed out ofdevenv.exe.config; all in range.Gates, each mutation-checked so a pass is not a tautology:
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 transitiveSystem.Text.Encodings.Webreddens it).[17.8.1,19.0)- a patch-component range the original regex skipped silently - and on bumping the SDK to18.0. Its floor is derived from the centralMicrosoft.VisualStudio.SDKPackageVersion, so the pin and the manifest cannot drift apart.ReactorChildLauncherEnvironmentTestsreddens when the env-var call is removed.Reactor.VsExtension.Tests87,Reactor.VsExtension.SdkTests6,Reactor.DocPipeline.Tests472 - all passing on the merged tree.dotnet restore Reactor.slnxthendotnet 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.
Microsoft.VisualStudio.SDK17.14, so this makes an existing constraint honest rather than imposing a new one.System.Text.Jsonversion, so Dependabot bumps no longer reach it. The host gate derives its floor from the SDKPackageVersion, so an SDK bump fails the build until the manifest is raised deliberately.10.0.11was also past the ceiling, so the extension was broken from that bump onward, not just the latest.