Skip to content

Phase 2B: deserialize the parser into the wire model#566

Open
MGudgin wants to merge 3 commits into
mainfrom
user/gudge/versioning_phase2b
Open

Phase 2B: deserialize the parser into the wire model#566
MGudgin wants to merge 3 commits into
mainfrom
user/gudge/versioning_phase2b

Conversation

@MGudgin

@MGudgin MGudgin commented Jun 24, 2026

Copy link
Copy Markdown
Member

Summary

This PR changes the config parser to deserialize directly into the typed wire model (wxc_common::wire::MxcConfig) instead of an intermediate set of permissive structs, so the JSON schema source of truth and the parser's trust boundary share a single definition of the wire shape and cannot drift.

What changed

  • The parser deserializes both one-shot and state-aware requests straight into wire::MxcConfig, discriminating state-aware requests by the phase key and keeping the experimental block as a raw value for per-backend dispatch typing.
  • The wire structs are the deserialization target; the intermediate structs and their conversion helpers are removed (a large net reduction in config_parser.rs).
  • The boundary is strict by construction: invalid containment / network / clipboard / configurationId values are rejected, and unknown nested fields are rejected (deny_unknown_fields). Deprecated spellings (appcontainer, macos_sandbox, appContainer) are accepted via serde aliases.
  • The wire module is always compiled as the parser target; its JsonSchema derive stays behind the schema-gen feature.
  • The TypeScript SDK and the in-process mxc-sdk crate emit only fields the wire objects define: the container id is carried at top-level containerId and the teardown / preserve flags at lifecycle.destroyOnExit / lifecycle.preservePolicy, so processContainer.name, lxc.containerName, lxc.destroyOnExit, and filesystem.clearPolicyOnExit are no longer sent.
  • Documentation (versioning.md, authoring-a-new-feature.md, schema-codegen.md, copilot-instructions.md, and the state-aware lifecycle docs) describes the wire-model parser.

Verification

  • cargo test --workspace; cargo fmt --all -- --check; cargo clippy --workspace --all-targets -- -D warnings
  • schema codegen + config corpus (169/169) + schema-version gates
  • cd sdk && npm test
Microsoft Reviewers: Open in CodeFlow

@MGudgin MGudgin requested a review from a team as a code owner June 24, 2026 21:41
Copilot AI review requested due to automatic review settings June 24, 2026 21:41

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.

Pull request overview

This PR is Phase 2B of the versioning remediation: it rewires wxc_common’s config parsing to deserialize directly into the typed wire model (wire::MxcConfig) and removes the separate permissive Raw* serde structs, reducing drift risk between schema and parser.

Changes:

  • Switch config parsing (one-shot and state-aware) to serde_jsonwire::MxcConfig, then map wire → domain (ExecutionRequest / ParsedStateAwareRequest).
  • Tighten the stable surface to strict enums + deny_unknown_fields at every stable level; keep the experimental surface permissive/forward-compatible.
  • Remove processContainer.name emission from both the Rust mxc-sdk crate and the TypeScript SDK to match the strict wire model.
Show a summary per file
File Description
src/core/wxc_common/src/wire.rs Makes the wire types always-compiled, gates schema generation derives, and adds/adjusts aliases + permissiveness boundaries.
src/core/wxc_common/src/state_aware_request.rs Removes Phase::from_wire now that phase parsing is handled via the wire enum.
src/core/wxc_common/src/lib.rs Exposes wire unconditionally as the parser’s deserialization target.
src/core/wxc_common/src/config_parser.rs Replaces Raw* deserialization + conversions with wire-model deserialization and wire→domain mapping for one-shot and state-aware.
src/core/mxc-sdk/src/policy.rs Stops emitting processContainer.name in the Rust library’s config builder.
sdk/src/sandbox.ts Stops emitting processContainer.name in the TS SDK policy→config builder.
schemas/dev/mxc-config.schema.0.8.0-dev.json Updates generated schema to reflect the new permissive experimental nested structs.
docs/versioning.md Updates documentation to reflect wire-model parsing and strict-enum behavior (including alias observability changes).
docs/state-aware-lifecycle/mxc-state-aware-sandbox-api.md Updates state-aware spec text/examples to reflect wire::MxcConfig as the Rust mirror/parse target.
docs/state-aware-lifecycle/mxc-state-aware-sandbox-api-overview.md Same: aligns overview with the new wire-model parse flow.
docs/schema-codegen.md Updates roadmap/status now that the parser is wired to the wire model and Raw structs are gone.
docs/nanvix-microvm/nanvix-integration-plan.md Updates the plan references to reflect the wire-model-based parser flow.
docs/bwrap-support/bubblewrap-backend-plan.md Marks older Raw-struct steps as superseded; points to wire-model approach.
docs/authoring-a-new-feature.md Updates the feature-authoring checklist to edit wire model + regenerate schema, then map wire→domain.
.github/copilot-instructions.md Updates repository conventions/docs to reflect the new config parsing flow and feature-authoring steps.

Copilot's findings

  • Files reviewed: 15/15 changed files
  • Comments generated: 0

This PR changes the config parser to deserialize directly into the typed wire
model (`wxc_common::wire::MxcConfig`) instead of an intermediate set of
permissive structs, so the JSON schema source of truth and the parser's trust
boundary share a single definition of the wire shape and cannot drift.

Details:
- The parser deserializes both one-shot and state-aware requests straight into
  `wire::MxcConfig`, discriminating state-aware requests by the `phase` key and
  keeping the `experimental` block as a raw value for per-backend dispatch typing.
- The wire structs are the deserialization target; the previous intermediate
  structs and their conversion helpers are removed (a large net reduction in
  `config_parser.rs`).
- The boundary is strict by construction: invalid containment, network,
  clipboard, and configurationId values are rejected, and unknown nested fields
  are rejected (`deny_unknown_fields`). Deprecated spellings (`appcontainer`,
  `macos_sandbox`, `appContainer`) are accepted via serde aliases.
- The `wire` module is always compiled as the parser target; its `JsonSchema`
  derive stays behind the `schema-gen` feature.
- The TypeScript SDK and the in-process `mxc-sdk` crate emit only fields the wire
  objects define: the container id is carried at top-level `containerId` and the
  teardown / preserve flags at `lifecycle.destroyOnExit` / `lifecycle.preservePolicy`,
  so `processContainer.name`, `lxc.containerName`, `lxc.destroyOnExit`, and
  `filesystem.clearPolicyOnExit` are no longer sent.
- Docs (`versioning.md`, `authoring-a-new-feature.md`, `schema-codegen.md`,
  `copilot-instructions.md`, state-aware lifecycle) describe the wire-model parser.

Tests:
- cargo test --workspace; cargo fmt --all -- --check; cargo clippy --workspace
  --all-targets -- -D warnings.
- Schema codegen, config corpus (169/169), and schema-version gates.
- SDK unit tests (cd sdk && npm test).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Generated-with: claude-opus-4.8
@MGudgin MGudgin force-pushed the user/gudge/versioning_phase2b branch from c837fc4 to 6d3538b Compare June 24, 2026 23:42
@MGudgin MGudgin changed the title Phase 2B: deserialize the parser into the wire model (delete Raw* structs) Phase 2B: deserialize the parser into the wire model Jun 24, 2026
Comment thread src/core/wxc_common/src/wire.rs
Comment thread src/core/wxc_common/src/config_parser.rs Outdated
Comment thread src/core/wxc_common/src/config_parser.rs
Comment thread src/core/wxc_common/src/config_parser.rs Outdated
Comment thread sdk/src/sandbox.ts Outdated
Comment thread docs/authoring-a-new-feature.md Outdated
Comment thread .github/copilot-instructions.md Outdated
Comment thread docs/bwrap-support/bubblewrap-backend-plan.md Outdated
Comment thread docs/state-aware-lifecycle/mxc-state-aware-sandbox-api.md Outdated
Gudge and others added 2 commits June 25, 2026 09:23
…doc cleanup)

This change applies code-review feedback on the wire-model parser without
altering behavior: it makes the trivial wire->domain enum/struct conversions
idiomatic, groups the schema-generation code behind one feature gate, and removes
stale references to the deleted Raw* structs.

Details:
- Replace the free `map_wire_*` enum/struct converters with `From` impls beside
  the domain types: `From<wire::LaunchMethod>`, `From<wire::IsolationConfigurationId>`,
  `From<wire::IsolationUser>` in models.rs and `From<wire::Phase>` in
  state_aware_request.rs; call sites now use `.into()`. `map_wire_containment`
  stays a function (it resolves `Option<&_>` per target_os, not a pure conversion).
- Group the schema-gen functions (`generate_config_schema_json` and its helpers)
  into a single `#[cfg(feature = "schema-gen")] mod schema_gen`, re-exporting the
  one public entry point, instead of repeating the attribute per item. The wire
  structs stay un-gated (they are the parser's deserialization target); only the
  schemars-dependent generation code is gated.
- Rename the discriminator `value` to `parsed_json` in the parse entry point.
- Drop the now-redundant SDK comments explaining omitted wire fields, and remove
  stale `Raw*` references from docs (authoring-a-new-feature, bubblewrap plan,
  state-aware API, copilot-instructions).

Tests:
- cargo build -p wxc_common (default + schema-gen); cargo clippy --workspace
  --all-targets -- -D warnings; cargo fmt --all -- --check.
- cargo test --workspace; schema codegen byte-identical; corpus 169/169.
- cd sdk && npm test -> 183 pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Generated-with: claude-opus-4.8
This change adds a "Wire Model vs Runtime Model" section to docs/versioning.md
explaining why the parser keeps two Rust representations (the JSON-mirroring wire
model and the validated runtime model) with a mapping at the parse boundary,
rather than a single shared type.

Details:
- Describe the wire model (schema/SDK codegen source, faithful JSON shape) and the
  runtime/domain model (validated, invariant-rich, what backends consume), and the
  parser as the single validate/normalize boundary between them.
- Lay out the pros (one boundary, parse-don't-validate, pure schema source,
  decoupled evolution) and cons (boilerplate, internal drift, indirection), and
  note the costs are addressable without merging the layers.

Tests:
- Documentation-only change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Generated-with: claude-opus-4.8
Some("read") => ClipboardPolicy::Read,
Some("write") => ClipboardPolicy::Write,
Some("all") => ClipboardPolicy::All,
if let Some(raw_ui) = cfg.ui {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

some of these ones can also have the from<T> added for them as well.

  • wire::ClipboardPolicy -> ClipboardPolicy,
  • wire::NetworkEnforcement-> NetworkEnforcementMode
  • wire::NetworkPolicy-> NetworkPolicy
  • wire::Containment -> ContainmentBackend

Then for wire_ui_isolation_str we'd probably want a to_string() function in the enum itself kind of like what we do here:

pub fn wire_name(&self) -> &'static str {

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants