Skip to content

feat: register a local development build without building an MSIX - #41

Merged
paulcam206 merged 1 commit into
mainfrom
paulcam206-deploy-local-package
Sep 15, 2026
Merged

paulcam206 merged 1 commit into
mainfrom
paulcam206-deploy-local-package

Conversation

@paulcam206

@paulcam206 paulcam206 commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

What Problem This Solves

Running a local build of the gateway requires composing, signing, and installing an MSIX, so a contributor cannot get a runnable openclaw.exe from a clean checkout in one step.

User Impact

Contributors can run .\scripts\Deploy-LocalPackage.ps1 to go from a clean checkout to a registered, runnable OpenClaw.Gateway in about a minute, and re-run it after an edit in seconds. There is no end-user behavior change: the launcher, packaged application, MSIX composition, signing policy, and release workflow are untouched.

Two things contributors need to know:

  • It requires Developer Mode, and it cannot coexist with an MSIX-installed OpenClaw.Gateway. Windows refuses to replace a packaged install with a local layout and cannot preserve that package's app data across the switch, so the script stops and explains rather than removing anything implicitly. -ReplaceExistingInstall accepts that trade. Returning to a released package requires running -Unregister first: Windows will not replace a loose registration with a packaged install either, regardless of version.
  • The registered package reads its files from the repository. Deleting artifacts\local-package or removing the checkout breaks the registration until the command runs again; run -Unregister first if you plan to delete the checkout.

Why This Change Was Made

Windows already has a primitive for this. With Developer Mode, Add-AppxPackage -Register takes a manifest and a directory and registers a real package with real identity — no package file and no signature — so the compress, sign, and stage steps are not needed to run local code.

The script acquires the payload and the matching Node.js runtime, publishes the NativeAOT launcher, assembles a layout, registers it, and runs clawctl setup so openclaw works immediately. It is idempotent: the up-to-date check verifies the live layout rather than trusting that a previous run wrote one, and records completion only after setup succeeds. The expanded application is linked into the layout instead of copied, and the payload cache commits its selection only after the whole deployment succeeds, so a failed refresh leaves the previously working payload selected.

Nothing in the packaging path changes. The layout is assembled from the published launcher and the source manifest rather than from packaging content, so Build-LocalMSIX.ps1, Build-MSIX.ps1, Sign-TestMSIX.ps1, the launcher project, and the build properties are untouched. The change is purely additive.

Evidence

All evidence below is from head ab940113341f4eaed184fbab5dc17848dbaa9199, on Windows 11 x64 with Developer Mode enabled.

Scenario tests

.\scripts\Test-Deploy-LocalPackage.Tests.ps1
exit=0  Local package deployment scenarios passed.

They inject every GitHub, publish, registration, and deployment operation, so no test registers, removes, or modifies a real package. Covered: clean checkout, idempotent re-runs, a launcher change reaching the layout, each individually damaged layout input, payload refresh and its failure recovery, skipped and failed setup, the conflicting and foreign-install paths with and without consent, and the failures that must stop before registering.

Clean checkout to runnable — cold cache

[Resolve payload]            Payload: downloading run 34906920514   51.81s
[Resolve Node.js runtime]    downloading node-v24.20.0-win-x64.zip   0.54s
[Build launcher (NativeAOT)]                                         8.33s
[Assemble layout]                                                    0.13s
[Register package]                                                   1.31s
[Prepare bundled Node.js runtime]                                    0.48s
Registered: OpenClaw.Gateway_0.1.2448.5371_x64__kaa03rpbbqef6
Total 64.11s.

Idempotency — two consecutive runs, nothing changed

run 1 : 10.4s -> Already up to date: OpenClaw.Gateway_0.1.2448.5407_x64__kaa03rpbbqef6
run 2 :  8.1s -> Already up to date: OpenClaw.Gateway_0.1.2448.5407_x64__kaa03rpbbqef6

