# Test Coverage Summary **Historical count snapshot**: 2026-08-06 (source revision not recorded)
**Framework**: xUnit / .NET 10.0
**Snapshot validation status**: passing (`.\build.ps1`, Shared tests, Tray tests) These dated totals are not current-head validation. Use fresh result reports for completion claims. The maintained sections below describe suite ownership, selection, and proof boundaries; they do not imply that every lane ran. ## Required validation suites These are the suites every agent must run after code changes, as documented in `AGENTS.md`. | Suite | Historical runtime result | |---|---:| | `OpenClaw.Shared.Tests` | 3,444 total: 3,412 passed, 32 skipped | | `OpenClaw.Tray.Tests` | 2,165 total: 2,165 passed, 0 skipped | Runtime totals come from `dotnet test` on 2026-08-06. They are higher than method counts because some `[Theory]` tests expand into multiple cases. ## Test project inventory | Project | Primary scope | Test methods at snapshot | |---|---|---:| | `OpenClaw.Connection.Tests` | Gateway registry, credential resolution, connection manager/state machine, setup codes, pairing, diagnostics | 452 | | `OpenClaw.Shared.Tests` | Shared models, gateway client, capabilities, MCP, exec approval, A2UI security, URL handling, notification categorization | 2,373 | | `OpenClaw.Tray.Tests` | Tray state/UI helpers, settings isolation, onboarding, connection page behavior, localization, local gateway setup/uninstall | 1,717 | | `OpenClaw.Tray.UITests` | Native WinUI/A2UI control, rendering, and accessibility scan coverage | 89 | | `OpenClaw.WinNode.Cli.Tests` | Windows node CLI argument parsing, command behavior, JSON output, uninstall flow | 89 | | `OpenClaw.SetupEngine.Tests` | Setup engine, WSL gateway installation, setup-code, and local setup policy coverage | 387 | | `OpenClawTray.FunctionalUI.Tests` | Functional UI smoke coverage | 19 | | `OpenClaw.E2ETests` | Gateway-mediated setup/connect, revocation recovery, and network recovery suites | 21 | | `OpenClaw.Tray.IntegrationTests` | Real-process tray/MCP integration tests gated by `OPENCLAW_RUN_INTEGRATION=1` | 19 | The method inventory is a source scan of `[Fact]`, `[Theory]`, and repo custom xUnit attributes such as `[WindowsFact]`, `[E2EFact]`, `[MxcE2EFact]`, `[IntegrationFact]`, and `[IntegrationTheory]`. Use `dotnet test` for authoritative runtime totals. ## Coverage highlights ### Fixture-backed application smoke The [Gateway fixture harness](GATEWAY_FIXTURE_TESTING.md) adds a middle tier: the real desktop app and production Gateway/chat stack consume deterministic synthetic Gateway responses, with no AI or real WSL Gateway. Protocol tests run in Shared; profile/preflight and concurrent-app tests live in Tray Integration; native sidebar/240-message/late-history proofs live in Tray UI. Use `.\scripts\test-gateway-fixture.ps1 -AppPath ''` for the opt-in real-app lane. It rejects skipped or zero-test runs and preserves per-run artifacts. Existing MCP-only integration defaults remain unchanged. ### Cross-repository candidate compatibility The reusable [`release-candidate-e2e.yml`](../.github/workflows/release-candidate-e2e.yml) workflow tests one exact OpenClaw package against an independently identified Windows app. `windows_node_workflow_sha` pins both the workflow and the maintained setup/test harness. `windows_node_source_sha` identifies only the app under test: a verified published ZIP/MSIX in release mode, or a separately checked-out and built Tray in main mode. Fixing the harness does not rebuild historical releases. The fixture installs the candidate (including exact beta/alpha versions), waits for operator/node readiness, and runs `FullSetup_SafeNodeInvocation_RoutesThroughRealGateway` to invoke `system.which` through the real Gateway. Current-feature capability inventories and recovery scenarios remain in ordinary product E2E, not historical-app compatibility. Success requires an executed, passing test. Only an opted-in lane may accept `PROTOCOL_MISMATCH`: the initial fixture must observe the exact structured app error, write its receipt, and fail the selected test with the dedicated exception. Skipped tests, setup timeouts, unrelated failures, and run-level errors fail the lane. [`Resolve-ReleaseCandidateE2EOutcome.ps1`](../scripts/Resolve-ReleaseCandidateE2EOutcome.ps1) owns result admission; its executable regression cases run in fast-validation CI. Evidence names include the app source kind and release tag or source SHA so stable, prerelease, and main invocations can upload in the same caller run. ### OpenClaw.Shared.Tests - **Command output-drain timing** - The unsupported-Windows runner fixture gates launcher release, then requires completion within three seconds of root exit while a descendant retains stdout, stderr, or both. Ten repetitions per case check short output and PID/start-time-bound child cleanup. The nonparallel `MxcOwnership` collection isolates these wall-clock budgets from unrelated tests. Native SDK finalization has separate managed ownership tests and opt-in runtime proof. - **Fixture process identity** - MXC pipe-fixture metadata, cleanup ownership checks, and the opt-in native MXC descendant-identity check query the observed full executable image through a retained process handle, not the loader's nullable `MainModule` or a requested launch path. `ProcessImagePathTests` checks a real, never-resumed Windows process with no initialized loader view, a different command-line image name, caller-handle lifetime, and explicit native query failure. This deterministically covers unavailable loader metadata; it does not reproduce the timing of a normally scheduled child or prove native MXC containment. - **Model and display formatting** - activity glyphs, app version display, session labels, gateway usage/node display, channel status, and rich text helpers. - **Gateway and WebSocket behavior** - gateway client parsing, session keys, WebSocket base handling, URL normalization, local gateway classification, and token sanitization. The signing-identity regression covers native and managed loopback WebSockets with a bounded client-initiated disconnect and a queued health request. The fixture drains application frames already in flight until the client's close frame, replies, and closes its transport under one deadline rather than relying on the client's EOF fallback timer. A separate fixture regression checks the close frame and actual TCP EOF on both transports, including fragmented text and binary data queued before close. - **Capabilities and MCP** - app/canvas/screen/camera/system capabilities, MCP auth token reset, MCP HTTP server, MCP tool bridge, MXC availability, MXC policy building, and command runners. - **Exec approval** - legacy policy coverage plus V2 evaluator, input validation, normalization, prompt adapter, routing, coordinator, store, environment sanitizing, and shell-wrapper parsing. - **A2UI and web bridge** - A2UI capability security, asset hash pinning, web bridge message handling, and channel payload/status tests. - **Security and localization-adjacent helpers** - HTTP URL validation/risk evaluation, device identity, identity migration, notification categorization, speech language normalization, and non-fatal action handling. The CI Shared-test step uses the existing five-minute hang watchdog and a 15-minute outer step limit. A hang fails the run and preserves the interrupted test sequence without collecting a memory dump; it is not retried or skipped. ### OpenClaw.Tray.Tests The composer lifetime- and voice-disposal reentrancy regressions use dedicated workers so their bounded reentrancy assertions (`OPENCLAW_TRAY_TEST_REENTRANCY_TIMEOUT_SECONDS`, default 10 s) measure controller lock ownership rather than shared thread-pool scheduling latency. - **Tray UI and state** - app state, menu display/position/sizing, tray tooltip formatting, activity streams, async list loading, diagnostics contracts, markup regressions, and chat timeline/markdown handling. - **Connection and pairing** - connection manager node connector tests, connection page approval/channel metrics/row state, operator and Windows tray node pairing approval, and gateway action transport. - **Settings and startup** - settings round-trip/isolation, consent and settings save, auto-start defaults, startup setup state, existing config guard policy, and local setup progress stage mapping. - **Onboarding and local gateway setup** - onboarding completion/chat bootstrapper/existing config guard, wizard flow/selection/error/step parsing, setup code decoding, local gateway setup diagnostics, uninstall, WSL keep-alive, and auto-pair flags. - **Localization and resources** - localization key parity, capability page localization, fluent icon catalog coverage, and installer assertion tests. ### Additional projects Gateway cleanup task registration, refresh, quoting and failure propagation run in the ordinary SetupEngine suite. The registration fixture replaces only the persistence boundary and cannot register a real scheduled task. The two desktop visibility tests use the existing integration opt-in because their positive control deliberately displays a classic console. Run them on an interactive Windows desktop without concurrent desktop automation: ```powershell $env:OPENCLAW_REPO_ROOT = (Get-Location).Path $env:OPENCLAW_RUN_INTEGRATION = '1' dotnet test .\tests\OpenClaw.SetupEngine.Tests\OpenClaw.SetupEngine.Tests.csproj --no-restore --filter 'FullyQualifiedName~GatewayCleanupTaskTests' ``` The visibility contract is no on-screen visible window or taskbar entry, not absence of an HWND. WinEvent observations and periodic window sampling cover startup through worker exit; native UI Automation checks the shell taskbar, including visible custom-shell surfaces when present. The visible-console positive control must be detected by both window observers and actual taskbar enumeration. Unavailable shell/provider access fails the selected proof instead of treating a window-style heuristic as taskbar evidence. Sampling gaps remain a limit of the measurement, not proof that an arbitrarily short transient is impossible. The chat composer's real keyboard regression is opt-in because it uses Windows `SendInput` on a visible, focused proof window. Run it only on an isolated interactive desktop with no concurrent computer-use or human input, Caps Lock off, and a Latin keyboard layout: ```powershell $env:OPENCLAW_RUN_KEYBOARD_PROOF = '1' $env:OPENCLAW_TRAY_DATA_DIR = '' .\scripts\run-proof-tests.ps1 -Project 'tests\OpenClaw.Tray.UITests\OpenClaw.Tray.UITests.csproj' -Filter 'Category=NativeKeyboard' -ResultName 'composer-keyboard' -RuntimeIdentifier win-x64 ``` The tests check foreground ownership before every key sequence and fail rather than inject into another window. They exercise Shift+Enter at the beginning, middle, and end, selection replacement, typing after the newline, repeated newlines, draft synchronization after rendering, zero sends for Shift+Enter, and exactly one send with no extra newline for plain Enter. Optional `OPENCLAW_VISUAL_TEST=1` and `OPENCLAW_VISUAL_TEST_DIR=` save the active multiline composer through the existing capture harness. Ordinary CI skips this lane; a skip is not keyboard proof. On a proof host every selected case must pass with zero skips. This lane does not synthesize an IME composition: record actual IME commit behavior separately or report it blocked. Unset the opt-in after the run. - **OpenClaw.Connection.Tests** keeps connection architecture tests separate from tray UI concerns. - **OpenClaw.Tray.UITests** covers A2UI/native WinUI rendering behavior that is awkward to validate through pure unit tests. Markdown renderer record-shape tests also belong to `UICollection`: their WinRT-backed types must not initialize concurrently with the shared Windows App SDK bootstrap. A smoke-test contract guards both Markdown test classes' collection membership. - Onboarding AI card-action tests wait up to 10 seconds for the target card to be loaded, enabled, click-enabled, and attached to the page's non-null XamlRoot. Readiness timeouts report each condition; regression tests hold each readiness flag false before releasing it and verify that a permanently blocked card never invokes its action. A dispatcher yield alone is not a control-loaded guarantee. - Native current-model UIA coverage requires the standard disabled HRESULT (`UIA_E_ELEMENTNOTENABLED`, `0x80040200`) even when the client worker carries unrelated COM error info. It verifies that the same card remains available and mounted without activation, that non-clickable cards do not advertise Invoke, and that enabled native invocation activates the selected model. - **OpenClaw.WinNode.Cli.Tests** covers the standalone Windows node CLI contract. - **OpenClaw.SetupEngine.Tests** covers gateway setup and local WSL installation policy. - **OpenClawTray.FunctionalUI.Tests** covers newer UI surfaces outside the main tray test project. - **OpenClaw.E2ETests** uses custom `[E2EFact]` / `[MxcE2EFact]` attributes that inherit xUnit `FactAttribute`; CI exercises them with shard filters. - **OpenClaw.Tray.IntegrationTests** uses custom `[IntegrationFact]` attributes and runs only when `OPENCLAW_RUN_INTEGRATION=1`. - **PackagingTests** is a legacy PowerShell-script lane under `tests\PackagingTests\`, not a dotnet test project. ## Formal validation paths Use the smallest lane that proves the changed subsystem, but always include the required closeout lane for code changes. | Lane | Entry point | Required when | |---|---|---| | Required closeout | `.\build.ps1`, Shared tests, Tray tests | Every code change and every agent closeout | | Fixture-backed app smoke | `.\scripts\test-gateway-fixture.ps1 -AppPath ''` | Chat session switching/history/scrolling and populated navigation changes; run both Debug and production-shaped Release for runtime regressions | | Proof-pool inventory | `.\scripts\validate-proof-pools.ps1`, `.\scripts\test-proof-pool-validator.ps1`, and `.\scripts\test-validate-docs-proof-pool-flow.ps1` | Every inventory or proof scheduling change; the documentation gate runs core schema and parent-flow checks, while CI runs the full malformed-contract matrix | | Agent skills | `.\scripts\validate-agent-skills.ps1` and `.\scripts\test-agent-skills-validator.ps1` | Changes under `.agents\skills`; validates skill metadata, agent-facing prose, and local links | | Result and evidence contracts | `.\scripts\test-proof-evidence.ps1` | Changes to report admission, generic proof, CI test wrappers, evidence summaries, or their source/artifact correspondence | | Agent readiness | `.\scripts\test-agent-readiness.ps1` | Changes to read-only host and optional agent-tool readiness reporting | | GitHub-hosted PR/main CI | `.github\workflows\ci.yml` | Every pull request and push to `main`; explicit conservative impact outputs select the required test, E2E, and release-publish lanes | | Accessibility scan | `.\scripts\run-proof-tests.ps1 -Project 'tests\OpenClaw.Tray.UITests\OpenClaw.Tray.UITests.csproj' -Filter 'Category=Accessibility' -ResultName 'winui-accessibility' -RuntimeIdentifier win-x64` | UI changes and CI quality gate; runs real-process Axe.Windows scans; see `docs\ACCESSIBILITY.md` | | Local E2E | `OPENCLAW_RUN_E2E=1` with `OpenClaw.E2ETests` | Gateway setup/connect, recovery, or pairing changes that need real WSL Gateway coverage | | Local MXC E2E | `.\scripts\validate-mxc-e2e.ps1` | MXC sandboxing, `system.run`, exec approvals, Windows node command execution, gateway setup/connect changes that affect MXC | | Product WSL setup validation | `OPENCLAW_RUN_E2E=1` with `OpenClaw.E2ETests.Setup.SetupAndConnectTests` | Tray onboarding/setup-engine changes that must prove the current product WSL install path | | Installer source checks | `.\scripts\run-proof-tests.ps1 -Project 'tests\OpenClaw.Tray.Tests\OpenClaw.Tray.Tests.csproj' -Filter 'FullyQualifiedName~InstallerIssAssertionTests' -ResultName 'installer-source'` | Installer source, payload, identity, cleanup, or protocol changes; pair with the clean installer/upgrade pool for runtime claims | | Migration preservation source checks | `dotnet test .\tests\OpenClaw.Tray.Tests\OpenClaw.Tray.Tests.csproj --filter FullyQualifiedName~InnoMigrationContractTests` | Protects completion/checker-failure exits and deletion authorization, including unsafe source mutations; not a substitute for real installer proof | | Migration record and cleanup scripts | `dotnet test .\tests\OpenClaw.Connection.Tests\OpenClaw.Connection.Tests.csproj --filter FullyQualifiedName~MigrationRecordTests` | Exercises real Windows PowerShell receipt checks and cleanup lock contention. The subprocess deadline defaults to 30 seconds so a genuine regression fails fast; the cleanup lock-contention theory overrides it to 120 seconds because its failure path writes log and result files that a loaded runner can stall on. Timeout handling attempts process-tree termination before fixture teardown and reports output, script logs, the observed process state, and cleanup failures without replacing the original timeout | | Migration startup admission | `dotnet test .\tests\OpenClaw.Connection.Tests\OpenClaw.Connection.Tests.csproj --filter "FullyQualifiedName~InnoInstallationDetectorTests\|FullyQualifiedName~StoreMigrationStartupCoordinatorTests\|FullyQualifiedName~MigrationStartupRecordReaderTests\|FullyQualifiedName~MigrationVersionPolicyTests"` | Read-only exact Inno discovery, version/architecture admission, pending-record precedence and preservation; pair with packaged preview guidance proof, not a claim of completed migration | | Store migration helper fixtures | `.\scripts\test-store-migration-test-script.ps1` | Actual helper execution with in-memory registry/version adapters, blocked process commands and test-owned files. Covers complete version compatibility before mutation, native draft/atomic-sibling preservation, explicit unknown-file quarantine, exclusive non-tray lock contention, lock/receipt preservation, opt-in reset, path-specific reparse refusal, relabel/revert drift, WhatIf and failure paths. Runs in Windows fast-validation CI; not installer, signature, Store UI or gateway-preservation proof | | Disposable Inno to Store migration | `.\scripts\Enable-StoreMigrationTest.ps1` with [`STORE_MIGRATION_TESTING.md`](./STORE_MIGRATION_TESTING.md) | Manual signed-artifact, installer and visible handoff proof on a disposable machine. Select eligible source/target metadata and matching architecture; alpha ProductVersion cannot be fixed by registry relabeling. Helper preflight is not completed migration proof. Record exact artifacts, actual outcomes and preservation evidence, or report the missing artifact/runtime prerequisite as blocked | | Local AI model fixture | `dotnet test .\tests\OpenClaw.Connection.Tests\OpenClaw.Connection.Tests.csproj --filter "FullyQualifiedName~SparseFixtureFileTests\|FullyQualifiedName~LocalAiPortLifecycleTests"` | Test-only sparse files preserve the catalog-sized logical length and readable zeros without model downloads. Checks cover Windows sparse allocation, independent mutation, overwrite refusal, and cleanup. Windows requires a sparse-capable filesystem and fails explicitly if marking the fixture sparse fails; Unix uses the holes created by `SetLength`. Production receipt, size, and hash checks remain unchanged | | Legacy installer runtime ordering | `.\tests\PackagingTests\Test-InnoUninstallOrdering.ps1` | Deprecated. It targets the old `[UninstallRun]` layout, while current cleanup is owned by `[Code]`; do not use it as current installer runtime proof | The hosted setup/connect filter also includes `SetupAndConnectTestsSearchProof.ReleasedGateway_SearchDenialAndDashboardRoutes`. It reuses the run's disposable WSL Gateway, seeds one synthetic conversation without calling an AI provider, and exercises the production metadata, transcript and schema readers. A separately scoped operator connection must receive `FORBIDDEN` for transcript search. Preinstalled headless Edge uses a fresh fixture-owned profile to render authenticated Dashboard root and Skills routes and capture screenshots. Credentials are sent through local CDP, not browser command-line arguments; only the test's synthetic session is deleted. Browser profile storage is excluded from collected artifacts; only its screenshots and bounded proof outcomes are retained. The browser helper discovers the initial `about:blank` page through the owned loopback debugger's `/json/list` endpoint and connects directly to that page. It does not create another target through the browser-level WebSocket. Startup failures report the stage, elapsed time, process state, stderr character count and exception type, never raw Edge output or debugger URLs. The existing fixture owns Gateway and profile teardown. This is a real released WSL Gateway/browser boundary, not native packaged-Gateway or Store upgrade proof. The run's `gateway-release.json`, result report and screenshots identify actual execution; compilation or a locally gated skip is not successful runtime proof. Local execution still requires the existing WSL E2E runtime authority. For browser-only startup proof, set `OPENCLAW_RUN_E2E=1` and run `dotnet test .\tests\OpenClaw.E2ETests\OpenClaw.E2ETests.csproj --filter FullyQualifiedName~SetupAndConnectTestsBrowserStartup`. This class does not use the setup fixture or provision a Gateway. Its native smoke owns one temporary Edge process/profile and verifies page evaluation and PNG capture; discovery tests cover readiness, cancellation and endpoint isolation. The hosted setup/connect filter includes this class. This is not authenticated Dashboard route proof. Capacity-dependent Windows validation is named in [`PROOF_POOLS.md`](./PROOF_POOLS.md). Pull requests declare the exact pool IDs they need. A declaration does not claim that the pool ran, and unavailable proof must remain reported as blocked. ### Choose the local execution target Choose by the behavior under test, not merely a name containing `Mxc`. This is testing guidance, not automatic CI migration, host reservation or a new scheduler. Keep mandatory code-change closeout and every applicable proof pool. | Behavior to prove | Default target and boundary | |---|---| | Build, unit/component logic, pure MXC policy and mocked capabilities | Task-owned host worktree with isolated test settings; no VM needed solely because the code concerns MXC | | App layout, navigation, chat/history and fixture-covered client behavior | [Isolated app with the deterministic fixture Gateway](GATEWAY_FIXTURE_TESTING.md); cheapest app-only default, not real pairing, node invocation or MXC proof | | Isolated test builds needing a real native Gateway, including single-instance and multi-instance E2E | [Task-owned builds with named side-by-side development patches](../.agents/skills/native-gateway-from-source/SKILL.md#prepare-an-isolated-test-build) on one authorized, capable Windows 11 client host with Developer Mode; separate packages/sessions/profiles/ports, not one Hyper-V VM per Gateway | | Integration with an existing Gateway | Explicitly operator-approved endpoint and operations, with a task-owned app profile; discovery is not permission to reconfigure, restart or use a shared runtime | | Clean-machine/image, signed-package install/upgrade, genuine OS isolation or unavailable host capabilities | Separate reusable local or approved cloud Windows client VM; development patches do not prove signed Store/MSIX lifecycle | | Required WSL Gateway or MXC `system.run` path | Existing real WSL suite, including `.\scripts\validate-mxc-e2e.ps1` where required, on an authorized capable host/VM; native patch startup does not replace it | The named-patch default also applies when selecting future cloud native-Gateway E2E targets, after actual Windows client/session/listener capabilities are qualified. Do not assume `windows-latest` or Windows Server is MXC-ready or claim the cloud lane ran. Unsupported or skipped proof remains blocked. For real native Gateway tests, build Companion in the task-owned worktree and prepare the Gateway with an explicit unique `-Patch` and task-owned `-WorkRoot`. Launch that binary with `OPENCLAW_NATIVE_GATEWAY_DEV_PATCH` and separate app data. `run-app-local.ps1 -Isolated` alone isolates only the app profile; it does not allocate a unique Gateway and can reuse the shared `npm-latest` slot. Keep fixture-only app tests on the cheaper synthetic path. Serialize deployment/cache mutation and initial port selection/startup, then allow independent already-prepared instances to serve concurrent tests within host capacity. Package separation does not remove CPU/RAM costs, isolate the OS, or grant concurrent desktop-input ownership. Use unique task-owned names, explicit Companion patch selection, distinct app data/endpoints and ownership-checked cleanup as described in the canonical skill. Never stop another task's runtime or copy the user's profile/credentials to make a test pass. ### Explain the validation plan Use the existing classifier in read-only planning mode: ```powershell .\scripts\Get-CiChangeClassification.ps1 -EventName pull_request ` -ChangedPaths 'src\OpenClaw.Cli\Program.cs' -Human .\scripts\Get-CiChangeClassification.ps1 -EventName pull_request ` -ChangedPaths 'README.md' -Explain ``` For actual branch comparison, pass known `-BaseSha` and `-HeadSha` values. `-ChangedPaths` is an explicit scenario, not discovery of all current edits. Include staged, unstaged, and relevant untracked changes when planning an uncommitted task. Invalid revisions, empty/unknown inputs, and infrastructure changes remain conservative. The default JSON stays compatible with CI. `-Explain` adds schema-versioned path reasons, hosted lanes, mandatory local closeout, and inventory proof candidates. `-Human` renders the same decisions for a reader. Code changes still require full build, Shared, and Tray even when hosted lanes are narrower. The shipped `winnode` reference keeps its CLI/core contract treatment. Hosted lanes describe impact-selected CI Gate jobs, including release metadata and MSIX jobs where selected. Event/authorization-conditioned reservation and publication jobs remain governed by the workflow, not a path-only prediction. Non-documentation plans list the inventory pools for applicability review. They are **not automatically required or scheduled**: use each pool's `requiredWhen`, prerequisites, and `doesNotProve` against the behavior change. The planner does not run tests, authenticate, launch an app, acquire a host, or dispatch CI. The [proof skill](../.agents/skills/openclaw-proof-validation/SKILL.md) owns the evidence checklist. ### CI impact fast paths Core, Tray, UI, and the three E2E jobs start as soon as classification selects them, in parallel with `fast-validation`, and keep their cancellation guards. CI Gate still requires `fast-validation` to succeed, so this removes it from the critical path without relaxing acceptance. A `fast-validation` failure no longer skips selected test lanes; their runner time is the accepted cost. Release asset preparation still waits for `fast-validation`. The setup/connect, revocation recovery and network recovery E2E jobs each run once against npm Latest, matching normal WSL setup. `scripts\Resolve-CiGateway.ps1` resolves Latest once in change-classification and supplies the exact version to the existing candidate-version setup path, so setup and assertions use the same version even if the tag moves during a run. No npm resolution failure falls back to the Store or another channel. Each job's artifact includes `gateway-release.json` with the npm selector, exact version, integrity and tarball URL. Resolution runs only when E2E is selected, without downloading an MSIX or consulting native packaging releases. Every selected E2E job contributes to the existing CI Gate. These are real WSL Gateway-to-Windows-node lanes, not native MSIX runtime proof. There is no duplicate Packaged WSL lane and no native Latest/Packaged runtime matrix. Native unit/helper tests and upstream package build/install checks are not substitutes for live isolated-session startup, pairing and recovery on a capable Windows client. Native developer builds use the verified npm archive for Latest, or released app bytes and the release packaging commit for Packaged. `scripts\test-gateway-npm-release.ps1` covers channel selection, source/Store overrides, exact pins, archive integrity and provenance without network access or changing installed packages. `scripts\test-gateway-packaged-release.ps1` covers release/tag resolution, embedded payload identity, asset digest, extraction safety and inventory-verified cache reuse. The `change-classification` job emits explicit booleans for `core_tests`, `tray_tests`, `ui_tests`, `setup_e2e`, `revocation_e2e`, `network_e2e`, `x64_release`, `arm64_release`, and `full`. Jobs consume those outputs directly. The summary classification is `docs_only`, `fast_only`, `targeted`, or `full`; it is not the authority for individual job conditions. `fast_only` applies only to the exact repository-tooling paths `.github/scripts/repository-triage.cjs` and `.github/scripts/repository-triage.test.cjs`, optionally mixed with safe documentation. These files serve repository reports and ownership-label maintenance, not the shipped product, and their Node tests run in the always-on `fast-validation` job. All product and release lane outputs remain false; tooling code is not classified as documentation. This is not an allowlist for other scripts, actions, workflows, extensions, or untested tools. Mixed tooling and product changes retain the union of product lanes, including the WinNode skill's core-test requirement. Unknown paths and build, workflow, project, dependency, classifier, or gate infrastructure still select `full`. Non-PR events still require full validation including ARM64. `fast-validation` and `proof-pool-contracts` must both succeed even for `fast_only`. The proof-pool selector is unchanged: any changed proof boundary still runs its regression matrix, including when mixed with allowlisted tooling. Hosted timing savings are not verified until a tooling-only PR exercises this path. The classifier uses conservative project boundaries: | Change surface | Core / CLI | Tray / setup / integration | UI / accessibility | Setup E2E | Recovery E2E | Release publish | |---|---:|---:|---:|---:|---:|---:| | Maintained docs or non-executable agent-skill content | | | | | | | | `OpenClaw.Cli`, `OpenClaw.WinNode.Cli` | Yes | | | | | | | Tray WinUI, including pure service logic | | Yes | Yes | | | | | Chat, FunctionalUI, XAML, resources, pages, controls | | Yes | Yes | | | | | SetupEngine, onboarding, setup, pairing | | Yes | When UI-bound | Yes | | | | Connection, gateway, node, MCP, WSL paths | As applicable | Yes | When UI-bound | Yes | Yes | | | Broad Shared or protocol changes | Yes | Yes | Yes | Yes | Yes | | | Test-only change | Corresponding lane | Corresponding lane | Corresponding lane | Corresponding shard | Corresponding shard | | | Packaging, project/build files, installer, workflow or classifier contracts | Yes | Yes | Yes | Yes | Yes | x64 | | Push to `main` or a tag | Yes | Yes | Yes | Yes | Yes | x64 and ARM64 | Known mixed changes take the union of their lanes. Any unknown path, a mixed change containing an unrecognized path, invalid or unavailable revisions, an empty diff, workflow/build infrastructure, project/package metadata, installer input, or classifier/contract change selects `full` fail-closed. A full pull request runs all test and E2E lanes plus the x64 publish smoke. Pushes to `main` and tags also run ARM64 publish. The former serialized Windows `test` job is split into three clean-runner lanes. Each lane restores and builds its own minimal project graph and publishes a separate TRX artifact: | Lane | Projects | |---|---| | `core-tests` | Shared, Connection, WinNode CLI | | `tray-tests` | Tray, SetupEngine, Tray Integration | | `ui-tests` | FunctionalUI, Tray UI, Axe.Windows accessibility | The core lane uses a dedicated runner-temp NuGet package directory and a key scoped to its complete transitive project graph plus the SDK, NuGet, and imported MSBuild manifests that affect restore. It has no fallback to the repository-wide cache. Tray, UI, E2E, and release lanes retain the existing repository-wide NuGet cache behavior. Only downloaded NuGet packages are cached; build outputs, test results, source, SDK installs, and Node packages remain uncached. Tray settings remain isolated through `OPENCLAW_TRAY_DATA_DIR`, integration tests retain `OPENCLAW_RUN_INTEGRATION=1`, UI tests install WindowsAppRuntime, and every test project continues to publish TRX output. The Tray UI step uses a five-minute VSTest hang watchdog and a fifteen-minute outer step timeout. A hung test fails the step and leaves a test-sequence artifact for diagnosis instead of waiting for GitHub's six-hour job limit. Memory dumps are disabled because they can contain credentials or user data. Other `Invoke-CiTest.ps1` callers retain their existing timeout behavior unless they opt in with `-HangTimeoutSeconds`. `fast-validation` still runs for every invocation and owns repository hygiene, documentation, agent-skill, classifier, gate, workflow, and release-ordering contracts. The heavyweight malformed proof-pool matrix moved to the independent `proof-pool-contracts` job. Its existing decision helper still fails closed, but ordinary product code no longer waits for the matrix. The three existing E2E shards are unchanged in scope and have separate job conditions; no E2E shards were added. ### WSL latest versus packaged native Gateway proof The WSL E2E fixture installs npm `latest` by default. `OPENCLAW_E2E_GATEWAY_VERSION` selects explicit candidate validation, not a native package test. Running the packaging repository's pinned npm version in WSL does not prove Microsoft Store acquisition, package identity, MXC session setup, or the packaged Gateway lifecycle. Both E2E tray fixtures read `gateways.json` through `GatewayRegistryFile`, which joins the production `PersistenceFileLease` before opening the file. This keeps fixture reads out of an in-flight atomic replacement without retrying IO or hiding malformed JSON. The fixture-free `SetupAndConnectTestsRegistryReads` regressions cover writer/reader ordering, concurrent saves and error propagation; they do not replace the real WSL Gateway setup/connect proofs. Actual package E2E needs a disposable Windows account/host with a successful MXC session capability probe and a verified installable MSIX from [`openclaw-windows-packaging`](https://github.com/openclaw/openclaw-windows-packaging). Record the packaging commit, release policy, payload commit/version, architecture, package name/family/publisher/version, and verified artifact digest. An older development registration or unsigned Store-submission archive is not proof of the current released package. The current packaging contract owns an agent-session Gateway through `clawctl setup` and `clawctl gateway-service start/status/restart/stop`. Configuration belongs inside that session's profile. Companion's existing host-child native runtime and same-user listener ancestry checks are a different contract and cannot be relabeled as proof of the isolated package. The integration must demonstrate authenticated Companion connectivity and ownership/lifecycle behavior against the real session endpoint before claiming native E2E coverage. Missing package provenance, installation consent/isolation, capable runner, or session endpoint integration is a blocker, not a passing skip or an npm fallback. Investigation snapshot (2026-09-24): packaging commit `3f2e3268ca0961a08a12e31a25b0c2c174548955` selects upstream `2026.9.4` and MSIX revision 4 (`2026.9.404.0`) with the Store identity `OpenClawFoundation.OpenClawGateway`. Its [producer run](https://github.com/openclaw/openclaw-windows-packaging/actions/runs/36033664419) failed verified-release-tag admission and produced no artifacts; the corresponding release was unavailable. The published revision-3 package uses the old identity, so it cannot stand in for revision-4 Store proof. The hosted Windows image in that run was build 26100, below packaging's MXC fallback minimum 26340.9212, with no successful session capability proof. A matching signed package, a disposable session-capable test account/runner, and the Companion lifecycle integration described above remain prerequisites. The existing developer package/profile must not be replaced or repurposed to make this test pass. Release metadata is produced by the small `metadata` job only when release publish validation is required. It preserves GitVersion `semVer`, `majorMinorPatch`, `isPrerelease`, `isStableCorrection`, and stable-correction tag validation. The x64 and ARM64 publish jobs depend only on classification and metadata, so they can run in parallel with tests and E2E. Ordinary product pull requests produce no release artifact. Packaging/build/release-sensitive pull requests run only x64 publish smoke; main and tags run x64 plus ARM64. MSIX packaging also consumes the readonly preview or the single official tagged-release reservation before starting its x64/ARM64 matrix. `scripts\test-msix-versioning.ps1` covers allocation arithmetic, durable state, retry/idempotence, competing claims, provenance, preview isolation, and failure boundaries without modifying remote refs. `scripts\test-msix-preview-source-version.ps1` covers stable and numeric correction tags, the canonical upstream Latest API request, fail-closed release validation, and token-safe errors. Artifact and alpha-staging contracts verify that the actual package versions and metadata match the selected allocation. `test-ci-workflow-contract.ps1` executes the actual preview/write context guards, including fork, PR, branch, tag, cancellation, and failure cases, and requires fork previews to read the canonical upstream reservation ledger. The always-running **CI Gate** validates every classifier output against the corresponding job result. A required lane must succeed, an unrequired lane must be skipped, and classification, fast validation, proof-contract selection, missing outputs, cancellation, or unexpected results fail closed. #### Timing and runner-minute model The pre-split full PR baseline is run `33815204229`: the serialized `test` job took 22 minutes 11 seconds, including 6 minutes 48 seconds for the malformed proof-contract matrix and 3 minutes 28 seconds for accessibility. Existing E2E shards took 14 minutes 43 seconds, 9 minutes 33 seconds, and 11 minutes 7 seconds. x64 and ARM64 publishes took 5 minutes 13 seconds and 6 minutes 17 seconds. The merged docs/skill fast path is run `33819700640` at 51 seconds. Using those step baselines, expected pull request critical paths are: | Change class | Expected wall-clock | Expected runner cost relative to old full PR | |---|---:|---:| | Docs / agent-skill prose | About 1 minute | About 2% | | CLI-only | 5 to 7 minutes | About 10% | | Tray logic | 8 to 12 minutes | About 25% | | UI / XAML | 8 to 12 minutes | About 25% | | Setup / connection / Shared | 10 to 16 minutes, governed by required E2E | About 55% to 85% | | Build or workflow infrastructure | 10 to 16 minutes | Within 15% of the old full runner total | The full test split duplicates checkout, SDK setup, and cache restore, but removes the serialized proof matrix from product paths. Estimated full-infrastructure runner minutes remain below the 30% rejection threshold, while ordinary code latency falls to the longest required lane instead of the sum of every lane and release publish. ## Writing a regression test ### Fixed file-worker owner checks `OpenClaw.FileWorker.Tests` exercises the production native filesystem owner in-process on isolated repository-local test directories. It does not launch Companion, the worker executable, MXC, a Gateway, or an installer. Run it on Windows: ```powershell $env:OPENCLAW_REPO_ROOT = (Get-Location).Path dotnet test .\tests\OpenClaw.FileWorker.Tests\OpenClaw.FileWorker.Tests.csproj dotnet test .\tests\OpenClaw.Shared.Tests\OpenClaw.Shared.Tests.csproj --filter 'FullyQualifiedName~FileTransfer' ``` The core CI lane restores, builds and runs the worker suite alongside Shared, Connection and winnode tests. It collects a separate FileWorker TRX report. The fixed worker has no native speech dependency and uses the SDK's self-contained .NET runtime. Its manifest generator validates both direct and delayed PE imports and fails if a payload acquires an external VC runtime dependency. Companion's native speech runtime requirements remain unchanged. Before inventory generation, it removes PDB debug symbols from the worker publish directory, including stale symbols from earlier publishes. Build-output symbols remain available separately. This keeps the copied payload and its exact manifest consistent across MSIX (which strips PDBs), Inno and portable distributions for both configurations and worker RIDs. `FileWorkerManifestTests` checks symbol removal, repeat generation and preservation of every runtime file and digest at this build boundary. Actual MSIX archive inventory and embedded-manifest checks remain a separate packaging acceptance boundary. Release worker EXE/DLL signatures are verified before manifest hashing. `FileWorkerManifestTests.ReleaseManifestRejectsUnsignedWorkerBeforeWritingDigests` checks rejection for both RIDs; workflow contracts verify the signed input reaches portable/Inno and MSIX builds without republishing the worker. These checks do not prove Azure signing or installed-package acceptance. Companion packages each worker payload in a manifest-addressed RID slot, and its embedded manifest selects that exact slot. Obsolete dependencies in a prior slot are neither loaded nor silently adopted. Resolver tests retain full digest/inventory checks inside the selected slot. Installer-shaped protected LocalAppData sources exercise contained preparation into the executor-private persistent code cache without relaxing source denies or expanding data rights. Native metadata admission and hardlink fixtures use extended-length Windows paths, so long worktrees do not depend on the machine's `LongPathsEnabled` policy. The metadata regression also denies file-data reads to verify that long-path support preserves attributes-only admission. The resolver and MXC reparse tests create real symbolic links. Run these tests with symbolic-link privileges (Developer Mode or an elevated test process); without those privileges they fail during fixture creation. Do not replace their alias-rejection assertions with skips or change machine policy automatically. Source and cached-file pins, full digests/inventories, atomic publication, tamper/alias rejection, bounded inventory and failure/cancellation ownership are covered at the shared owner boundary. The legacy per-operation-copy negative control fails the repeated-success regression on distinct executable directories; the cache keeps the same published native file identity across calls. Ordinary test-owned subprocess fixtures exercise concurrent builders, abrupt parent death after publication, real preparation interruption, restart backpressure and distinct manifest versions. Their fixed 64 MiB dependency fixture gives interruption a real copy boundary without launching any code from the payload. They use the existing resolver filesystem constructor, not a fake trust or permission implementation. These tests do not prove power-loss durability, MXC containment or installed-worker runtime loading. Active-failure regressions observe nonempty output during that real 64 MiB copy, then cancel or place a native byte-range read lock on the pinned source. They prove same-version retry after identity-backed unpublished rollback and that a real copy I/O error survives the executor as a cache-specific command failure. Separate failure-first fixtures inject added, replaced, hardlinked or reparse children, and a create obstacle containing an independently identified foreign marker. They do not also request cancellation: verification may observe tampering before a later cancellation, and that failure must remain a typed cache cleanup failure. Cancellation-first cases hold a real apphost verification open pending on a native file oplock. They add a foreign child, retain a non-delete-sharing read handle, or independently change the native EA, then request cancellation before acknowledging the pending open. These cases prove both the original cancellation token and the distinct cleanup refusal, without depending on mutation-versus-cancellation scheduling or supplying a fake owner result. Cleanup must retain the exact foreign, replacement, alias and locked file identities and bytes, and reject execution on retry. A changed native EA blocks rollback of otherwise owned children, rather than deleting an entry whose publication is no longer disproven. A matching no-obstacle control requires ordinary cancellation, successful rollback and same-version retry; the oplock fixture itself must not manufacture a cleanup failure. Post-publication cancellation uses a read adapter over real pinned streams. The fixture independently observes the published native EA and matches the stream's full identity before holding read completion. A handshake requests cancellation before verification resumes, without replacing publication or the digest. Assertions require the original token, no inner exception, unchanged entry and child identities, and verified reuse by the default reader. A separate no-cancellation control proves that the held read can complete successfully. A read-only child handle held across completion must not block publication or weaken the lease's write/delete veto. The corresponding pre-fix owner case failed with the directory-rename publisher. Other owner cases inspect the non-executable initial EA in an unowned legacy root, reject complete but unpublished code, and reject added, tampered or identically replaced children without cancellation. Abrupt process death at directory creation must leave that initial veto, never a recordless legacy fallback. These ordinary in-process failure cases are distinct from abrupt parent death, whose persisted preparation remains blocked. Upgrade regressions advance through thirteen independently versioned manifests, retain eight real resolver leases, and run simultaneous new-version builders against an old pinned lease. Native identity checks cover added, replaced, hardlinked, reparse, sparse and missing children, caller JSON ownership claims, corrupt/transplanted native records, replaced entry directories and a copied root record with the wrong native identity. Unknown legacy roots are never adopted. Real Windows readonly-deletion failures exercise both first-apphost rejection (unchanged inventory and metadata) and partial deletion with a persisted `deleting` record across subprocess exit. A same-version resolver must finish an owned interrupted deletion before publishing a fresh complete entry, without requiring another upgrade. Readonly or actively held remaining children block that recovery without recreating the apphost or changing the remaining inventory. Both same-version recovery and later-version admission reject a substituted remaining child. These are native disposition failure/restart tests, not manufactured SDK termination receipts, forced mid-instruction process death or power-loss proof. `WorkerCacheProcessTests` launches the actual built FileWorker apphost with `--stdio`, reaches the binary fetch listener, and retains no resolver lease. It independently demonstrates that a nonshared read/delete open can succeed while the worker is live, yet non-POSIX image disposition returns Windows error 5. Capacity admission must preserve the complete live entry, permit verified reuse, and reclaim it only after the actual worker exits. The ordinary fixture uses the framework-dependent apphost; current self-contained archive execution, installed activation and native MXC policy remain separate proof boundaries. The worker test project's aliased Shared reference exercises the real resolver without conflating its types with the worker's linked wire contracts. Its cache is isolated under the system temp directory rather than the worktree because apphost launch has a separate Windows process-path limit. Process evidence uses `RUNNER_TEMP` in CI and the owned fixture directory when that variable is absent locally. Concurrent file/system.run requests, private-data exclusion under broad write grants, caller override rejection and logical/physical private denies are covered separately from immutable sensitive-root protection. The worker suite covers native bindings, retained handles, no-clobber creation, in-place overwrite identity, preflight non-mutation, duplex start/ACK/EOF semantics, gzip-tar interoperability, data-only policy and retained-upload cleanup. Native regressions cover no-follow junction listing with whole-archive rejection, final-parent ACL inheritance with a synthetic SID, private staging and uploads, atomic EA creation, interrupted create/delete phases, cancellation, replacement/unknown-content preservation and complete legacy-record migration. These interruption fixtures close real native handles in adverse parent/child order; they do not kill a worker process or prove power-loss recovery. Native EA support through the installed MXC worker remains a separate runtime boundary. Shared transport fixtures cover generic Gateway casing/depth and large results, explicit file serialization failures on both invoke envelopes, unchanged generic MCP response sizes, bounded deep file requests, and remembered-only retention scheduling. Shared tests use independent upstream contract literals for base64, bindings and metadata, plus the private process codec and typed semantic outcome. Shared executor tests hold a real Windows directory handle to prove that transient scratch sharing violations settle before completion; a persistently locked directory produces a bounded deferred-cleanup warning instead of holding an execution slot indefinitely. Later admission retries only the ended operation whose native identity/ancestry it still owns. Cleanup regressions preserve live/unrelated/replaced/reparse targets, and enforce the 16-lease backpressure bound. An uncertain launch/finalization retains its payload pins until owner shutdown, without authorizing deletion. Scratch parent-process crash recovery remains unimplemented; a later process never adopts old operation directories. Published code persists by design and is reverified, never automatically replaced or pruned. Uncertain preparation is non-executable and consumes cache capacity; it is not guessed safe to delete. See [the cache and cleanup contract](WINDOWS_NODE_TESTING.md) for admission bounds and safe repair requirements. Neither ordinary owner suite establishes SDK interception, real installed-distribution loading, native ARM64 execution, Gateway/MCP framing, or visible Sandbox status. Those remain distinct build, transport and runtime proof boundaries. The opt-in `MxcRuntimeNamespaceNativeTests` adds actual SDK deny/child-grant and overlapping-policy proof, with owned process identities and fixture ACL checks. It is not whole-app or installed-worker proof. The two original cases use PowerShell `-File`. A separately named `NativeSdk_InlinePayloadControlExercisesTheSameNamespaceAssertions` diagnostic case sends the same synthetic script and literal arguments through `-EncodedCommand`, retaining the same direct-SDK policy, readiness handshake, deny/scratch assertions and ACL checks. It does not set execution policy, replace a failed file-based case, or establish script-file authorization. Run and report it alongside the original cases when investigating startup `AuthorizationManager` failures. Inline success with file-based failure narrows the failing boundary; it is not overall namespace or FileWorker acceptance. The artifacts identify `LaunchMode` and use a distinct inline-control prefix. Ordinary `MxcNamespaceLaunchTests` protect the unchanged file argv and literal inline payload construction without launching PowerShell or MXC. CLI `FileWorkerProcessTests` add the concrete process boundary: the production MCP listener, file capability, shared executor, private binary protocol and actual compiled worker are called through `winnode` in isolated test folders. All eight commands, streaming create/fetch, create-exists and semantic failures are exercised. The test deliberately selects the positive-unsupported host adapter; it does not assert that the current machine lacks MXC support and does not launch Companion, MXC, a Gateway or an installer. Payload trust is covered separately by resolver/packaging checks, not by the test resolver. ### Regression authoring Use the [test-audit skill](../.agents/skills/test-audit/SKILL.md) when adding or reviewing tests. Start with the observable contract, its production owner, a credible regression, and the risk not covered by existing tests. - Assert independent expected values, not values computed by the same helper. A mock should control external input, not supply the result being asserted. - For a bug fix, show the test failing for the intended pre-fix defect and passing after repair, or explicitly report unavailable baseline proof. A fixture setup failure is not the negative control. - Prefer one faithful owner regression. Add another layer only for a distinct transport, persistence, permission, lifecycle, or rendering risk. - Reuse `TempDirectory`, `EnvironmentScope`, `CliHarness`, and the [Gateway fixture](GATEWAY_FIXTURE_TESTING.md) rather than introducing duplicate state or process hosts. Bind test listeners to loopback. - Keep legitimate independent static contracts. WinUI source-shape guards protect concrete prohibited patterns and require the [ledger's retirement condition](ARCHITECTURE.md#how-to-use-this-document). The shipped winnode-to-capability registry check is not disposable. - A snapshot is not a rendering assertion. The fixture's session/history tests observe the selected mounted controls and visible text; they still do not prove live Gateway pairing, provider behavior, installer state, or MXC. - Preserve serial native-input/desktop constraints. Measure cost when making a performance claim; do not hide a race with sleeps, blind retries, weaker assertions, or blanket skips. ### Accepting result reports `Invoke-CiTest.ps1` and `run-proof-tests.ps1` use invocation-specific result directories. Their expected TRX must be fresh, parseable, internally consistent, and contain actual executed tests; successful process exit alone is insufficient. File modification times and embedded TRX invocation times permit one second of clock granularity tolerance. Reports older than that remain stale; each run still uses a fresh invocation directory and validates the embedded timestamps. Generic custom proof requires every selected result to pass. Ordinary CI may report supported skipped cases alongside executed passing tests, but all-skipped or zero-execution runs fail. Existing hosted MXC skip policy is not native MXC proof. Skipped counts come from actual result rows and must match total minus executed. VSTest may leave its aggregate `notExecuted` counter at zero even when skipped rows are present; a contradictory nonzero aggregate still fails validation. See [local execution evidence](PROOF_POOLS.md#validate-local-execution-evidence) for inventory-bound summaries and their assurance limits. ## Running tests Native reveal tests require Windows client-area animations to be enabled. The required `AnimationPreferenceEnabled` CI selection establishes that preference in `Invoke-CiTest.ps1 -AnimationPreference Enabled -RequireAllTestsPassed`, not in production code or the testhost. Ordinary UI tests keep their original Windows preference. The parent records and verifies the original value, holds a per-user/session cross-process lock, starts the test subprocess with the verified preference, and restores/read-checks the original even when the testhost fails. It never writes the persistent profile setting. Restoration or prerequisite failures fail the command. Ordinary Tray UI wrapper invocations hold the same lock without mutating the preference; unrelated managed suites are not serialized. This coordinates cooperating wrappers, not direct `dotnet test` commands or unrelated Windows processes. Do not terminate the parent wrapper while it owns the preference. A force-killed parent cannot restore it, so recover the recorded original or a known-good setting before proceeding. Detectable mutex abandonment fails closed, but if the last handle disappeared the next owner can create a fresh mutex; this is not durable crash recovery. The separate required `PageHeroAnimationsDisabledTests` step runs with `-AnimationPreference Disabled -RequireAllTestsPassed`; its environment opt-in keeps it out of ordinary runs, while missing, skipped or failed selected proof cannot pass that step. The six animation-enabled regressions retain their native assertions and are not skipped. Local or self-hosted use additionally requires explicit `-AllowUserAnimationPreferenceChange`; app-data isolation does not isolate this Windows logon-session preference. After building, an authorized local focused run can use: ```powershell .\scripts\Invoke-CiTest.ps1 -Project tests\OpenClaw.Tray.UITests ` -Runtime win-arm64 -ResultsDirectory TestResults\PageHero ` -TrxFileName PageHero.trx -Filter 'FullyQualifiedName~PageHeroTests' ` -AnimationPreference Enabled -AllowUserAnimationPreferenceChange -RequireAllTestsPassed ``` ```powershell # Required validation after code changes $env:OPENCLAW_REPO_ROOT = (Get-Location).Path .\scripts\validate-proof-pools.ps1 .\build.ps1 dotnet test .\tests\OpenClaw.Shared.Tests\OpenClaw.Shared.Tests.csproj --no-restore dotnet test .\tests\OpenClaw.Tray.Tests\OpenClaw.Tray.Tests.csproj --no-restore # All local-dev tests in the solution. E2E is intentionally excluded from the # solution and runs in CI before merge; run it locally only when explicitly needed. dotnet test # Explicit local E2E run $env:OPENCLAW_RUN_E2E = "1" dotnet test .\tests\OpenClaw.E2ETests\OpenClaw.E2ETests.csproj -r win-x64 # Formal MXC validation path. This sets the required integration/E2E env vars # itself and fails when MXC proofs skip unless -AllowSkip is explicitly supplied. .\scripts\validate-mxc-e2e.ps1 # Accessibility scan, matching the CI quality gate. dotnet test .\tests\OpenClaw.Tray.UITests\OpenClaw.Tray.UITests.csproj -r win-x64 --filter Category=Accessibility # Single project dotnet test .\tests\OpenClaw.Connection.Tests\OpenClaw.Connection.Tests.csproj # Specific test class dotnet test --filter "FullyQualifiedName~MenuDisplayHelperTests" # Verbose output dotnet test --logger "console;verbosity=detailed" ``` In a fresh worktree, run the project once without `--no-restore` or build it first so `dotnet test --no-restore` cannot no-op before `bin\` exists. Test-owned TCP listeners must bind only to loopback addresses. A successful wildcard bind from `testhost.exe` can trigger a per-worktree Windows Defender Firewall consent dialog and block unattended validation. Tests for production LAN-bind conflict handling should occupy loopback first so the production wildcard bind fails without opening a network-reachable listener. ## Not fully covered by automated tests - Real shell tray hover/click behavior against Explorer. - Full live gateway/node pairing against a remote gateway. - Long-running soak behavior for reconnects, high-frequency activity updates, and memory usage over multi-day sessions. - Manual visual acceptance for complex WinUI surfaces where screenshot comparison would be brittle. For these gaps, affected changes must include the manual UI/MCP smoke described in `AGENTS.md` and `.agents/skills/openclaw-proof-validation/SKILL.md`: launch the tray from the current worktree, use computer-use / desktop automation for visible WinUI paths, and validate local MCP with `winnode --list-tools` plus the changed command when node capabilities are involved. When node command surfaces change, include `OpenClaw.WinNode.Cli.Tests` in focused validation because `SkillMdDriftTests` guards the capability registry, MCP descriptions, and `winnode` skill reference from drifting apart.