Skip to content

Work in progress: msix installer#732

Draft
kmahone wants to merge 24 commits into
mainfrom
user/kmahone/msix
Draft

Work in progress: msix installer#732
kmahone wants to merge 24 commits into
mainfrom
user/kmahone/msix

Conversation

@kmahone

@kmahone kmahone commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Not yet ready for review.

@clawsweeper

clawsweeper Bot commented Jun 9, 2026

Copy link
Copy Markdown

Codex review: found issues before merge. Reviewed June 10, 2026, 1:34 PM ET / 17:34 UTC.

Summary
The draft PR replaces the Inno/Updatum Windows distribution path with an MSIX/AppInstaller-based build, signing, update-feed, documentation, runtime, and test pipeline.

Reproducibility: yes. for the patch defects: exact current-head source shows the SSH-port and localization removals, invalid bootstrap install target, and disabled release publishing. The full installer migration itself still lacks real-device proof.

Review metrics: 3 noteworthy metrics.

  • Files changed: 60 files. The PR crosses runtime behavior, packaging, release automation, documentation, localization, and tests.
  • Diff size: 1,776 added, 3,621 removed. The branch removes the shipped installer and updater while introducing a new distribution system.
  • Temporary release controls: 2 temporary artifact uploads, 1 release step disabled. Current tagged builds are configured for a signing rehearsal rather than the production publishing path required by AppInstaller.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🧂 unranked krab
Result: blocked until real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Fix the four current-head findings and remove every temporary CI or release rehearsal bypass.
  • [P1] Add redacted Windows fresh-install, AppInstaller update, uninstall, and existing-user migration evidence.

Proof guidance:

  • [P1] Needs real behavior proof before merge: No after-fix real Windows proof shows a fresh MSIX install, AppInstaller update, uninstall, or migration from the shipped Inno/Updatum path; add redacted screenshots, recording, terminal output, logs, or linked artifacts, then update the PR body to trigger a fresh review or ask a maintainer to comment @clawsweeper re-review.

Risk before merge

  • [P1] Existing users configured for SSH on a non-default port would fail to establish the managed tunnel because their saved port is ignored.
  • [P2] Gateway host-access UI can display resource identifiers instead of localized prose because application localization delegates are no longer registered.
  • [P1] The advertised x64 and ARM64 installer links currently resolve to feeds referencing nonexistent v0.0.0 packages.
  • [P1] Tagged workflows cannot publish the release assets required by AppInstaller while the temporary release rehearsal configuration remains.
  • [P1] Replacing Inno/Updatum without fresh-install, update, uninstall, and existing-user migration proof could strand users or silently remove expected upgrade behavior.

Maintainer options:

  1. Finish and prove the MSIX migration (recommended)
    Fix the runtime regressions, restore production release publishing and CI gates, replace bootstrap links with a valid signed feed, and provide real fresh-install, update, uninstall, and existing-user migration evidence.
  2. Preserve the shipped distribution path
    Pause or close this branch and retain Inno/Updatum until a complete MSIX replacement can preserve existing user workflows and upgrades.

Next step before merge

  • [P2] The collaborator draft needs author fixes plus explicit maintainer approval of the installer/update migration and upgrade policy; automation cannot supply the required real Windows proof.

Security
Cleared: The signing workflow uses OIDC-based Azure authentication and scoped permissions; no concrete secret-handling or supply-chain regression was found beyond the functional release-automation blocker.

Review findings

  • [P1] Preserve the configured SSH tunnel port — src/OpenClaw.Tray.WinUI/App.xaml.cs:93-98
  • [P2] Restore GatewayHostAccess localization wiring — src/OpenClaw.Tray.WinUI/App.xaml.cs:212-214
  • [P1] Do not publish placeholder AppInstaller links — README.md:33-36
Review details

Best possible solution:

Keep the PR draft until it preserves unrelated tray behavior, restores every production CI and release gate, exposes AppInstaller links only after they reference real signed assets, and demonstrates redacted fresh-install, feed-update, uninstall, and existing Inno/Updatum migration behavior on Windows.

Do we have a high-confidence way to reproduce the issue?

Yes for the patch defects: exact current-head source shows the SSH-port and localization removals, invalid bootstrap install target, and disabled release publishing. The full installer migration itself still lacks real-device proof.

Is this the best way to solve the issue?

No. MSIX/AppInstaller may be an appropriate end state, but this branch is not yet a safe or focused implementation because it combines the migration with unrelated runtime regressions and temporary production controls.

Full review comments:

  • [P1] Preserve the configured SSH tunnel port — src/OpenClaw.Tray.WinUI/App.xaml.cs:93-98
    Pass _settings.SshTunnelSshPort to EnsureStarted as current main does. Otherwise users configured for SSH on a non-default port silently fall back to the service default and the tunnel cannot connect.
    Confidence: 0.99
  • [P2] Restore GatewayHostAccess localization wiring — src/OpenClaw.Tray.WinUI/App.xaml.cs:212-214
    Re-register GatewayHostAccessLocalization.GetString and .Format before InitializeComponent. Without these delegates, the classifier's identity fallback returns resource keys in the running app instead of localized user-facing text.
    Confidence: 0.98
  • [P1] Do not publish placeholder AppInstaller links — README.md:33-36
    These end-user links point at checked-in feeds whose MainPackage references the nonexistent v0.0.0 MSIX. Keep the existing installer links or withhold the new links until the feed targets real signed release assets, otherwise installation fails immediately.
    Confidence: 0.99
  • [P1] Restore production release publishing before merge — .github/workflows/ci.yml:562-583
    The workflow explicitly comments out softprops/action-gh-release and uploads signed MSIX files only as temporary workflow artifacts. Tagged releases therefore lack the assets required by the AppInstaller feed, so revert the rehearsal configuration and restore the production release step.
    Confidence: 0.99

Overall correctness: patch is incorrect
Overall confidence: 0.98

AGENTS.md: found and applied where relevant.

Codex review notes: reasoning high; reviewed against 0e61fa287afb.

Label changes

Label justifications:

  • P2: This is a significant but still draft Windows distribution improvement with limited current-user blast radius until merged.
  • merge-risk: 🚨 compatibility: Merging would replace the shipped installer/updater and currently regresses saved SSH configuration and localized UI behavior.
  • merge-risk: 🚨 automation: The new MSIX signing and feed automation becomes load-bearing while production release creation remains explicitly disabled.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🧂 unranked krab.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: No after-fix real Windows proof shows a fresh MSIX install, AppInstaller update, uninstall, or migration from the shipped Inno/Updatum path; add redacted screenshots, recording, terminal output, logs, or linked artifacts, then update the PR body to trigger a fresh review or ask a maintainer to comment @clawsweeper re-review.
Evidence reviewed

What I checked:

  • Configured SSH port is dropped: Current main passes SettingsManager.SshTunnelSshPort to tunnel startup, but the PR removes that argument, causing non-default SSH configurations to fall back to the service default. (src/OpenClaw.Tray.WinUI/App.xaml.cs:93, 9f195d68f293)
  • Localization initialization is removed: The PR removes registration of GatewayHostAccessLocalization.GetString and Format, leaving the identity fallback in the running app and exposing resource keys instead of localized text. (src/OpenClaw.Tray.WinUI/App.xaml.cs:212, 9f195d68f293)
  • Public installer links target bootstrap placeholders: README links users directly to AppInstaller files at version 0.0.0.0 whose MainPackage URI references a nonexistent v0.0.0 release asset, so the advertised install path fails. (README.md:35, 9f195d68f293)
  • Production release publishing is disabled: The workflow comments out the GitHub Release step and substitutes temporary signed workflow-artifact uploads, despite the AppInstaller feed depending on published MSIX release assets. (.github/workflows/ci.yml:562, 9f195d68f293)
  • SSH feature provenance: The configurable managed-tunnel SSH-port behavior dates to commit e0a4b7c on current main, confirming that its removal is an unrelated regression rather than obsolete code. (src/OpenClaw.Tray.WinUI/App.xaml.cs:99, e0a4b7c7c28d)
  • Real behavior proof is absent: The PR body remains “Not yet ready for review” and provides no after-fix fresh MSIX installation, AppInstaller update, or existing Inno/Updatum user migration evidence. (9f195d68f293)

Likely related people:

  • kmahone: The current branch contains the staged MSIX packaging, signing, AppInstaller feed, release, and versioning implementation. (role: feature owner; confidence: high; commits: 83d161335873, c0b9cc5f37d2, 2516bd389234; files: .github/workflows/ci.yml, .github/workflows/appinstaller-feed-pr.yml, src/OpenClaw.Tray.WinUI/OpenClaw.Tray.WinUI.csproj)
  • Christine Yan: Commit e0a4b7c added configurable SSH server ports for managed tunnels, including the current-main argument that this PR removes. (role: introduced behavior; confidence: high; commits: e0a4b7c7c28d; files: src/OpenClaw.Tray.WinUI/App.xaml.cs, src/OpenClaw.Tray.WinUI/Services/SshTunnelService.cs, src/OpenClaw.Tray.WinUI/Services/SettingsManager.cs)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. merge-risk: 🚨 automation 🚨 Merging this PR could break CI, automerge, proof capture, label sync, or automation. labels Jun 9, 2026
@kmahone kmahone force-pushed the user/kmahone/msix branch from 3559f81 to 4b0d617 Compare June 9, 2026 18:55
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 9, 2026
Keith Mahoney and others added 17 commits June 10, 2026 09:51
Master shipped sizes 24/32/48/256; Windows down-scaled larger PNGs for
Start Menu / taskbar / Alt+Tab surfaces that prefer 16/20/44. These three
PNGs fill the gap and are auto-discovered by MakePri via the existing
Assets\**\* content include and the targetsize-NN_altform-unplated
filename convention. No code, csproj, or manifest changes required.

Cherry-picked from origin/pr-468 (Square44x44Logo.targetsize-{16,20,44}_altform-unplated.png).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the conditional dispatch that produced an unpackaged
WindowsPackageType=None + app.manifest build for the Inno installer
path. Default packaging mode is now unconditionally MSIX; only the
PackageMsix=true opt-in for .msix file production remains.

Changes:
- Make <WindowsPackageType>MSIX</WindowsPackageType> +
  <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
  unconditional.
