Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
746b4a4
feat(migration): complete the Store migration engine
natalie-aguinaldo Sep 25, 2026
e837a39
feat(migration): localize the Store migration experience
natalie-aguinaldo Sep 25, 2026
8d570f5
feat(migration): add the gated Store handoff experience
natalie-aguinaldo Sep 25, 2026
dc1b273
build(migration): publish an installable migration test package
natalie-aguinaldo Sep 25, 2026
7b0a7f9
docs(migration): document the Store migration release journey
natalie-aguinaldo Sep 25, 2026
7fbba71
test(migration): cover the Store handoff and its recovery paths
natalie-aguinaldo Sep 25, 2026
c5cf53d
fix(migration): honor Not now and never trap a migrated user
natalie-aguinaldo Sep 25, 2026
352c30c
docs(migration): correct the startup and enablement guidance
natalie-aguinaldo Sep 25, 2026
d6dc1e7
fix(migration): block startup while an unsupported source is installed
natalie-aguinaldo Sep 25, 2026
50ddd1e
feat(migration): say the Store app stays inactive while a source remains
natalie-aguinaldo Sep 25, 2026
13829d1
fix(migration): unbreak UI and accessibility lanes for the Store entr…
natalie-aguinaldo Sep 28, 2026
40fc6e7
fix(migration): keep the Store status InfoBar announced to screen rea…
natalie-aguinaldo Sep 28, 2026
93d9059
fix(migration): route a prerelease source to update guidance
natalie-aguinaldo Sep 28, 2026
dde0a9b
docs(releasing): correct the prerelease refusal message in release QA
natalie-aguinaldo Sep 28, 2026
ac5ff78
fix(migration): recover verified orphaned Inno registrations
natalie-aguinaldo Sep 29, 2026
59e06bc
fix(migration): apply the startup task off the UI thread when finalizing
natalie-aguinaldo Sep 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,20 @@ jobs:
shell: pwsh
run: ./scripts/test-msix-ci-artifacts.ps1

- name: Validate developer migration test package
shell: pwsh
run: ./scripts/test-migration-test-msix.ps1

# installer.iss is only compiled for real in the tag-only release job, so a
# Pascal Script error would otherwise be found at release time. The runner image
# does not ship Inno Setup; the release job installs it the same way.
- name: Install Inno Setup
run: choco install innosetup -y --no-progress

- name: Compile installer script
shell: pwsh
run: ./scripts/Test-InstallerScriptCompiles.ps1 -RequireCompiler

- name: Validate Store MSIX bundle construction
shell: pwsh
run: ./scripts/test-msix-bundle.ps1
Expand Down Expand Up @@ -936,6 +950,50 @@ jobs:
artifacts/msix/${{ matrix.architecture }}/msix-metadata.json
if-no-files-found: error

- name: Resolve migration build switch
id: migration-switch
shell: pwsh
run: |
$override = $env:MigrationProductionEnabled
if ([string]::IsNullOrWhiteSpace($override)) {
$propsPath = Join-Path $env:GITHUB_WORKSPACE 'src\OpenClaw.Tray.WinUI\Migration.Build.props'
[xml]$props = Get-Content -LiteralPath $propsPath -Raw
$node = $props.SelectSingleNode('/Project/PropertyGroup/MigrationProductionEnabled')
if ($null -eq $node) {
throw "MigrationProductionEnabled is not declared in $propsPath."
}
$override = $node.InnerText
}
$value = $override.Trim().ToLowerInvariant()
if ($value -ne 'true' -and $value -ne 'false') {
throw "MigrationProductionEnabled must be true or false, got '$override'."
}
"enabled=$value" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append

- name: Stage developer migration test package
if: steps.migration-switch.outputs.enabled == 'true'
shell: pwsh
run: >
.\scripts\Export-MigrationTestMsix.ps1 -Architecture ${{ matrix.architecture }}
-PackageDirectory "artifacts\msix\${{ matrix.architecture }}"
-OutputDirectory "artifacts\msix-migration-test\${{ matrix.architecture }}"
-VersionInfoPath "$env:RUNNER_TEMP\openclaw-msix-version.json"