The remaining time is dotnet publish deciding it has nothing to do; registration is skipped entirely.

Registered package actually runs

Name              : OpenClaw.Gateway
Version           : 0.1.2448.5407
Architecture      : X64
IsDevelopmentMode : True
Status            : Ok

openclaw --version   ->  OpenClaw 2026.8.2 (0965053)
clawctl --version    ->  1.0.0.0

OpenClaw 2026.8.2 (0965053) matches the payload's recorded packageVersion and resolvedCommit, confirming the linked application tree is what the package actually serves.

Defect-injection checks for the two highest-severity review findings

Tests written alongside a fix can pass for the wrong reason, so each was run against the defect restored:

  • Recording completion regardless of setup → A run after -SkipSetup did not complete the setup it skipped.
  • Committing the payload cache selection before deployment succeeds → A failed deployment left the unusable payload selected.

Both fail against the defect and pass against the fix.

A registration whose layout was deleted underneath it was also verified by hand: the package still reports Status: Ok while both aliases fail with The process has no package identity, and re-registering does not repair it. Registration now removes the prior development registration first, preserving app data, and recovery was confirmed by deleting the live layout and re-running.

Round trip against the real signed release

Using OpenClawGateway-0.0.0.0-x64.msix from release v0.0.0.0. Installing the release over a local registration is refused:

Add-AppxPackage -Path .\OpenClawGateway-0.0.0.0-x64.msix
Deployment failed with HRESULT: 0x80073CFB
The current user has already installed an unpackaged version of this app.
A packaged version cannot replace this.

The cause is the packaged/unpackaged mutual exclusion in the other direction, not version ordering. After -Unregister the release installs and runs:

Unregistered OpenClaw.Gateway_0.1.2449.37191_x64__kaa03rpbbqef6; its app data was preserved.
release install: OK in 70.4s
Version: 0.0.0.0   IsDevelopmentMode: False
clawctl --version  -> 0.0.0.0
openclaw --version -> OpenClaw 2026.8.2 (0965053)

Returning to the local build is refused without consent, then succeeds with it:

FAILED during Check current registration: OpenClaw.Gateway is already installed
from a package (version 0.0.0.0). ... Re-run with -ReplaceExistingInstall ...

WARNING: Removing the packaged OpenClaw.Gateway 0.0.0.0; its app data cannot be preserved.
Registered: OpenClaw.Gateway_0.1.2449.37345_x64__kaa03rpbbqef6
Ready to run: `openclaw`      Total 17.98s.
openclaw --version -> OpenClaw 2026.8.2 (0965053)

Packaged app data is lost on that final step, which is the documented trade.

Registration ownership, against a real foreign registration

The machine carried a registration from a different worktree, so this was exercised for real rather than simulated. Deployment refuses it:

FAILED during Check current registration: OpenClaw.Gateway is already registered
from another location: ...\session-integration\artifacts\local-package\x64\layout.
Run -Unregister from that checkout, or re-run with -ReplaceExistingInstall to
take over the identity here.

-Unregister refuses the same registration, and -ReplaceExistingInstall performs a real takeover rather than reporting no change:

WARNING: Taking over the registration at ...\session-integration\...\layout.
Registered: OpenClaw.Gateway_0.1.2449.37191_x64__kaa03rpbbqef6

Ownership is decided by normalized InstallLocation through one helper shared by the up-to-date short circuit, the deployment gate, and -Unregister. The regression test covering a foreign registration whose version matches retained local state was confirmed to fail against the unfixed short circuit with A same-version foreign registration was reported as up to date.

@clawsweeper

clawsweeper Bot commented Sep 15, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@clawsweeper clawsweeper Bot added 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. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Sep 15, 2026
@clawsweeper

clawsweeper Bot commented Sep 15, 2026

Copy link
Copy Markdown

Codex review: needs maintainer review before merge. Reviewed September 15, 2026, 1:42 PM ET / 17:42 UTC (Revision 3).