- Delete the <PropertyGroup Condition=\'$(Unpackaged)' == 'true'\>
  block (WindowsPackageType=None + ApplicationManifest=app.manifest).
- Delete the CopyWebView2Loader target (only needed for the unpackaged
  layout; MSIX bundles the loader automatically). The unrelated
  CopyWebView2Loader target in OpenClaw.SetupPreview is untouched.
- Rewrite the explanatory comment block to drop the Unpackaged mode
  paragraph (now describes one opt-in flag instead of two).

src/OpenClaw.Tray.WinUI/app.manifest is now orphaned but left in place;
Phase 3 will delete it together with the Inno installer.

Callers that still pass -p:Unpackaged=true (scripts/build-inno-local.ps1
and the Inno publish job in .github/workflows/ci.yml) are transiently
non-functional between Phase 2 and Phase 3 — the property becomes a
no-op so the build emits an MSIX layout that Inno cannot consume. Both
callers are deleted in Phase 3, per the all-phases-land-together branch
workflow.

Audit of App.xaml.cs:355-405 _isPostSetupRestart retry: KEEP. The
branch is not Inno-specific. The tray itself spawns a fresh tray with
--post-setup-restart --wait-for-pid <oldPid> after in-process
SetupWindow completes (RestartAfterSetupAsync, line 3097). The 15s
retry + AbandonedMutexException handler prevents the new tray from
giving up on the single-instance mutex while the old tray is still
exiting. AppRefactorContractTests:135-140 enforces the call pattern.

Validation (per AGENTS.md):
- build.ps1: all 5 projects built.
- Shared.Tests: 2049 passed, 29 skipped (env-only), 0 failed.
- Tray.Tests: 958 passed, 0 skipped, 0 failed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Delete Inno Setup distribution path now that MSIX is the always-on package
type. Phase 3B will follow up with Updatum removal; Phase 4 will rebuild
the release pipeline around the .msix file produced by `-p:PackageMsix=true`.

Deleted files (5 source + 2 test):
- installer.iss (256 lines)
- scripts/build-inno-local.ps1
- scripts/Uninstall-LocalGateway.ps1
- src/OpenClaw.Tray.WinUI/app.manifest (orphaned by Phase 2)
- tests/PackagingTests/Test-InnoUninstallOrdering.ps1 (parent dir removed)
- tests/OpenClaw.Tray.Tests/InstallerIssAssertionTests.cs (9 tests)
- tests/OpenClaw.Tray.Tests/ReleaseSigningWorkflowTests.cs (4 tests; Phase 5
  will replace with MSIX-signing assertions)

ci.yml: drop -p:Unpackaged=true from build/publish (Phase 2 cleanup), retire
the Download VC Redist + Install Inno Setup + Build x64/arm64 Installer +
Sign Installers steps in the release job, remove .exe entries from the
release files: list and body, and update the paused build-msix comment.

App.xaml.cs: remove the AppMutex coordination comment that referenced
installer.iss; the OpenClawTray mutex itself stays unchanged.

SetupEngine.UI/LogFileLauncher.cs: rewrite the "Unpackaged process" comment
to cover both the no-package-identity and library-only call sites.

Ship-guard ported from installer.iss to MSBuild: two new targets on
OpenClaw.Tray.WinUI.csproj (ValidateSetupEngineUiNotShipped after Build,
ValidateSetupEngineUiNotPublished after Publish) fail the build if
OpenClaw.SetupEngine.UI.exe lands in the tray bin/publish output. Pairs
with a new "Hazards" section in docs/SETUP_ENGINE_REDESIGN.md explaining
the in-process design and pointing at PR #468's bootstrapper diffs as the
worked example anyone splitting SetupEngine into its own process would
need to study first.

