[WSLC] State-aware sandbox lifecycle over the experimental.wslc surface (PR 2b/3) - #801
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
Adds daemon-backed, state-aware WSLc lifecycle support across Rust dispatch, wire schema, documentation, and E2E coverage.
Changes:
- Implements WSLc provision/start/exec/stop/deprovision dispatch and policy validation.
- Extends generated schema and wire types with WSLc provision configuration.
- Adds lifecycle fixtures, E2E harness, daemon timeout controls, and documentation.
Reviewed changes
Copilot reviewed 30 out of 31 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
.github/copilot-instructions.md |
Documents WSLc architecture and tests. |
docs/wsl/wslc-state-aware.md |
Describes lifecycle behavior and limitations. |
schemas/dev/mxc-config.schema.0.8.0-dev.json |
Adds generated WSLc provision schema. |
sdk/node/src/generated/wire.ts |
Adds generated provision wire types. |
src/backends/wslc/common/src/daemon_client.rs |
Adds typed errors and daemon spawning changes. |
src/backends/wslc/common/src/lib.rs |
Exports state-aware modules. |
src/backends/wslc/common/src/policy.rs |
Implements per-phase policy validation. |
src/backends/wslc/common/src/state_aware.rs |
Implements the state-aware backend. |
src/backends/wslc/daemon/src/main.rs |
Adds configurable idle timing. |
src/core/mxc_engine/src/state_aware.rs |
Wires WSLc into engine dispatch. |
src/core/wxc_common/src/state_aware_dispatch.rs |
Registers the wslc ID prefix. |
src/core/wxc_common/src/wire.rs |
Adds WSLc provision wire configuration. |
tests/configs/wslc_state_aware_stop.json |
Adds stop fixture. |
tests/configs/wslc_state_aware_start.json |
Adds start fixture. |
tests/configs/wslc_state_aware_provision.json |
Adds basic provision fixture. |
tests/configs/wslc_state_aware_provision_with_filesystem.json |
Adds volume fixture. |
tests/configs/wslc_state_aware_provision_rejected_proxy.json |
Tests provision-time proxy rejection. |
tests/configs/wslc_state_aware_provision_rejected_hosts.json |
Tests host-filter rejection. |
tests/configs/wslc_state_aware_provision_rejected_denied.json |
Tests denied-path rejection. |
tests/configs/wslc_state_aware_provision_bridged.json |
Adds bridged-network fixture. |
tests/configs/wslc_state_aware_exec_write_marker.json |
Writes warm-reuse marker. |
tests/configs/wslc_state_aware_exec_rejected_filesystem.json |
Tests immutable filesystem policy. |
tests/configs/wslc_state_aware_exec_read_marker.json |
Reads warm-reuse marker. |
tests/configs/wslc_state_aware_exec_proxy.json |
Tests proxy environment injection. |
tests/configs/wslc_state_aware_exec_exit_7.json |
Tests exit code 7. |
tests/configs/wslc_state_aware_exec_exit_1.json |
Tests exit code 1. |
tests/configs/wslc_state_aware_exec_exit_0.json |
Tests successful exit. |
tests/configs/wslc_state_aware_exec_env.json |
Tests per-exec environment. |
tests/configs/wslc_state_aware_exec_basic.json |
Adds basic exec fixture. |
tests/configs/wslc_state_aware_deprovision.json |
Adds deprovision fixture. |
tests/scripts/run_wslc_state_aware_tests.ps1 |
Adds multi-invocation E2E harness. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
69876f6 to
499f351
Compare
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 31 out of 32 changed files in this pull request and generated 1 comment.
Suppressed comments (3)
src/backends/wslc/common/src/policy.rs:124
- This value-based check silently accepts an explicitly supplied
network.defaultPolicy: "block"on post-provision phases, and it also misses explicit default-valued fields such asallowLocalNetwork: falseorenforcementMode: "capabilities". Those settings are not applied after provision, despite the documented immutable-policy contract. Start/stop/deprovision can rejectnetwork_specified; exec needs field-level presence tracking so a proxy-only block remains allowed while all other network fields are rejected.
fn reject_non_default_network(request: &ExecutionRequest) -> Result<(), MxcError> {
if request.policy.default_network_policy != NetworkPolicy::Block {
return Err(MxcError::policy_validation(ERR_NETWORK_IMMUTABLE));
}
src/backends/wslc/common/src/state_aware.rs:270
- A missing WSLC runtime is reported by the daemon as
WorkerError::Backend/ErrKind::Backend, which this maps tobackend_error. This contradicts the documentedbackend_unavailablecontract and means the E2E availability probe only skips feature-off builds; a host without the runtime continues into the suite and fails every lifecycle test. Add a distinct unavailable/not-ready classification for SDK load/prerequisite failures and map it tobackend_unavailable.
DaemonError::Daemon { kind, message } => match kind {
ErrKind::NotProvisioned => MxcError::not_provisioned(message),
ErrKind::NotStarted => MxcError::not_started(message),
ErrKind::Busy | ErrKind::NotReady | ErrKind::Protocol | ErrKind::Backend => {
MxcError::backend_error(message)
}
},
DaemonError::Transport(e) => MxcError::backend_error(format!("{e:#}")),
tests/scripts/run_wslc_all_tests.ps1:342
- The delegated harness exits 0 both when it passes and when it skips because the daemon/runtime is unavailable, but this always records
Skipped = $falseand counts either case as a pass. As a result, the advertised single entry point can report all coverage passing without running any state-aware tests. Preserve a distinct skip result from the child harness and record it here.
499f351 to
f96a091
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 33 out of 34 changed files in this pull request and generated 1 comment.
Suppressed comments (4)
src/backends/wslc/common/src/policy.rs:123
- This value-only check cannot distinguish an absent network section from an explicit
defaultPolicy: "block". A sandbox provisioned asallow/Bridged therefore accepts a later exec requestingblockbut stays bridged, silently violating the requested restriction. Preserve presence for the network-mode fields (separately from a proxy-only network section) and reject any post-provision mode request.
fn reject_non_default_network(request: &ExecutionRequest) -> Result<(), MxcError> {
if request.policy.default_network_policy != NetworkPolicy::Block {
return Err(MxcError::policy_validation(ERR_NETWORK_IMMUTABLE));
src/backends/wslc/common/src/policy.rs:57
- Provision validation accepts
network.allowLocalNetwork=true, butmap_networkconsumes onlydefault_network_policy; no inbound-access or port-forward setting is applied. The one-shot WSLc path explicitly rejects this unsupported request (wsl_container_runner.rs:646-650), so state-aware provision should also fail rather than silently claiming the policy is honored.
This issue also appears on line 121 of the same file.
reject_host_filtering(request)?;
src/backends/wslc/common/src/state_aware.rs:268
- Missing WSLC runtime components fail inside
load_sdk_checked/session creation as daemonErrKind::Backend, so this maps them tobackend_error. That contradicts the documentedbackend_unavailablecontract and prevents the new E2E availability probe (which only skipsbackend_unavailable) from skipping unsupported hosts. Add a distinct daemon classification for runtime unavailability and map it accordingly.
ErrKind::Busy | ErrKind::NotReady | ErrKind::Protocol | ErrKind::Backend => {
MxcError::backend_error(message)
}
src/core/wxc_common/src/wire.rs:527
- This adds a configuration field to the generated schema, but the canonical schema reference still documents only the flat one-shot
experimental.wslcshape. The repository convention requires schema additions to updatedocs/schema.md; add the nested state-awareprovisionshape there so callers can discoverimageandimageTarPath.
/// State-aware provision-phase configuration
/// (`experimental.wslc.provision`). Carries the container-creation knobs
/// for the state-aware lifecycle; the flat sibling fields above remain the
/// one-shot surface. Absent on one-shot configs and non-provision phases.
pub provision: Option<WslcProvisionPhase>,
f96a091 to
f311b6c
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 33 out of 34 changed files in this pull request and generated 1 comment.
Suppressed comments (4)
src/backends/wslc/common/src/state_aware.rs:73
- State-aware provisioning maps the filesystem policy directly to mounts, bypassing the object-alias normalization and delegation checks used by the one-shot WSLc path (
wsl_container_runner.rs:1282-1309). As a result, the same host object can be exposed read-write through one alias despite being read-only through another, and paths are mounted without the established caller-access check. Normalize/tighten the policy and runcheck_delegationbefore overlap validation and volume construction.
let image_tar_path = config.and_then(|c| c.image_tar_path);
let volumes = build_daemon_volumes(request)?;
tests/scripts/run_wslc_all_tests.ps1:342
- The delegated script exits 0 for prerequisite skips (for example, a missing daemon or
backend_unavailable), so this records a skipped state-aware suite asPass = true, Skipped = false. The aggregate summary then claims the lifecycle coverage passed when none ran. Propagate a distinct skip result or perform the prerequisite probe here and setSkippedaccurately.
src/backends/wslc/common/src/policy.rs:58 - The state-aware validator never rejects
network.allowLocalNetwork=true, althoughmap_networkignores it and the one-shot WSLc validator explicitly rejects it (wsl_container_runner.rs:646-650). Provision parsing catches this in the CLI path, but post-provision requests resolve by sandbox ID and direct engine callers bypass that parser guard, so the unsupported grant can be silently accepted. Apply the same rejection in the backend validation for every phase.
reject_host_filtering(request)?;
if request.policy.network_proxy.is_enabled() {
src/core/wxc_common/src/wire.rs:527
- This adds a public config field, but the canonical schema reference in
docs/schema.mdstill shows only the flat one-shotexperimental.wslcfields and omits the nestedprovisionshape. Update that reference so state-aware users can discover and author this field consistently with the generated schema.
/// State-aware provision-phase configuration
/// (`experimental.wslc.provision`). Carries the container-creation knobs
/// for the state-aware lifecycle; the flat sibling fields above remain the
/// one-shot surface. Absent on one-shot configs and non-provision phases.
pub provision: Option<WslcProvisionPhase>,
f311b6c to
f7802dc
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 33 out of 34 changed files in this pull request and generated no new comments.
Suppressed comments (4)
src/backends/wslc/common/src/policy.rs:124
- Comparing only the mapped value cannot distinguish an omitted network mode from an explicit
defaultPolicy: "block". For example, after provisioning a bridged (allow) sandbox, an exec request that explicitly asks forblockpasses this check even though the container remains bridged, silently dropping a restrictive policy. Track presence of the mode field separately (while still allowing a proxy-only network block) and reject any post-provision mode request.
fn reject_non_default_network(request: &ExecutionRequest) -> Result<(), MxcError> {
if request.policy.default_network_policy != NetworkPolicy::Block {
return Err(MxcError::policy_validation(ERR_NETWORK_IMMUTABLE));
}
tests/scripts/run_wslc_state_aware_tests.ps1:176
ProcessStartInfo.ArgumentListis unavailable in Windows PowerShell 5.1's .NET Framework, and this Windows harness does not require PowerShell 7. The first invocation therefore throws before any lifecycle test runs on the default Windows shell. These arguments contain no spaces, so assigning the joined string keeps the harness compatible with both editions.
tests/scripts/run_wslc_all_tests.ps1:341- The child harness exits 0 both when it passes and when it skips because the daemon or backend is unavailable. Treating every zero as
Pass = trueand hard-codingSkipped = falsemakes the aggregate summary claim state-aware coverage passed when no state-aware test ran. Propagate a distinct skip status from the child (or otherwise report its prerequisite outcome) before adding this result.
src/backends/wslc/common/src/state_aware.rs:268 - A missing WSLC runtime is reported by the daemon worker as
ErrKind::Backendbecauseensure_sessionmapsload_sdk_checkedfailures toWorkerError::Backend; this arm therefore returnsbackend_error, not the documentedbackend_unavailable. As a result the new prerequisite probe does not skip a host without the runtime and the public error mapping contradicts this PR's documentation. Add a distinct unavailable classification through the worker/protocol/client mapping.
DaemonError::Daemon { kind, message } => match kind {
ErrKind::NotProvisioned => MxcError::not_provisioned(message),
ErrKind::NotStarted => MxcError::not_started(message),
ErrKind::Busy | ErrKind::NotReady | ErrKind::Protocol | ErrKind::Backend => {
MxcError::backend_error(message)
}
…lc surface Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f3ae1af2-7b79-4340-a5ce-a5402e7ede3d
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f3ae1af2-7b79-4340-a5ce-a5402e7ede3d
f7802dc to
20b01fc
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 33 out of 34 changed files in this pull request and generated no new comments.
Suppressed comments (5)
src/backends/wslc/common/src/state_aware.rs:73
- Provision builds daemon mounts directly from the raw policy, bypassing the object-identity normalization and delegation check used by the one-shot WSLc path (
wsl_container_runner.rs:1282-1325). A caller can therefore mount an alias that should have been tightened to denied, or delegate a path the caller is not authorized to share. Normalize first, runcheck_delegationon the effective policy, and re-run overlap validation before constructingVolumeMounts; PR #806’s follow-up diff confirms this gap and the required ordering.
let volumes = build_daemon_volumes(request)?;
src/backends/wslc/common/src/policy.rs:126
- This post-provision network check misses
allowLocalNetwork. Forstart/exec/stop/deprovision, containment is resolved fromsandboxId, so the parser cannot apply its WSLc-specific rejection;allowLocalNetwork=truetherefore reaches this validator, passes whendefaultPolicyis Block, and is silently ignored. Reject it here as the one-shot WSLc validator does atwsl_container_runner.rs:646-650.
fn reject_non_default_network(request: &ExecutionRequest) -> Result<(), MxcError> {
if request.policy.default_network_policy != NetworkPolicy::Block {
return Err(MxcError::policy_validation(ERR_NETWORK_IMMUTABLE));
}
Ok(())
docs/wsl/wslc-state-aware.md:59
- The state-aware exec path does not forward stdin: it sends no stdin data and returns
null_pipe_handle()for stdin instate_aware.rs:195. The linked PR #806 also explicitly defers piped stdin to a later tier, so documenting it as supported will mislead callers.
| exec | `WslcCreateContainerProcess` in the warm container; stream stdout/stderr, forward stdin, return the process exit code. A timeout SIGKILLs the **process**, not the container. |
tests/scripts/run_wslc_all_tests.ps1:342
- The delegated harness exits 0 both when it passes and when it skips due to a missing daemon/backend, but this result is always recorded as
Skipped = $false. Consequently the aggregate summary counts an unexecuted state-aware suite as a passing test. Return a distinct skip status/result from the child and map it toSkipped = $truehere.
tests/scripts/run_wslc_state_aware_tests.ps1:314 - This probe does not actually catch a missing WSLc runtime as documented. The daemon becomes ready before loading the SDK; the first provision’s
load_sdk_checkedfailure is converted toWorkerError::Backend, thenErrKind::Backend, and finallybackend_error, so thisbackend_unavailable-only branch is skipped and the entire suite fails instead of skipping. Preserve an unavailable classification across the daemon protocol (without treating every backend error as a skip).
…network-mode changes by presence post-provision
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 35 out of 36 changed files in this pull request and generated no new comments.
Suppressed comments (5)
src/backends/wslc/common/src/state_aware.rs:94
- Re-run denied-path overlap validation after object normalization. Normalization can move a mounted alias into
deniedPathswhile leaving its parent mount in place; because the only overlap check ran on the raw policy, the parent is then mounted and exposes the newly denied alias. The one-shot path deliberately validates again after normalization (wsl_container_runner.rs:1312-1327).
let volumes = build_daemon_volumes(request)?;
src/backends/wslc/common/src/policy.rs:57
- Provision currently accepts
network.allowLocalNetwork=true, butmap_networkonly mapsdefaultPolicy, so this requested network posture is silently ignored. The one-shot WSLc validator rejects this unsupported setting (wsl_container_runner.rs:646-650); state-aware provision should do the same rather than creating a sandbox with different exposure than requested.
reject_host_filtering(request)?;
tests/scripts/run_wslc_state_aware_tests.ps1:176
ProcessStartInfo.ArgumentListis unavailable in the .NET Framework used by the default Windows PowerShell 5.1, so this harness throws before launching its first phase unless run under PowerShell 7. The repository does not require PowerShell 7 and sibling harnesses useArguments; these arguments are flags plus base64, so joining them is safe and keeps the test runnable from Windows PowerShell.
src/core/mxc_engine/src/state_aware.rs:35- This branch also runs on non-Windows targets, where the message incorrectly claims the build lacks the
wslcfeature even when that feature is enabled. Report both supported conditions so Linux/macOS callers receive an accurate availability error.
"the WSLc backend is not available in this build (compiled without the `wslc` feature)",
src/core/wxc_common/src/wire.rs:541
- The wire format has no top-level
policysection; mounts and network mode come from top-levelfilesystemandnetwork. This incorrect wording is propagated into the generated schema and TypeScript file, whiledocs/schema.mdstill shows only the flat one-shot WSLc fields. Update the source wording, document the nested state-awareprovisionshape in the schema reference, and regenerate both artifacts.
/// Filesystem mounts and network mode derive from the top-level `policy`
/// section (readwrite / readonly paths, network), not from here. The
Gudge (MGudgin)
left a comment
There was a problem hiding this comment.
Summary
Requesting changes for one blocking reliability issue: the new production state-aware calls can wait forever on the daemon's single synchronous worker, allowing one hung SDK operation to wedge every WSLc lifecycle on the host, including teardown. The parser-contract/test gap and the Medium test/documentation findings should also be addressed; Low findings are non-blocking cleanup.
Range verified: aa05d77207476f88a63e2486013307a9e89795ec...e5e4e050b7224e678fe5900d9b5e64766cc73b0b (36 files, +3094/-439). The merge base exactly matches GitHub's PR base. git diff and gh pr diff both contain 4,022 lines; their only differences are abbreviated index hashes.
Verified clean, with receipts: the WSLc state-aware implementation is gated behind experimental dispatch; the new policy validator has direct unit coverage for filesystem, host-filter, proxy-form, and immutable-network rejection; and the E2E harness covers the basic lifecycle, bridged networking, cooperative exec proxy injection, and provision-time rejection cases. No Windows/WSL runtime tests were run as part of this filing.
Findings kept in the review body
Medium (proportionality) - Remove the unrelated whole-file line-ending rewrite. .github/copilot-instructions.md reports 379 additions / 375 deletions in the raw diff, but git diff --ignore-cr-at-eol reports only 4 additions / 0 deletions. The base blob has 375 CR bytes and the head blob has none. Please restore the existing CRLF convention so this PR contains only its four semantic additions; perform any repository-wide normalization separately with a .gitattributes rule.
Low (documentation-drift) - Update the WSL roadmap entry made obsolete by this PR. docs/linux-wsl-roadmap-june-2026.md:458,485 is byte-identical between base and head, so this is not attributed as pre-existing faulty code. However, this PR newly makes its claims that WSLc still needs StatefulSandboxBackend and that none of the three Linux/WSL backends implement it false. Please mark the WSLc item addressed and update the summary sentence.
Verified pre-existing - not independently attributed to this PR
src/backends/wslc/common/src/daemon_protocol.rs,src/backends/wslc/daemon/src/session_manager.rs, andsrc/backends/wslc/common/src/container_steps.rsare byte-identical base vs head. They are cited only as supporting evidence for the newly reachable daemon-wedge finding; the newstate_aware.rscalls are what expose that behavior to production requests.docs/linux-wsl-roadmap-june-2026.mdis byte-identical base vs head; it is raised only because this PR's new implementation makes the roadmap statement newly stale.
Requested findings not filed after verification
- The global-stdio/null-handle finding was withdrawn:
ExecHandlesentinel handles are supported by the current dispatcher, whose relay intentionally calls only the waiter until a backend exposes relayable pipe handles. WSLc writes to the executor's stdio by design, which remains capturable by the outer phase process. - The missing
DaemonClientinjection seam was withdrawn: it is an architectural preference without a demonstrated behavior defect; policy logic and transport behavior already have separate unit/integration seams. - The
mxc_engineinstruction-list finding was withdrawn: the sentence says "including" rather than claiming an exhaustive list, and this PR adds a dedicated WSLc architecture row that documents the state-aware backend.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 36 out of 37 changed files in this pull request and generated 2 comments.
Suppressed comments (2)
src/backends/wslc/common/src/daemon_client.rs:343
- Timing out here abandons the pipe reader but does not cancel the daemon operation. In particular, a slow
Provisioncan complete after the deadline, create a container, and send an ID that nobody receives; that container keeps the daemon's count nonzero and cannot be deprovisioned by the caller. In long-livedmxc-sdk/FFI hosts the blocked reader thread also remains indefinitely. The timeout needs cancellation, request reconciliation/idempotency, or another mechanism that cannot orphan successful operations.
let timeout = call_timeout();
read_frame_with_deadline::<DaemonResponse, _>(move || read_frame(&mut pipe), timeout)
tests/scripts/run_wslc_all_tests.ps1:341
- The delegated harness intentionally exits 0 when the daemon is missing or the backend reports
backend_unavailable, but this code maps every zero exit to a pass and hard-codesSkipped = $false. Consequently the aggregate runner reports state-aware coverage as passed when no lifecycle test ran. Use a distinct skip result (or structured result) from the child and preserve it in this summary.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f3ae1af2-7b79-4340-a5ce-a5402e7ede3d
ed87c5b to
272fd48
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 36 out of 37 changed files in this pull request and generated 1 comment.
Suppressed comments (3)
src/backends/wslc/common/src/state_aware.rs:194
- State-aware dry runs return immediately after
validate_provision, but the delegation check, normalized denied-path overlap check, and volume-path validation currently run only insideprovisionviabuild_provision_config. Consequently--dry-runreports success for requests that real provision rejects (for example an inaccessible path or UNC mount). Run the host-independent provisioning plan from validation as well.
fn validate_provision(
&self,
request: &ExecutionRequest,
_config: Option<&WslcProvisionPhase>,
) -> Result<(), MxcError> {
validate_provision_policy(request)
}
src/backends/wslc/common/src/daemon_client.rs:343
- This client-side deadline does not cancel or reconcile the daemon operation. In particular, a provision that finishes after 600 seconds creates a container after the client has discarded its response, permanently losing the sandbox ID; that container keeps the daemon's count nonzero and prevents idle cleanup. Start/stop/deprovision similarly return an ambiguous failure even if they later succeed. State-changing calls need protocol-level operation IDs plus cancellation/status reconciliation, or must not return a timeout while the daemon can still commit the operation.
let timeout = call_timeout();
read_frame_with_deadline::<DaemonResponse, _>(move || read_frame(&mut pipe), timeout)
tests/scripts/run_wslc_all_tests.ps1:342
- The delegated harness exits 0 both on success and on prerequisite skips (for example, missing daemon or
backend_unavailable), so this always records those skips asPass = true, Skipped = false. The aggregate can therefore report the state-aware lifecycle suite as passed when no lifecycle test ran. Return a distinct skip status/result from the child and map it toSkipped = truehere.
| .as_ref() | ||
| .and_then(|c| c.image.clone()) | ||
| .unwrap_or_else(|| DEFAULT_IMAGE.to_string()); | ||
| let image_tar_path = config.and_then(|c| c.image_tar_path); |
Summary
PR 2a/3 (#767) hardened the per-user WSLc daemon internals. This PR (2b/3) puts the
daemon to work: it wires the state-aware sandbox lifecycle (provision → start →
exec → stop → deprovision) through the public
experimental.wslc.*wire schema, so acaller can drive a long-lived WSLc container across multiple invocations instead of
one-shot run-to-completion.
No new public SDK types yet — that's PR 3/3. This PR lands the Rust backend, wire/schema
surface, engine dispatch, and the E2E harness.
Changes:
wslc/common/state_aware.rsimplementsStatefulSandboxBackend(dispatch prefix
wslc), translating each lifecycle phase into daemon protocol framesover the owner-only named pipe via
daemon_client.policy.rsmaps + validates theper-phase policy (filesystem/network/host rules honored at provision; rejected where the
backend can't enforce them).
Wslc*Phaseconfig added to the wire model(
wxc_common::wire); regenerated dev schema (schemas/dev/mxc-config.schema.0.8.0-dev.json)and generated TS wire types (
sdk/node/src/generated/wire.ts) — both codegen artifacts,not hand-edited.
mxc_enginestate-aware arm for WSLc, and thewslcsandbox-id prefix now resolves toContainmentBackend::Wslcinstate_aware_dispatch(with a unit test).main.rshandles the state-aware phase requests.tests/configs/wslc_state_aware_*.jsonfixtures (provision/start/exec/stop/deprovision plus rejection cases for denied paths, hosts, proxy, and filesystem)
and a multi-invocation
tests/scripts/run_wslc_state_aware_tests.ps1harness withwarm-reuse and idle-teardown assertions.
docs/wsl/wslc-state-aware.md(fixture ordering + sandbox-id substitution)and a
copilot-instructions.mdupdate.Testing
cargo clippy --workspace --all-targets -- -D warningsandcargo fmt --checkclean.wslc_commonunit tests green (incl. thewslc-prefix dispatch resolution test);the G8
notify_oneidle-wakeup regression test and the daemonresolve_durationenv-overridetests both pass.
build.bat --with-wslcsucceeds.run_wslc_all_tests.ps1) passing; the state-aware harness(
run_wslc_state_aware_tests.ps1) drives provision → start → exec → exec → stop → deprovisionon a WSL2 host.
Coming in the pipeline
live stdout/stderr from state-aware
execand adds the provision-time filesystemdelegation gate.
Wslc*Config/*Resulttypes + brandedSandboxId<'wslc'>and helper prefix wiring, mirroring the LXC state-aware SDK surface.
Let me clean up the temp file I created.
🔗 References
✅ Checklist
docs/wsl/wslc-state-aware.md)Cargo.lock, thedependency-feed-checkcheck passes📋 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