Skip to content

Depend on fixed MSIX build tools; fix duplicate lib/ folder in the Reactor package - #1276

Merged
Alexandre Zollinger Chohfi (azchohfi) merged 27 commits into
mainfrom
azchohfi-reactor-pri-packaging-layout
Oct 14, 2026
Merged

Alexandre Zollinger Chohfi (azchohfi) merged 27 commits into
mainfrom
azchohfi-reactor-pri-packaging-layout

Conversation

@azchohfi

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

Copy link
Copy Markdown
Collaborator

Summary

Microsoft.UI.Reactor now depends on Microsoft.Windows.SDK.BuildTools.MSIX 1.7.260930100. Without it, apps get 1.7.251221100 through Microsoft.WindowsAppSDK.Base, which has two bugs that affect Reactor apps:

  • Expanding a package's .pri with makepri.exe Dump fails past MAX_PATH (PRI175), so apps failed to build in deeply nested folders (Shipping Reactor.pri under lib/ triggers the Windows App SDK PRI-expansion step, which fails on long paths #1271, WindowsAppSDK#6795).
  • An unpackaged app's own .pri is left out of its publish output (WindowsAppSDK#6394). Published self-contained apps crashed at startup, and framework-dependent ones couldn't load their own XAML. This replaces the _CopyWinUIResourcesForAot workaround, which is removed from 11 projects. docs/aot-support.md now says no setup is needed, and gives apps on 0.1.0-preview.16 or earlier a one-line fallback: reference the build tools directly.

It also fixes the core package layout. ReactorApplication.xbf was packed to a literal lib\$(TargetFramework)\ path, which created a second lib/ folder, and through an item evaluated before the build, so a clean pack left it out. A pack-time target now adds it as build output.

An earlier revision of this PR stopped shipping the packages' .pri files to avoid the MAX_PATH failure. The new build tools make that unnecessary, so the packages keep NuGet's standard layout.

Linked issue / spec

Fixes #1271. Spec: docs/specs/022-packaging-and-distribution.md.

Test plan

Every A/B varies only the build-tools version.

  • Deep paths: consumers of all three packages (with their .pri files) at the depths that failed before (framework-dependent in a 172-character folder, self-contained in a 174-character one). Both fail with PRI175 on 1.7.251221100 and build with Reactor's dependency.
  • Publish: on 1.7.251221100, self-contained apps (JIT and NativeAOT) crash with 0xC000027B, and framework-dependent apps can't load their own XAML. With Reactor's dependency, published self-contained and framework-dependent apps and a registered packaged (MSIX) app all run, with WinUI resources and their own XAML resolving.
  • The documented fallback, checked on the published 0.1.0-preview.16. Without the app-level reference, the published app has no .pri and crashes with 0xC000027B. With it, the .pri is published and the app runs.
  • NativeAOT selftest host: its .pri is published by the upstream target, and the TabView, NavigationView and Theme selftests pass against the AOT binary.
  • PriPackagingTests (integration): the core package contains the .xbf/.xaml, and the publish output contains the app .pri. That test's control pins 1.7.251221100 and must not produce the .pri. I reproduced both halves locally.
  • The Pack job now checks that each packed .nupkg has one lib/ folder, with ReactorApplication.xbf beside Reactor.dll. I ran the check's script locally: it passes on this branch's Pack artifacts, and fails on 0.1.0-preview.16 (two lib/ folders) and on a core package with the .xbf removed.
  • dotnet restore Reactor.slnx + dotnet build Reactor.slnx --no-restore -c Release: 0 errors. dotnet test tests/Reactor.Tests: 14,623 total, 0 failed in three runs; one earlier run had a single failure that didn't reproduce.

Risk / breaking changes

No public API change. One new package dependency: every consumer, packaged apps included, moves from build tools 1.7.251221100 to 1.7.260930100, upstream's release with these fixes. Dependabot and the Windows NuGet update workflow skip it, so it moves by hand with the Windows App SDK. Remove it once the Windows App SDK resolves a fixed version by itself (noted in Reactor.csproj and spec 022).

Reactor's three packages each shipped their generated .pri beside the
assembly in lib/<tfm>/. RAR relates a same-base-name .pri to its reference
(AllowedReferenceRelatedFileExtensions defaults to ".pdb;.xml;.pri;..."), so
the Windows App SDK ran makepri.exe Dump on all three into
$(IntermediateOutputPath). Those paths break past MAX_PATH, failing consumer
builds with PRI175/PRI222/APPX0002 at depths where an equivalent XAML app
builds fine.