Test-ReleaseNativeDependencies.ps1: remove -RequireInstallerVCRedist /
-InstallerVCRedistPath params and their dead handler (only Inno called
them); -RequireAppLocalVCRuntime and -SkipNativeLoadProbe stay (still used
by ci.yml's Verify Native Runtime Payload steps).

Docs: drop the Inno helper section + .exe-installer references from
DEVELOPMENT.md, docs/RELEASING.md, docs/VERSIONING.md, and the Inno
comment in scripts/validate-msix-storage-paths.ps1. README.md + docs/SETUP.md
download tables are replaced with TODO placeholders that Phase 7 will fill
with MSIX-flavored content.

Validation (on user/kmahone/msix, Windows):
- ./build.ps1 green
- Shared.Tests: 2049 passed / 29 skipped (unchanged from Phase 2)
- Tray.Tests: 945 passed (was 958; -13 = 9 InstallerIssAssertion + 4
  ReleaseSigningWorkflow tests removed, as expected)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Updatum becomes dead under MSIX-primary publishing once <AutomaticBackgroundTask />
in the .appinstaller XML (Phase 4) is the update mechanism. No in-app updater
under MSIX, no manual "Check for updates" button.

Removed:
- Updatum NuGet PackageReference from OpenClaw.Tray.WinUI.csproj
- App.xaml.cs: using Updatum, AppUpdater static field, BuildInitialUpdateInfo
  call, startup update-check gate, IAppCommands.CheckForUpdates impl, dispatcher
  case "checkupdates", HandleDeepLink wiring, and the entire #region Updates
  block (~460 lines).
- IAppCommands.CheckForUpdates declaration.
- DeepLinkHandler.cs: case "updates"/"update"/"check-updates"/"update-check"
  block + CheckForUpdates Func field.
- AboutPage.xaml CheckUpdatesButton + AboutPage.xaml.cs OnCheckUpdatesClick.
- HubWindow.xaml.cs orphan CheckForUpdatesAction property.
- SettingsData.SkippedUpdateTag + 4 SettingsManager references.
- Dialogs/UpdateDialog.cs (deleted file).
- Dialogs/DownloadProgressDialog.cs (deleted file).
- Update*, CheckUpdates*, DownloadProgress* resource keys from all 5 locale
  resw files (en-us, zh-tw, zh-cn, nl-nl, fr-fr; 18 keys per file).
- ci.yml: Updatum auto-update ZIP comment + x64/arm64 "Create Release ZIP"
  steps + ZIP entries from release files: + Portable bullets from release
  body. Release will have no binary artifacts until Phase 4 MSIX pipeline.
- Test fixtures: SettingsRoundTrip (4 SkippedUpdateTag refs), DeepLinkParser
  (2 InlineData rows + fixture init), TrayMenuWindowMarkup (2 Assert.Contains),
  AppRefactorContract (CheckForUpdatesAsync from AssertInOrder),
  LocalizationValidation (WindowTitle_Update + Update_OK invariants).
- Docs: README.md + docs/SETUP.md (openclaw://check-updates row);
  docs/RELEASING.md (Portable ZIP Updatum block);
  docs/VERSIONING.md (Updatum Library reference).

Kept:
- UpdateCommandCenterInfo DTO in OpenClaw.Shared/Models.cs - public protocol
  type sent to external agent clients; default-initialized (Status="Unknown",
  CurrentVersion=null). AppStateSnapshot.LastUpdateInfo wiring stays.

Validation:
- ./build.ps1 green
- Shared.Tests: 2049 passed, 29 skipped (unchanged)
- Tray.Tests: 943 passed (was 945; -2 = DeepLinkParserTests InlineData rows)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Symptom (caught while smoke-testing the MSIX-primary build): asking the
agent to show a Windows notification throws

  InvalidOperationException: Your app manifest must have a
  toastNotificationActivation extension with a valid
  ToastActivatorCLSID specified.
  at ToastNotificationManagerCompat.CreateToastNotifier()
  at ToastContentBuilder.Show()
  at ToastService.ShowToast(...)

Root cause: Microsoft.Toolkit.Uwp.Notifications (which we still use for
all toasts via App.OnToastActivated + interactive AddButton flows) has
two code paths -- an unpackaged shortcut/COM-self-registration path and
a packaged path that reads ToastActivatorCLSID from the appx manifest.
Phase 2 of the MSIX-primary branch dropped -p:Unpackaged=true, so we now
always hit the packaged path. With no manifest extension declared, the
toolkit cannot find a CLSID and throws on first ShowToast call.

Fix: declare a stable CLSID for the toast activator in the manifest:

- Add desktop:Extension Category="windows.toastNotificationActivation"
  with ToastActivatorCLSID="EF9297B3-EEEB-4E50-8306-D1D118E04BC7".
- Add the matching com:Extension/com:ComServer/com:ExeServer entry so
  the COM server is wired to OpenClaw.Tray.WinUI.exe with the
  conventional -ToastActivated arg.
- Declare the desktop + com namespaces and include them in
  IgnorableNamespaces.

The CommunityToolkit generates the actual activator type at runtime and
binds it to this CLSID; no extra C# is needed. App.ToastActivation.cs
already wires ToastNotificationManagerCompat.OnActivated to
OnToastActivated and parses arguments via ToastArguments.Parse, so the
button-click roundtrip works as soon as the CLSID is reachable.

Validation:
- ./build.ps1 green (manifest passes MakeAppx schema check).
- Tray.Tests still pass (no source changes; manifest-only edit).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously the precheck only warned and the post-build summary suggested `Add-AppxPackage -AllowUnsigned`. That command does not work for this MSIX package on stock Windows (AllowUnsigned only applies to a narrow set of developer-mode scenarios), so users following the suggestion would fail at install time.

Now: missing cert => Write-Error + exit 1 in the preflight, with a clear message pointing at scripts\setup-dev-msix-cert.ps1. The post-build install hint always shows the signed Add-AppxPackage form (the unsigned branch is now unreachable). Docstring updated.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… workflow

Adopts the publishing-infrastructure pattern from PR #468 (treated as a
reference design only, not cherry-picked) and adapts it to our
WindowsAppSDKSelfContained=true MSIX. Because the WindowsAppRuntime is
bundled inside the .msix, the AppInstaller feed has no <Dependencies>
block, no runtime-URI rendering, and no separate runtime MSIX release
asset.

What this commit adds
- installer/openclaw-companion.appinstaller.template — 6 placeholders
  (VERSION, PUBLISHER, IDENTITY_NAME, PROCESSOR_ARCHITECTURE, MSIX_URI,
  APPINSTALLER_URI), AutomaticBackgroundTask-only UpdateSettings.
- installer/appinstaller/openclaw-{x64,arm64}.appinstaller — bootstrap
  feed files at version 0.0.0.0; the appinstaller-feed-pr workflow
  rewrites these on each stable release tag.
- installer/appinstaller/README.md — explains the stable-feed model.
- scripts/render-appinstaller.ps1 — substitutes placeholders, asserts
  the rendered XML parses and contains no <Dependencies> block.
- scripts/validate-appinstaller-hosting.ps1 — Content-Type / Content-
  Length / Range checks against the hosted .appinstaller and .msix URLs,
  with -AllowGitHubContentTypes for raw.githubusercontent.com.
- scripts/test-appinstaller-update.ps1 — local HttpListener-backed
  vN -> vN+1 upgrade smoke using PackageManager
  .AddPackageByAppInstallerFileAsync.
- .github/workflows/appinstaller-feed-pr.yml — workflow_dispatch input
  takes a release tag, renders the two feed files, validates them, and
  opens a PR to advance the stable feed. Uses OpenClaw Foundation
  publisher; rejects pre-release tags; here-string PR body uses
  Set-Content -Value so the body renders as Markdown (fixes the
  8-space-indent code-block bug in PR #468's workflow).
- tests/OpenClaw.Tray.Tests/AppInstallerTemplateAssertionTests.cs — 8
  test methods covering template shape, the new
  Template_HasNoDependenciesBlock invariant, the two bootstrap feed
  files, the validation script, the smoke script, and the feed-update
  workflow.
- README.md — replaces the Phase-7 TODO placeholder with x64 and ARM64
  Install links pointing at the raw GitHub .appinstaller URLs.

What this commit does NOT add
- No in-app "Check for updates" button or AppInstallerUpdateService.
  Windows AppInstaller's AutomaticBackgroundTask handles all polling
  at OS level under MSIX.
- No Microsoft.WindowsAppRuntime.2 release asset or feed dependency —
  the runtime is bundled (WindowsAppSDKSelfContained=true).

Validation
- ./build.ps1: green.
- Shared.Tests: 2049 passed / 29 skipped (matches baseline).
- Tray.Tests: 957 passed (was 943; +14 effective test cases from the
  new file).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Phase 4 appinstaller feed workflow expected release assets named

'OpenClawCompanion-<3partVersion>-win-<arch>.msix', which would have required a

rename step in the Phase 5 release job (Option A). Switching to Option B:

match whatever MSBuild emits by default for GenerateAppxPackageOnBuild=true,

i.e. 'OpenClaw.Companion_<4partVersion>_<arch>.msix'.

* appinstaller-feed-pr.yml: Get-RequiredAsset patterns updated to use the

  4-part $version (not the 3-part $versionText), dotted identity, and

  underscore separators. Uses ${version} to keep _ from being absorbed

  into the variable name.

* AppInstallerTemplateAssertionTests: matching Assert.Contains /

  Assert.DoesNotContain updates so the pin reflects the new convention.

Inside the rendered .appinstaller, <MainPackage Uri> uses the actual

GitHub release asset name, so AppInstaller behavior is unaffected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Unpause the build-msix matrix job and wire its outputs into the release
job so that tagged builds produce signed .msix artifacts attached to the
GitHub Release.

- ci.yml build-msix: drop `if: false` paused gate and
  `continue-on-error: true` placeholder; the job is now load-bearing.
- ci.yml release: add `build-msix` to `needs:` plus matching
  `needs.build-msix.result == 'success'` guard.
- Download the per-arch `openclaw-msix-win-{x64,arm64}` artifacts.
- Sign each .msix in place using `azure/artifact-signing-action@v2`
  with `files-folder-filter: msix` (mirrors the existing exe signing
  pattern: same endpoint, signing account, certificate profile, OIDC
  auth via azure/login).
- Attach both signed .msix files to the release via `files:` and
  rewrite the release body so it points users at the AppInstaller links
  in the README (primary install path) and notes the .msix assets as a
  direct-install fallback.

Validation:
- ./build.ps1 green
- Shared.Tests: 2049 passed / 29 skipped (baseline)
- Tray.Tests: 957 passed (baseline)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
After Phase 3A removed Inno and Phase 5 wired the MSIX into the release,
the `build` job's loose unpackaged-tray artifact ceased to be shipped
to any user. The release job was still downloading it, signing the
loose exe, and running signature/native-dep validation against bits
that were then discarded. This commit removes that dead infrastructure
and preserves the most valuable validation against what we actually
ship.

ci.yml changes:
- Delete the `build` job in its entirety (loose `dotnet publish`,
  Test-ReleaseNativeDependencies on publish/, GitVersion verify,
  Upload Tray Artifact -> openclaw-tray-{rid}).
- Drop `build` from `release.needs:` and from the corresponding
  `needs.build.result == 'success'` guard.
- In `release`, remove: 2 tray-artifact download steps, 2 stage exe
  for signing steps, 2 sign-loose-exe steps, and 4 verify steps
  (Test-ReleaseExecutableSignatures and Test-ReleaseNativeDependencies
  against artifacts/tray-win-*).
- In `build-msix`, add a new `Verify MSIX Package Contents` step
  that runs immediately after the .msix is produced. It extracts the
  .msix (a zip), confirms `OpenClaw.Tray.WinUI.dll` is present with
  the correct GitVersion ProductVersion, and runs the existing
  Test-ReleaseNativeDependencies.ps1 against the extracted payload so
  the libsodium / VC++ runtime presence canary continues to fire --
  but now against the actual shipped MSIX bits, not a phantom
  unpackaged build.

Net diff: -108 lines from ci.yml; pipeline shape simplified from
six jobs to five (release no longer depends on a parallel build job
whose output it never used).

Validation:
- ./build.ps1 green
- Shared.Tests: 2049 passed / 29 skipped (baseline)
- Tray.Tests: 957 passed (baseline)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Phase 4 AppInstaller infrastructure was authored against master but the default branch was renamed to main (commit 37b0ea6). The stale refs would cause silent failure: AppInstaller polling would forever fetch frozen XML from the now-inactive origin/master branch, and the feed-update PR workflow would target a branch that doesn't accept commits.

Fixed:

- installer/appinstaller/openclaw-{x64,arm64}.appinstaller: <AppInstaller Uri> -> main

- installer/appinstaller/README.md: hosting URLs + prose -> main

- README.md: end-user install links -> main

- .github/workflows/appinstaller-feed-pr.yml: ref/raw URL/PR --base -> main

- scripts/render-appinstaller.ps1: docstring URLs -> main

- scripts/validate-appinstaller-hosting.ps1: docstring -> main

- tests/OpenClaw.Tray.Tests/AppInstallerTemplateAssertionTests.cs: assertions tracking the above

Also swept pre-existing master refs that the rename PR (37b0ea6) missed:

- docs/RELEASING.md, docs/VERSIONING.md: prerelease prose

- tests/.../LocalizationValidationTests.cs: comment

- src/.../ConnectionPage.xaml.cs: comment

Left alone (per main's rename PR intent): dual [main, master] CI

triggers, GitVersion ^(master|main)$ regex, third-party URLs, and all

'Master switch/toggle/control' UX terminology (unrelated to git branch).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
build-msix invokes VS MSBuild without first running GitVersion, so the GitVersion.MsBuild package can't compute version metadata and the WinUI .dll inside the MSIX falls back to AssemblyVersion 1.0.0. The Verify MSIX Package Contents step then correctly fails:

  MSIX-internal ProductVersion '1.0.0' did not match GitVersion SemVer '0.6.4-PullRequest732.54'.

Mirror the gitversion/setup + gitversion/execute steps from the test job before Build MSIX Package. The execute action exports GitVersion_* environment variables that GitVersion.MsBuild picks up to inject Assembly/File/InformationalVersion, satisfying the verify assertion.

fetch-depth: 0 is already set on the checkout, so GitVersion can read git history. The existing manifest-patch step (which sets Appx package identity version) is unaffected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…t PR iteration

build-msix only consumed needs.test.outputs.{semVer,majorMinorPatch}, which are now produced locally by the gitversion/execute step inside the job. e2etests was a pure gate (no outputs).

Swap manifest-patch + verify references from needs.test.outputs.* to steps.gitversion.outputs.*, then drop the needs:[] list so build-msix starts in parallel with test/e2etests on every push.

REVERT before merge — release job still gates on all four (line 491), so production releases are unaffected by this temporary change; the only effect is faster PR feedback. Restore 'needs: [test, e2etests]' on build-msix and switch the two version refs back to needs.test.outputs.*.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…X payload

Test-ReleaseNativeDependencies.ps1 -RequireAppLocalVCRuntime fails after the MSIX-payload extract because vcruntime140.dll is missing next to libsodium.dll:
  Missing app-local vcruntime140.dll next to libsodium.dll.

Root cause: the CopyOpenClawVCRuntime* targets in src/Directory.Build.targets only ship a current VS-resolved runtime via CopyOpenClawVCRuntimeToPublish (AfterTargets=Publish). The pre-fix CI step ran 'msbuild /t:Build', which only triggers CopyOpenClawVCRuntimeToOutput - x64-only AND sourced from the stale 14.29 NuGet that the comment explicitly warns is too old for onnxruntime >= 1.20. Worse, the WinUI single-project AppX packaging task collects payload from publish output, not from the build TargetDir, so even the stale copies wouldn't end up inside the MSIX.

Switching to 'dotnet publish ... -p:PackageMsix=true' matches what build.ps1 does locally and what produces working MSIX packages on dev machines. publish triggers CopyOpenClawVCRuntimeToPublish (VS-install resolution -> current 14.40+ DLLs) and ValidateOpenClawVCRuntimePublished, which guarantees vcruntime140.dll lands in PublishDir before the AppX packager collects payload.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keith Mahoney and others added 2 commits June 10, 2026 09:53
…ds in the MSIX

Even after switching CI to 'dotnet publish ... -p:PackageMsix=true', the verify
step still failed with:

  Missing app-local vcruntime140.dll next to libsodium.dll.

Reproduced locally: publish dir had all VS-resolved VC++ runtime DLLs, but only
libsodium.dll and vcruntime140_cor3.dll (the .NET self-contained one) made it
into the .msix payload.

Root cause: the WinUI single-project AppX packaging target collects payload
from @(PackagingOutputs), which for publish builds is populated from
PublishItemsOutputGroupOutput - i.e. the .NET SDK's ResolvedFileToPublish
collection - NOT from arbitrary files sitting in PublishDir. The old
CopyOpenClawVCRuntimeToPublish target ran AfterTargets='Publish' and copied
files into PublishDir, which was enough for the loose-tray Inno pipeline (now
retired) but invisible to the AppX packager.

Fix: add AddOpenClawVCRuntimeToPublishItems target that runs
BeforeTargets='ComputeFilesToPublish' and registers the resolved VC++ runtime
files as ResolvedFileToPublish items. The .NET SDK publish flow then:
  1. Copies them to PublishDir naturally (still covers any loose-publish use).
  2. Emits them in PublishItemsOutputGroupOutput.
  3. -> PackagingOutputs.
  4. -> Inside the .msix.

Also narrow VC Redist version selection. A VS install can carry side-by-side
versions (e.g. 14.42 / 14.44 / 14.51). The previous glob
'MSVC\*\<arch>\Microsoft.VC*.CRT\vcruntime140*.dll' matched every version,
which the .NET SDK rejects under publish with NETSDK1152 (duplicate publish
output files with the same relative path). Now we resolve the single highest
version directory once via vswhere + a powershell sort, then glob just that
directory.

Kept CopyOpenClawVCRuntimeToPublish and ValidateOpenClawVCRuntimePublished as
defensive safety nets that fire after publish.

Validated:
- Local 'dotnet publish ... -p:PackageMsix=true' for win-x64 produced an .msix
  containing vcruntime140.dll v14.51.36231.0 (well above the 14.38 floor).
- 'Test-ReleaseNativeDependencies.ps1 -RequireAppLocalVCRuntime' against the
  extracted MSIX payload now reports 'Release native dependency policy passed.'
- ./build.ps1 succeeded.
- Shared tests: 2130 passed / 29 skipped.
- Tray tests: 975 passed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…1.0.0

Every .msix produced by 'build.ps1 -PackageMsix' and the CI build-msix job shipped tagged as 1.0.0.0 (Identity/@Version) regardless of the real release version, even though the assembly's AssemblyInformationalVersion was correct.

Two cooperating bugs in the GitVersion -> MSIX chain:

1. GitVersion.MsBuild only updates the MSBuild Version property when the opt-in <UpdateVersionProperties>true</UpdateVersionProperties> is set (GitVersion.MsBuild.targets line 72). The repo never opted in, so Version stayed at the .NET SDK default '1.0.0'.

2. GitVersion's GetVersion target uses BeforeTargets=GitVersionTargetsBefore, which is empty in this repo. That means GetVersion never runs as an implicit side effect of any standard target - including BeforeBuild. SyncAppxManifestVersionTarget was hooked to BeforeBuild and read Version, so even if (1) were fixed, it would still read '1.0.0' because GetVersion had not run yet.

(Some downstream target chain does eventually pull GetVersion in - that is why the produced DLL's AssemblyInformationalVersion matched GitVersion. But by then the manifest is already patched and packaged.)

Fix:
- Set UpdateVersionProperties=true in src/Directory.Build.props so GetVersion writes Version = GitVersion_SemVer.
- Add DependsOnTargets=GetVersion to SyncAppxManifestVersionTarget so GetVersion runs first and Version is populated before the manifest is patched.

After the fix, local dotnet publish -p:PackageMsix=true produces OpenClaw.Tray.WinUI_0.6.4.0_x64.msix with Identity/@Version=0.6.4.0 and the DLL's ProductVersion matching GitVersion_SemVer exactly, which is what the build-msix CI verify step asserts.

Validated:
- ./build.ps1 succeeded.
- Shared tests: 2130 passed / 29 skipped.
- Tray tests: 975 passed.
- Local MSIX build (-p:PackageMsix=true) produced 0.6.4.0 file + manifest.
- Test-ReleaseNativeDependencies.ps1 -RequireAppLocalVCRuntime against the extracted MSIX payload still reports 'Release native dependency policy passed.'

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kmahone kmahone force-pushed the user/kmahone/msix branch from eb28c9b to 2516bd3 Compare June 10, 2026 16:55
Keith Mahoney and others added 2 commits June 10, 2026 10:19
… artifact instead

Lets us validate the Azure Trusted Signing leg of the release job on a throwaway alpha tag without publishing a public GitHub Release. The Create Release step (softprops/action-gh-release@v3) is left commented in place so the revert is a trivial uncomment+delete of the two new upload-artifact steps.

Signal flow during the test:
  push tag v0.6.4-alpha.testN -> repo-hygiene / test / e2etests / build-msix
  -> release job: download unsigned MSIX artifacts, azure/login, sign with
     azure/artifact-signing-action@v2, upload signed MSIX as
     openclaw-msix-signed-win-{x64,arm64} artifacts.

Must be reverted before merging to main. Tracked in session todo
revert-release-create-temp.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…hearsal tags

The Verify tag version output step asserts GitVersion's computed SemVer exactly matches the tag minus its 'v' prefix. GitVersion only honors a tag as an exact version source when the tag's prerelease label is compatible with the branch's auto-derived label. On user/kmahone/msix the branch label is user-kmahone-msix, so v0.6.4-msixtest.1 is silently ignored and GitVersion falls back to 0.6.4-user-kmahone-msix.1.

For a signing-pipeline rehearsal we do not care about strict SemVer/tag agreement - the MSIX-internal ProductVersion comes from GitVersion regardless, and the msixtest token is only there to make the tag/artifact name self-describing.

Skip the verify step when the tag name contains 'msixtest'. Tracked in session todo revert-verify-tag-msixtest-skip; restore the unconditional gate before merging.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kmahone kmahone temporarily deployed to release-signing June 10, 2026 17:40 — with GitHub Actions Inactive
Lets us validate the end-to-end MSIX path on a feature branch without disturbing the production AppInstaller feed:
  push tag vX.Y.Z-msixtest.N
  -> CI builds signs and publishes a prerelease GitHub Release
  -> branch-only openclaw-msixtest-x64.appinstaller feed points at it
  -> ms-appinstaller: link installs, raw.githubusercontent.com feed bump
     triggers silent auto-update on subsequent Add-AppxPackage poll.

Changes:

1. ci.yml build-msix Patch MSIX manifest metadata step: when the tag matches
   ^v\d+\.\d+\.\d+-msixtest\.(\d+)$, set Identity/@Version=X.Y.Z.N (using the
   tag's prerelease counter as the 4th part). Without this, successive
   msixtest tags would all map to X.Y.Z.0 and AppInstaller would no-op the
   second install.

2. ci.yml build-msix Build MSIX Package step: pass -p:Version=X.Y.Z.N when
   the override is active so SyncAppxManifestVersionTarget does not clobber
   the manifest version back to GitVersion's SemVer-derived .0 default.

3. ci.yml release job: for msixtest tags, publish a real (prerelease,
   not-latest) GitHub Release so AppInstaller can fetch the signed .msix from
   a stable HTTPS URL (workflow artifacts require auth and cannot be used).
   Non-msixtest tags still skip Release creation (TEMP carryover); the
   original Create Release step is left commented in place for a clean
   revert.

4. New installer/appinstaller/openclaw-msixtest-x64.appinstaller. Branch-only
   test feed, pinned to v0.6.4-msixtest.3 initially. Self-Uri points at the
   raw.githubusercontent.com URL on user/kmahone/msix. OnLaunch is set to
   HoursBetweenUpdateChecks=0 UpdateBlocksActivation=true ShowPrompt=true
   to make the rehearsal visible and synchronous; the production feed will
   keep ShowPrompt=false.

All TEMP edits and the new feed file must be reverted before merging to
main. Tracked in session todos revert-msixtest-version-override,
revert-msixtest-prerelease-create, and delete-msixtest-appinstaller-file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@kmahone kmahone deployed to release-signing June 10, 2026 18:42 — with GitHub Actions Active
Keith Mahoney and others added 2 commits June 10, 2026 11:55
… 4th-part override

The previous attempt to encode the rehearsal counter in the MSIX Identity's 4th version part (X.Y.Z.N) did not produce the expected MSIX version in CI - run for v0.6.4-msixtest.3 still emitted OpenClaw.Tray.WinUI_0.6.4.0_x64.msix instead of _0.6.4.3_. Rather than debug that path, switch to bumping the SemVer patch part for the rehearsal:
  v0.6.4-msixtest.3 -> MSIX 0.6.4.0  (install rehearsal)
  v0.6.5-msixtest.1 -> MSIX 0.6.5.0  (update rehearsal)

This uses the existing majorMinorPatch + .0 logic in the Patch MSIX manifest metadata step, no overrides needed. The msixtest counter is now only there to differentiate rehearsal runs of the same 3-part SemVer.

Changes:
- ci.yml: drop TEMP msixtest 4th-part override in Patch MSIX manifest metadata step, drop the id: patch-manifest output, drop the conditional -p:Version= in Build MSIX Package step. revert-msixtest-version-override todo is now obsolete (marked done in session SQL).
- openclaw-msixtest-x64.appinstaller: re-pin Version and MainPackage Version to 0.6.4.0, MainPackage Uri to the existing v0.6.4-msixtest.3 release asset OpenClaw.Tray.WinUI_0.6.4.0_x64.msix. Header comment updated with the new bump procedure.

Reuses the existing v0.6.4-msixtest.3 release as the install-rehearsal source (it is already signed 0.6.4.0). Next push will be v0.6.5-msixtest.1 for the update rehearsal.

Still TEMP-tracked for revert before merge:
  revert-msix-needs-temp (build-msix needs:[])
  revert-verify-tag-msixtest-skip (Verify tag version output skip)
  revert-msixtest-prerelease-create (release-job msixtest-only prerelease)
  delete-msixtest-appinstaller-file

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…a is invalid" on install

The 2018 schema can choke on validators in newer Windows AppInstaller builds. Switching to the 2021 namespace makes Windows use the newer parser, which accepts the same element set we already use (MainPackage, OnLaunch, AutomaticBackgroundTask).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 automation 🚨 Merging this PR could break CI, automerge, proof capture, label sync, or automation. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P2 Normal priority bug or improvement with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant