From 3d21f25e7239d4e5555a93c955e2a63f6148ebf5 Mon Sep 17 00:00:00 2001 From: adpa-ms <> Date: Wed, 19 Aug 2026 17:06:33 -0700 Subject: [PATCH] Require a positive LocalAgentUser feature level for isolation_session Class registration alone was reported as availability, so a host that registers the WinRT class but does not support the capability the lifecycle needs was advertised as able to run isolation sessions. Query the feature level and treat anything but a positive value, including a failed query, as unavailable. The test-script README, the suite comments and the skip messages each enumerated the causes behind a false read of the probe field. Drop the enumerations rather than extend them: the text around them is about how the suites skip, not why. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../common/src/availability.rs | 37 ++++++++++--------- tests/scripts/README.md | 4 +- .../run_isolation_session_resize_smoke.ps1 | 2 +- ...un_isolation_session_state_aware_tests.ps1 | 6 +-- tests/scripts/run_isolation_session_tests.ps1 | 9 +---- 5 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/backends/isolation_session/common/src/availability.rs b/src/backends/isolation_session/common/src/availability.rs index d8226c45f..0bd3d65e9 100644 --- a/src/backends/isolation_session/common/src/availability.rs +++ b/src/backends/isolation_session/common/src/availability.rs @@ -3,13 +3,13 @@ //! IsolationSession host-availability probe. //! -//! Availability is whether the in-proc `Windows.AI.IsolationSession` -//! `IsoSessionOps` WinRT class is registered on the OS (its activation factory -//! resolves), matching PR #761 rather than gating on a Windows build number. +//! Available when `IsoSessionOps` activates and reports a feature level above +//! zero for `LocalAgentUser`, which the lifecycle requires. No build number is +//! consulted. use std::sync::OnceLock; -use isolation_session_bindings::bindings::IsoSessionOps; +use isolation_session_bindings::bindings::{IsoSessionFeature, IsoSessionOps}; use windows::Win32::System::Com::{CoInitializeEx, CoUninitialize, COINIT_MULTITHREADED}; use windows_core::HRESULT; @@ -17,25 +17,23 @@ static AVAILABLE: OnceLock = OnceLock::new(); /// Cached for the process; never requires elevation. pub fn is_isolation_session_available() -> bool { - *AVAILABLE.get_or_init(|| available_from(probe_activation())) + *AVAILABLE.get_or_init(|| available_from(probe_feature_level())) } -/// Split from [`probe_activation`] so the decision is testable without COM/WinRT. -/// Any activation failure (class not registered, or the OS feature gate off) -/// means not available here. -fn available_from(activation: Result<(), HRESULT>) -> bool { - activation.is_ok() +/// Split from [`probe_feature_level`] so the decision is testable without +/// COM/WinRT. A failed probe or a zero level means not available. +fn available_from(probe: Result) -> bool { + matches!(probe, Ok(level) if level > 0) } -fn probe_activation() -> Result<(), HRESULT> { +fn probe_feature_level() -> Result { // Guard uninitializes on drop, so a panic in `IsoSessionOps::new()` still - // balances `CoInitializeEx`. The `_ops` handle drops before `_apartment` + // balances `CoInitializeEx`. The `ops` handle drops before `_apartment` // (reverse declaration order), preserving COM's create-before-uninit rule. let _apartment = ComApartment::enter(); - match IsoSessionOps::new() { - Ok(_ops) => Ok(()), - Err(e) => Err(e.code()), - } + let ops = IsoSessionOps::new().map_err(|e| e.code())?; + ops.GetFeatureLevel(IsoSessionFeature::LocalAgentUser) + .map_err(|e| e.code()) } /// Owns the COM apartment for the duration of a probe and uninitializes it on @@ -71,8 +69,11 @@ mod tests { use windows::Win32::Foundation::{CLASS_E_CLASSNOTAVAILABLE, REGDB_E_CLASSNOTREG}; #[test] - fn only_successful_activation_means_available() { - assert!(available_from(Ok(()))); + fn availability_needs_a_positive_feature_level() { + assert!(available_from(Ok(1))); + assert!(!available_from(Ok(0))); + // Pins the predicate as `> 0` rather than `!= 0`. + assert!(!available_from(Ok(-1))); assert!(!available_from(Err(CLASS_E_CLASSNOTAVAILABLE))); assert!(!available_from(Err(REGDB_E_CLASSNOTREG))); assert!(!available_from(Err(HRESULT(0x8000_4005u32 as i32)))); diff --git a/tests/scripts/README.md b/tests/scripts/README.md index f3e3d534a..62887713b 100644 --- a/tests/scripts/README.md +++ b/tests/scripts/README.md @@ -97,9 +97,7 @@ node scripts/ci/resolve-validation-test-matrix.mjs --plan nightly **Skip semantics.** Several suites degrade gracefully on an unsupported host: the IsolationSession suites decide availability from a single `wxc-exec --probe` -read of `probes.isolationSessionAvailable` (covering both a host that cannot -activate the API and a binary built without `--features isolation_session`), -print `SKIPPED`, and exit 0. +read of `probes.isolationSessionAvailable`, print `SKIPPED`, and exit 0. Because a skip exits 0 and the dispatchers propagate only the exit code, **a green CI job does not by itself prove the suite ran.** Anything treating these diff --git a/tests/scripts/run_isolation_session_resize_smoke.ps1 b/tests/scripts/run_isolation_session_resize_smoke.ps1 index ac1650c5e..5d40eae89 100644 --- a/tests/scripts/run_isolation_session_resize_smoke.ps1 +++ b/tests/scripts/run_isolation_session_resize_smoke.ps1 @@ -148,7 +148,7 @@ function Get-IsolationSessionProbe { $probeResult = Get-IsolationSessionProbe -Exe $WxcExec if ($probeResult.Status -eq 'unavailable') { - Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false (host cannot activate the isolation session API, or this binary was built without --features isolation_session)" -ForegroundColor Yellow + Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false" -ForegroundColor Yellow exit 0 } if ($probeResult.Status -ne 'available') { diff --git a/tests/scripts/run_isolation_session_state_aware_tests.ps1 b/tests/scripts/run_isolation_session_state_aware_tests.ps1 index 10ad8438d..2a7af4dd2 100644 --- a/tests/scripts/run_isolation_session_state_aware_tests.ps1 +++ b/tests/scripts/run_isolation_session_state_aware_tests.ps1 @@ -21,9 +21,7 @@ PowerShell on that host. Prerequisite probe (skip if unavailable -- not a failure): - - `wxc-exec --probe` reports `probes.isolationSessionAvailable`. This - single signal covers an OS that cannot activate the isolation session - API as well as a binary built without `--features isolation_session`. + - `wxc-exec --probe` reports `probes.isolationSessionAvailable`. .PARAMETER WxcExePath Path to wxc-exec.exe. Default probes the host-arch target dir, then @@ -148,7 +146,7 @@ function Get-IsolationSessionProbe { $probeResult = Get-IsolationSessionProbe -Exe $WxcExec if ($probeResult.Status -eq 'unavailable') { - Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false (host cannot activate the isolation session API, or this binary was built without --features isolation_session)" -ForegroundColor Yellow + Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false" -ForegroundColor Yellow exit 0 } if ($probeResult.Status -ne 'available') { diff --git a/tests/scripts/run_isolation_session_tests.ps1 b/tests/scripts/run_isolation_session_tests.ps1 index f63c239fe..c41f258fe 100644 --- a/tests/scripts/run_isolation_session_tests.ps1 +++ b/tests/scripts/run_isolation_session_tests.ps1 @@ -114,12 +114,7 @@ Write-Host "Configs: $ConfigDir`n" -ForegroundColor Gray # ---------------- Backend-availability probe ---------------- # # Availability is decided by a single call to `wxc-exec --probe`, which reports -# `probes.isolationSessionAvailable`. That one signal covers both reasons this -# suite can be unrunnable: -# -# - the host cannot activate the isolation session API, or -# - wxc-exec was built without `--features isolation_session`, in which case -# the field stays false because the backend is not compiled in. +# `probes.isolationSessionAvailable`. # # This deliberately replaces the earlier checks for a specific DLL and a # hard-coded WinRT activatable-class registry key. Those named a runtime class @@ -199,7 +194,7 @@ function Get-IsolationSessionProbe { $probeResult = Get-IsolationSessionProbe -Exe $WxcExec if ($probeResult.Status -eq 'unavailable') { - Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false (host cannot activate the isolation session API, or this binary was built without --features isolation_session)" -ForegroundColor Yellow + Write-Host "SKIPPED: wxc-exec --probe reports isolationSessionAvailable=false" -ForegroundColor Yellow exit 0 } if ($probeResult.Status -ne 'available') {