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
Reactor's dotnet new scaffolding moves to the official Windows App SDK template pack, and the in-repo Microsoft.UI.Reactor.ProjectTemplates package (dotnet new reactorapp) is removed.
Scaffolded apps are now packaged (single-project MSIX), not unpackaged.dotnet run launches with full package identity and requires Developer Mode. No template produces the unpackaged shape any more — scaffold, then set <WindowsPackageType>None</WindowsPackageType> yourself. Both shapes are documented in the packaging guide.
Scaffolding is winapp new
The Windows App SDK CLI installs the template pack on demand, so there is no separate install step and no version to pin:
winget install Microsoft.WinAppCli
winapp new -t reactor -n MyApp
winapp new --list installs the pack without scaffolding, and offers to update a stale one. dotnet new reactor still works once the pack is installed.
What changed
bootstrap.ps1 installs the pack via winapp new --list --use-defaults — installs when missing, keeps an already-installed pack, never prompts. Params: -WinAppSdkTemplatesVersion (→ --template-version), -SkipTemplates.
mur templates status reports whether dotnet new reactor resolves, with load-bearing exit codes (0 available, 1 probe failed, 2 pack too old, 3 pack missing). It backs mur doctor and bootstrap's verification.
mur templates install does not exist. An earlier revision of this PR added one; it was retired once winapp covered the same ground. The subcommand is still recognised and names its replacement rather than reporting "unknown subcommand", since it briefly shipped in docs and bootstrap.
mur upgrade checks template availability instead of installing — the pack ships externally, so git pull never invalidates it.
Deletedtools/Templates/ and the steps that packed/published it. Published NuGet versions are unaffected (NuGet doesn't allow deletion) and will be deprecated with a pointer to dotnet new reactor — a portal action, noted under [Unreleased].
mur clean-local deliberately still knows the old package id, so it can clean up stale artifacts on machines bootstrapped before this change.
Why Reactor doesn't carry its own installer
Two dotnet new install hazards drove ~700 lines of workaround in an earlier revision. winapp handles both, so that code is gone rather than maintained:
No --prerelease switch — it resolves stable-only, so installing the bare package id fails while the pack is prerelease-only (it is). Working around it meant resolving versions off the NuGet flat container, which dragged in redirect re-validation and credential redaction.
--force uninstalls before downloading, so a failed install leaves the machine with no templates at all. Observed for real during development: a bare id + --force uninstalled a working prerelease, then failed with exit 103.
mur docs compile — generated output committed and verified idempotent (a fresh compile byte-matches), which is what the CI freshness gate checks
Verified against the real CLI, not its help text: winapp new -t reactor emits a packaged Reactor app (App.cs, Package.appxmanifest, EnableMsixTooling, Microsoft.UI.Reactor reference); --list shows all four templates.
Scaffold → launch smoke, restored and strengthened. The deleted CreateTemplateTests asserted a UIA element on the unpackagedreactorapp template, so it could not survive this migration unchanged. The Bootstrap job now replaces it for the packaged shape: it enables Developer Mode (same AppModelUnlock approach as ci.yml's Packaged Selftests), dotnet runs the scaffolded app, and asserts three things a build cannot — the loose layout registers, the app activates, and the Reactor UI renders (winapp ui search for the TitleBar the blank template emits). Green in CI:
The install-from-nothing path was left to CI deliberately — it could not be exercised locally without destroying a shared machine's template pack. The Bootstrap job proves it: on a clean runner, bootstrap installed the pack from scratch and the independent check found all four short names.
mur templates status / install / --help and mur doctor smoke-tested directly
Mutation-verified: dropping the exit-code arm in the template probe, the package-header bound in the version parser, or the redirect policy each reddens its own case and nothing else
Ran the repo pr-review skill (8 dimensions + multi-model cross-check) before opening. It caught a CI blocker I'd introduced: packaging.md.dt still resolved a snippet from the deleted tools/Templates/, and SnippetExtractor treats a missing source as fatal, so the docs-build job would have failed.
Risk / notes
Capability dropped:-WinAppSdkTemplatesSource (install the pack from a local folder, for testing an unpublished build) is gone — winapp has no local-folder equivalent.
mur pack-local no longer produces a templates nupkg and its --framework-version flag is gone.
CreateTemplateTests.cs also contained the shared TemplatePackageTestFixture; it was extracted to LocalPackageFeedFixture rather than losing the unrelated SourceMapPackageConsumerTests coverage.
getting-started.md.dt / packaging.md.dt are also being touched by a parallel workstream; changes here are the minimum factual correction, not a redesign.
Two DataGridTests E2E failures appeared on one run of this branch. They reproduce on an unrelated branch with the identical Row edit did not start — no 'Save' button appeared signature, predate the merge, and this PR touches no product or E2E code — a pre-existing intermittent flake, not a regression here. A later run of this branch was green.
The Windows App SDK template pack now ships first-class Reactor templates
(microsoft/WindowsAppSDK#6620): `reactor`, `reactor-mvu`, `reactor-navview`,
and `reactor-tabview`. Switch to those as the supported scaffolding path.
The in-repo `Microsoft.UI.Reactor.ProjectTemplates` pack (`dotnet new
reactorapp`, unpackaged) is kept — `mur pack-local` still builds it and the
release workflow still publishes it — but nothing installs it automatically
any more.
The big user-visible change is that scaffolded apps are now **packaged**
(single-project MSIX) rather than unpackaged, so `dotnet run` launches with
package identity and needs Developer Mode. Docs are updated accordingly.
- bootstrap.ps1: step 5 installs the Windows App SDK pack via `mur templates
install`. New `-WinAppSdkTemplatesSource` (local feed, for testing an
unpublished build), `-WinAppSdkTemplatesVersion`, and `-SkipTemplates`.
- New `mur templates install|status`, shared by bootstrap and `mur upgrade`.
- `mur doctor` now checks for the Windows App SDK pack; the legacy local
template nupkg drops from FAIL to WARN.
Two hazards found and handled while validating:
- `dotnet new install <id>` has no `--prerelease` switch and resolves
stable-only, so installing the bare package id fails outright while the
pack is prerelease-only (it is today, at 0.0.6-alpha). We resolve the
newest published version ourselves — newest stable, else newest
prerelease — and install an explicit `<id>::<version>`.
- `dotnet new install --force` uninstalls the existing package *before*
downloading the replacement, so a failed install leaves the machine with
no templates at all. Observed for real: bare id + `--force` + no stable
version uninstalled a working prerelease and then failed with exit 103.
`--force` is now only used to replace an install with a version already
known to exist, and `Install` leaves an existing install untouched when
it cannot resolve a target.
Tests: version-selection rules (prefer stable, else newest prerelease
ordered numerically), local-folder version discovery, a source-level guard
against re-pairing `--force` with an unresolved spec, and bootstrap guards
that it installs the new pack and does not install the legacy one. All
mutation-checked. Full suite green (13,380).
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
De-stales the branch (was 73 commits behind). Six conflicts, all in docs and
agent-kit prose; bootstrap.ps1, src/Reactor.Cli/*, the tests, and the workflows
all auto-merged clean.
Conflict resolutions:
- docs/_pipeline/templates/{getting-started,packaging}.md.dt and their
generated docs/guide/*.md: took main's side wholesale, by agreement with the
sibling session that is rewriting both templates against a new three-path
structure (dotnet new / winapp new / single-file). getting-started.md.dt
auto-merged but was reset to main for the same reason, so those four files
are now byte-identical to main. Consequence: those two guides still describe
the old unpackaged `dotnet new reactorapp` flow — the sibling session owns
reconciling them with this branch's code.
- docs/_pipeline/templates/dev-tooling.md.dt: combined both sides — main's new
`mur doctor` / `mur upgrade` / `mur figma watch` rows plus this branch's
updated pack-local row and the new `mur templates` row. Regenerated
docs/guide/dev-tooling.md from it.
- plugins/reactor/skills/reactor-getting-started/SKILL.md: kept main's newer and
more careful packaging-mode guidance (it explains both shapes and warns
against flipping a project's packaging mode), and updated only the stale
template names within it.
Also fixes a false PASS found while validating the merge. `mur doctor` and
`mur templates status` probed for the *package id*, but the pack ships versions
that predate the Reactor templates: with 0.0.6-alpha installed, both reported
success while `dotnet new reactor` failed with "No templates found" (exit 103).
They now probe the template short name via `dotnet new list reactor`, and
distinguish "installed but too old" from "not installed" so the remediation is
actionable. The not-found message itself contains the search term, so the
interpretation checks the negative marker first — a naive substring match
reports the template as present exactly when it is absent. Extracted as
`InterpretTemplateListOutput` and unit-tested against both real outputs;
mutation-checked.
Build/test status: `dotnet build Reactor.slnx -c Release` 0 errors;
tests/Reactor.Tests 14,011 passed / 0 failed / 64 skipped.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
Found by running the command against the real published template pack
(0.0.7-alpha) with NuGet unreachable. The non-destructive guard worked
correctly -- it kept the installed pack and uninstalled nothing -- but the
command still printed "Installed.", telling the user an install had happened
when none had.
A bare exit code cannot express the difference: "kept the existing install
because nothing could be resolved" is a success for exit-code purposes but is
not an install. `Install` now returns an `InstallOutcome`
(Installed / Updated / AlreadyCurrent / KeptExisting / Failed) and the callers
report the real outcome.
Also adds the credential-provider hint to the failure path: `dotnet new
install` does not use the NuGet credential provider, so an authenticated feed
reports "the package does not exist" -- indistinguishable from the package
genuinely not being published. That cost real time today.
Verified against the live pack, all three non-failure paths:
no resolution + installed -> "Kept the existing install"
--version 0.0.7-alpha -> "Already up to date"
(failure path unchanged)
Tests: outcome enum completeness, plus a source-level guard that the command
does not unconditionally print "Installed." -- the bug was in the reporting,
so asserting on Install() alone would have missed it.
Full suite 14,152 passed / 0 failed.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
Deletes `Microsoft.UI.Reactor.ProjectTemplates` (`tools/Templates/`, short name
`dotnet new reactorapp`). Reactor's project templates now ship in the official
Windows App SDK pack, `Microsoft.WindowsAppSDK.WinUI.CSharp.Templates`, which
published 0.0.7-alpha today carrying `reactor`, `reactor-mvu`, `reactor-navview`
and `reactor-tabview` (microsoft/WindowsAppSDK#6620 + #6786). Verified against
the published pack: a scaffold emits Microsoft.UI.Reactor 0.1.0-preview.16 and a
Package.appxmanifest.
Behaviour change worth calling out: no template produces the **unpackaged**
shape any more. The Windows App SDK templates scaffold packaged single-project
MSIX apps, so `dotnet run` launches with package identity and needs Developer
Mode. For an unpackaged app, scaffold and set WindowsPackageType=None by hand --
the packaging guide documents both shapes.
Published versions on NuGet.org are unaffected (NuGet does not allow deletion);
they are to be deprecated with a pointer to `dotnet new reactor`. That is a
portal action, not something this commit can do.
Removed:
- tools/Templates/ and its solution + Directory.Build.props references
- the `Pack Templates` step in release.yml and the internal ADO pipeline
- `mur pack-local`'s templates pack and its `--framework-version` flag
(plus the now-dead ResolveTemplateFrameworkVersion helper); the SemVer
helpers it shared with `mur templates` are kept and still used
- `mur doctor`'s local-template-nupkg check
- TemplateMetadataTests and CreateTemplateTests
`mur clean-local` deliberately still knows the old package id: dev machines
bootstrapped before this change have a stale nupkg and template registration,
and cleaning those up is exactly what that command is for.
CreateTemplateTests also *contained* TemplatePackageTestFixture, which
SourceMapPackageConsumerTests depends on via LocalPackageFeedCollection.
Extracted it to LocalPackageFeedFixture (renamed, template plumbing dropped)
rather than losing unrelated coverage.
Tests: three guards -- bootstrap installs the Windows App SDK pack, bootstrap
never installs the removed id, and the repo no longer ships the package. The
last asserts on tracked source rather than Directory.Exists, because bin/obj
under tools/Templates are gitignored and survive a pull; an existence check
would fail for any contributor who had built it. Mutation-verified by
resurrecting the csproj. Suite 14,151 passed / 0 failed; Release build clean.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
Ran the repo `pr-review` skill (8 dimensions + multi-model cross-check, all 6
high findings confirmed by the second model). Fixes below.
CI BLOCKER (introduced by the package deletion):
docs/_pipeline/templates/packaging.md.dt still resolved a snippet from the
deleted tools/Templates/. SnippetExtractor raises REACTOR_DOC_SNIPPET_001 on a
missing source file and CI runs `docs compile --ci`, so this branch would have
failed the docs-build job. Replaced with an inline example.
Stale docs the deletion invalidated:
getting-started.md.dt and packaging.md.dt still told users to install
Microsoft.UI.Reactor.ProjectTemplates and run `dotnet new reactorapp`, and
still described the scaffold as unpackaged. Both rewritten for
`dotnet new reactor` + the packaged MSIX shape, and both guides recompiled.
(These two templates are also being rewritten by a parallel workstream; this
is the minimum factual correction, not a redesign.)
Destructive-install guard was incomplete (high):
An explicit --version bypassed it entirely — the pin was trusted without any
existence check and then paired with --force, which uninstalls before
downloading. A typo'd pin would destroy a working install. The decision is now
a pure `PlanInstall` table: --force is reachable only for a target confirmed
present in the feed or folder; an unconfirmed pin is refused with the existing
install left intact.
Credential leak (high):
The echoed `dotnet new install ... --add-source <url>` line printed feed URLs
verbatim, so a PAT in user-info or query landed in console and CI logs. Added
RedactSource; local folder paths are still printed in full.
`install --help` performed an install (high):
Help was only handled before the subcommand. Added install-level help and
strict argv parsing — unknown options, stray positionals and flags missing a
value are now rejected before any side effect.
Install outcomes were pinned by source-greps, not behaviour (high):
Replaced the regex-on-source guards with a table test over `PlanInstall`
(7 rows + an exhaustive property that ForcedReplace implies a confirmed
target), and extracted `DescribeOutcome` so the reporting bug is tested
directly rather than by grepping for a string literal.
Also:
- --source URL was ignored during version resolution, so a custom feed
silently resolved a version from nuget.org and installed a different
package. URL sources now require an explicit --version.
- Refuse plaintext http:// sources for a code-generating template package.
- Extracted InterpretInstalledVersionOutput with tests for the present,
absent and malformed listings (an adjacent package id must not be matched).
- `mur upgrade` returned 0 when an explicitly requested --templates-source /
--templates-version install failed; it now fails, while the routine
no-argument refresh stays best-effort.
Mutation-verified: reintroducing the unconfirmed-pin force reddens both
PlanInstall guards. Exit codes checked directly (help 0; unknown flag, missing
value, http source and unpinned URL source all 1).
Suite 14,163 passed / 0 failed; Release build 0 errors; both guides recompile.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
Artifact sizes for 45e0302 vs the base branch (a58008a).
Packages (compressed .nupkg)
Artifact
base
PR
Δ
Microsoft.UI.Reactor.nupkg
1.72 MB
1.72 MB
+458 B (+0.03%)
≈
Microsoft.UI.Reactor.Advanced.nupkg
487.6 KB
487.5 KB
-21 B (0.00%)
≈
Microsoft.UI.Reactor.Devtools.nupkg
284.2 KB
284.2 KB
-23 B (-0.01%)
≈
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.
Migrates Reactor scaffolding to the official Windows App SDK template pack and removes the in-repository template package. Scaffolded apps now use packaged MSIX by default.
Changes:
Adds mur templates install|status with version and source handling.
Updates bootstrap, upgrade, doctor, CI, documentation, and agent guidance.
Removes legacy template packaging and related integration coverage.
Coverage for 45e0302 vs the base branch (a58008a) — unit + selftest merged.
Metric
base
PR
Δ
Line
85.80%
85.79%
-0.01 pp
≈
Branch
77.58% (962/1240)
77.58% (962/1240)
0.00 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.
Fixes all 11 findings. Two were real defects I had missed.
CI failure I would have shipped (high):
Deleting tools/Templates/ broke
Reactor.DocPipeline.Tests/MinimalCsprojDocTests.Documented_package_id_matches_the_scaffolded_template,
which loads the deleted Company.ReactorApp1.csproj and asserts it exists. My
earlier "suite passes" only covered tests/Reactor.Tests, so this never ran.
Confirmed failing, then retargeted: the page's fast path is now a pack outside
this repo, so there is no in-repo csproj to cross-check; the test now pins that
the documented block names the real framework package id, keeping its positive
control. Also emptied the now-stale WinAppSDKReferenceGuardTests allowlist,
which existed solely for that template.
Credential exposure beyond the echo (high):
Redacting the printed command line does not stop another process from reading
this process's command line on Windows, where the feed URL is an argument. A
--source URL carrying credentials in user-info or the query string is now
refused outright, pointing at NuGet.config + a local folder instead. The
Process.Start failure diagnostic also formatted the raw argument list; it now
redacts like the normal echo.
Destructive --force via a pinned URL feed (medium):
ResolveAvailableVersions does not query a URL source — it falls through to the
public index. A version that exists publicly but is absent from the user's feed
therefore set targetExists=true and authorized the --force path, uninstalling a
working pack before failing to download. URL sources are now always treated as
unverifiable, so a pin against one can never force.
Bootstrap reported success it had not verified (medium):
`mur templates install` returns success for KeptExisting, which includes keeping
a pack too old to contain any Reactor template (0.0.6-alpha shipped without
them). Bootstrap printed "templates registered" regardless. It now confirms
`dotnet new list reactor` actually resolves, and warns with a remediation
otherwise.
Documented install commands could not work (low x5):
README, the getting-started template, the release runbook and the agent
fallback all told users to run a bare `dotnet new install <id>`. By this PR's
own reasoning that cannot resolve a prerelease-only pack, and the pack is
prerelease-only today. All pinned to ::0.0.7-alpha with the reason stated;
CHANGELOG now points at `mur templates install` or the pinned spec.
Verified: tests/Reactor.Tests 14,163 passed; tests/Reactor.DocPipeline.Tests 462
passed (was 1 failed); Release build 0 errors; getting-started recompiled.
Reactor.IntegrationTests fails locally only on NU1301/TLS reaching nuget.org from
temp consumer projects — environmental, unrelated to the extracted fixture, which
constructs successfully.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
This resolver is hard-coded to the public NuGet flat container and the install path only forwards the explicit source argument. bootstrap.ps1 selects an effective -NuGetConfig/-NuGetSource, but those MSBuild restore environment variables are not a source for dotnet new install, so a contributor behind the repo's configured mirror cannot resolve or install the prerelease pack even though the rest of bootstrap uses that mirror. Thread the selected feed/config into template version resolution and --add-source (or explicitly document that template installation bypasses the mirror).
Require exact short-name matches in positive template probes
The positive probe still uses an unbounded substring match, so output containing only reactor-mvu (or another template whose short name starts with reactor) is reported as if the blank reactor template were available. That makes mur doctor/mur templates status pass even though dotnet new reactor would fail. Parse the short-name column and require an exact reactor token, and add a negative test for a listing containing only reactor-mvu.
Copilot review round 2.
Short-name detection matched with Contains("reactor"), which also matches
`reactor-mvu` and `winui-reactor` — a pack that shipped the richer shells but
not the blank template would read as available, and `mur doctor` would report a
false PASS. A `\breactor\b` regex has the same hole, because '-' is a word
boundary. Match the short name as a whole token instead, delimited by
whitespace, a comma, or the edge of the text (short names appear
comma-separated within a whitespace-delimited column).
The match is deliberately case-sensitive: the Template Name column carries the
capitalised prose word ("Reactor MVU App"), which *is* a standalone token, so a
case-insensitive token match still passes when the blank template is absent.
The new test caught exactly that.
bootstrap.ps1 hand-rolled the same check as a `\breactor\b` regex over
`dotnet new list` output, so it carried both bugs. It now calls
`mur templates status`, which is the tested implementation, and keys off its
exit code.
Credential rejection and redaction covered user-info and the query string but
not the fragment, which UriBuilder preserves verbatim. A `--source` URL whose
fragment carries a PAT would have been echoed to the console and passed on the
child process command line, readable by any other process on Windows.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
Copilot review round 3.
`dotnet new install` has no feed-isolation switch — `--add-source` only *adds*
to the configured sources. So `mur templates install --source <feed-url>
--version X` could be satisfied from nuget.org instead of the requested feed,
installing a different package. The version pin cannot disambiguate them
either: an unpublished build and the published pack routinely carry the same
version string (a locally packed 0.0.7-alpha vs the published 0.0.7-alpha).
A local folder has neither problem — it is enumerable, so a pin is confirmable,
and `--add-source <folder>` with an exact version resolves the file that is
actually there. `dotnet new install` also ignores the NuGet credential
provider, so an authenticated feed URL fails anyway with a misleading "the
package does not exist". So require a folder and point at the
restore-then-install-from-cache workflow, which is what the help text and
bootstrap.ps1 already described.
That subsumes the previous URL-specific guards (insecure scheme, credentials in
user-info/query/fragment) and makes the property stronger: no URL now reaches
the child process command line at all.
PlanInstall gains the matching rule — with an explicit source, refuse anything
not confirmed to be in it, including the empty-folder case where no version
resolves and a bare package id would fall through to the configured feeds. The
previous table allowed that whenever nothing was installed.
The bootstrap CI guard verified each short name with a plain substring test, so
the `reactor` probe was satisfied by `reactor-mvu` — the same prefix-matching
hole just fixed in the CLI. It now uses the same delimited token match.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
GitHub code-quality review, 10 findings.
Generic `catch` clauses narrowed to the exceptions the guarded operation can
actually raise, so a bug inside the try block surfaces instead of being
swallowed as "offline" or "best-effort cleanup":
- the NuGet flat-container query to HttpRequestException /
TaskCanceledException / JsonException,
- local nupkg enumeration to IOException / UnauthorizedAccessException /
ArgumentException,
- both `dotnet` process launches to Win32Exception /
InvalidOperationException / IOException,
- the two temp-directory cleanups in the tests and the integration-test
fixture to IOException / UnauthorizedAccessException.
EnumerateLocalVersions now maps with Select instead of an accumulate-only
foreach.
`Path.Combine(repoRoot, "local-nupkgs")` became `Path.Join`: Combine discards
everything before a rooted later argument, which is not what this call wants.
Dropped a null-coalesce on a value the compiler already knows is non-null on
that branch.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
Reject malformed and unknown upgrade template arguments
src/Reactor.Cli/Upgrade/UpgradeCommand.cs:297
The new --templates-source/--templates-version options are parsed permissively here: a missing value (for example, mur upgrade --templates-source) or a misspelled option is silently treated as if no override was supplied, so the upgrade can fetch from the default source and still report success. This is inconsistent with the strict mur templates install parser and is especially risky because the caller believes it requested a specific template source/version; reject malformed or unknown upgrade arguments before running the upgrade.
Remove obsolete claims about building the legacy template package
This new file still claims that the removed Microsoft.UI.Reactor.ProjectTemplates package is built by mur pack-local and published by the release workflow, but this PR deletes that project and removes both packing steps. That statement will mislead maintainers about the source of the legacy package; describe only its published/deprecated status instead.
Fix stale CreateTemplateTests XML documentation reference
The fixture rename leaves the class-level XML documentation referring to the deleted CreateTemplateTests type (SourceMapPackageConsumerTests.cs:20). That cref is now stale (and can produce an unresolved-cref warning when documentation comments are validated); remove the sibling reference or update the paragraph to describe this test directly.
Deleting the in-repo template package removed a `snippet=` reference from
packaging.md.dt, dropping it to 2 resolved snippets. The solid tier requires 3,
so REACTOR_DOC_TIER_003 failed both the "Docs build" and "Docs tier-drift" CI
jobs.
Rather than pad the count, add the snippet the MSIX section was missing: the
identity block of this repo's own packaged selftest host, which is a real
single-project MSIX app. The prose said "Package.appxmanifest declares the
package identity" and then showed no manifest at all.
Also corrects the publish-shape table, which still labelled the unpackaged row
"(template default)". The Windows App SDK `reactor` templates scaffold a
packaged app, so MSIX is the default now — the page's own opening paragraph
already said so.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
Copilot review round 5.
The workflow's "pack is installed" assertion was a substring match over the
whole `dotnet new uninstall` transcript, so a longer id that merely contains
ours — `...CSharp.Templates.Extras` — satisfied it while the required pack was
absent. That is the same prefix false-positive
`WinAppSdkTemplates.InterpretPackageInstalledOutput` already guards against in
the CLI; the CI mirror had not been updated to match.
Both assertions now compare trimmed whole lines. Verified in both directions
rather than by inspection: against a transcript containing only the `.Extras`
pack the old check reports installed=True and the new one False, and against
this machine's real listing the new check still reports True.
Rewriting the positive assertion left `$packages` undefined at the negative
one, which would have made the "the removed ProjectTemplates pack must not be
installed" check silently vacuous — a guard that always passes. Both now read
from the same `$packageLines`.
Also reconciled the PR description, which still claimed the scaffold→launch
smoke was deliberately not replaced. It is replaced, and strengthened: the
description now documents the registration/activation/render assertions and
quotes the CI output.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
This step invokes a bare winapp even though bootstrap's Get-WinAppCliPath explicitly handles the case where the app-execution alias exists at %LOCALAPPDATA%\Microsoft\WindowsApps\winapp.exe but is not resolvable through the current process PATH (lines 441-457). The workflow does not add that alias path to PATH after bootstrap, so the UI assertion can fail on a runner with exactly the installation state this fallback is meant to support; resolve and invoke the concrete path here as bootstrap does.
Copilot review round 6 — the same alias-path bug it caught in bootstrap.ps1,
which I then reintroduced in the CI step added two commits later.
winget's MSIX install drops an app-execution alias that exists on disk without
always being resolvable through the current process's PATH; bootstrap's
Get-WinAppCliPath handles that, and the workflow does not add the alias
directory to PATH afterwards. So the rendered-UI assertion could fail on a
runner with exactly the installation state the fallback exists to support —
reported as "the Reactor UI did not render" rather than "winapp was not found",
which is the misdiagnosis that costs the most time.
The step now resolves the concrete path the same way and invokes it, and fails
with an explicit "winapp CLI not found" if neither location has it.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
The reason will be displayed to describe this comment to others. Learn more.
Copilot review overview
🔵 Needs a closer look
Two moderate bootstrap CI issues and several documentation/code cleanup findings remain unresolved.
Review effort: Lite Findings: None
Previously missed (1)
In code that hasn't changed since last review
Ensure smoke app and job cleanup runs on every failure path
.github/workflows/bootstrap.yml:376
This cleanup only runs after the UI assertion succeeds. If package registration, winapp resolution, or the UI search fails, the TestApp process and Start-Job remain alive because the finally block only pops the location; that can contaminate later steps (including the idempotence/bootstrap checks) or leave the job waiting on an orphaned dotnet run. Move the guarded process/job cleanup into finally so every failure path tears down the smoke app.
Copilot review round 7.
The launch smoke's cleanup sat on the success path, so any throw between
starting the job and the final assertion left a live packaged TestApp and a
blocked `dotnet run` job behind. That is worse than a leak: the steps that
follow are the `mur upgrade` and bootstrap idempotence checks, which a
still-registered, still-running smoke app can contaminate — and the orphaned
job can leave the runner waiting on it.
Process and job teardown moved into `finally`, guarded so it is safe when the
failure happened before either was created.
Verified rather than assumed, with a PID-specific oracle: a probe that throws
before the old cleanup point leaves no surviving process. (The first probe used
`notepad`, which was already running on this machine — an oracle that cannot
tell a leak from a pre-existing process, so it was replaced.)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
main's #1277 replaced Reactor's own dotnet new templates with the Windows App
SDK ones and deleted CreateTemplateTests.cs, renaming the shared fixture
TemplatePackageTestFixture -> LocalPackageFeedFixture. That collided with this
branch two ways:
1. modify/delete on CreateTemplateTests.cs. Accepted main's deletion - the
template-scaffolding test is genuinely obsolete now that the templates are
gone. My only change to it was the MAX_PATH fix below, which is carried over
rather than lost.
2. PriPackagingTests took TemplatePackageTestFixture by constructor injection.
Repointed to LocalPackageFeedFixture; every member it uses (PackageVersion,
PackageSourceDir, NugetPackagesDir, RunArchitecture, CommandEnvironment)
exists unchanged on the new fixture.
Also restores the fix from e481021, which the rewrite dropped. That commit
shortened the fixture's directory names because the feed directory becomes the
consumer's globalPackagesFolder, and the longest resolved reference hit exactly
260 characters on CI - MAX_PATH - failing the XAML compiler with WMC1006. The
new naming reverted to nuget-global-packages, measured at 253: under the limit,
but 7 characters of headroom. rlf-{guid}/gp takes it back to 219.
Not scope creep: the test that WMC1006 broke is the one added by this PR, and
without this the branch reintroduces a failure it already fixed once.
CHANGELOG auto-merged; both of this PR's entries survive alongside main's.
Integration test project compiles clean.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
…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
)
* Pin the VS extension's System.Text.Json to the VS SDK baseline
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
* Diagnose packaged (MSIX) projects in the VS preview instead of failing 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
* Make packaged (MSIX) apps work in the VS preview
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
* Trim the System.Text.Json changelog entry to the outcome
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2
* Address Copilot review: raise the VSIX minimum host to 17.14
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
* Parse the VSIX manifest as XML so the host gate cannot skip a range
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
* Correct compatibility and packaging claims in the docs and changelog
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
* Derive the VSIX host minimum from the central VS SDK version
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
* Scope the packaged-preview claim to projects with WinApp run support
WinAppRunUseExecutionAlias is consumed only by the targets that
Microsoft.Windows.SDK.BuildTools.WinApp installs, so "works with no project
changes" was true only for project shapes that already reference it -- which is
what the reproduction used. It is not true of this repository's own shapes:
tools/Templates/templates/WinUIApp-CSharp references only Reactor and Devtools,
and the MSIX conversion documented in packaging.md.dt adds only MSIX properties
and a manifest. A project following either route would still launch without the
alias, and the environment property would have no consumer.
Narrow both claims rather than overstate the fix. The changelog now says the
preview supports packaged apps, without the no-edit promise, and the guide
states the run-support prerequisite and that a hand-converted MSIX project needs
that package added.
The launcher behaviour is unchanged and remains correct: setting the property is
the whole of what the extension can do from its side. It cannot make a target
project import MSBuild targets, which is why the launcher test asserts the
property is set rather than asserting a project shape.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2
* Qualify the packaged-preview summary on the launcher helper
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
* Harden the repo-file lookup against a rooted path (code-quality bot)
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
* Drop the dormant ceiling entry and make dormant entries impossible
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
* Qualify the changelog claim to WinApp-enabled packaged projects
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
* Update spec 056 to the 17.14 host baseline
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
* Restore the packaged claim: the Reactor templates are packaged by default
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
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c522596-4cdf-4ae1-981e-1fb5af52d4a2
* Run the packaged template smoke on framework changes
PR #1277 replaced the deleted CreateTemplateTests with a packaged scaffold →
build → launch smoke in the Bootstrap job, which asserts what no other suite
does: that the loose-layout MSIX registers, the app activates with identity,
and the Reactor UI renders. Every other suite runs unpackaged.
But that job is path-filtered, and `src/Reactor/**` was not in the list — so the
smoke only ran when bootstrap's own inputs changed. The test it replaced lived
in Reactor.IntegrationTests, gated on `non-md`, so it ran on any framework
change. Coverage did not disappear, but its reach narrowed: a framework
regression that broke packaged startup, identity activation or first render
would not have been caught.
Adds `src/Reactor/**` and `src/Reactor.Devtools/**`. Devtools is not incidental
— the scaffolded template references both packages at `$ReactorVersion$`
(verified in the shipped template's ProjectTemplate.csproj), and CI builds the
app against the source-built 0.0.0-local packages, so a Devtools break fails the
smoke too.
Also excludes markdown, so a doc-only edit to one of the three .md files now
inside those paths doesn't spin a 12-minute job. Negations are order-sensitive
and kept last; a PR touching both a .cs and a .md still runs.
Verified by modelling GitHub's filter semantics over eight cases: framework,
devtools and bootstrap inputs trigger; doc-only under those paths does not;
mixed .cs + .md still triggers; unrelated docs, tests and samples do not.
Found via a cross-repo review from the WindowsAppSDK side, which noted that the
upstream template harness builds every App template with
`-p:WindowsPackageType=None` (dev/Templates/Dotnet/Test-DotnetNewTemplates.ps1
line 370) and launches the raw exe rather than `dotnet run` — so packaging is
validated in neither repo by that path. Reactor does own it, via this job; it
just wasn't watching the input most likely to break it.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
* Add Reactor.Advanced, and stop overstating the gap
Copilot review on #1289 — both findings correct.
Reactor.Devtools project-references Reactor.Advanced, and the scaffolded
template references Devtools, so an Advanced change flows into the package
graph this smoke builds and runs. Added to both trigger lists; verified the
reference in src/Reactor.Devtools/Reactor.Devtools.csproj rather than assuming
it from the pack-local output.
The comment also claimed "every other suite runs unpackaged", which is wrong:
ci.yml's Packaged Selftests run the in-repo host under MSIX identity on every
non-md change. Reworded to state the actual, narrower gap — the *scaffolded
template* path: a fresh app from the external template pack, built against
local packages, registering and rendering. That is what no other job covers.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
* Invert the src/ filter: default-on, minus what cannot matter
Copilot review round 2 on #1289. The finding is right, and understates itself:
Reactor.csproj project-references six projects whose output is packed into the
Microsoft.UI.Reactor package consumers compile against — Analyzers,
Analyzers.Internal, and the Localization / Wrappers / SourceMap generators,
plus Wrappers.Abstractions. The review listed five; Analyzers.Internal is
referenced too.
The deeper problem is the shape, not the contents. Counting Reactor itself,
Devtools (referenced by the template), Advanced (project-referenced by
Devtools) and the CLI, that is 10 of the 14 projects under src/. My allowlist
had already drifted twice inside this one PR — it shipped without Advanced,
then without those six — and each miss is invisible: it surfaces as a packaged
regression nobody caught, never as a red build.
So the filter is now `src/**` minus the four that cannot affect a scaffolded
app: Reactor.Compile.Analyzer, Reactor.Interop.WinForms, vs-reactor,
vscode-reactor. A generator added to Reactor.csproj tomorrow is covered by
default; the failure mode becomes an occasional unnecessary 12-minute run
rather than a silent hole, which is the right way round for this job.
Verified over 19 cases, 0 mismatches: all ten package inputs trigger; the four
excluded projects, markdown, docs, tests and samples do not; a mixed .cs + .md
change still runs.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b893b2b8-78f8-46c6-9227-515e1dc3a563
…pps (#1283)
* Document the single-file path in Getting Started
PR #1277 moved scaffolding onto the Windows App SDK templates and rewrote the
setup story, but the third path from the plan was never written: a Reactor app
does not need a .csproj at all. .NET 10 runs a lone .cs file whose
#:package / #:property header supplies what a project file otherwise would.
Both shapes are now covered, and the header is the switch between them: keep
WindowsPackageType=None for a plain unpackaged window, or drop it and add
Microsoft.Windows.SDK.BuildTools.WinApp, whose targets intercept `dotnet run`
and launch with real package identity. That second half is winappCli#794 and
#874 (the latter implements the issue we filed as winappCli#871); both shipped
in winapp 0.7.0 on 2026-09-24, which is what unblocked this.
Verified end to end against the currently published versions -- preview.16 and
BuildTools.WinApp 0.7.0 -- not against the versions this was first prototyped
with. All three commands were run: `dotnet run` unpackaged opens a window,
`winapp run` opens a packaged one, and packaged `dotnet run` prints
"[WinAppRunSupport] Intercepted 'dotnet run' for packaged app" and launches with
identity. Test packages were unregistered afterwards.
Re-verifying was necessary: the header prototyped in August no longer builds,
because the Windows App SDK now defaults to self-contained and refuses an
unspecified architecture. The fix is the one the docset already documents --
index.md.dt's minimal .csproj resolves RuntimeIdentifier from
$(NETCoreSdkPortableRuntimeIdentifier), and that works verbatim as a #:property.
An earlier draft instead pinned Microsoft.WindowsAppSDK, which built and ran but
contradicted index.md.dt on why that package is ever added by hand, and implied
`Platform=x64` was the only alternative when the docset's own guarded block
shows an architecture-neutral one. Both pages now tell one story.
The guide names the two failures whose error text does not point at the cause,
because both cost time here: omitting WindowsPackageType=None builds clean and
then dies at startup with REGDB_E_CLASSNOTREG (the bootstrapper ships but never
auto-initializes), and a Windows version below 22621 compiles against nothing
and reports CS0234 "the namespace 'Reactor' does not exist", which reads like a
missing package.
On verification: InlineSnippetLedgerTests correctly rejected these blocks, since
nothing compiles inline C#. They cannot be snippet-backed -- a file-based app is
defined by having no .csproj -- and they stay contiguous rather than splitting
into a ledgered header plus a snippet-backed body, because the section's claim
is "this is the entire file". What that costs is bounded and stated in the
ledger: every construct in the body already appears in a compiled snippet on the
same page (hello-world and usestate-counter), so an API rename reddens those
first. The unverified remainder is the directives, and SingleFileGuideHeaderTests
covers the one that can rot silently by pinning the documented TargetFramework
to src/Reactor/Reactor.csproj.
Mutation-checked, including the case a weaker assertion missed: a stale TFM
reddens, deleting WindowsPackageType=None reddens, and *moving* it from the
unpackaged block into the packaged one reddens -- the last of these is the
realistic drift, since the two blocks differ by two lines, and a whole-file
Contains check stays green through it while both examples become wrong.
Reactor.slnx Release: 0 errors. Reactor.Tests 14,205 passed / 0 failed.
DocPipeline.Tests 474 passed / 0 failed. WinAppSDKReferenceGuardTests 86 passed.
No image churn. The two Win2D cross-link warnings are pre-existing in
packaging.md.dt, which this does not touch.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 393937bf-e01f-4a84-a862-ca06ed302d2f
* Document OutputType=WinExe, the fifth header directive
The prose said 'the four properties' while the header carries five, and the
table omitted OutputType -- which made the table read as complete while missing
a load-bearing line.
It is the easiest of the five to drop, because nothing fails: the build
succeeds and the app runs. It links for the wrong subsystem instead. Measured
on this tree by reading the PE subsystem byte of both builds -- 3 (console)
without the directive, 2 (Windows GUI) with it -- so omitting it leaves a
console window sitting behind the app's UI for as long as it runs.
Guarded in both examples, since the table now claims it. Mutation-checked:
removing it from either header block reddens.
DocPipeline.Tests 475 passed / 0 failed. Reactor.slnx Release: 0 errors.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 393937bf-e01f-4a84-a862-ca06ed302d2f
* Align the shipped agent-kit skills with the documented single-file header
Two findings from review round 2.
SKILL.md ships inside the NuGet agent kit and told consumers a different
single-file setup than the guide now does: pin Microsoft.WindowsAppSDK, and pass
-p:Platform=ARM64 (or x64) on every run. Both work, but the arch flag has to be
repeated on each invocation and pins the file to one architecture, and having
two shapes for one task is how the docset drifts. Same defect class as the
index.md.dt contradiction caught in round 1, except this one ships to users.
It now uses the same RuntimeIdentifier header the guide documents and links to
the packaged variant.
reactor-build-and-check ships too, and its claim that single-file builds require
-p:Platform became incomplete rather than wrong once the header can supply the
architecture. Its build command and its MSB4025 / NETSDK1136 rows now mention
the header alternative alongside the flag, which keeps the existing advice
working while pointing at the less fragile option.
Also switches the new test file from Path.Combine to Path.Join, which is what
this suite already uses (105 occurrences to 36) and is the idiomatic answer to
the CodeQL finding: Join concatenates, where Combine silently discards earlier
segments if a later one is rooted. Behaviour here is identical, since both
constants are compile-time relative.
Reactor.Tests 14,205 passed / 0 failed. DocPipeline.Tests 475 passed / 0 failed.
AgentKit + WinAppSDKReferenceGuard gates 222 passed. Release: 0 errors.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 393937bf-e01f-4a84-a862-ca06ed302d2f
* Guard every directive the header table claims
The guard covered TargetFramework, WindowsPackageType and OutputType but not
RuntimeIdentifier or UseWinUI, so deleting either from a header would leave the
suite green while a reader's dotnet run failed with the architecture error the
table documents. Each row of that table is a claim; each is now checked, and the
three presence checks share one theory rather than accreting a method apiece.
WindowsPackageType stays separate because it is the one directive that must
differ between the blocks, so it is asserted for placement rather than presence.
Also corrects a stale rationale. The class remarks still said the header's
Microsoft.WindowsAppSDK pin was covered by WinAppSDKReferenceGuardTests -- true
of an earlier draft, but the headers now carry
Microsoft.Windows.SDK.BuildTools.WinApp, which that guard's regex does not match.
Rather than claim a guard that does not exist, the remarks now say plainly that
the build-tools version is unguarded and why: there is no central pin in this
repo to check a doc against, since it is a consumer-side tool rather than a
framework dependency. Its presence is asserted; its version is not.
Mutation-checked: removing RuntimeIdentifier, UseWinUI, or the build-tools
package from either block reddens.
DocPipeline.Tests 478 passed / 0 failed. Reactor.slnx Release: 0 errors.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 393937bf-e01f-4a84-a862-ca06ed302d2f
* Match the blog's single-file app, header, and command
The guide and the published blog quick-start showed the same feature two
different ways. The blog's version is the better one, and it is what readers
will have seen first, so the guide now matches it: same app, same four-directive
header, same `winapp run counter.cs`.
RuntimeIdentifier turned out to be conditional, not universal -- which is what
prompted this. Measured all three ways:
4 directives + winapp run -> works (winapp passes the architecture through)
4 directives + dotnet run -> "WindowsAppSDKSelfContained requires a
supported Windows architecture"
4 + RuntimeIdentifier
+ WindowsPackageType=None
+ dotnet run -> works
So the line is needed only when `dotnet` drives the build, and the guide had
been presenting it as part of the base header. It now sits where it belongs, in
a "Running it with plain dotnet" delta, and the primary block is the shortest
thing that actually runs.
The packaged `dotnet run` path (BuildTools.WinApp intercepting the run) drops
from a third code block to a sentence naming the one-line delta. It is a real
capability and still documented, but it was the least common of the three and
did not earn a full block once `winapp run` leads.
Test changes follow the restructure rather than adding coverage: the directives
are now asserted per-block -- OutputType/TargetFramework/UseWinUI in the primary
header, RuntimeIdentifier/WindowsPackageType in the delta, and each absent from
the other. That second half matters because a directive migrating into the
primary block would silently fork it from the blog, and a whole-file Contains
check stays green through exactly that move. Mutation-checked in both
directions, plus the BuildTools mention which is now prose and otherwise
unguarded.
Also trims the changelog entry. REGDB_E_CLASSNOTREG and CS0234 are the right
level of detail for the guide, where a reader is staring at the error; in a
release note they were noise.
Reactor.Tests 14,205 passed / 0 failed. DocPipeline.Tests 478 passed / 0 failed.
Reactor.slnx Release: 0 errors. No image churn.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 393937bf-e01f-4a84-a862-ca06ed302d2f
* Depend on WindowsAppSDK.Runtime; package single-file apps by default
Apps that referenced only Microsoft.UI.Reactor were silently built
self-contained. Since #822 the package carried Microsoft.WindowsAppSDK.WinUI
alone, and without Microsoft.WindowsAppSDK.Runtime the Windows App SDK's
Base.targets defaults WindowsAppSDKSelfContained to true, which then demands an
architecture: `dotnet run` without -r or -p:Platform failed with
"WindowsAppSDKSelfContained requires a supported Windows architecture".
Directory.Build.targets now gives every framework-dependent WinUI project,
libraries included, WinUI + Runtime, so the published package flows Runtime to
consumer apps. WinAppSDKReferenceGuardTests asserts that Reactor resolves
Runtime and does not mark it private; restricting Runtime to apps again, or
making it private, reddens the guard.
With apps framework-dependent again, the single-file header in Getting Started
adds Microsoft.Windows.SDK.BuildTools.WinApp, so `dotnet run counter.cs`
launches the app packaged with no architecture flag, the same as
`winapp run counter.cs`. Both runners obey WindowsPackageType: None runs the app
unpackaged, and with neither that nor the BuildTools package, plain `dotnet run`
dies at startup with REGDB_E_CLASSNOTREG. Bundling the runtime is documented as
the WindowsAppSDKSelfContained=true opt-in, which is what needs -r; -r alone
bundles nothing. SKILL.md and reactor-build-and-check teach the same header,
with WindowsPackageType=None as the fallback when Developer Mode is off.
CONTRIBUTING, spec 022 and the packaging guide describe the new dependency.
Corrects earlier commits on this branch, which were measured against the
self-contained preview.16: the self-contained default came from #822, not from
a Windows App SDK change, and WindowsPackageType=None was a no-op for those apps
rather than the cause of REGDB_E_CLASSNOTREG. That crash only applies to
framework-dependent apps launched as a plain exe, which Reactor-only apps are
again after this change.
Measured against a locally packed Reactor with the Runtime dependency: the
guide's header runs packaged under both `dotnet run` and `winapp run`, and
unpackaged under both with WindowsPackageType=None; an explicit
WindowsAppSDKSelfContained=true app still builds and runs with -r win-x64.
Reactor.slnx Release: 0 errors. DocPipeline.Tests 479 passed / 0 failed.
WinAppSDKReferenceGuard, agent-kit, skill-prose and search-index gates pass.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 393937bf-e01f-4a84-a862-ca06ed302d2f
* Document bumping WinUI through the full Windows App SDK package
Now that Microsoft.UI.Reactor depends on Microsoft.WindowsAppSDK.Runtime, a
consumer that references a newer Microsoft.WindowsAppSDK.WinUI on its own gets
a build error. NuGet raises WinUI (and the Foundation and InteractiveExperiences
packages it brings in) but leaves Runtime at the version Reactor asks for,
because neither package depends on the other. Runtime's component-version check
then fails the build with "One or more referenced Windows App SDK components are
newer than the versions expected by the Microsoft.WindowsAppSDK.Runtime
package". This is the Windows App SDK's intended behaviour: Runtime decides the
minimum Windows App Runtime the app asks for, so it must not lag the components
the app compiles against.
The error's own advice points the wrong way for someone upgrading on purpose:
it says to change the component references to match Runtime, which undoes the
upgrade, and it never mentions the full Microsoft.WindowsAppSDK package, which
moves Runtime and the components together. So the fix is now documented where a
consumer will look: the packaging guide quotes the error text so it can be
searched, the landing page and spec 022 say to bump the full package (spec 022
previously said NuGet would unify a consumer's own WinUI reference), and the
CHANGELOG entry and the reactor-build-and-check error table cover it.
Reproduced against a locally packed Reactor from this branch: adding
Microsoft.WindowsAppSDK.WinUI 2.3.9 fails with the error above; adding
Microsoft.WindowsAppSDK 2.5.1 instead builds, with Runtime 2.5.1 and WinUI
2.3.9. DocPipeline.Tests 479 passed / 0 failed. Agent-kit, skill-prose,
search-index and WinAppSDKReferenceGuard gates 293 passed / 0 failed.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 393937bf-e01f-4a84-a862-ca06ed302d2f
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 393937bf-e01f-4a84-a862-ca06ed302d2f
… `reactor` (#949)
## Description
If you install the standalone `Microsoft.UI.Reactor.Templates` pack
alongside the WinUI templates pack, both packs register the short name
`reactor`. `winapp new -t reactor` passed that name straight to `dotnet
new`. Because the name matches templates in two packs, `dotnet new`
refused it and scaffolding failed.
`winapp new` now checks which short names other installed packs also
register. It creates the WinUI pack's template using the first of its
aliases that no other pack claims:
- **One name is shared:** `winapp new -t reactor` scaffolds the WinUI
pack's *Reactor Blank App* by running `dotnet new reactor-blank`. The
template name `winapp` shows and reports in `--json` (`"Template":
"reactor"`) is unchanged.
- **Every name is shared:** `winapp new` stops before asking for a
project name. It exits with code 4 and prints a message that names the
conflicting pack and how to remove it.
`winapp new` still only installs, lists and scaffolds templates from the
official WinUI pack.
## Usage Example
This is the output observed with both packs installed (WinUI pack
`0.0.7-alpha`, .NET SDK 10.0.401):
```text
> dotnet new list reactor
Template Name Short Name Language Tags
----------------------------------------- ------------------------------------- -------- ------------------------------------------
Microsoft.UI.Reactor App reactor [C#] Windows/WinUI/Desktop/Reactor
Reactor Blank App (Experimental) reactor,reactor-blank,winui-reactor [C#] Windows/WinUI/Desktop/Reactor/Experimental
...
```
**Before (main):** `winapp new -t reactor --name BeforeFix
--use-defaults` exits with code 5.
```text
❌ Failed to scaffold WinUI app: Unable to resolve the template, the following installed templates are conflicting:
Identity Template Name Short Name ... Package
Microsoft.UI.Reactor.StandIn.CSharp Microsoft.UI.Re... reactor ... Microsoft.UI.Reactor.Templates
Microsoft.WindowsAppSDK.Reactor.CSharp.BlankApp Reactor Blank A... reactor,reactor-blank,winui-reactor ... Microsoft.WindowsAppSDK.WinUI.CSharp.Templates
```
**After (this PR):** `winapp new -t reactor --name AfterFix2
--use-defaults` exits with code 0. With `--verbose`, the log shows
`dotnet new reactor-blank`, and the scaffolded app builds with 0 errors.
```text
✅ Created WinUI app AfterFix2 at ...\AfterFix2.
⚠ reactor is an experimental template. It depends on prerelease packages whose
APIs can change or be removed in a future release.
👉 Next: cd "AfterFix2" then winapp run.
```
When another pack claims every alias (`reactor`, `reactor-blank` and
`winui-reactor`), `winapp new` exits with code 4 and creates nothing:
```text
❌ Template 'reactor' can't be created because every short name it has (reactor, reactor-blank, winui-reactor) is also registered by another installed template pack (Microsoft.UI.Reactor.Templates), so 'dotnet new' can't tell them apart. Remove the other pack with 'dotnet new uninstall Microsoft.UI.Reactor.Templates', then re-run 'winapp new'.
```
With only the WinUI pack installed, `winapp new -t reactor` still runs
`dotnet new reactor`, as before.
## Related Issue
Fixes#948
## Type of Change
- 🐛 Bug fix
## Checklist
- [x] New tests added for new functionality (if applicable)
- [x] Tested locally on Windows
- [x] [docs/usage.md](../docs/usage.md) updated (if CLI commands
changed)
## Screenshots / Demo
N/A (CLI behavior; see the observed output above).
## Additional Notes
- **Where the pack list comes from:** `winapp new` already runs `dotnet
new uninstall` to confirm which templates the WinUI pack owns. That
output lists every installed pack and its aliases, so this fix needs no
extra `dotnet` call.
- **How the conflicting pack was reproduced:**
`Microsoft.UI.Reactor.Templates` isn't published on nuget.org. The
template project that microsoft/microsoft-ui-reactor removed in
microsoft/microsoft-ui-reactor#1277 used the short name `reactorapp`,
not `reactor`. The repro therefore used a local pack built to match the
issue exactly: package ID `Microsoft.UI.Reactor.Templates`, name
"Microsoft.UI.Reactor App", short name `reactor`, and tags
`Windows/WinUI/Desktop/Reactor`. It ran in an isolated
`DOTNET_CLI_HOME`.
- **Limitation:** templates built into the .NET SDK don't appear in
`dotnet new uninstall` output, so a collision with one of them isn't
detected. None of them use WinUI or Reactor short names.
- **Validation:** all `WinUiTemplateCatalogTests` and `NewCommand*`
tests pass (181 passed, 1 skipped), including new handler tests for both
cases above. `scripts\build-cli.ps1 -SkipTests -SkipMsix -SkipNpm`
succeeds, and the CLI schema is unchanged.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8e69318a-5a91-45e5-a82c-9c7b91487c8b
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
Reactor's
dotnet newscaffolding moves to the official Windows App SDK template pack, and the in-repoMicrosoft.UI.Reactor.ProjectTemplatespackage (dotnet new reactorapp) is removed.Microsoft.WindowsAppSDK.WinUI.CSharp.Templates0.0.7-alphacarriesreactor,reactor-mvu,reactor-navviewandreactor-tabview(microsoft/WindowsAppSDK#6620 + microsoft/WindowsAppSDK#6786).Scaffolded apps are now packaged (single-project MSIX), not unpackaged.
dotnet runlaunches with full package identity and requires Developer Mode. No template produces the unpackaged shape any more — scaffold, then set<WindowsPackageType>None</WindowsPackageType>yourself. Both shapes are documented in the packaging guide.Scaffolding is
winapp newThe Windows App SDK CLI installs the template pack on demand, so there is no separate install step and no version to pin:
winapp new --listinstalls the pack without scaffolding, and offers to update a stale one.dotnet new reactorstill works once the pack is installed.What changed
bootstrap.ps1installs the pack viawinapp new --list --use-defaults— installs when missing, keeps an already-installed pack, never prompts. Params:-WinAppSdkTemplatesVersion(→--template-version),-SkipTemplates.mur templates statusreports whetherdotnet new reactorresolves, with load-bearing exit codes (0available,1probe failed,2pack too old,3pack missing). It backsmur doctorand bootstrap's verification.mur templates installdoes not exist. An earlier revision of this PR added one; it was retired oncewinappcovered the same ground. The subcommand is still recognised and names its replacement rather than reporting "unknown subcommand", since it briefly shipped in docs and bootstrap.mur upgradechecks template availability instead of installing — the pack ships externally, sogit pullnever invalidates it.tools/Templates/and the steps that packed/published it. Published NuGet versions are unaffected (NuGet doesn't allow deletion) and will be deprecated with a pointer todotnet new reactor— a portal action, noted under[Unreleased].mur clean-localdeliberately still knows the old package id, so it can clean up stale artifacts on machines bootstrapped before this change.Why Reactor doesn't carry its own installer
Two
dotnet new installhazards drove ~700 lines of workaround in an earlier revision.winapphandles both, so that code is gone rather than maintained:--prereleaseswitch — it resolves stable-only, so installing the bare package id fails while the pack is prerelease-only (it is). Working around it meant resolving versions off the NuGet flat container, which dragged in redirect re-validation and credential redaction.--forceuninstalls before downloading, so a failed install leaves the machine with no templates at all. Observed for real during development: a bare id +--forceuninstalled a working prerelease, then failed with exit 103.Test plan
dotnet test tests/Reactor.Tests— 14,205 passed / 0 failed / 64 skipped;Reactor.DocPipeline.Tests— 472 / 0dotnet restore Reactor.slnxthendotnet build Reactor.slnx --no-restore -c Release— 0 errorsmur docs compile— generated output committed and verified idempotent (a fresh compile byte-matches), which is what the CI freshness gate checkswinapp new -t reactoremits a packaged Reactor app (App.cs,Package.appxmanifest,EnableMsixTooling,Microsoft.UI.Reactorreference);--listshows all four templates.CreateTemplateTestsasserted a UIA element on the unpackagedreactorapptemplate, so it could not survive this migration unchanged. The Bootstrap job now replaces it for the packaged shape: it enables Developer Mode (sameAppModelUnlockapproach asci.yml's Packaged Selftests),dotnet runs the scaffolded app, and asserts three things a build cannot — the loose layout registers, the app activates, and the Reactor UI renders (winapp ui searchfor theTitleBarthe blank template emits). Green in CI:mur templates status/install/--helpandmur doctorsmoke-tested directlyRan the repo
pr-reviewskill (8 dimensions + multi-model cross-check) before opening. It caught a CI blocker I'd introduced:packaging.md.dtstill resolved a snippet from the deletedtools/Templates/, andSnippetExtractortreats a missing source as fatal, so the docs-build job would have failed.Risk / notes
-WinAppSdkTemplatesSource(install the pack from a local folder, for testing an unpublished build) is gone —winapphas no local-folder equivalent.mur pack-localno longer produces a templates nupkg and its--framework-versionflag is gone.CreateTemplateTests.csalso contained the sharedTemplatePackageTestFixture; it was extracted toLocalPackageFeedFixturerather than losing the unrelatedSourceMapPackageConsumerTestscoverage.getting-started.md.dt/packaging.md.dtare also being touched by a parallel workstream; changes here are the minimum factual correction, not a redesign.DataGridTestsE2E failures appeared on one run of this branch. They reproduce on an unrelated branch with the identicalRow edit did not start — no 'Save' button appearedsignature, predate the merge, and this PR touches no product or E2E code — a pre-existing intermittent flake, not a regression here. A later run of this branch was green.