Nothing was lost: the .Advanced and .Devtools indexes were entirely empty,
and the core one held a single embedded copy of ReactorApplication.xbf that
also ships loose. Relocating the file would not have worked - a .pri that
reaches the consumer's layout by any route is also expanded via
_ExpandPriFiles - so it is excluded from pack entirely. Generation is
untouched, leaving ProjectReference consumers unaffected.

Measured on a 261-character output path: build goes from failing to clean,
and Reactor makepri Dump calls drop from six to zero, leaving only the
Windows App SDK's own framework indexes.

Two further defects found while verifying this:

- dotnet pack from a clean tree silently omitted ReactorApplication.xbf. It
  was packed by a <None> guarded with Exists(), which MSBuild evaluates
  before the build produces the file. Now resolved at pack time, and its
  absence is a hard error.

- The package exposed two lib/ folders for the same framework: the .xbf used
  a literal lib\$(TargetFramework)\... path (net10.0-windows10.0.22621.0)
  while NuGet shortens build output to net10.0-windows10.0.22621. The
  sidecar is now emitted as build output with TargetPath so NuGet places it.

Also fixes WindowsAppSDK#6394 for every consumer: an unpackaged app's own
.pri and .xbf never reached the publish output, so published apps started
with an empty resource dictionary and crashed with 0xC000027B. The new
_ReactorCopyWinUIResourcesToPublish target in the shipped targets replaces
the per-project _CopyWinUIResourcesForAot snippet in 11 projects. That
snippet was gated on PublishAot=true, but the gap is not AOT-specific - a
plain dotnet publish -c Release loses the .pri too, so apps that never opted
into AOT were broken with no workaround available.

Fixes: #1271

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

📦 Build metrics

Artifact sizes for a36841a vs the base branch (60b3a10).

Packages (compressed .nupkg)

Artifact base PR Δ
Microsoft.UI.Reactor.nupkg 1.81 MB 1.81 MB +558 B (+0.03%) ≈
Microsoft.UI.Reactor.Advanced.nupkg 493.4 KB 493.4 KB -5 B (0.00%) ≈
Microsoft.UI.Reactor.Devtools.nupkg 288.3 KB 288.3 KB -4 B (0.00%) ≈

Assemblies in Microsoft.UI.Reactor

Artifact base PR Δ
Reactor.Analyzers.dll 386.0 KB 386.0 KB +0 B (0.00%) ≈
Reactor.dll 2.54 MB 2.54 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 1.00 MB 1.00 MB +0 B (0.00%) ≈

Assemblies in Microsoft.UI.Reactor.Devtools

Artifact base PR Δ
Microsoft.UI.Reactor.Devtools.dll 788.0 KB 788.0 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.

Comment thread tests/Reactor.IntegrationTests/Packaging/PriPackagingTests.cs Fixed
@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

🧪 Merged coverage

Coverage for a36841a vs the base branch (0f84736) — unit + selftest merged.

Metric base PR Δ
Line 86.74% 86.74% 0.00 pp ≈
Branch 77.44% (968/1250) 77.44% (968/1250) 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.