ClawSweeper review

What this changes

Adds a PowerShell command that builds and registers a runnable Windows development package without creating an MSIX, with cached downloads, scenario tests, and usage documentation.

Merge readiness

Ready for maintainer review

Keep open: this remains a distinct, useful development workflow absent from main. Both prior findings are resolved, and the supplied Windows evidence supports deployment, ownership checks, and release round trips. No blocking defect was found.

Priority: P2
Reviewed head: ab940113341f4eaed184fbab5dc17848dbaa9199

Review scores

Measure Result What it means
Overall readiness 🦞 diamond lobster (5/6) Strong production-path evidence and focused failure coverage support the implementation, with both previous findings resolved.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (terminal): The exact-head Windows 11 x64 transcript exercises the deployment command through real downloads, native publish, Windows registration, setup, and working aliases, with observed idempotency, foreign-registration refusal, recovery, and signed-release round trips.
Patch quality 🦞 diamond lobster (5/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The exact-head Windows 11 x64 transcript exercises the deployment command through real downloads, native publish, Windows registration, setup, and working aliases, with observed idempotency, foreign-registration refusal, recovery, and signed-release round trips.
Evidence reviewed 8 items Policy and introduced scope: No AGENTS.md or maintainer-notes directory was found. CONTRIBUTING.md was read fully; its isolated-test, NativeAOT, and release-file guidance was considered. The pinned delta contains six additive files or changes, matching the supplied introduction evidence.
Current main still requires MSIX composition: The existing local build script composes an unsigned MSIX and instructs contributors to sign it before installation. Main has no equivalent local registration command, so the proposed workflow is not a duplicate of the supported packaging path.
Prior findings resolved: Deployment checks normalized installation ownership before accepting an unchanged registration; unregistering also refuses foreign layouts. README now documents unregistering before installing a released package. The previous completed review inspected this same head and reported no remaining findings.
Findings None None.
Security None None.

How this fits together

The Windows launcher runs the packaged OpenClaw application using a bundled Node.js runtime. This development command assembles those inputs into a local directory, registers Windows execution aliases, and prepares the runtime through clawctl setup.

flowchart TD
  A[Application payload and Node archive] --> C[Local development layout]
  B[Published native launcher] --> C
  C --> D{Registration owned or replacement approved?}
  D -->|No| E[Stop with guidance]
  D -->|Yes| F[Register Windows package]
  F --> G[Prepare runtime with clawctl setup]
  G --> H[Runnable openclaw alias]
Loading

Before merge

None.

Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production and test growth Production scripts +880 lines; scenario tests +360 lines The growth implements the stated deployment, caching, ownership, and recovery workflow with dedicated behavioral coverage.

Technical review

Best possible solution:

Retain the additive development workflow with explicit replacement consent, installation ownership checks, and the demonstrated release-switching guidance.

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

Not applicable as a bug reproduction: this adds a development mode. The supplied Windows transcript directly demonstrates the new registration workflow and its refusal and recovery paths.

Is this the best way to solve the issue?

Yes: using Windows loose-package registration avoids MSIX composition while preserving the existing launcher and setup owners; explicit consent and release round-trip proof address the installation transition.

AGENTS.md: not found in the target repository.

Codex review notes: model internal, reasoning medium; reviewed against 9a8cd4af1395.

Labels

Label changes:

  • add rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • remove rating: 🐚 platinum hermit: Current PR rating is rating: 🦞 diamond lobster, so this older rating label is no longer current.

Label justifications:

  • P2: This is a bounded contributor-workflow improvement with demonstrated practical value and no urgent end-user regression.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The exact-head Windows 11 x64 transcript exercises the deployment command through real downloads, native publish, Windows registration, setup, and working aliases, with observed idempotency, foreign-registration refusal, recovery, and signed-release round trips.
  • proof: sufficient: Contributor real behavior proof is sufficient. The exact-head Windows 11 x64 transcript exercises the deployment command through real downloads, native publish, Windows registration, setup, and working aliases, with observed idempotency, foreign-registration refusal, recovery, and signed-release round trips.

Evidence

What I checked:

  • Policy and introduced scope: No AGENTS.md or maintainer-notes directory was found. CONTRIBUTING.md was read fully; its isolated-test, NativeAOT, and release-file guidance was considered. The pinned delta contains six additive files or changes, matching the supplied introduction evidence. (CONTRIBUTING.md, ab940113341f)
  • Current main still requires MSIX composition: The existing local build script composes an unsigned MSIX and instructs contributors to sign it before installation. Main has no equivalent local registration command, so the proposed workflow is not a duplicate of the supported packaging path. (scripts/Build-LocalMSIX.ps1:138, 9a8cd4af1395)
  • Prior findings resolved: Deployment checks normalized installation ownership before accepting an unchanged registration; unregistering also refuses foreign layouts. README now documents unregistering before installing a released package. The previous completed review inspected this same head and reported no remaining findings. (scripts/LocalPackage.psm1:628, ab940113341f)
  • Real Windows deployment and compatibility proof: The supplied body snapshot, sourceRevision f109120cc0d3fdd9599ae873446530d916b1e6e2b5f8bfa1d6d56d93617287d3, identifies this exact head and Windows 11 x64 with Developer Mode. It records cold deployment in 64.11 seconds, two unchanged runs skipping registration, and functioning package aliases. It also records refusal of a foreign registration, explicit takeover, damaged-layout recovery, and a round trip through the signed release. Packaged-to-local replacement explicitly loses packaged app data; default refusal, consent, and the required unregister step are documented and exercised. (ab940113341f)
  • Failure recovery and isolated scenario coverage: The scenario suite injects external operations and checks observable registration, cache, setup, and ownership results. Deployment records setup completion only after success and commits a refreshed payload selection afterward. Tests were inspected, not executed during this read-only review. (scripts/Test-Deploy-LocalPackage.Tests.ps1:176, ab940113341f)
  • Download and execution boundary: Default payload acquisition uses this repository's successful main workflow, and Node archives come from nodejs.org with architecture and version derived from payload metadata. Setup uses the existing launcher-owned archive extraction validation. The workflow adds only the isolated scenario suite; it changes no action references, permissions, signing inputs, or dependency declarations. (scripts/LocalPackage.psm1:365, ab940113341f)

Likely related people:

  • xlinush: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • paulcam206: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
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.

Workflow

  • 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.

History

Review history (2 earlier review cycles)
  • reviewed 2026-09-15T02:54:38.793Z sha 8338297 :: blocked before merge. :: [P2] Require the installed layout to match before skipping deployment | [P2] Document unregistering before reinstalling the current release
  • reviewed 2026-09-15T17:33:31.731Z sha ab94011 :: needs maintainer review before merge. :: none

Registration ownership is checked by install location, so the no-change short
circuit cannot report "already up to date" for a registration belonging to
another checkout. Without that check the identity takeover requested by
-ReplaceExistingInstall silently did not happen and the aliases kept running
the other checkout's layout.

Returning to a released package requires -Unregister first. Windows will not
replace a loose registration with a packaged install: Add-AppxPackage fails
with 0x80073CFB, reporting that an unpackaged version is already installed and
a packaged version cannot replace it. That is the same mutual exclusion as the
packaged-to-local direction and is unrelated to version ordering.
@paulcam206
paulcam206 force-pushed the paulcam206-deploy-local-package branch from 8338297 to ab94011 Compare September 15, 2026 17:26
@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. labels Sep 15, 2026
@paulcam206
paulcam206 marked this pull request as ready for review September 15, 2026 17:39
@clawsweeper clawsweeper Bot added rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. labels Sep 15, 2026
@paulcam206
paulcam206 merged commit 814b02a into main Sep 15, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants