Skip to content

Emit errors and warnings as structured ScriptResponse JSON - #1482

Open
Elliot (theelliotm) wants to merge 20 commits into
mainfrom
user/emichlin/cross-backend-error-contract
Open

Elliot (theelliotm) wants to merge 20 commits into
mainfrom
user/emichlin/cross-backend-error-contract

Conversation

@theelliotm

@theelliotm Elliot (theelliotm) commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

📖 Description

Every backend now reports failure through one contract: a structured ScriptResponse
envelope on stderr, a wire error code that says who can act on the failure, and an exit
code that follows from that code. Previously each executor invented its own prose and its
own exit convention, so a caller could not tell a refused request from an unusable host
without string-matching backend-specific text.

The wire contract

Code Meaning Exit
policy_validation the request is at fault — a field was refused 1
backend_unavailable the host is at fault — no tier/provider could be built, so the policy was never judged -1
backend_error a runtime fault during an already-accepted run -1

backend_unavailable is deliberately broader than "fall back a tier": it also covers a
missing sandbox, an absent provider, and other host conditions that no change to the
request can resolve. That is why it is not an exit 1.

Warnings. ScriptResponse gains a warnings field, emitted as one JSON envelope per
line, so advisories (for example the Bubblewrap process.cwd advisory) are machine-readable
instead of free prose. The field is Option<Box<Vec<String>>> to keep ScriptResponse at
120 bytes; going over 128 trips clippy::result_large_err in the release gate, and a size
test locks that in.

The one subtle classification. DispatchError in the process-container backend carries
two genuinely different failures, and collapsing them either way broke a test suite:

Variant Classification
Fallback(EnumeratePathsUnsupported | IngressUnsupported | DaclFallbackDisabled) names a policy field the tier cannot represent → policy_validation, exit 1
Fallback(WriteDacUnavailable | SystemRootUnresolved), CaptureDenialsUnsupported, CaptureDenialsUnavailable no tier was built / provider absent → backend_unavailable, exit -1
Sid(e) delegates to the inner error
Dacl { .. } runtime fault applying ACEs → backend_error, exit -1

Scope notes

  • warnings is added to the executor's ScriptResponse only. The versioned v1 Rust/.NET/Node
    SDK result types are unchanged; surfacing warnings there is a separate versioned-API change.
  • Backends still print their human-readable prose alongside the envelope on stderr. That
    double-emission predates this branch and is left as-is.
  • Future work could include sending errors through a separate channel to differentiate them from sandboxed stdout/stderr.

🔗 References

Resolves #612

  • Exit codes are consistent cross-backend w/ structured ScriptResponse JSON #1336 was the starting point for the exit-code and envelope work, but it was not a
    complete implementation for the process-container backend; that backend's dispatcher,
    fallback, and capture-denials paths are classified here for the first time.
  • Contract documented in docs/development/architecture/versioning.md (## Error Contract),
    with the per-backend notes updated under docs/backends/.

🔍 Validation

Local, after merging main (Windows 11 build 26700, PSEC 1.1 + base-container tier):

Command Result
cargo fmt --all -- --check passed
cargo clippy --workspace --all-targets --all-features -- -D warnings passed, 0 warnings
cargo test --workspace 2853 passed, 2 failed (pre-existing failures caused by local machine)
cargo test -p mxc-sdk --lib -- dispatcher::tests 33 passed
tests/scripts/run_processcontainer_all_tests.ps1 241 total, 220 passed, 0 failed, 20 skipped, 1 warning

CI: scheduled validation run
38377047465 on ad5b809c

This shows that it fixes the remaining errors related to BaseContainer and AppContainer. The [P8d]/[P8e] hostLoopback failures are fixed by this branch.

The debian13-bubblewrap failure is pre-existing in main, and the ubuntu failure is transient.

✅ Checklist

📋 Issue Type

  • Bug fix
  • Feature
  • Task
Microsoft Reviewers: Open in CodeFlow

Elliot (theelliotm) and others added 7 commits October 12, 2026 16:40
Entry-point failures printed a bare line and exited 1, which a caller could
not tell apart from a typed policy rejection, and warnings were written as
unstructured stderr text.

Classify every wxc-exec failure by whose problem it is, emit it as a typed
JSON envelope, and carry warnings on ScriptResponse.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Backends reported failures inconsistently: some flattened everything to a
generic backend error, the Bubblewrap and LXC runners set no failure phase at
all, and WSLC rejections exited -1 while ProcessContainer rejections exited 1.
A caller could not tell a refused request from an unavailable host, and the
PowerShell suites had to match on prose banners to find out.

Classify each failure by whose problem it is and derive both the wire code and
the exit code from that:

  the request is at fault      Rejected             policy_validation    exit 1
  the host is at fault         BackendUnavailable   backend_unavailable  exit -1
  the runtime faulted mid-run  other phases         backend_error        exit -1

Every backend now sets a phase, and `ScriptResponse::failure_exit_code` is the
single place the exit code comes from.

Two consequences are worth calling out. A capability the host's Windows build
cannot represent -- `enumeratePaths`, `hostLoopback='allow'` -- now reaches the
wire as `policy_validation`, because dropping the field runs; it used to be
reported as an unavailable backend, which told the caller to give up on the
host. Conversely, a backend this build did not compile in now reports
`backend_unavailable` from the one-shot path too, matching what the state-aware
path already returned, because only a rebuilt executor can serve it.

Add an optional `warnings` array to `ScriptResponse` so the executors stop
interleaving advisory diagnostics with the error channel. It is boxed to keep
the struct under clippy's `result_large_err` threshold, and omitted from the
wire when a run produced none.

Fixes #612

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The P14b and P14c behavioural assertions gate on the `nativeCaptureAvailable`
probe, which answers whether the Learning Mode API is present. That is not the
same question as whether it is implemented: on a host whose exports resolve but
return E_NOTIMPL, the probe says the provider is usable, both runs then fail
before an output path exists, and P14c reports three failures on empty strings.

The runtime already classifies that outcome correctly -- `is_api_unavailable()`
maps it to `BackendUnavailable`, which reaches stderr as a typed
`backend_unavailable` envelope. Gate on what the run reported rather than on
the probe alone, via a shared `Test-BackendUnavailable`. Keying on the typed
code covers every reason a host cannot provide capture, not just the ones a
probe knows to ask about.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Dispatch runs after the policy is accepted, so a failure there is the host
failing to build the tier the policy asked for, not a verdict on the request.
Classifying fallback and capture-provider failures as policy_validation made
wxc-exec log them as unsupported_field_for_backend and exit 1, which the
process-container suites read as a validation rejection of a config they
require to be accepted. Only a SID that could not be derived or ACEs that
could not be applied keep their own codes.

Update the harnesses that encoded the previous exit codes: WSLC and isolation
session rejections now exit 1, and the two process-container dispatch refusals
now exit -1. Match the Bubblewrap cwd advisory piecewise, since the advisory is
carried in a JSON warning envelope that escapes the quotes around the path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tier

Two different failures share DispatchError. A field the selected tier cannot
represent names that field and is documented as a rejection, so collapsing it
into backend_unavailable made hostLoopback=allow and enumeratePaths exit -1,
which the suites read as 'accepted and silently dropped' -- the one outcome
P8d and P8e exist to rule out. A tier that could not be built at all, or a
missing denial-capture provider, stays backend_unavailable: no tier existed to
judge the policy against.

Restore the enumeratePaths harness to exit 1 to match.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Audit finalization raised its cleanup diagnostics as bare stderr prose, so a
caller had to scrape them while every other warning arrived as a JSON
envelope. They now travel on the response and are emitted with the rest.

The finalization failure arm hard-coded exit -1 regardless of how the backend
had already classified the run, which could pair a rejection envelope with an
exit status that contradicts it. Deriving the status from the failure phase
keeps the two in agreement, and the appended message reaches stderr through
the error envelope rather than a separate prose line.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 13, 2026 18:45
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Failure classification remains inconsistent across validation and streaming paths, and the Seatbelt availability check can reject valid default executions.

5 open findings
What changed in this PR

Standardizes backend failures by responsible party and emits machine-readable error/warning envelopes.

Changes:

  • Maps policy, host availability, and runtime failures to consistent codes and exits.
  • Adds structured warning handling to ScriptResponse.
  • Updates backend implementations, tests, and documentation.
File Description
tests/​scripts/​run_wslc_all_tests.ps1 Updates rejection exit expectations.
tests/​scripts/​run_processcontainer_filesystem_enumerate_test.ps1 Clarifies enumeration rejection behavior.
tests/​scripts/​run_processcontainer_dacl_disabled_test.ps1 Expects unavailable-tier semantics.
tests/​scripts/​run_processcontainer_capture_denials_test.ps1 Skips assertions on unavailable hosts.
tests/​scripts/​run_isolation_session_tests.ps1 Updates policy rejection exits.
tests/​scripts/​run_bwrap_cwd_advisory_test.sh Validates structured warning output.
tests/​scripts/​lib/​WinProcessContainer.Common.ps1 Adds typed availability detection.
src/​tools/​wxc/​src/​main.rs Emits typed errors and warnings.
src/​tools/​mxc_darwin/​src/​main.rs Adds structured executor diagnostics.
src/​tools/​lxc/​src/​main.rs Adds structured executor diagnostics.
src/​mxc-sdk/​src/​core/​mxc_engine/​run.rs Classifies resolution failures.
src/​mxc-sdk/​src/​core/​mxc_engine/​error.rs Converts SDK errors back to wire errors.
src/​mxc-sdk/​src/​core/​mxc_engine/​dispatch.rs Preserves dispatcher classifications.
src/​mxc-sdk/​src/​core/​mxc_common/​script_runner.rs Adds shared envelope emitters.
src/​mxc-sdk/​src/​core/​mxc_common/​mxc_error.rs Centralizes error exits and serialization.
src/​mxc-sdk/​src/​core/​mxc_common/​models.rs Adds warnings and failure helpers.
src/​mxc-sdk/​src/​core/​mxc_common/​error.rs Classifies common errors.
src/​mxc-sdk/​src/​core/​mxc_common/​config_parser.rs Converts parse failures to typed errors.
src/​mxc-sdk/​src/​backends/​wslc/​common/​wsl_container_runner.rs Updates rejection assertions.
src/​mxc-sdk/​src/​backends/​wslc/​common/​error.rs Derives exits from failure phase.
src/​mxc-sdk/​src/​backends/​windows_sandbox/​lifecycle/​state_aware.rs Classifies unavailable sandbox hosts.
src/​mxc-sdk/​src/​backends/​windows_sandbox/​lifecycle/​error.rs Separates host and policy failures.
src/​mxc-sdk/​src/​backends/​seatbelt/​common/​seatbelt_runner.rs Classifies Seatbelt failures.
src/​mxc-sdk/​src/​backends/​process_container/​common/​dispatcher.rs Classifies dispatch failures.
src/​mxc-sdk/​src/​backends/​process_container/​common/​base_container_runner.rs Distinguishes unsupported host baselines.
src/​mxc-sdk/​src/​backends/​process_container/​common/​appcontainer_runner.rs Classifies BFS and launch failures.
src/​mxc-sdk/​src/​backends/​nanvix/​runner/​mod.rs Splits policy, availability, and runtime errors.
src/​mxc-sdk/​src/​backends/​lxc/​common/​lxc_runner.rs Adds detailed LXC failure phases.
src/​mxc-sdk/​src/​backends/​lxc/​common/​availability.rs Adds uncached execution-time probing.
src/​mxc-sdk/​src/​backends/​isolation_session/​common/​sandbox.rs Preserves typed launch classifications.
src/​mxc-sdk/​src/​backends/​isolation_session/​common/​one_shot.rs Classifies lifecycle and process exits.
src/​mxc-sdk/​src/​backends/​isolation_session/​common/​error.rs Maps backend errors to response phases.
src/​mxc-sdk/​src/​backends/​hyperlight/​common/​mod.rs Splits Hyperlight failure categories.
src/​mxc-sdk/​src/​backends/​bubblewrap/​common/​bwrap_runner.rs Classifies Bubblewrap failures.
docs/​development/​architecture/​versioning.md Documents error and warning contracts.
docs/​development/​architecture/​container-lifecycle.md Documents exit-code behavior.
docs/​development/​architecture/​backends/​isolation-session/​state-aware-rust.md Updates IsolationSession semantics.
docs/​backends/​seatbelt/​seatbelt-backend.md Documents Seatbelt failures.
docs/​backends/​lxc/​lxc-backend.md Adds LXC troubleshooting guidance.
docs/​backends/​hyperlight/​hyperlight-backend.md Adds Hyperlight failure guidance.
docs/​backends/​bwrap/​bubblewrap-backend.md Adds Bubblewrap failure guidance.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/mxc-sdk/src/backends/seatbelt/common/seatbelt_runner.rs Outdated
Comment thread docs/development/architecture/versioning.md
Comment thread src/mxc-sdk/src/backends/bubblewrap/common/bwrap_runner.rs
Comment thread src/mxc-sdk/src/core/mxc_engine/run.rs
Comment thread docs/development/architecture/versioning.md Outdated
@theelliotm Elliot (theelliotm) changed the title Classify every backend failure by who can act on it Emit errors and warnings as structured ScriptResponse JSON Oct 13, 2026
@theelliotm Elliot (theelliotm) self-assigned this Oct 13, 2026
Three surfaces reached the same condition by a different route and reported
it differently.

Shared request validation returned a runtime error, so an empty script was a
rejection through `validate` but a `backend_error` through `spawn`, which is
the path callers actually take. Both shared checks judge the request, so they
are rejections. `enumeratePaths` on a backend that cannot represent it now
also matches how the process-container dispatcher classifies the same field.

The streaming and PTY arms for a backend this build omits returned
`unsupported_containment`, while run-to-completion resolution called the same
build and host `backend_unavailable`. The code no longer depends on which
operation the caller chose. Arms for a backend belonging to another operating
system are untouched.

Seatbelt probed for Apple's sandbox CLI on every launch, but only the `Open`
method shells out to it; `Exec`, the default, applies the profile through
`sandbox_init`. A host without the CLI no longer loses the launch method that
does not need it.

Documents the stderr warning envelope alongside the response array, since the
two shapes differ and only the array was described, and records why a missing
experimental opt-in is a host-side code.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 13, 2026 19:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Several post-telemetry early exits still discard retained warnings instead of emitting structured warning envelopes.

2 open findings
3 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Drain retained telemetry warnings before early exits

src/​tools/​wxc/​src/​main.rs:1424

This early exit occurs after telemetry::init may have retained a warning (for example, ETW registration failure), but it bypasses the only one-shot warning drain at line 1660. The error envelope is emitted while that warning is silently lost from stderr, despite warnings being documented as emitted on failures too. Drain structured warnings before exiting; the same is needed at the other post-telemetry early exits.

This issue also appears in the following locations of the same file:

  • line 1436
  • line 1465
  • line 1591

🧠 Review effort: Balanced

@theelliotm
Elliot (theelliotm) marked this pull request as ready for review October 13, 2026 19:17
@theelliotm
Elliot (theelliotm) requested a review from a team as a code owner October 13, 2026 19:17
Copilot AI balanced review requested due to automatic review settings October 14, 2026 23:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/mxc-sdk/src/backends/bubblewrap/common/bwrap_runner.rs Outdated
Comment thread src/mxc-sdk/src/backends/isolation_session/common/one_shot.rs Outdated
Comment thread src/mxc-sdk/src/core/mxc_common/script_runner.rs
Comment thread src/tools/wxc/src/main.rs Outdated
Comment thread docs/development/architecture/container-lifecycle.md

@MGudgin Gudge (MGudgin) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified adversarial review of #1482

I reviewed the 43-file diff from base 8188df6 to head 9b7a541 and confirmed that its normalized contents match gh pr diff 1482. The inline comments below are anchored only on added lines; unchanged supporting files were byte-compared with the base. The strongest gap is that Unix executable tests do not assert the new JSON-code/exit contract. This is a COMMENT review, not a request for changes based on claim-only findings. No local build, platform-dependent integration suite, or end-to-end test was run as part of filing this review.

Findings on existing open threads

  • #3, live Bubblewrap DNS failures mislabeled as policy refusal: continued in discussion_r4277475899.
  • #6, state-aware error/status mismatch with the PR's blanket claim: continued in discussion_r4277476083 as claim_mismatch, not an introduced runtime regression.
  • #13, warning JSON test can miss a malformed line: continued in discussion_r4277475994.
  • #20, ProcessContainer DACL warnings plus other unstructured wxc-exec advisories despite “Resolves #612”: continued in discussion_r4277476034 as claim_mismatch.

Claim-only and unchanged supporting locations

  • #15 (claim_mismatch, Medium): new docs/development/architecture/versioning.md:718-719 promises wrong-OS unsupported_containment, but the byte-identical state-aware WSLC/IsolationSession gates still return backend_unavailable. The inline comment targets the new claim, not the old gates.
  • #16-19 (newly_exposed_by_change, Medium): the changed IsolationSession policy mapping, one-shot parser, Hyperlight policy phase, and Windows WSLC feature-off arm leave docs/development/architecture/backends/isolation-session/oneshot.md, the one-shot paragraph of docs/development/architecture/container-lifecycle.md, the earlier Hyperlight exit row, and docs/backends/wslc/wsl-container-getting-started.md stale. Each comment is anchored to the changed behavior rather than forcing an anchor into an unchanged paragraph.
  • #22 (claim_mismatch, Low): the PR's all-failures envelope wording includes dry-run failures, but the unchanged handle_dry_run_exit path in wxc, lxc, and mxc_darwin exits before emit_backend_error_envelope; dry-run callers still get a human summary rather than an error JSON line. Either emit the envelope on a failing dry run or limit the claim explicitly. This pre-existing early-exit path is not asserted as a new regression.

Verified pre-existing behavior, not attributed as a new regression

  • mxc_engine/state_aware.rs is byte-identical between base and head; it already returned backend_unavailable for certain missing-feature paths. The state-aware terminal branches already hard-coded exit 1. Finding #6 addresses only the PR's new universal promise.
  • The host DNS lookup in bubblewrap/common/proxy_network.rs is byte-identical. Finding #3 concerns the new rejected conversion of its errors at bwrap_runner.rs:472, not the resolver itself.
  • The older Seatbelt Exec-versus-Open rejection and the streaming feature-off classification have resolved review threads. New comments #11, #15, #19, and #24 address separate residual test, state-aware, and documentation gaps; they are not requests to reopen the fixed behavior.

Comment thread src/mxc-sdk/src/core/mxc_common/script_runner.rs
pub fn error_code(&self) -> MxcErrorCode {
match self {
FailurePhase::Rejected => MxcErrorCode::PolicyValidation,
FailurePhase::BackendUnavailable => MxcErrorCode::BackendUnavailable,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium (security) - workload stderr can impersonate MXC's JSON error.

Attribution: newly_exposed_by_change - the new backend_unavailable code is presented as a machine-readable host-failure signal on the same stderr channel the sandboxed program can write.

wxc-exec relays workload stderr before calling emit_backend_error_envelope; that emitter writes nothing for an ordinary nonzero workload exit because error_message is empty (script_runner.rs:104-120). A workload can print a plausible {"error":{"code":"backend_unavailable",...}} and exit nonzero. A CLI consumer parsing these lines cannot establish whether this diagnostic came from MXC or from contained code. This is conditional on a consumer trusting stderr; I did not find an in-repo SDK making fallback decisions from it.

Fix: Put executor-originated results on a host-owned channel the workload cannot write to. Until then, document these shared-stream lines as untrusted and do not recommend switching containment based solely on them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pre-existing. I think another PR could add a host-owned channel.

Comment thread src/mxc-sdk/src/backends/lxc/common/lxc_runner.rs Outdated
Comment thread src/mxc-sdk/src/backends/process_container/common/dispatcher.rs Outdated
/// A refused request and an unusable host have their own constructors on
/// [`ScriptResponse`]; this covers the faults that stay `backend_error` and
/// only say *where* the run came apart.
fn response_with_phase(phase: FailurePhase, message: &str) -> ScriptResponse {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium (maintainability) - centralize phase-aware ScriptResponse construction.

Attribution: introduced_by_change - this PR adds the same response/phase assembly here and in lxc_runner.rs:47, appcontainer_runner.rs:95, seatbelt_runner.rs:1104, and isolation_session/common/sandbox.rs:91.

The implementations already differ in whether they copy the diagnostic into standard_err, which affects bare-prose duplication alongside the new JSON envelope. Maintaining one error contract through five near-identical constructors invites drift.

Fix: Put the phase-aware response construction in a small shared ScriptResponse constructor and use it at the backend call sites; preserve intentionally different stderr behavior explicitly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread src/tools/wxc/src/main.rs
Comment thread src/mxc-sdk/src/backends/hyperlight/common/mod.rs
Comment thread src/mxc-sdk/src/core/mxc_engine/dispatch.rs
Comment thread docs/backends/seatbelt/seatbelt-backend.md Outdated
Comment thread docs/development/architecture/versioning.md Outdated

@MGudgin Gudge (MGudgin) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested

This follow-up changes my verdict from the COMMENT review 5534936733 on the same PR head (9b7a541). The 17 inline comments in that review and four subsequent replies already contain the detailed evidence; I am not opening duplicate threads.

Please address these change-attributable issues before merge:

  • High - Unix executor contract coverage (discussion_r4284388975, introduced_by_change): no Unix executor suite asserts both the structured error code and its exact process exit status for the new failure contract. Add a real refusal and unavailable-backend subprocess case, plus an envelope-serialization assertion.
  • Medium - Bubblewrap host DNS misclassified as request refusal (discussion_r4277475899, introduced_by_change): SandboxProxy::resolve can fail on host DNS timeout/thread failure, but the new rejected mapping emits policy_validation/exit 1. Keep static invalid-input errors as policy refusals and classify host lookup failures separately.
  • Medium - response warnings are missing for in-process callers (discussion_r4284389019, introduced_by_change): the new ScriptResponse.warnings field is not populated by mxc_engine::run even when the logger records advisories. Populate it at the shared run boundary or narrow the published response-level contract.

The state-aware exit mismatch in discussion_r4277476083 predates this PR and is a claim_mismatch, not a merge condition by itself. Other Medium/Low comments remain actionable but are not the basis for this verdict. The PR base and head are unchanged since the earlier review; no local build or host-dependent test suite was run for this follow-up.

@microsoft-github-policy-service microsoft-github-policy-service Bot added the Needs-Author-Feedback Waiting for additional information or action from the issue or pull-request author. label Oct 15, 2026
@microsoft-github-policy-service microsoft-github-policy-service Bot removed the Needs-Author-Feedback Waiting for additional information or action from the issue or pull-request author. label Oct 15, 2026
…exit codes

- IsolationSession one-shot reports a wait timeout as FailurePhase::Timeout instead of ProcessExited.
- Warning envelopes start on a fresh stderr line.
- Lifecycle error envelopes exit with their code's status instead of always 1.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c677fc2-77c3-43b0-a06f-da11d11447c8
Copilot AI balanced review requested due to automatic review settings October 16, 2026 00:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

IsolationSession has a compilation-blocking type mismatch, an incorrect success exit code, and incomplete timeout classification.

4 open findings
3 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Service timeout bypasses timeout response

src/​mxc-sdk/​src/​backends/​isolation_session/​common/​one_shot.rs:126

Preserving ExecOutcome fixes local wait timeouts, but service-enforced timeouts still bypass the new timeout response. create_process gives start_process and the later started.wait the same timeout (manager.rs:451,582). As documented in process_options.rs:111–118, the earlier service timer can kill the workload with exit 1, which is returned as Exited(1) without an error envelope. Apply with_service_timeout_grace to the service options while retaining the original local wait deadline, and update the timeout tests and documentation accordingly.

🧠 Review effort: Balanced

Comment thread src/mxc-sdk/src/backends/isolation_session/common/one_shot.rs Outdated
Comment thread src/mxc-sdk/src/backends/isolation_session/common/one_shot.rs Outdated
Copilot AI balanced review requested due to automatic review settings October 16, 2026 00:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Structured-error coverage and host-versus-policy classification still have unresolved gaps.

4 open findings
2 resolved since last review

🧠 Review effort: Balanced

Comment thread src/mxc-sdk/src/backends/seatbelt/common/seatbelt_runner.rs Outdated
Comment thread src/mxc-sdk/src/core/mxc_common/script_runner.rs
Copilot AI balanced review requested due to automatic review settings October 16, 2026 00:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Service-enforced timeouts can still lack timeout diagnostics, and error envelopes can join unterminated workload stderr.

2 open findings
2 resolved since last review
Previously missed (2)

In code that hasn't changed since last review

Medium severity Apply service timeout grace to create_process

src/​mxc-sdk/​src/​backends/​isolation_session/​common/​one_shot.rs:126

Preserving ExecOutcome fixes locally detected timeouts, but not service-enforced ones. create_process passes the original timeout to start_process (manager.rs:451), then starts the same-duration local wait after relay setup (582). The service can terminate the workload first with exit 1, so this becomes Exited(1) without a timeout envelope. Apply with_service_timeout_grace to the service options while retaining the original local timeout, as the piped path does. Update the helper's streaming-only documentation and tighten the host timeout test, which still accepts either 1 or -1.

Medium severity Start timeout error envelope on a fresh line

src/​mxc-sdk/​src/​core/​mxc_common/​script_runner.rs:106

The new IsolationSession timeout response reaches this emitter after workload stderr has been relayed. If the workload writes partial without a newline before timing out, and no later diagnostic supplies a newline, this produces partial{"error":...} rather than a parseable JSON line. Start the error envelope on a fresh line, as the warning emitter and lifecycle exec-error path already do.

🧠 Review effort: Balanced

- Seatbelt profile building separates policy errors from host setup errors; a missing HOME is LaunchFailed rather than a policy rejection.
- Failed create-and-run dry runs emit the typed error envelope on stderr.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c677fc2-77c3-43b0-a06f-da11d11447c8
Copilot AI balanced review requested due to automatic review settings October 16, 2026 00:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Error-envelope line boundaries and IsolationSession service-timeout classification remain unresolved.

1 open finding
2 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Preserve local timeout while extending service timeout grace

src/​mxc-sdk/​src/​backends/​isolation_session/​common/​one_shot.rs:126

Preserving ExecOutcome fixes local wait timeouts, but not service-side timeouts. create_process still passes the original timeout to start_process (manager.rs:451), then starts a local wait for the same duration (line 582). As process_options.rs documents, the earlier service timer can terminate the workload with exit 1. That arrives here as Exited(1), leaving no timeout message or error envelope. Apply with_service_timeout_grace only to the service options, keep the original local deadline, and require backend_error/exit -1 in the host timeout test.

🧠 Review effort: Balanced

Comment thread src/mxc-sdk/src/core/mxc_common/script_runner.rs Outdated
- create_process arms the service timer with the grace margin and enforces the caller's deadline locally, so a timeout reports backend_error/exit -1.
- Error envelopes start on a fresh stderr line.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c677fc2-77c3-43b0-a06f-da11d11447c8
Copilot AI balanced review requested due to automatic review settings October 16, 2026 00:47
@theelliotm

Copy link
Copy Markdown
Contributor Author

Copilot's 'Preserve local timeout while extending service timeout grace' finding is fixed in 2ed5f6f: create_process arms the service timer with the grace margin, and the host test now requires backend_error/exit -1.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Cross-platform failure semantics and Windows timeout changes warrant final platform-aware human review with host-backed validation.

0 open findings

1 resolved since last review

🧠 Review effort: Balanced

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c677fc2-77c3-43b0-a06f-da11d11447c8
Copilot AI balanced review requested due to automatic review settings October 16, 2026 05:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Cross-platform failure semantics need platform-owner validation, and lifecycle timeout reporting remains unresolved.

1 open finding

🧠 Review effort: Balanced

Comment thread src/mxc-sdk/src/backends/isolation_session/common/state_aware.rs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1c677fc2-77c3-43b0-a06f-da11d11447c8
Copilot AI balanced review requested due to automatic review settings October 16, 2026 05:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Cross-platform error routing and native timeout changes require platform-specific validation and final human review.

0 open findings

1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Correct DaclFallbackDisabled contract classification in PR description

src/​mxc-sdk/​src/​backends/​process_container/​common/​dispatcher.rs:307

DaclFallbackDisabled produces policy_validation and exit 1, consistent with the new tests and versioning documentation. The PR description instead lists it under backend_unavailable and exit -1, giving consumers conflicting contracts. Move this variant into the description's policy-refusal row to match the implemented behavior.

🧠 Review effort: Balanced

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs-Attention Requires attention or a decision from the MXC maintainers.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Emit wxc executor errors and warnings as structured ScriptResponse JSON

3 participants