The upstream issue (WindowsAppSDK#6394) carries a community suggestion to
set EnableMsixTooling=true instead of copying the resources by hand.
Measured both claims rather than repeating them:

- It DOES fix the publish gap: the app .pri lands in the publish output and
  the published app runs. It works as a side effect of importing the MSIX
  packaging targets, which also drops the reference-path arm of the PRI
  expansion (makepri.exe Dump calls halve on a normal build).

- It does NOT fix issue #1271. Built against pre-fix packages at the failing
  177-character geometry with and without the switch: identical failure,
  PRI175/PRI222 on Microsoft.UI.Reactor.Devtools.pri.xml. Library .pri files
  are still expanded, so keeping them out of lib/ remains necessary.

Reactor does not set the property for consumers: whether an app wants MSIX
packaging tooling is the app's decision, and this is a community workaround
on an open issue rather than a documented contract. The shipped target is
narrower and works whichever way an app sets it. Verified the two compose
cleanly - no errors, no duplicated files, app runs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
CI failure: ConsumerPublishIncludesTheAppPriAndXbfSidecars published the same
project twice from one directory, once with an extra global property. That
forces a full re-evaluation over the same obj/, and the second XAML compile
failed with:

  WMC1006: Cannot resolve Assembly or Windows Metadata file
  '...\nuget-global-packages\microsoft.windowsappsdk.foundation\2.1.0\lib\...'

That reference path is ~258 characters under the fixture's package folder and
the XAML compiler is not long-path aware, so the first publish squeaked under
the limit and the second did not. Give each cell its own project directory,
shorten the temp-root and cell names, and reference only the core package for
this test - the target under test ships there, and dropping Advanced (Win2D)
and Devtools removes references the XAML compiler would otherwise resolve.

Also address the code-quality finding: Dispose() caught every exception while
cleaning up its temp directory. Narrow it to IOException and
UnauthorizedAccessException, which is what Directory.Delete realistically
throws here; anything else is a real bug and should surface.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
…sion

Checked the claim that WindowsAppSDK#6394 is fixed and that a newer SDK would
let us drop the workaround. Both halves of that are worth recording.

A first-party fix DOES exist: Microsoft.Windows.SDK.BuildTools.MSIX.Pri.targets
defines AddProjectPriToResolvedFileToPublish and AddXbfFilesToResolvedFileToPublish,
which add the project .pri and the .xbf files to @(ResolvedFileToPublish) - exactly
the gap. But it is reachable only through
EnableMsixTooling -> MsixPackageSupport -> ShouldImportMsixCommonTargets ->
MsixCommonTargets -> Packaging.targets -> Pri.targets. With the flag off the build
takes the MrtCore branch, which has no publish step at all. That is why the
community EnableMsixTooling=true workaround works.

So it is not version-gated and an SDK bump will not remove the need. Measured:

- the fix targets are present in BOTH the MSIX build tools a current app resolves
  (1.7.251221100) and the newest published (1.7.260903100);
- MsixPackageSupport is assigned in exactly one place, conditioned solely on
  EnableMsixTooling, and no Microsoft.WindowsAppSDK* package sets it (verified with
  a positive control so the empty result is a measurement, not a broken grep);
- publishing the stock template on the newest stable SDK (metapackage 2.5.1,
  WinUI 2.3.9, Foundation 2.3.12) with Reactor's target disabled still produces no
  app .pri and still crashes 0xC000027B. Note the template already pins the newest
  stable SDK via its dotnet-new post-action, so earlier runs were on it too.

Replace the vague "remove once #6394 closes" guidance in both the doc and the
target's comment with the real signal: delete this when that fix runs WITHOUT
EnableMsixTooling. PriPackagingTests' opt-out control starts failing when the
platform begins doing the copy itself, so CI reports the moment it lands.

Re-verified the shipped target after the edit: publish with it ON copies the .pri
and the app runs; OFF reproduces the crash.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
Follow-up after checking the claim that Microsoft.Windows.SDK.BuildTools.MSIX
1.7.260903100 specifically carries the fix. It does carry a real one, and the
previous commit undersold that: AddXbfFilesToResolvedFileToPublish is genuinely
NEW in 1.7.260903100. Only the .pri half (AddProjectPriToResolvedFileToPublish)
predates it in 1.7.251221100.

The conclusion is unchanged, because both targets live in Pri.targets, which is
imported only behind the EnableMsixTooling gate. Measured directly rather than
inferred from the import chain this time - pinned 1.7.260903100 and published
with -v:detailed, counting how many times each target actually executes:

  no EnableMsixTooling : AddProjectPri=0, AddXbf=0, app .pri NOT published
  EnableMsixTooling=true: AddProjectPri=2, AddXbf=2, app .pri published

The second cell is the control that proves the probe can match, so the zeroes in
the first are a measurement rather than a broken grep.

So upgrading the build tools alone still does not remove the need for the
workaround; the fix has to leave the EnableMsixTooling gate first. Record the
per-version detail so nobody has to re-derive which half landed when.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
…packaging-layout

# Conflicts:
#	CHANGELOG.md
…ementary

Correcting an overstatement. Saying "apps do not set EnableMsixTooling" was too
broad: packaged (MSIX) WinUI apps normally DO - Visual Studio's packaged template
sets it, and so does every MSIX project in this repo.

That does not change the conclusion, it explains the target's scoping. The two
populations do not overlap:

  unpackaged (WindowsPackageType=None) -> flag off -> platform fix does NOT run
                                       -> Reactor's target runs
  packaged   (WindowsPackageType=MSIX) -> flag on  -> platform fix runs
                                       -> Reactor's target excluded by
                                          AppxPackage/WindowsPackageType

Verified on samples/ReactorGallery, which builds both ways. Evaluated with
dotnet msbuild -getProperty rather than read off the file: default mode reports
EnableMsixTooling=false with MsixPackageSupport empty; -p:GalleryPackaged=true
reports true/true with AppxPackage=true. Publishing it unpackaged, the platform's
AddProjectPriToResolvedFileToPublish and AddXbfFilesToResolvedFileToPublish run 0
times, Reactor's target runs, the app .pri is published, and the build reports 0
errors.

Worth recording because I got this wrong first by grepping the csproj: the
WindowsPackageType=None and EnableMsixTooling=true lines sit in different
Condition'ed PropertyGroups, so a naive read makes it look like an unpackaged app
with the flag on. They never co-exist.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
The Fixed entries for this PR had grown to four entries over ~47 lines, against
a repo median of 6 lines per entry. Most of that was intermediate work on the way
to the fix rather than anything a consumer experiences.

Cut to the two changes a consumer actually sees:

  - packages no longer ship a .pri, so builds stop failing on deep paths (#1271)
  - published unpackaged apps no longer start with an empty resource dictionary

Folded the "two lib/ folders for one target framework" entry into the first as a
single clause - it is a packaging-shape detail that never produced a failure,
since NuGet happened to select the right group.

Dropped the "dotnet pack silently omits ReactorApplication.xbf" entry entirely.
It was a real bug in our own pack wiring, but no consumer ever received a broken
package from it: the .pri shipped an embedded copy of the same file, so the gap
only became load-bearing once this PR removed the .pri. It is a contributor-facing
robustness fix, already documented in the csproj comment and spec 022.

Net: 47 lines -> 17, with the two remaining entries at 9 and 8 lines.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The publish test misses consumer-generated XBF coverage, and the upstream-fix status in the documentation is contradictory.

Get a fresh assessment by requesting another Copilot review.

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

Open (2)
What changed in this PR

Fixes Reactor package and publish-time WinUI resource handling to prevent long-path PRI failures and missing runtime resources.

Changes:

  • Excludes library .pri files while reliably packaging ReactorApplication.xbf.
  • Centralizes unpackaged-app .pri/.xbf publishing.
  • Adds integration coverage and updates packaging/AOT documentation.
File Description
tests/​Reactor.IntegrationTests/​Packaging/​PriPackagingTests.cs Adds package and publish integration tests.
tests/​Reactor.AppTests.Host/​Reactor.AppTests.Host.csproj Removes local publish workaround.
tests/​perf_bench/​PerfBench.ControlModel/​PerfBench.ControlModel.csproj Removes local publish workaround.
tests/​aot_trim_proof/​Reactor.AotHelloWorld/​Reactor.AotHelloWorld.csproj Uses centralized publish handling.
tests/​aot_trim_proof/​Reactor.AotHelloWorld.Advanced/​Reactor.AotHelloWorld.Advanced.csproj Uses centralized publish handling.
tests/​aot_trim_proof/​Reactor.AotHelloWorld.Advanced.Positive/​Reactor.AotHelloWorld.Advanced.Positive.csproj Uses centralized publish handling.
src/​Reactor/​Reactor.csproj Excludes PRI and packs XBF correctly.
src/​Reactor/​build/​Reactor.targets Copies unpackaged WinUI resources during publish.
src/​Reactor.Devtools/​Reactor.Devtools.csproj Excludes the empty Devtools PRI.
src/​Reactor.Advanced/​Reactor.Advanced.csproj Excludes the empty Advanced PRI.
samples/​apps/​window-styles/​WindowStyles.csproj Removes duplicated workaround.
samples/​apps/​widget-creator/​widget-creator.csproj Removes duplicated workaround.
samples/​apps/​particle-storm/​ParticleStorm.csproj Removes duplicated workaround.
samples/​apps/​hello-world-aot/​HelloWorldAot.csproj Removes duplicated workaround.
samples/​apps/​hello-world-aot-devtools-on/​HelloWorldAotDevtoolsOn.csproj Removes duplicated workaround.
samples/​apps/​command-palette-window/​CommandPaletteWindow.csproj Removes duplicated workaround.
docs/​specs/​022-packaging-and-distribution.md Documents the corrected package layout.
docs/​aot-support.md Documents automatic publish handling.
CHANGELOG.md Records the consumer-facing fixes.

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

Comment thread tests/Reactor.IntegrationTests/Packaging/PriPackagingTests.cs Outdated
Comment thread docs/aot-support.md Outdated
Two findings from the Copilot review, both valid.

1. The publish test's XBF assertion was vacuous. It checked for
   ReactorApplication.xbf, which the package already delivers through a
   <None CopyToPublishDirectory="PreserveNewest"> item, so deleting the target's
   $(OutputPath)**\*.xbf glob would have left it green. Confirmed against my own
   earlier data, where the opt-out cell still reported that file present.

   The consumer now compiles its own Themes/ConsumerStyles.xaml, and the test
   asserts on the resulting Themes/ConsumerStyles.xbf. Measured both cells:

     target ON  -> Consumer.pri True,  Themes/ConsumerStyles.xbf True
     target OFF -> Consumer.pri False, Themes/ConsumerStyles.xbf False

   (ReactorApplication.xbf is True in BOTH, which is exactly why it was the wrong
   file to assert on.) The subfolder also covers the copy's RecursiveDir handling.

2. WindowsAppSDK#6394 was closed as fixed 19 minutes after I last checked it, in
   MSIX build tools 1.7.260903100 - the version flagged for review. The docs still
   said OPEN and claimed categorically that no upgrade could remove the workaround.
   Both are now corrected, and the apparent contradiction is resolved rather than
   restated: the fix is real, but lives behind the EnableMsixTooling gate, and the
   official WinUI templates set that flag by default. So the mainstream WinUI app
   IS fixed by upgrading; Reactor's template is the case outside it, shipping
   WindowsPackageType=None without the flag.

   That also corrects my earlier overstatement that apps do not set the flag - the
   official template sets it unconditionally. The discriminator is the flag, not
   packaged vs unpackaged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
… MAX_PATH

ConsumerPublishIncludesTheAppPriAndXbfSidecars failed on CI with
"WMC1006: Cannot resolve Assembly or Windows Metadata file" pointing at
microsoft.windowsappsdk.foundation's projection DLL. That path measured EXACTLY
260 characters - MAX_PATH - and the WinUI XAML compiler is not long-path aware.

It surfaced now because this test gained a consumer-owned .xaml in the previous
commit, so the XAML compiler runs on the first publish too. The same root cause
already bit once (fixed then by splitting the two publishes into separate
directories), which was treating the symptom.

The real cause is the shared fixture's naming: it becomes the consumer's
globalPackagesFolder, so every resolved reference is rooted under it.

  reactor-template-packages-{guid}/nuget-global-packages -> rtp-{guid}/gp

Saves 41 characters, taking that reference from 260 to 219. Verified against the
whole closure rather than the one path that happened to fail first: the longest
of 496 reference files under a real restore is 142 characters relative, which
with CI's 80-character root is 222 - 38 under the limit. Comment added so the
names are not lengthened back for readability.

Note the local run cannot catch this class of failure: my temp root is 3
characters shorter than CI's runneradmin one, so the old naming measured 257
locally and squeaked through. The two local failures here are NU1301 (nuget.org
is TLS-blocked in this environment), which is the documented limitation these
tests already carry - not WMC1006.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
Comment thread tests/Reactor.IntegrationTests/Packaging/CreateTemplateTests.cs Fixed
Code-quality flagged the Path.Combine call on the line shortened in e481021:
"Call to System.IO.Path.Combine may silently drop its earlier arguments."

That is Path.Combine's documented rooted-path behaviour - if a later segment is
absolute it discards everything before it. Not reachable here, since the segment
is always "rtp-{guid}", but Path.Join has no such behaviour and is already what
the sibling PriPackagingTests uses, so there is no reason to keep the sharper
tool. Scoped to the flagged line only.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
Answering a fair challenge: if #6394 is fixed upstream, why does
_ReactorCopyWinUIResourcesToPublish exist, and shouldn't the template just set
EnableMsixTooling=true?

Measured rather than argued. The two halves of the upstream fix shipped
separately, and only one is reachable today:

  AddProjectPriToResolvedFileToPublish  -> in 1.7.251221100
  AddXbfFilesToResolvedFileToPublish    -> only in 1.7.260903100

and no Microsoft.WindowsAppSDK release pulls the newer one yet: a restore
against the newest SDK (2.5.1) resolves Microsoft.Windows.SDK.BuildTools.MSIX
1.7.251221100. Publishing an unpackaged consumer with EnableMsixTooling=true on
that resolved version:

  app .pri published      : True
  consumer's own .xbf     : False

So turning the flag on today fixes half the gap while pulling in the entire
single-project MSIX pipeline. That is why the template has not adopted it, and
why this target is not redundant with the upstream fix.

Also drops the previous wording that the template "does not enable the MSIX
tooling, so the platform's fix never runs" - true, but it read as a
justification when it is really a state with a revisit trigger. The deletion
criteria are now concrete: an SDK release pulling build tools >= 1.7.260903100,
AND either those targets leaving the MsixPackageSupport gate or the template
adopting the flag - at which point the flag alone covers both halves and this
target can go.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
Two claims in this PR were argued but never measured end to end. Both now are,
and one of them closed a real gap.

1. Packaged (MSIX) consumers were never tested - every earlier cell was
   unpackaged. The worry was legitimate: for a packaged app the old lib/ .pri
   was NOT inert. It reached the app layout, so the app's own makepri New merged
   it into resources.pri. Measured, a packaged consumer of 0.1.0-preview.16
   indexes ms-resource://<App>/Files/Reactor/Hosting/ReactorApplication.xbf;
   built against this branch it does not. So the .pri really was carrying the
   sidecar there.

   It is still safe to drop. Registering and launching the same packaged app:
   preview.16 runs with theme resources resolving; this branch runs identically;
   deleting the loose .xaml+.xbf crashes it with brush/style False. That third
   cell is the control that makes the second mean something.

2. "Just relocate the .pri out of lib/" - previously reasoned from the targets,
   now measured. Shipping Reactor.pri from a sidecar/ folder via a plain
   <None CopyToOutputDirectory> still produces a makepri.exe Dump on it, because
   it lands in @(PackagingOutputs) and _ExpandPriFiles picks it up. Absence is
   the only escape.

Also re-verified the headline fix at the issue's exact geometry with all three
packages referenced, adding a plain-WinUI cell as the reference point:

  BEFORE (preview.16, has .pri) -> FAILS, 10 PRI/APPX errors
  AFTER  (this branch, no .pri) -> builds clean
  NO-reactor (plain WinUI)      -> builds clean

which is the actual claim: parity with a plain WinUI app, not immunity to depth.
A deeper path fails for everyone on the SDK's own framework PRIs - at 177 chars
those land at 258-259 while Microsoft.UI.Reactor.Devtools.pri.xml landed at 261,
matching the arithmetic in the issue.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
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
…tire this

Re-checked both upstream issues after @azchohfi flagged that upstream movement
might make this PR redundant. Two developments, both 2026-09-25, and neither
makes it redundant today.

WindowsAppSDK#6394 was REOPENED the day after it closed. Upstream's reason is
the gate this PR already documents: "Reopening because fix on MRT core version
used when not enabling msix tooling is still not released." Our docs and the
target's comment said CLOSED, which is now wrong - corrected, and the upstream
quote recorded since it independently confirms the measurement rather than
merely agreeing with it.

WindowsAppSDK#6795 - the MAX_PATH defect behind issue #1271 - is verified fixed
in MSIX build tools 1.7.260925103. That is the one that could retire the main
fix here, so spec 022 now says so explicitly, along with why it does not yet:
build tools reach an app only through Microsoft.WindowsAppSDK.Base, the sole
package in the graph referencing them, and the newest Base (2.0.4, pulled by
Windows App SDK 2.5.1) still pins 1.7.251221100. Checked against nuget.org
directly, with positive and negative controls: 1.7.260925103 returns 404 while
1.7.251221100 and 1.7.260903100 return 200.

Also records what happens when it does flow: the .pri removal stops being a
build-failure fix, but the layout stays right on its own terms - the .Advanced
and .Devtools indexes are entirely empty, and shipping them costs every consumer
six wasted makepri Dump invocations per build. Flagged for re-evaluation rather
than asserted as permanent.

Fixes a duplicated "When to delete the shipped target" paragraph introduced by
an earlier edit in this PR - the section had two, with drifting criteria.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
Strengthens the #6795 note after cross-checking with the session tracking these
upstream issues. My previous wording claimed the weaker fact - that the fixed
version is not published yet. The stronger one is true:

  1.7.251221100 (ships today)      -> FAILS, 6 PRI / 4 APPX
  1.7.260903100 (latest published) -> FAILS, 6 PRI / 4 APPX
  1.7.260925103 (private drop)     -> builds clean

So no published version fixes it, not merely "not the newest one". That version
was A/B'd at a failing depth by the other session, and matches the structure:
1.7.260903100's MrtCore.PriExpansion.targets still passes a bare
IntermediateDirectory="$(IntermediateOutputPath)" and its task assembly has no
\\?\ handling at all.

Independently confirmed the private-drop read from this machine, which can reach
nuget.org (the other session's proxy cannot, and they flagged that as their one
unverified number): 1.7.260925103 returns 404 while 1.7.251221100 and
1.7.260903100 return 200 and a nonsense version returns 404, so the probe
distinguishes absent from broken. Same for Base 2.0.5/2.0.6/2.1.0 and
WindowsAppSDK 2.5.2/2.6.0 - all 404.

Also records why this section outlives the upstream fix, which the previous
wording left as "re-evaluate then": build tools are transitive via
Microsoft.WindowsAppSDK.Base - every Base including 2.0.5-experimental2 pins
1.7.251221100 - so a consumer cannot opt into a fixed build-tools release by
upgrading Windows App SDK. Removing the .pri keeps the trigger away from every
consumer still resolving an older version, which is not a version they control.

Deliberately not recording anything about #6570 (MSB6003 MakePri output
collision). The 925103 content-addressed cache may address it, but neither
session has a repro, and an unverified claim in the spec is one we would have
to walk back.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
Precision follow-up from the session tracking these upstream issues, so a future
reader does not read our two probes as disagreeing.

My earlier nuget.org sweep reported Microsoft.WindowsAppSDK.Base 2.0.5 -> 404.
Theirs reported a 2.0.5-experimental2 that exists. Both are right: plain 2.0.5
does not exist, only 2.0.5-experimental2. A probe for the former 404s without
that meaning there is no newer Base, so the spec now says so explicitly.

The substantive finding is stronger with the prerelease included, not weaker.
Verified at nuspec level (my own earlier fetch agrees with theirs):

  Base 2.0.4                      -> BuildTools.MSIX 1.7.251221100
  Base 2.0.5-experimental2        -> BuildTools.MSIX 1.7.251221100
  Base 2.0.250911001-experimental -> BuildTools.MSIX 1.7.20250829.1

So the claim is not "no stable Base carries the fix" but "no Base at all,
stable or prerelease, pins even the newest PUBLISHED build tools" - which makes
the no-published-route conclusion airtight rather than merely likely.

Three independent legs now support it: the fix exists in no published
build-tools version; no Base pins even the newest published one; and that newest
published one still fails the repro.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
…ed both

#1283 made Reactor depend on Microsoft.WindowsAppSDK.Runtime again, which turns
a Reactor-only consumer from silently self-contained into framework-dependent.
Every earlier measurement in this PR predates that, so both fixes were
re-measured on the merged tree rather than assumed to carry over.

1. The .pri removal (#1271) is unaffected.

   The fair baseline is main itself - it has #1283 but still ships the .pri, so
   the only variable is this PR. (preview.16 was the wrong baseline here: it
   fails earlier with "WindowsAppSDKSelfContained requires a supported Windows
   architecture", the #822 regression #1283 fixed, and never reaches the PRI
   step. Counting that as a reproduction would have been a false positive.)

   Framework-dependent output drops the win-x64 path segment, so the failing
   geometry shifts. At a 185-char project dir only
   Microsoft.UI.Reactor.Devtools.pri.xml crosses MAX_PATH (261) while the SDK's
   own longest lands at 259:

     main (#1283, ships .pri) -> FAILS, 10 PRI/APPX, on Reactor.Devtools.pri.xml
     this branch              -> builds clean
     plain WinUI, same depth  -> builds clean

   Build tools still resolve 1.7.251221100 via Base 2.0.4; #1283 does not change
   that path.

2. The publish target is narrowed, not made redundant.

   Measured on a published Reactor-only app with the target disabled:

     framework-dependent (new default) -> runs, theme resources resolve
     self-contained (opt-in)           -> crashes 0xC000027B

   A framework-dependent app loads WinUI from the installed runtime - its
   publish output contains no Microsoft.UI.Xaml.dll at all - so its own .pri is
   not on that path. Self-contained bundles WinUI and resolves it through its own
   .pri. Before #1283 every Reactor-only app was silently self-contained, which
   is why the crash was the default experience; now it is the opt-in case.

   The framework-dependent pass is not vacuous: deleting Reactor's loose
   sidecars from that same output drops both probes to False.

   Kept unconditional rather than keyed on WindowsAppSDKSelfContained, and
   verified that is harmless: for a framework-dependent publish the target adds
   exactly App.pri and the consumer's .xbf, and the app behaves identically.

The CHANGELOG entry, aot-support.md, and the target's comment all claimed the
crash was universal; each is now scoped to self-contained apps.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
After #1283 made framework-dependent the default, the publish test's
WindowsAppSDKSelfContained=true looks like an arbitrary leftover that someone
might reasonably simplify away. It is load-bearing: self-contained is the one
deployment where a missing app .pri actually breaks a published app, since it
resolves WinUI through its own .pri. A framework-dependent app loads WinUI from
the installed runtime and runs fine without it.

So switching the consumer to the framework-dependent default would leave the
test passing while it stopped testing the case the target exists for. Documented
on the helper, with the measurement that justifies it.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
…packaging-layout

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
…ound

Microsoft.Windows.SDK.BuildTools.MSIX 1.7.260930100 fixes WindowsAppSDK#6394
(the app .pri missing from publish output, now also on the MrtCore path that
apps without EnableMsixTooling take, as Reactor's template does) and #6795
(MAX_PATH in makepri Dump). Every Microsoft.WindowsAppSDK.Base release still
resolves 1.7.251221100, so Microsoft.UI.Reactor now declares the fixed version
as a dependency floor and the shipped _ReactorCopyWinUIResourcesToPublish
target is deleted.

Measured with the build-tools version as the only variable: self-contained
publish (JIT and NativeAOT) crashes with 0xC000027B without it and runs with
it; a framework-dependent app cannot load its own XAML without it; deep-path
builds fail with PRI175 without it, including on the SDK's own PRIs, which
the .pri removal cannot address.

The publish integration test now uses a consumer pinned back to
1.7.251221100 as its control. Also fixes two stale references to removed
code (a SelfTestRunner comment and CreateTemplateTests).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
The MSIX build-tools dependency (1.7.260930100) fixes the MAX_PATH failure
behind issue #1271 by itself. Measured with only the build-tools version
changed, consumers of all three packages with their .pri files restored
fail with PRI175 on 1.7.251221100 and build on 1.7.260930100, at the same
depths that failed before (framework-dependent and self-contained).
Self-contained, framework-dependent and packaged (MSIX) consumers all run.

So the packages return to NuGet's standard layout: Advanced, Devtools and
Reactor.targets match main again, and core only appends .xbf to the
allow-list for _PackReactorApplicationXbf. The two tests that asserted the
exclusion are gone. CHANGELOG, aot-support.md, spec 022 and the remaining
code comments are cut down to what a reader needs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
…packaging-layout

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087
@azchohfi Alexandre Zollinger Chohfi (azchohfi) changed the title Stop shipping .pri in lib/; fix publish and lib-folder packaging defects Depend on fixed MSIX build tools; fix duplicate lib/ folder in the Reactor package Oct 14, 2026
@azchohfi Alexandre Zollinger Chohfi (azchohfi) added the agent-preparing Agent is addressing feedback or completing required validation and CI label Oct 14, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

No blocking issues remain, and differential integration tests plus reported runtime checks support the packaging and dependency changes.

0 open findings

2 resolved since last review

🧠 Review effort: Balanced

- docs/aot-support.md: the no-setup statement now names the first Reactor
  version it applies to, with a one-line fallback for 0.1.0-preview.16 and
  earlier (verified on the published preview.16: without it the published app
  crashes with 0xC000027B; with it the .pri is published and the app runs).
- release.yml: check that each packed .nupkg has one lib/ folder with
  ReactorApplication.xbf beside Reactor.dll. Only a pack of a built tree, as
  in that job, shows the second-folder defect, so the clean-tree integration
  test for it is removed. Verified against this head's artifacts (pass),
  0.1.0-preview.16 (fails: two lib/ folders) and a core package without the
  .xbf (fails).
- Keep the bump bots off Microsoft.Windows.SDK.BuildTools.MSIX: it moves by
  hand with the Windows App SDK.
- threat-model.md: six NuGet dependencies, not five.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3c093b4a-b747-4495-9317-0ecf8597d087

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

No blocking issues remain, and packaging checks, differential publish tests, and reported AOT validation support the changes.

0 open findings

🧠 Review effort: Balanced

@azchohfi Alexandre Zollinger Chohfi (azchohfi) added ready-for-review Agent work and technical checks complete; awaiting review, not approval or merge and removed agent-preparing Agent is addressing feedback or completing required validation and CI labels Oct 14, 2026
@azchohfi
Alexandre Zollinger Chohfi (azchohfi) merged commit 3584661 into main Oct 14, 2026
48 checks passed
@azchohfi
Alexandre Zollinger Chohfi (azchohfi) deleted the azchohfi-reactor-pri-packaging-layout branch October 14, 2026 23:05
@azchohfi Alexandre Zollinger Chohfi (azchohfi) removed the ready-for-review Agent work and technical checks complete; awaiting review, not approval or merge label Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Shipping Reactor.pri under lib/ triggers the Windows App SDK PRI-expansion step, which fails on long paths

2 participants