- name: Upload developer migration test artifact
if: steps.migration-switch.outputs.enabled == 'true'
uses: actions/upload-artifact@v7
with:
name: openclaw-msix-dev-migration-test-${{ matrix.architecture }}
path: |
artifacts/msix-migration-test/${{ matrix.architecture }}/OpenClaw-MigrationTest-${{ matrix.architecture }}.msix
artifacts/msix-migration-test/${{ matrix.architecture }}/OpenClaw-MigrationTest.cer
artifacts/msix-migration-test/${{ matrix.architecture }}/msix-metadata.json
artifacts/msix-migration-test/${{ matrix.architecture }}/INSTALL.txt
if-no-files-found: error
# The disposable signer is valid for 30 days and the package is not timestamped,
# so a longer retention would only offer an artifact that can no longer install.
retention-days: 30

- name: Provision disposable Dev MSIX certificate
shell: pwsh
run: .\scripts\setup-dev-msix-cert.ps1
Expand Down
4 changes: 4 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,10 @@ leading and trailing pipe. Columns, in order:
| inno-migration-startup | authoritative | src/OpenClaw.Tray.WinUI/App.xaml.cs | migration record parsing and completed-handoff admission | MigrationRecordCodec + InnoMigrationStartupGuard | App invokes the guard before ordinary startup; explicit destructive CLI uninstall remains separate | completed migration prevents normal unpackaged startup before settings or activation; development and packaged apps are unaffected | InnoMigrationContractTests.CompletedMigrationGuard_PrecedesSettingsAndActivation | source-shape | when packaged/unpackaged migration startup is fully hosted outside App |
| store-migration-startup | authoritative | src/OpenClaw.Tray.WinUI/App.xaml.cs | Inno discovery, pending-record inspection, and Store migration admission policy | InnoInstallationDetector + MigrationStartupRecordReader + StoreMigrationStartupCoordinator + StoreMigrationStartupGuard | App calls the compile-time-gated adapter before instance forwarding and normal services | disabled builds perform no migration inspection; preview admission never mutates source state or enables normal startup for pending migration | StoreMigrationStartupCoordinatorTests.Disabled_DoesNotInspectInstallationOrRecords | behavioral | - |
| store-migration-startup-closed | closed | src/OpenClaw.Tray.WinUI/App.xaml.cs | direct registry discovery and migration startup policy | StoreMigrationStartupGuard + StoreMigrationStartupCoordinator | one adapter invocation after explicit CLI uninstall and before protocol processing and instance forwarding | App does not regain installation discovery, record parsing, or admission decisions | InnoMigrationContractTests.StorePreviewGuard_PrecedesInstanceForwardingAndNormalServices | source-shape | when packaged migration bootstrap is hosted outside App |
| store-migration-workflow | authoritative | StoreMigrationStartupGuard | native message-box consent loop and inline adoption composition | StoreMigrationWorkflow + StoreMigrationOperations + StoreMigrationWindow | guard gates bootstrap and owns the pre-services window lifetime only | UI actions are serialized; explicit consent precedes shutdown and validation; only verified finalization permits normal startup | StoreMigrationWorkflowTests.ExplicitConfirmation_RecordsConsentBeforeGracefulShutdownAndCompletion | behavioral | - |
| inno-migration-handoff | authoritative | SettingsPage + App activation composition | explicit consent, Store listing policy, and migration shutdown authorization | InnoMigrationHandoff + InnoMigrationConsentStore + StoreMigrationListing | Settings forwards the action and displays its result; App supplies its existing shutdown callback | only configured production Release or explicit preview builds expose handoff; Dev stays isolated; exact same-user Inno and protected consent precede canonical graceful exit; inventory never means consent | InnoMigrationContractTests.InnoPreview_GatesHandoffAndUsesCanonicalShutdown | source-shape | when migration handoff is retired |
| migration-build-policy | authoritative | OpenClaw.Tray.WinUI.csproj | shared Inno/Store compile-time activation and fixed source floor | Migration.Build.props | the tray project imports one policy for packaged and unpackaged publishing | production requires a pinned nonzero source floor and supported Release RID; Debug and Dev cannot inherit production activation | MigrationBuildConfigurationTests.Production_InnoAndStoreShareThePinnedReleasePolicy | behavioral | - |
| migration-consent-locking | authoritative | InnoMigrationConsentStore | protected consent publication and inspection while source is running | MigrationOperationLock + InnoMigrationConsentStore | consent uses a shared preparation lease and a separate exclusive writer lock | consent remains compatible with Inno runtime readers but cannot overlap exclusive Store mutation; finalization removes the writer lock before deleting the completion receipt | InnoMigrationConsentStoreTests.RuntimeReadLease_AllowsExplicitGrantAndInspection | behavioral | - |
| chat-markdown-presentation | authoritative | src/OpenClaw.Tray.WinUI/Chat/ReactorChatTimeline.cs | paragraph and heading typography, literal code-frame presentation and Copy | ChatMarkdownPresentation | timeline keeps sanitization, parser flags, inert links/images and the existing list-wrapping callback | presentation uses public MarkdownOptions callbacks; code content is never truncated or parsed as markup | ReactorChatLayoutProofTests.CodeBlock_PreservesLiteralContentAndCopiesExactText | behavioral | - |
| chat-model-picker-presentation | authoritative | src/OpenClaw.Tray.WinUI/Chat/ReactorChatComposer.cs, then custom Button rows in ChatModelPicker | catalog search, provider groups, native row interaction states and model metadata presentation | NativeChatModelPicker through a generated Reactor wrapper | ChatModelPicker owns flyout lifetime; composer controller owns model mutation and authorization | native single-selection navigation never writes a model until explicit activation; filtering and resizing preserve raw provider-qualified identities | ReactorChatLayoutProofTests.ModelMenu_UsesNativeSelectionStatesAndPreservesNavigationDuringResize | behavioral | - |
| chat-model-picker-button-rows-closed | closed | src/OpenClaw.Tray.WinUI/Chat/ChatModelPicker.cs | hand-built Button rows, custom selected fill/checkmark and search-box styling | NativeChatModelPicker | declarative trigger and flyout lifetime only | native AutoSuggestBox/ListView own search and row interaction visuals; no duplicate hand-styled selection path | ReactorChatLayoutProofTests.ModelMenu_UsesNativeSelectionStatesAndPreservesNavigationDuringResize | behavioral | - |
Expand Down
37 changes: 37 additions & 0 deletions docs/ONBOARDING_WIZARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,43 @@ The setup flow walks users through:

The setup flow no longer configures remote/manual gateways inline. The Welcome page's **Connect to an existing gateway** option routes through `AdvancedSetupPage`, closes setup, and opens the tray app's Connections tab.

### Store migration preview

The gated Inno-to-Store migration window reuses the wizard's visual design:
Mica backdrop, mascot, centered heading, themed content card, and persistent
footer actions. Its content scrolls independently so consent and recovery
actions remain available in smaller windows or with enlarged text.
Consent uses three short titled sections and a separate, non-dismissible warning.
Manual close and removal use step-specific headings and short instructions;
warnings stay in the same native InfoBar style as setup. Buttons retain the
wizard's 100-DIP minimum width, neutral left-hand dismissal, accent right-hand
primary action, and standard content/footer spacing. The title bar stays
"Move to the Store version"; retry/recovery states do not use setup step dots.
The window also sets the native OpenClaw icon for taskbar previews and the
window switcher; the custom title-bar image alone does not supply that icon.
The migration-capable Inno Settings page promotes this handoff with a
theme-aware recommended card that reuses the same card padding, typography,
accent badge, and primary-button styling as the rest of the app. The card
carries no decorative icon, so its text aligns with every other settings row,
and its action shares that single row. Its confirmation dialog
asks a question rather than repeating the button label, and states the handoff
grant and preserved data; detailed removal and finalization guidance stays in
the Store wizard, where the user can act on it.

This is a separate pre-start workflow, not a page in the setup pipeline.
`StoreMigrationStartupGuard` and `StoreMigrationWorkflow` retain migration
ownership; displaying the window does not construct `SetupWindow`, install a
gateway, or start normal app services. Consent, retry, and uninstall verification
are unchanged. The same visual workflow serves configured production Release
builds and explicit Debug previews. The first supported Inno release is pinned
to `2026.9.5.0`, and `MigrationProductionEnabled` is checked in as `true`, so
non-Dev Release builds for `win-x64` and `win-arm64` carry the migration
surfaces. Enablement is gated at tag time, not by the checked-in default:
publication is blocked until that release's artifacts and acceptance are
verified, and the switch is set to `false` and retagged if acceptance fails.
See [Release migration gates](RELEASING.md) for the shared build contract and
the two-PR coordinated-release requirements.

## Screen Details

### Welcome
Expand Down
Loading
Loading