Uh oh!
There was an error while loading. Please reload this page.
[WSLC] Promote from experimental to the stable config surface - #1064
[WSLC] Promote from experimental to the stable config surface#1064Soham Das (SohamDas2021) wants to merge 10 commits into
Conversation
WSLc accepted four policy fields, carried them into the runner, and then
never read them. A caller asking for a UI lockdown, a firewall enforcement
mode, inbound local-network access, or policy preservation got a container
that silently did not have the posture they asked for. Close each hole with
an explicit policy_validation rejection.
Every rejection aborts before anything is created. All three entry surfaces
validate first: ScriptRunner::run ahead of execute, SandboxBackend::spawn
ahead of start_container, and the state-aware dispatcher ahead of each phase
body -- and connect_daemon() lives inside provision(), so a refused provision
never even spawns the daemon.
ui rejected on every phase, both surfaces
network.allowLocalNetwork rejected at state-aware provision (one-shot
already rejected it)
network.enforcementMode firewall/both rejected; capabilities accepted
lifecycle.preservePolicy rejected on one-shot (state-aware already
rejects the whole lifecycle section at parse)
ui is rejected by presence, not value. UiPolicy::default() is full lockdown,
so an explicitly supplied lockdown ui is indistinguishable by value from an
absent one -- a value-based check would let the single most restrictive
request a caller can write through unenforced. This uses the parse-derived
ContainerPolicy::ui_specified flag, mirroring IsolationSession.
enforcementMode and preservePolicy are rejected by value instead, because
their defaults honestly describe WSLc's behavior: an all-or-nothing container
network with nothing per-host to enforce, and auto-remove teardown. Refusing
those for mere presence would be dishonest.
destroyOnExit stays honored -- it selects WSLC_CONTAINER_FLAG_AUTO_REMOVE --
so only preservePolicy is refused. A blanket lifecycle rejection would have
broken the wslc_destroy_on_exit_{true,false} configs; a test pins both values
still passing.
The two allowLocalNetwork messages differ deliberately. One-shot points
callers at experimental.wslc portMappings, but WslcProvisionPhase has no
portMappings field at all, so repeating that advice on the state-aware
surface would be a lie.
Rejection ordering is filesystem -> ui -> network, documented in the policy.rs
module header and pinned by precedence tests.
No wire, schema, or parser-gating changes: this is a domain-model behavior
change only, so it lands while WSLc is still experimental and the nightly
WSLc suite exercises the new rejections before the surface moves.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c23ddc86-3848-452e-8355-e11d2ffa9b7fCloses#1049 WSLc is now configured through the top-level `wslc` section instead of `experimental.wslc`, and selecting `"containment": "wslc"` no longer requires `--experimental` / `{ experimental: true }` / `SandboxRequest::set_experimental(true)`. This mirrors the Seatbelt promotion precedent. Wire + schema - Move `wslc` from `wire::Experimental` to the `MxcConfig` root, and from `experimental.rs` to `stable.rs` in the 0.9.0-alpha closed contract. - Keep `experimental.wslc` as a rejection alias so a pre-promotion config fails with an actionable migration message rather than being silently ignored. - Regenerate the two dev schemas and the two TypeScript wire oracles. Parser + dispatch - Generalize the state-aware dispatcher with `SectionRoot` (`Experimental` | `Stable`); `StatefulSandboxBackend::SECTION_ROOT` defaults to `Experimental` and only `WslcStateAwareRunner` overrides it. `ParsedStateAwareRequest` gains `stable_raw` for stable-rooted backends. - Drop the experimental gate for WSLc on both the one-shot and state-aware surfaces. TypeScript SDK - Move `wslc` out of `ContainerConfig.experimental` and off `ExperimentalBackends`; add `BACKEND_SECTION_ROOT`, the TS twin of the Rust `SectionRoot`, so envelopes are built at the right root. - Add a `wslcAvailable` probe fact end to end (Rust probe -> engine -> `wxc-exec --probe` -> SDK). Without it, removing the experimental gate unmasked the fact that `wslc` was never in `availableMethods`, which would have made the backend unreachable through the SDK. Rust SDK, corpus, harnesses and docs - Drop the `set_experimental(true)` requirement from `mxc-sdk`. - Hoist `experimental.wslc` to top-level `wslc` in 31 test configs. - Remove `--experimental` from the WSLc test harnesses. - Update the WSLc docs, schema reference, SDK READMEs, setup script and `.github/copilot-instructions.md`. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9f02fa89-c89e-4a5d-b80d-5130252d3654
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
The promotion updated the WSLc-specific guides and SDK READMEs but left
four documents still describing WSLc as experimental. Two of them are
actively wrong rather than merely stale:
- README.md listed `wslc` among the "Experimental backends" that require
`{ experimental: true }` or `--experimental`. This is the repo's front
page, and the claim is now false.
- docs/linux-wsl-roadmap-june-2026.md carried a copy-pasteable JSON
example nesting `wslc` under `experimental` -- the exact shape the
parser now rejects with the migration error. Its "Notes" section also
still listed WSLC alongside Bubblewrap as awaiting promotion.
The remaining two were internally contradictory after the promotion:
- docs/wsl/wsl-container-support-plan.md had its preamble rewritten to
say WSLc is stable while three JSON examples, the dispatch snippet,
the architecture diagram, and a runnable CLI command all still showed
the experimental gate. (The mixed top-level/nested examples predate
this branch; this commit makes the whole document consistent.)
- sdk/dotnet/README.md described `WslcContainment` as selecting "the
experimental WSLC backend" and stated `Experimental` is required. The
sample also set `Experimental = true`, which is now dead weight --
`resolve_runner` gates only MicroVm, WindowsSandbox, IsolationSession,
and Hyperlight, and `require_experimental_optin` gates only
WindowsSandbox and IsolationSession.
Docs only; no behavior change. All 14 edited JSON blocks re-verified as
parseable.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9f02fa89-c89e-4a5d-b80d-5130252d3654…ing' into sohamdas2021-wslc-promotion-plan
There was a problem hiding this comment.
Pull request overview
Promotes WSLc to the top-level stable configuration surface and removes its experimental opt-in. The current 173-file diff also contains substantial unrelated C#, Bubblewrap, and LXC changes.
Changes:
- Moves WSLc configuration, dispatch, schemas, SDKs, tests, and documentation to the stable surface.
- Adds WSLc availability probing and native sidecar packaging.
- Includes unrelated runtime and SDK changes outside #1049’s stated scope.
Reviewed changes
Copilot reviewed 105 out of 107 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
.github/copilot-instructions.md | Updates WSLc architecture guidance. |
.github/workflows/Build.Linux.Job.yml | Adds Bubblewrap SDK tests. |
.github/workflows/SDK.Dotnet.Test.Job.yml | Adds optional-backend lanes. |
.github/workflows/Versioning.Checks.Job.yml | Adds C# API parity check. |
README.md | Lists WSLc as stable. |
build.bat | Stages WSLc runtime sidecars. |
docs/linux-wsl-roadmap-june-2026.md | Updates WSLc promotion status. |
docs/schema.md | Documents top-level WSLc. |
docs/state-aware-lifecycle/mxc-state-aware-sandbox-api.md | Updates lifecycle wire documentation. |
docs/wsl/wsl-container-getting-started.md | Updates WSLc usage. |
docs/wsl/wsl-container-support-plan.md | Updates WSLc design status. |
docs/wsl/wslc-state-aware.md | Removes experimental requirement. |
schemas/dev/mxc-config.schema.0.9.0-alpha.json | Regenerates exact schema. |
schemas/dev/mxc-config.schema.0.9.0-dev.json | Regenerates rolling schema. |
schemas/schema-version.json | Adds WSLc lifecycle version. |
scripts/check-dotnet-api-parity.js | Adds C# parity validation. |
scripts/check-dotnet-bindings-codegen.js | Expands binding checks. |
scripts/setup-wslc.ps1 | Updates configuration paths. |
scripts/versioning/check-schema-versions.js | Checks WSLc/C# versions. |
sdk/dotnet/CHANGELOG.md | Documents broad C# changes. |
sdk/dotnet/Microsoft.Mxc.Sdk.Sample/Program.cs | Uses explicit request containment. |
sdk/dotnet/Microsoft.Mxc.Sdk.Tests/JsonAssert.cs | Adds JSON golden comparison. |
sdk/dotnet/Microsoft.Mxc.Sdk.Tests/Microsoft.Mxc.Sdk.Tests.csproj | Embeds shared fixtures. |
sdk/dotnet/Microsoft.Mxc.Sdk.Tests/MxcLifecycleE2ETests.cs | Expands lifecycle coverage. |
sdk/dotnet/Microsoft.Mxc.Sdk.Tests/MxcLifecycleTests.cs | Tests lifecycle envelopes/features. |
sdk/dotnet/Microsoft.Mxc.Sdk.Tests/MxcSandboxProcessTests.cs | Expands streaming tests. |
sdk/dotnet/Microsoft.Mxc.Sdk.Tests/MxcSandboxTests.cs | Tests requests and discovery. |
sdk/dotnet/Microsoft.Mxc.Sdk.Tests/SandboxAdapterTests.cs | Tests SDK adapters. |
sdk/dotnet/Microsoft.Mxc.Sdk.Tests/SandboxPolicyDiscoveryTests.cs | Tests policy discovery. |
sdk/dotnet/Microsoft.Mxc.Sdk/Microsoft.Mxc.Sdk.csproj | Builds/packages optional backends. |
sdk/dotnet/Microsoft.Mxc.Sdk/MxcLifecycle.cs | Adds multi-backend lifecycle support. |
sdk/dotnet/Microsoft.Mxc.Sdk/MxcSandbox.cs | Expands managed SDK surface. |
sdk/dotnet/Microsoft.Mxc.Sdk/MxcSandboxProcess.cs | Changes streaming lifecycle behavior. |
sdk/dotnet/Microsoft.Mxc.Sdk/Native/SafeHandles.cs | Adds stream-closer handle. |
sdk/dotnet/Microsoft.Mxc.Sdk/NetworkProxyPolicyJsonConverter.cs | Adds proxy-union conversion. |
sdk/dotnet/Microsoft.Mxc.Sdk/PlatformDiscovery.cs | Adds typed platform discovery. |
sdk/dotnet/Microsoft.Mxc.Sdk/SandboxAdapters.cs | Adds injectable adapters. |
sdk/dotnet/Microsoft.Mxc.Sdk/SandboxId.cs | Documents WSLc identifiers. |
sdk/dotnet/Microsoft.Mxc.Sdk/SandboxPolicy.cs | Expands managed policy model. |
sdk/dotnet/Microsoft.Mxc.Sdk/SandboxPolicyDiscovery.cs | Adds policy discovery helpers. |
sdk/dotnet/Microsoft.Mxc.Sdk/SandboxRequest.cs | Adds explicit containment requests. |
sdk/dotnet/Microsoft.Mxc.Sdk/SchemaVersions.cs | Exposes schema constants. |
sdk/dotnet/Microsoft.Mxc.Sdk/StateAwareTypes.cs | Adds backend-specific lifecycle types. |
sdk/dotnet/README.md | Documents expanded C# API. |
sdk/node/README.md | Updates WSLc SDK documentation. |
sdk/node/src/generated/v0_9_0_alpha/wire.ts | Regenerates exact wire types. |
sdk/node/src/generated/wire.ts | Regenerates rolling wire types. |
sdk/node/src/platform.ts | Reports WSLc availability. |
sdk/node/src/sandbox.ts | Emits top-level WSLc config. |
sdk/node/src/state-aware-helper.ts | Supports stable section roots. |
sdk/node/src/state-aware-types.ts | Updates WSLc paths. |
sdk/node/src/types.ts | Removes WSLc from experimental types. |
sdk/node/tests/integration/wslc-e2e.test.ts | Updates WSLc E2E configuration. |
sdk/node/tests/unit/conformance-helpers.ts | Updates conformance documentation. |
sdk/node/tests/unit/sandbox.test.ts | Tests stable WSLc behavior. |
sdk/node/tests/unit/state-aware.test.ts | Tests stable lifecycle envelopes. |
sdk/node/tests/unit/wire-conformance-state-aware.test.ts | Updates lifecycle conformance. |
src/Cargo.lock | Records dependency changes. |
src/backends/appcontainer/common/src/probe.rs | Adds WSLc probe fact. |
src/backends/bubblewrap/common/src/bwrap_command.rs | Adds process-tree teardown flag. |
src/backends/bubblewrap/common/src/bwrap_runner.rs | Updates Bubblewrap teardown handling. |
src/backends/bubblewrap/common/src/bwrap_version.rs | Documents required flag version. |
src/backends/lxc/common/src/lxc_runner.rs | Adds network-readiness enforcement. |
src/backends/wslc/common/src/daemon_client.rs | Resolves daemon beside native module. |
src/backends/wslc/common/src/daemon_protocol.rs | Updates public-path documentation. |
src/backends/wslc/common/src/policy.rs | Updates WSLc policy paths. |
src/backends/wslc/common/src/state_aware.rs | Selects stable section root. |
src/backends/wslc/common/src/wsl_container_runner.rs | Updates stable WSLc paths. |
src/backends/wslc/common/src/wslc_bindings.rs | Resolves SDK DLL beside module. |
src/core/mxc-sdk/Cargo.toml | Updates feature/test dependencies. |
src/core/mxc-sdk/README.md | Removes WSLc opt-in requirement. |
src/core/mxc-sdk/src/lib.rs | Updates WSLc API documentation. |
src/core/mxc-sdk/tests/streaming_bubblewrap.rs | Adds Bubblewrap streaming coverage. |
src/core/mxc_config_contract/src/dev/experimental.rs | Removes experimental WSLc contract. |
src/core/mxc_config_contract/src/dev/mod.rs | Re-exports stable WSLc types. |
src/core/mxc_config_contract/src/dev/one_shot.rs | Adds top-level WSLc field. |
src/core/mxc_config_contract/src/dev/stable.rs | Defines stable WSLc contract. |
src/core/mxc_config_contract/src/dev/state_aware/provision/wslc.rs | Moves provision configuration. |
src/core/mxc_config_contract/tests/v0_9_0_alpha.rs | Registers WSLc tests. |
src/core/mxc_config_contract/tests/v0_9_0_alpha/experimental.rs | Removes old test module. |
src/core/mxc_config_contract/tests/v0_9_0_alpha/experimental/root.rs | Tests alias rejection. |
src/core/mxc_config_contract/tests/v0_9_0_alpha/fixtures/one_shot/invalid/port_out_of_range.json | Updates invalid WSLc fixture. |
src/core/mxc_config_contract/tests/v0_9_0_alpha/optional_fields.rs | Updates WSLc optional paths. |
src/core/mxc_config_contract/tests/v0_9_0_alpha/state_aware/provision/wslc.rs | Tests WSLc provision contract. |
src/core/mxc_config_contract/tests/v0_9_0_alpha/wslc.rs | Tests stable WSLc contract. |
src/core/mxc_config_contract/tests/version_boundaries.rs | Registers boundary tests. |
src/core/mxc_config_contract/tests/version_boundaries/experimental.rs | Removes experimental boundary case. |
src/core/mxc_config_contract/tests/version_boundaries/state_aware.rs | Updates lifecycle boundary fixture. |
src/core/mxc_config_contract/tests/version_boundaries/wslc.rs | Adds WSLc version boundaries. |
src/core/mxc_engine/src/dispatch.rs | Removes streaming opt-in gate. |
src/core/mxc_engine/src/guarded_capture.rs | Reuses module-path resolver. |
src/core/mxc_engine/src/lib.rs | Exports WSLc availability. |
src/core/mxc_engine/src/platform.rs | Adds serializable WSLc support. |
src/core/mxc_engine/src/policy.rs | Builds top-level WSLc config. |
src/core/mxc_engine/src/run.rs | Removes one-shot opt-in gate. |
src/core/mxc_engine/src/state_aware.rs | Removes lifecycle opt-in gate. |
src/core/wxc/src/main.rs | Publishes WSLc probe fact. |
src/core/wxc_common/src/config_contract_adapters/dev/one_shot.rs | Adapts stable WSLc config. |
src/core/wxc_common/src/config_contract_adapters/dev/one_shot_tests/experimental.rs | Updates adapter tests. |
src/core/wxc_common/src/config_contract_adapters/dev/state_aware.rs | Adapts stable lifecycle config. |
src/core/wxc_common/src/config_contract_adapters/dev/state_aware_tests/provision.rs | Updates provision adapter tests. |
src/core/wxc_common/src/config_contract_adapters/v0_6.rs | Initializes new wire field. |
src/core/wxc_common/src/config_contract_adapters/v0_7.rs | Initializes new wire field. |
src/core/wxc_common/src/config_contract_adapters/v0_8.rs | Initializes new wire field. |
src/core/wxc_common/src/config_parser.rs | Parses/rejects promoted WSLc shapes. |
src/core/wxc_common/src/models.rs | Moves WSLc into request model. |
src/core/wxc_common/src/process_util.rs | Adds native module-path resolver. |
src/core/wxc_common/src/state_aware_backend.rs | Adds backend section-root metadata. |
src/core/wxc_common/src/state_aware_dispatch.rs | Dispatches by section root. |
src/core/wxc_common/src/state_aware_request.rs | Retains stable-rooted raw config. |
src/core/wxc_common/src/wire.rs | Promotes WSLc wire section. |
src/ffi/mxc_ffi/Cargo.toml | Adds WSLc feature/dependency. |
src/ffi/mxc_ffi/src/lib.rs | Expands FFI SDK surface. |
src/ffi/mxc_ffi/src/request.rs | Adds request-contract handling. |
src/ffi/mxc_ffi/src/state_aware.rs | Tests shared lifecycle fixtures. |
src/ffi/mxc_ffi/src/streaming.rs | Expands streaming FFI. |
src/ffi/mxc_ffi/tests/ffi.rs | Expands FFI integration coverage. |
src/testing/wxc_e2e_tests/tests/e2e_bubblewrap_characterization.rs | Strengthens timeout teardown test. |
tests/configs/bubblewrap_network_egress_budget_rejected.json | Adds network-budget fixture. |
tests/configs/bubblewrap_readonly_denial.json | Adds read-only denial fixture. |
tests/configs/bubblewrap_teardown_timeout.json | Adds teardown fixture. |
tests/configs/bubblewrap_teardown_timeout_netns.json | Adds network teardown fixture. |
tests/configs/bubblewrap_version_gate.json | Adds version-gate fixture. |
tests/configs/wslc_custom_registry.json | Hoists WSLc section. |
tests/configs/wslc_custom_registry_ghcr.json | Hoists WSLc section. |
tests/configs/wslc_custom_registry_quay.json | Hoists WSLc section. |
tests/configs/wslc_denied_dotdot_alias.json | Hoists WSLc section. |
tests/configs/wslc_denied_masking.json | Hoists WSLc section. |
tests/configs/wslc_destroy_on_exit_false_rejected.json | Hoists WSLc section. |
tests/configs/wslc_destroy_on_exit_true.json | Hoists WSLc section. |
tests/configs/wslc_env_vars.json | Hoists WSLc section. |
tests/configs/wslc_exit_code.json | Hoists WSLc section. |
tests/configs/wslc_filesystem.json | Hoists WSLc section. |
tests/configs/wslc_filesystem_object.json | Hoists WSLc section. |
tests/configs/wslc_large_output.json | Hoists WSLc section. |
tests/configs/wslc_most_specific_denied_parent.json | Hoists WSLc section. |
tests/configs/wslc_network_isolated.json | Hoists WSLc section. |
tests/configs/wslc_network_proxy.json | Hoists WSLc section. |
tests/configs/wslc_port_mapping_multiple.json | Hoists WSLc section. |
tests/configs/wslc_port_mapping_tcp.json | Hoists WSLc section. |
tests/configs/wslc_python_hello.json | Hoists WSLc section. |
tests/configs/wslc_python_stdlib.json | Hoists WSLc section. |
tests/configs/wslc_readonly_mount.json | Hoists WSLc section. |
tests/configs/wslc_state_aware_provision.json | Hoists lifecycle WSLc section. |
tests/configs/wslc_state_aware_provision_bridged.json | Hoists lifecycle WSLc section. |
tests/configs/wslc_state_aware_provision_rejected_denied.json | Hoists lifecycle WSLc section. |
tests/configs/wslc_state_aware_provision_rejected_hosts.json | Hoists lifecycle WSLc section. |
tests/configs/wslc_state_aware_provision_rejected_proxy.json | Hoists lifecycle WSLc section. |
tests/configs/wslc_state_aware_provision_with_filesystem.json | Hoists lifecycle WSLc section. |
tests/configs/wslc_stderr.json | Hoists WSLc section. |
tests/configs/wslc_tar_import_docker_save.json | Hoists WSLc section. |
tests/configs/wslc_tar_import_rootfs.json | Hoists WSLc section. |
tests/configs/wslc_timeout.json | Hoists WSLc section. |
tests/examples/wslc_hello_world.json | Updates WSLc example. |
tests/policy/README.md | Documents shared fixtures. |
tests/policy/request-directional-network.json | Adds request golden. |
tests/policy/request-process-container.json | Adds request golden. |
tests/policy/request-wslc.json | Adds WSLc request golden. |
tests/policy/state-aware-wslc-exec.json | Adds WSLc exec golden. |
tests/policy/state-aware-wslc-provision.json | Adds WSLc provision golden. |
tests/scripts/run_bwrap_all_tests.sh | Registers new Bubblewrap tests. |
tests/scripts/run_bwrap_directional_test.sh | Tests network budget rejection. |
tests/scripts/run_bwrap_readonly_denial_test.sh | Tests read-only enforcement. |
tests/scripts/run_bwrap_teardown_test.sh | Tests process-tree teardown. |
tests/scripts/run_bwrap_version_gate_test.sh | Tests bwrap version gating. |
tests/scripts/run_lxc_all_tests.sh | Updates LXC suite. |
tests/scripts/run_wslc_all_tests.ps1 | Removes WSLc experimental flag. |
tests/scripts/run_wslc_denied_masking_test.ps1 | Removes experimental flag. |
tests/scripts/run_wslc_dotdot_alias_test.ps1 | Removes experimental flag. |
tests/scripts/run_wslc_most_specific_test.ps1 | Removes experimental flag. |
tests/scripts/run_wslc_object_test.ps1 | Removes experimental flag. |
tests/scripts/run_wslc_proxy_test.ps1 | Removes experimental flag. |
tests/scripts/run_wslc_state_aware_tests.ps1 | Removes lifecycle opt-in. |
Suppressed comments (1)
sdk/dotnet/README.md:540
- This newly added overview contradicts the promotion: WSLc is no longer experimental, while IsolationSession and Windows Sandbox remain experimental. Distinguish WSLc from those two so managed SDK users do not retain an unnecessary opt-in.
state-aware lifecycle supports IsolationSession, Windows Sandbox, and WSLC on
Windows; all three are experimental).
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
The promotion left three consumers still speaking the pre-promotion wire shape, which surfaced as four CI failures across two root causes. `MxcLifecycle.SetBackendConfig` nested every backend's per-phase config under `experimental.<backend>.<phase>` unconditionally. Now that the parser rejects `experimental.wslc` outright, that produced a `malformed_request` before backend resolution could report anything useful. Give the C# SDK the same section-root switch the TypeScript SDK already carries in `BACKEND_SECTION_ROOT`, so a promoted backend writes a closed top-level `wslc.<phase>` section while the still-experimental backends keep their `experimental` nesting. The switch is exhaustive over the known backends and throws on an undeclared one, so adding a backend without choosing a root fails loudly instead of silently defaulting. That single emitter fix clears three of the four failures: the .NET `WslcBuildSwitch_MatchesNativeAvailabilityAndStagesRuntimeUnit` test on all three platforms, and the Rust `managed_state_aware_goldens_are_accepted_by_native_contract` test in `mxc_ffi`, which feeds the C# golden fixtures back through the native contract. The golden `state-aware-wslc-provision.json` and the `BuildProvisionEnvelope_WslcUsesV08AndNestsImageOptions` assertion move with it; the latter now also asserts that no `experimental` section is emitted at all, so a regression cannot pass by nesting the section twice. Separately, `check-dotnet-api-parity.js` compared the managed `StateAwareContainment` enum against the `require_experimental_optin` list in `state_aware.rs`. That list is the *experimental subset*, not the set of state-aware backends; the two were only coincidentally equal before this promotion and diverge the moment a backend is promoted. Compare against `backend_from_prefix` in `state_aware_dispatch.rs`, which is the actual registry of reachable state-aware backends. This also strengthens the check, since it now tracks the dispatcher rather than a gate that shrinks over time, and it drops a now-redundant second read of `state_aware_dispatch.rs`. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9f02fa89-c89e-4a5d-b80d-5130252d3654
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 109 out of 111 changed files in this pull request and generated 6 comments.
Suppressed comments (3)
Previously missed (1) — in code that hasn't changed since the last review.
sdk/node/src/platform.ts:171
- This availability fact is obtained by probing the auto-discovered
wxc-exec, butresolveExecutableAndArgsmay later runoptions.executablePath. A caller supplying a WSLc-enabled custom binary is therefore rejected when the separately discovered binary lacks WSLc (and the inverse can be incorrectly accepted). Probe the selected executable, or defer this per-binary feature check to an explicitly supplied executable.
if (facts.wslcAvailable === true) {
support.availableMethods.push('wslc');
sdk/node/tests/unit/sandbox.test.ts:1292
- This assertion passes only when the current host reports WSLc unavailable. On a WSLc-capable host the helper correctly returns arguments, so the test fails. Bypass the unrelated availability check and directly verify that promotion removed the CLI flag.
assert.throws(
() => resolveExecutableAndArgs(makeConfig('wslc'), { executablePath: fakeExe }),
(e: Error) => !/experimental mode/.test(e.message),
);
sdk/node/README.md:145
- WSLc does not work out of the box: default Windows builds omit the
wslcfeature and the README elsewhere requiresbuild.bat --with-wslc. Describe it as non-experimental but build-time opt-in.
The default `processcontainer`, `bubblewrap`, `lxc`, `seatbelt`, and `wslc` backends work out of the box. **Experimental backends** (`windows_sandbox`, `microvm`, `isolation_session`, `hyperlight`) require `{ experimental: true }` in `SandboxSpawnOptions` when you spawn — see [Choosing a Backend](#choosing-a-backend).
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| } | ||
| ], | ||
| "description": "WSL container backend config." | ||
| "description": "WSL container backend config (pre-promotion alias). Promoted to the top-level `wslc` section; still parsed here so the parser can reject it with a migration message instead of silently ignoring it." |
There was a problem hiding this comment.
question: I'm now realizing that we have an alpha and a dev and now I'm wondering if the promotion process is now that you do the same thing you did in the alpha, in the dev?
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
adpa-ms
commented
Sep 3, 2026
[AgentAssisted] Two documentation spots a comment sweep may not reach — both outside the current diff, so not attachable to a line.
|
The merge-base changed after approval.
There was a problem hiding this comment.
🟡 Changes recommended
Several public API and generated-schema descriptions still incorrectly document WSLc as experimental or report the previous schema default.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 131/133 changed files
- Comments generated: 4
- Review effort level: Balanced
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
83ea3b7 to
57a0fdcCompareThere was a problem hiding this comment.
🟡 Changes recommended
The SDK builders can emit the 0.9-only WSLc shape with older schema versions, and several updated documents remain inaccurate.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 131/133 changed files
- Comments generated: 6
- Review effort level: Balanced
| config.wslc = { | ||
| image: 'alpine:latest', | ||
| }; |
| fn apply_wslc_backend(config: &mut serde_json::Value, wslc: &WslcSection) { | ||
| use serde_json::json; | ||
| config["containment"] = json!("wslc"); | ||
| config["experimental"] = json!({ "wslc": wslc.wire() }); | ||
| config["wslc"] = wslc.wire(); |
| "release": "3.19" | ||
| }, | ||
| "wslc": { // WSL Container settings (Windows only) |
| All backends implement the `ScriptRunner` trait. `main.rs` uses `Box<dyn ScriptRunner>` with a `match` on `request.containment`. WSLC is a stable backend and needs no `--experimental` gate; the remaining experimental backends (Sandbox, MicroVM, IsolationSession, Hyperlight) still do: | ||
| ```rust | ||
| // main.rs — current dispatch |
| ``` | ||
| Run with: `wxc-exec.exe --experimental --debug app-policy.json` | ||
| Run with: `wxc-exec.exe --debug app-policy.json` |
| var root = doc.RootElement; | ||
| Assert.Equal("0.8.0-alpha", root.GetProperty("version").GetString()); | ||
| Assert.Equal("0.9.0-alpha", root.GetProperty("version").GetString()); |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c28d1c5e-fe3e-4634-9dd7-246528cc9edd
57a0fdc to
d784cc4CompareThere was a problem hiding this comment.
🟡 Changes recommended
Stable WSLc builders still emit schema-invalid pre-0.9 configurations, and post-provision validation can silently discard a foreign backend block.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
sdk/node/src/sandbox.ts:122
- This now emits a top-level
wslcfield for every supported policy version, including 0.6–0.8. The 0.9 contract and version-boundary tests introduce this field only in 0.9, and the SDK README explicitly says WSLc configs must declare 0.9, socreateConfigFromPolicy({ version: '0.6.0-alpha' }, 'wslc')returns schema-invalid JSON. Reject pre-0.9 policy versions here (and update the existing 0.6 WSLc tests) rather than constructing a config that does not match its declared contract.
config.wslc = {
image: 'alpine:latest',
};
src/core/mxc_engine/src/policy.rs:977
- The Rust builder has the same contract hole as the TypeScript SDK: it writes the newly introduced top-level
wslcsection while preserving any caller-supplied 0.6–0.8 version (the current WSLc tests useminimal_policy()at 0.7). Such output is invalid against the closed contracts and conflicts with the new v0.9 boundary test. Validate that WSLc is paired with schema 0.9+ before building the wire object; this also protects the C# one-shot API, which delegates through this builder.
docs/wsl/wsl-container-support-plan.md:89 - This updated “current dispatch” description is still architecturally incorrect:
wxcis a thin CLI and backend selection lives inmxc_engine::run::resolve_runner_inner_windows(src/core/mxc_engine/src/run.rs:124-182), usingrequest.wslc, notmain.rs/request.container_config. Update or remove the paragraph and sample so the promotion docs point contributors to the actual dispatch layer.
All backends implement the `ScriptRunner` trait. `main.rs` uses `Box<dyn ScriptRunner>` with a `match` on `request.containment`. WSLC is a stable backend and needs no `--experimental` gate; the remaining experimental backends (Sandbox, MicroVM, IsolationSession, Hyperlight) still do:
- Files reviewed: 131/133 changed files
- Comments generated: 1
- Review effort level: Balanced
| @@ -1490,6 +1512,53 @@ fn convert_wire_state_aware( | |||
| validate_experimental_backend_keys(containment.as_ref(), experimental_raw.as_ref())?; | |||
Resolves the conflicts caused by #1058 (WSLc rejection hardening) being squash-merged to main as ab8930d while this branch already carried its original commits, plus four other changes that landed alongside it. Twelve files conflicted. Most were "both sides added a sibling", so the resolution keeps both additions rather than either side alone: - probe.rs / main.rs / platform.ts (+ tests): keep both the wslc_available gate from this branch and the hyperlight_available gate from #1059. - platform.test.ts: split the merged block into separate "wslc availability gate" and "hyperlight availability gate" describes. - sandbox.test.ts: keep main's new Linux backend-failure-reason test alongside the renamed windows_sandbox test. - docs/schema.md: combine two independent promotions -- top-level wslc from this branch and top-level seatbelt (with extraMachLookups) from #1033 -- leaving experimental holding only telemetry. - sdk/node/README.md: keep both the Windows wslc-gating sentence and main's Linux unavailableReasons sentence. The remaining five (policy.rs, wsl_container_runner.rs, the two WSLc test configs, copilot-instructions.md) are pure promotion conflicts where this branch's form -- wslc.portMappings, schema 0.9.0-alpha, the top-level wslc block -- is the correct one. Verified: cargo check/clippy/fmt/test --workspace --features wslc, the six versioning and codegen gates, npm run build + npm test (331 tests), and dotnet build of the C# SDK all pass. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c28d1c5e-fe3e-4634-9dd7-246528cc9edd
There was a problem hiding this comment.
🟡 Changes recommended
Stable WSLc lifecycle requests can still silently discard a foreign experimental backend section on non-provision phases.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
src/core/wxc_common/src/config_parser.rs:1454
- This migration check still allows a foreign experimental backend block on WSLc's non-provision phases. For example, an
execwith awslc:sandbox ID andexperimental.windows_sandboxreachesvalidate_experimental_backend_keyswithcontainment == None; its single key is accepted, then WSLc dispatch reads only the stable root and silently drops that block. Resolve the validation backend fromsandboxIdbefore validating so stable WSLc rejects every experimental backend section.
docs/wsl/wsl-container-support-plan.md:89 - This updated text still describes an architecture that no longer exists:
wxcdelegates backend selection tomxc_engine, andmain.rshas nomatch request.containment. Point the example atsrc/core/mxc_engine/src/run.rs::resolve_runner(and update the snippet) so the promotion documentation does not direct maintainers to the wrong dispatch layer.
All backends implement the `ScriptRunner` trait. `main.rs` uses `Box<dyn ScriptRunner>` with a `match` on `request.containment`. WSLC is a stable backend and needs no `--experimental` gate; the remaining experimental backends (Sandbox, MicroVM, IsolationSession, Hyperlight) still do:
sdk/dotnet/Microsoft.Mxc.Sdk.Tests/MxcLifecycleTests.cs:422
- The assertion now expects 0.9, but the test is still named
BuildProvisionEnvelope_WslcUsesV08AndNestsImageOptions. Rename it to V09 so failures and test filters identify the contract being exercised.
Assert.Equal("0.9.0-alpha", root.GetProperty("version").GetString());
- Files reviewed: 129/131 changed files
- Comments generated: 1
- Review effort level: Balanced
| //! separate from the public `wslc.*` wire schema. The state-aware | ||
| //! backend (a later PR) is the translator between the public wire model and |
| /// Target operating system inside the container. | ||
| #[serde(default)] | ||
| pub target_os: OptionalField<String>, |
There was a problem hiding this comment.
[AgentAssisted] targetOs has no consumer — no read of WslcConfig::target_os anywhere in src/backends/wslc/, and the parser passes any string through, so "windows" or "" silently runs Linux. Pre-existing and unchanged from main, so not a regression.
Flagging it here only because this block is the closed stable surface: removing the field is free today and a breaking change later. If it's reserved for planned multi-OS support, that's a good reason to keep it and no action is needed — otherwise dropping it, or validating "linux", would close the gap.
📖 Description
Closes#1049
111 files changed — 58 test, 53 non-test (42 source/config, 11 docs).
WSLc is now configured through the top-level
wslcsection instead ofexperimental.wslc, and selecting"containment": "wslc"no longer requires--experimental/{ experimental: true }/SandboxRequest::set_experimental(true). This mirrors the Seatbelt promotion precedent.Wire + schema
wslcfromwire::Experimentalto theMxcConfigroot, and fromexperimental.rstostable.rsin the 0.9.0-alpha closed contract.experimental.wslcas a rejection alias so a pre-promotion config fails with an actionable migration message rather than being silently ignored.Parser + dispatch
SectionRoot(Experimental|Stable);StatefulSandboxBackend::SECTION_ROOTdefaults toExperimentaland onlyWslcStateAwareRunneroverrides it.ParsedStateAwareRequestgainsstable_rawfor stable-rooted backends.TypeScript SDK
wslcout ofContainerConfig.experimentaland offExperimentalBackends; addBACKEND_SECTION_ROOT, the TS twin of the RustSectionRoot, so envelopes are built at the right root.wslcAvailableprobe fact end to end (Rust probe -> engine ->wxc-exec --probe-> SDK). Without it, removing the experimental gate unmasked the fact thatwslcwas never inavailableMethods, which would have made the backend unreachable through the SDK.Rust SDK, corpus, harnesses and docs
set_experimental(true)requirement frommxc-sdk.experimental.wslcto top-levelwslcin 31 test configs.--experimentalfrom the WSLc test harnesses..github/copilot-instructions.md.🔗 References
🔍 Validation
Unit / static:
cargo fmt --all -- --checkcargo clippy --workspace --all-targets --features wxc/wslc -- -D warningscargo test -p wxc_common -p wslc_common -p mxc_engine -p wxc -p mxc-sdk --features wxc/wslc,mxc-sdk/wslc— 1,346 testscargo test -p mxc_config_contract --features schema-gen— 707 testscd sdk/node && npm test— 283 tests, 0 failcheck-schema-versions,check-schema-codegen,check-sdk-types-codegen,check-contract-codegen,validate-configs(267 configs),check-version-sync,check-rust-toolchain-sync,check-psec-codegen,check-dotnet-errorcode-parityLive WSLc E2E on a WSL2 host (
run_wslc_all_tests.ps1, which chains the per-scenario scripts and delegates to the state-aware suite): 78/79. The 3 reported assertion failures are allnetwork.proxypost-provision error-code mismatches that are pre-existing on the base commit — verified by rebuilding at the merge-base and reproducing byte-identical output — and are fixed independently by #1042.Also verified end to end against a real
wxc-exec.exebuild: a promoted top-levelwslcconfig parses and runs a container to completion, the pre-promotionexperimental.wslcshape produces the migration error, and--probereportswslcAvailable: true.✅ Checklist
Cargo.lock, thedependency-feed-checkcheck passes (see docs/pull-requests.md)📋 Issue Type
GitHub Actions runs the PR validation build automatically. The ADO pipeline
(
MXC-PR-Build) is the Azure version of the PR pipeline, kept in parity with the GitHubActions build; it runs on merge to
main, and Microsoft reviewers with write access can trigger iton a PR with
/azp run. See docs/pull-requests.md.If the
dependency-feed-checkcheck fails on a new dependency, the crate must be added tothe feed before the PR can pass. See docs/pull-requests.md
for the steps.
Microsoft Reviewers: Open in CodeFlow