Skip to content

nvx.py: retire the run and sandbox commands in favor of nvx-rs - #902

Open
Pedro Henrique Penna (ppenna) wants to merge 5 commits into
devfrom
agents/fix-issue-725-validate-remote-server
Open

Pedro Henrique Penna (ppenna) wants to merge 5 commits into
devfrom
agents/fix-issue-725-validate-remote-server

Conversation

@ppenna

@ppenna Pedro Henrique Penna (ppenna) commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Closes #725.

nvx.py no longer fronts the runtime CLI. Sandboxes and microVMs run with nvx-rs
directly, nvx.py run and nvx.py sandbox are gone, and nvx-rs no longer reproduces
nvx.py's program name or argparse's usage layout.

Decisions

  • How a checkout gets nvx-rs: with Cargo. The docs give the pinned command
    (cargo build --release --locked --features cli --manifest-path nvx-rs/Cargo.toml --target-dir nvx-rs/target), and download still installs the nvx-rs of a release built
    from the checkout's sources. No new nvx.py command was added.
  • CPU-profile guidance: ported to nvx-rs run. It identifies the CPU with CPUID, as
    OpenVMM does, and reads a copy of the CPUs that OpenVMM's built-in profiles serve. Both NVX
    CLI jobs compare that copy with the submodule's profiles, as they compare the protocol
    vectors. doctor and benchmark keep the Python catalog.
  • Retirement: nvx.py run and nvx.py sandbox are removed outright, without a
    deprecation period, so argparse refuses them as unknown commands (status 2).
  • Usage layout: compact one-line usages, such as usage: nvx-rs run [OPTIONS] and
    usage: nvx-rs sandbox [{run,provision,start,exec,stop,deprovision}] [OPTIONS]. The help
    still lists every option, and usage errors still exit with status 2 and their
    argument-* codes.

Commits

  1. nvx-rs: guide failed cold boots on CPUs that no built-in profile serves. This ports the
    guidance from nvx.py run into nvx-rs run, replacing the hidden --cold-boot-guidance
    option, and adds the catalog copy, its sync test, and the CI comparison step. The one-shot
    run tests expect the guidance after a failed cold boot when the CPU is unserved.
  2. scripts: run the microVM scenarios' sandboxes and microVMs with nvx-rs. The
    test-microvm scenarios, the workflow's sandbox steps, and both setup scripts' checks now
    run nvx-rs. The setup scripts also record the provenance of the nvx-rs that they build,
    as nvx.py run did. The sandbox-lifecycle acceptance now waits for OpenVMM's last thread
    after it kills OpenVMM; see the note below.
  3. doc: run microVMs and sandboxes with nvx-rs. doc/usage.md gets an nvx-rs chapter,
    and the README quick start, doc/run.md, the other docs, and the skills run nvx-rs.
  4. nvx.py: remove the run and sandbox commands. This removes the parsers, handlers,
    argparse action, and signal hand-over. The real-nvx-rs tests now check the docs against
    nvx-rs's own help.
  5. nvx-rs: drop --prog and the argparse layout of its usage. This removes --prog, the
    80-column usage wrapping, tests/fixtures/run-help.txt, and the tests that pinned them. A
    unit test now checks that each help lists exactly the options its parser takes.

Behavior changes

  • nvx-rs takes no abbreviated options (--dry is unknown), where argparse accepted any
    unambiguous prefix. The docs say so.
  • --prog is gone. A caller that still passes it gets a usage error with status 2.
  • When a release ships no nvx-rs built from the checkout's sources, download now prints
    the Cargo command that builds one.

Note: race in the sandbox-lifecycle acceptance

Running nvx-rs sandbox directly exposed a race in the sandbox-lifecycle acceptance on a
Linux/MSHV host. check_crash SIGKILLs OpenVMM and waited only until its /proc/PID/cmdline
was empty. That happens as soon as the main thread is a zombie, while OpenVMM's other threads
are still exiting. nvx-rs counts such a process as running, by design, so an immediate
exec reported endpoint-closed instead of sandbox-exited. The Python start-up of
nvx.py sandbox used to hide that window. kill_openvmm now also waits until the recorded
process has no thread left, as nvx-rs tells it, with unit tests.

Validation

Remote bare-metal host: prometheus30, Linux/MSHV on an Intel Xeon Silver 4114
(Skylake-SP), Azure Linux 3.0. Tested at 26ffcfc, the head before the CI and review fixes,
with OpenVMM 2b681564 and the v0.1.0-dev.1ff24c52991a linux-mshv package.

  • nvx.py verify passed. nvx.py download --hypervisor mshv printed the new Cargo hint, and
    the documented Cargo command built nvx-rs.
  • Boot smoke test: nvx-rs/target/release/nvx-rs run --hypervisor mshv on a pty printed
    NVX-GUEST-BOOT-OK: alpine, and /sbin/nvx-exit 0 exited with status 0. No guidance was
    printed, since a built-in profile serves this CPU, and no OpenVMM was left running.
  • Affected scenarios: nvx.py test-microvm --backend mshv passed every scenario that this
    PR changes: filesystem-comma-paths, l3-l4-egress-policy, l3-l4-egress-protocols,
    l3-l4-egress-port-ranges, ipv6-egress-policy, managed-exec-config,
    sandbox-lifecycle, and sandbox-proxy-only. The sandbox scenarios used the package's
    Ubuntu layer and a scratch template made the way build-guest-artifacts makes it. After the
    kill_openvmm fix, sandbox-lifecycle also passed twice on its own.
  • nvx-rs tests:
    • The CLI tests passed, 53 on Linux, including the Unix-only interrupt, full-pipe, and
      CPU-guidance tests, as did the host::cli unit tests.
    • The ignored comparisons of the CPU profiles and protocol vectors with the submodule passed.
    • cargo test --all-features passed except three guest:: tests, which fail the same way
      on unchanged origin/dev on this host.
  • validate-nvx (Linux step): passed, with 1304 unit tests, including the pty test of
    nvx-rs run and the real-nvx-rs documentation tests, plus test_adversarial,
    test_hosts, and the Specula tests.

Local Windows/WHP host (AMD EPYC 7763):

  • check-quality: ruff, ruff format, pyright for Linux and Windows, the Markdown link
    check, the PowerShell syntax check, and shellcheck and shfmt on setup-linux-mshv.sh passed.
  • check-nvx-rs: fmt, the three clippy configurations, the all-features, default, and
    no-default-features tests, rustdoc, MSRV 1.89, and the runtime adapter's protocol and host
    configurations passed.
  • check-aci-edge-sandboxes: fmt, clippy, tests, rustdoc, and MSRV passed.
  • validate-nvx: 1254 unit tests, test_adversarial, and test_hosts passed.
    test_setup_azure and the Specula tests need bash and fcntl, so they ran on the Linux
    host only.

Fixes after the first CI run and review:

  • The one-shot run tests failed on a GitHub-hosted runner with a Xeon 6973P-C (Granite Rapids),
    which no built-in profile serves. They now expect the guidance there. With the host's own
    profile removed from the catalog, so that the host counts as unserved, the full CLI suites
    pass on Windows (50 tests) and on the Linux host (53).
  • On the review's request, both setup scripts now record the nvx-rs provenance. I ran the
    Linux snippet on prometheus30 and the Windows one locally; each record names the
    executable's SHA-256, the pinned toolchain, and the host target.

Copilot AI balanced review requested due to automatic review settings October 25, 2026 15:56

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

Missing setup-build provenance needs correction, and the breaking CLI migration with cross-platform runtime changes warrants final human review.

2 open issues
What changed in this PR

Moves NVX’s runtime interface entirely to nvx-rs, leaving builds, diagnostics, benchmarks, and test orchestration in Python.

Changes:

  • Removes nvx.py run, nvx.py sandbox, and Rust’s --prog compatibility option; simplifies usage output.
  • Moves CPU-profile guidance into Rust and adds catalog synchronization checks.
  • Migrates scenarios, setup scripts, workflows, and documentation to direct nvx-rs invocation, with improved crash-exit waiting.
File Description
scripts/​test_time_abi.py Removes tests for retired Python guidance.
scripts/​test_microvm_tests.py Updates command expectations and tests thread-exit waiting.
scripts/​setup/​setup-windows-whp.ps1 Builds and invokes nvx-rs directly.
scripts/​setup/​setup-linux-mshv.sh Builds and invokes nvx-rs directly.
scripts/​nvx.py Removes runtime commands and their parsers.
scripts/​nvx_tools/​time_abi.py Removes Python cold-boot guidance.
scripts/​nvx_tools/​sandbox_proxy_tests.py Updates the documented CLI name.
scripts/​nvx_tools/​sandbox_lifecycle_tests.py Uses Rust commands and waits for remaining threads.
scripts/​nvx_tools/​release.py Adds the manual Cargo build hint.
scripts/​nvx_tools/​nvx_rs.py Simplifies command construction and removes delegation helpers.
scripts/​nvx_tools/​microvm_tests.py Runs affected scenarios through nvx-rs.
scripts/​nvx_tools/​managed_exec_tests.py Invokes the Rust sandbox CLI.
scripts/​nvx_tools/​adversarial_executor.py Updates runtime-related comments.
README.md Updates quick starts and build instructions.
nvx-rs/​tests/​openvmm_cpu_profiles.rs Adds catalog comparison with OpenVMM profiles.
nvx-rs/​tests/​fixtures/​run-help.txt Removes the fixed help-layout fixture.
nvx-rs/​tests/​fake_openvmm.rs Updates comments to name the Rust CLI.
nvx-rs/​tests/​cli/​usage.rs Checks compact usage output.
nvx-rs/​tests/​cli.rs Updates usage, guidance, and interruption tests.
nvx-rs/​src/​testing/​fake_openvmm.rs Updates fake-runtime documentation.
nvx-rs/​src/​host/​sandbox/​options/​tests.rs Updates a dry-run helper comment.
nvx-rs/​src/​host/​sandbox/​options.rs Updates option-contract documentation.
nvx-rs/​src/​host/​sandbox/​mod.rs Updates sandbox-profile documentation.
nvx-rs/​src/​host/​sandbox/​layer.rs Updates layer documentation.
nvx-rs/​src/​host/​network/​policy/​mod.rs Updates policy-consumer documentation.
nvx-rs/​src/​host/​mod.rs Documents CLI ownership and CPUID usage.
nvx-rs/​src/​host/​cli/​session.rs Updates control-session documentation.
nvx-rs/​src/​host/​cli/​sandbox/​tests.rs Adapts tests to the simplified parser signature.
nvx-rs/​src/​host/​cli/​sandbox/​mod.rs Simplifies sandbox usage and parser calls.
nvx-rs/​src/​host/​cli/​run/​specification.rs Adapts property tests to parser changes.
nvx-rs/​src/​host/​cli/​run.rs Integrates native CPU guidance and compact usage.
nvx-rs/​src/​host/​cli/​mod.rs Removes --prog and checks help completeness.
nvx-rs/​src/​host/​cli/​egress.rs Simplifies parser and usage definitions.
nvx-rs/​src/​host/​cli/​cpu_generations.json Adds the built-in CPU profile catalog.
nvx-rs/​src/​host/​cli/​artifacts.rs Simplifies artifact-command usage and parsing.
nvx-rs/​src/​host/​cli/​args.rs Replaces usage wrapping with one-line formatting.
nvx-rs/​src/​host/​artifacts.rs Updates guest and artifact documentation.
nvx-rs/​README.md Documents the standalone runtime CLI.
doc/​setup.md Explains obtaining the Rust executable.
doc/​run.md Migrates runtime examples to nvx-rs.
doc/​project-structure.md Updates component responsibilities.
doc/​guests/​ubuntu-guest.md Updates guest command references.
doc/​guests/​azurelinux-guest.md Updates the guest command reference.
doc/​distribution.md Clarifies executable installation and build requirements.
doc/​contribute.md Documents migrated scenarios and catalog checks.
doc/​ci.md Updates runtime handoff and validation documentation.
doc/​build.md Clarifies executable provenance wording.
doc/​adversarial-testing.md Updates target runtime instructions.
aci_edge_sandboxes/​src/​openvmm/​config.rs Updates the documented source of defaults.
.github/​workflows/​run-nvx-microvm-tests.yml Invokes Rust directly in sandbox steps.
.github/​workflows/​ci.yml Adds CPU catalog comparisons on both platforms.
.github/​skills/​nvx-run/​SKILL.md Updates build and runtime instructions.
.github/​skills/​nvx-openvmm-promote/​SKILL.md Adds CPU catalog synchronization guidance.
.github/​skills/​nvx-host-connect/​SKILL.md Updates artifact preparation and dry-run instructions.
.github/​skills/​nvx-debug/​SKILL.md Updates diagnostic command examples.
.github/​copilot-instructions.md Updates runtime and rebuild guidance.

Review effort: Balanced

Comment thread scripts/setup/setup-linux-mshv.sh
Comment thread scripts/setup/setup-windows-whp.ps1
nvx-rs run now prints the CPU-profile guidance itself after OpenVMM fails
a cold boot on a host whose CPU no built-in CPU profile serves, instead of
printing what nvx.py run passed it in the hidden --cold-boot-guidance
option. Callers that run nvx-rs run directly, as they will once nvx.py
run is retired, keep the guidance.

nvx-rs identifies the host CPU with CPUID, as OpenVMM's cpu_profile crate
does: the vendor string of leaf 0, the display family, model, and
stepping of CPUID.1:EAX, with the model extended for Intel's families 6
and 15 and for AMD's family 15 alone, and the brand string of leaves
0x80000002 to 0x80000004 without its padding. A host without CPUID, which
is not an x86-64 host, gets no guidance. The CPUs that the built-in
profiles serve come from src/host/cli/cpu_generations.json, which nvx-rs
embeds: a copy of the ID, the vendor, and the generation of each profile
that OpenVMM pins.

The guidance keeps its text: what --cpu-profile auto does on the CPU, the
generations that the built-in profiles cover, and, on an Intel or AMD
CPU, the fingerprint command of the OpenVMM that the run ran and the
prefilled CPU profile request, whose fields it encodes as OpenVMM's
fallback warning does. It still applies to auto, and to host on a CPU of
a vendor that host profiles do not serve either, and still prints only
after a failed cold boot, never after a dry run, a restore, a refused
option, a usage error, or Ctrl-C.

The nvx-rs job cannot check out the OpenVMM submodule, so the ignored
test tests/openvmm_cpu_profiles.rs compares the copy with the profiles
that the submodule pins, and each NVX CLI job runs it beside the
comparison of OpenVMM's protocol vectors, as an OpenVMM pin promotion now
does too. The host modules deny unsafe code, so the one CPUID call allows
it locally, as the guest's time tools do: the intrinsic is still unsafe
in the crate's minimum Rust version, 1.89.

nvx.py run no longer reads the host CPU, nor looks OpenVMM up with
nvx-rs artifacts, so its parser no longer keeps the values that decided
on the guidance, nvx_rs.py drops the artifact lookup, and time_abi.py
drops the guidance functions and the issue table that only they used;
doctor and benchmark keep its CPU catalog and its profile requests. The
Python tests of the guidance become Rust unit tests of the same cases,
and a CLI test checks the guidance end to end on the host that runs it,
whose CPU the host OS reports through /proc/cpuinfo or
PROCESSOR_IDENTIFIER. The one-shot run tests, whose fake OpenVMM fails
some cold boots, expect the guidance after those on a host whose CPU no
built-in profile serves, such as the Xeon 6973P-C of some GitHub-hosted
runners, and nothing after them elsewhere.

Part of #725.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The test-microvm scenarios, the sandbox steps of the microVM test
workflow, and the checks of the Linux/MSHV and Windows/WHP setup scripts
now run their sandboxes and microVMs with nvx-rs itself, rather than
through nvx.py sandbox and nvx.py run, so that the test suites no longer
depend on the commands that #725 retires.

nvx_rs.command_line gives the command line that nvx.py ran: the nvx-rs
that nvx.py finds, NVX_RS first, or builds, with this checkout as its
artifact root, or another root, which the sandbox-lifecycle acceptance
names for the artifacts of another runtime identity. The public run
scenarios, the shares of host paths with commas and the L3/L4 and IPv6
egress acceptances, run nvx-rs run with the options that they passed
nvx.py run, and record nvx-rs run as the interface of their evidence.
The managed-exec-config, sandbox-lifecycle, and sandbox-proxy-only
acceptances run nvx-rs sandbox. The process tree of each command loses
the Python interpreter that stood between the scenario and nvx-rs, and
nothing else.

The workflow's bash and PowerShell steps run the nvx-rs that each
microVM job downloads into nvx-rs/target/release/, whose artifact root
is then the checkout, and the test that derives the guest artifacts that
each step reads recognizes those invocations. setup-linux-mshv.sh and
setup-windows-whp.ps1 build nvx-rs with the pinned Cargo command that
nvx.py runs, and the toolchain that each pins, and record the provenance
of that executable, before they dry-run nvx-rs run, where nvx.py run
built nvx-rs on demand and recorded its provenance, which packaging and
the adversarial executor require.

The sandbox-lifecycle acceptance, which kills OpenVMM as a crash would,
now waits until the last thread of OpenVMM exits before its next
transition, as nvx-rs tells that a process exited. It waited only until
OpenVMM's command line vanished, which happens once the main thread is a
zombie, while the other threads still exit and nvx-rs still counts the
process as running: without the interpreter that nvx.py started first,
exec reached that window and reported endpoint-closed rather than
sandbox-exited on a Linux/MSHV host. Unit tests check the wait for each
state that /proc reports and on a real child process.

The unit tests of the scenarios expect the nvx-rs command lines, with the
operation after --root ROOT, and substitute a stand-in path for nvx-rs
where they compile no egress policy. The documents that describe the
scenarios and the CI steps name nvx-rs sandbox and nvx-rs run.

Part of #725.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The documentation, the README quick start, and the skills now run NVX's
microVMs and sandboxes with nvx-rs itself, which #725 makes the only
command-line interface that runs them, rather than with nvx.py run and
nvx.py sandbox, which the next commit removes.

The command-line reference gives nvx-rs a chapter of its own. It names
the two ways that a checkout gets nvx-rs at nvx-rs/target/release/: the
download of a release that was built from the checkout's nvx-rs sources,
or the pinned Cargo command that nvx.py runs, which the reference gives
instead of a new nvx.py command. The chapter describes the artifact root
that nvx-rs uses without --root, the order in which the test-microvm
scenarios find or build nvx-rs, and the provenance that binds a build to
its sources. The run and sandbox sections move into that chapter under
their former titles, so that links to #run and #sandbox still resolve,
with nvx-rs synopses, the nvx-rs prefixes of their usage errors, and the
note that nvx-rs, unlike argparse, takes no abbreviated option names.
"Download and run" becomes "Releases", since running no longer goes
through nvx.py.

The quick start of the README builds nvx-rs with Cargo unless download
installed it, so that a checkout of a development release still runs
without Rust, and boots with nvx-rs run on each platform. The run guide;
the setup, build, CI, distribution, guest, and project-structure
documents; the nvx-run, nvx-debug, and nvx-host-connect skills; the
repository instructions; and the nvx-rs README run nvx-rs run and nvx-rs
sandbox in their examples and prose. The Rust doc comments that named
nvx.py run or nvx.py sandbox as the command behind a behavior name
nvx-rs run and nvx-rs sandbox.

The command table of the reference keeps its run and sandbox rows, which
now point at the nvx-rs sections, until the next commit removes the
commands from nvx.py.

Part of #725.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
nvx.py no longer fronts the runtime CLI. Its run and sandbox commands,
which ran nvx-rs run and nvx-rs sandbox with nvx.py's name, standard
streams, and exit status, are gone, and argparse refuses them as it
refuses any unknown command. The documentation, the test-microvm
scenarios, and the CI steps already run nvx-rs itself.

nvx.py loses the parsers of both commands, their handlers, the argparse
action that recorded their options for nvx-rs, and the constants that
only those parsers used. nvx_rs.py loses what only they needed: the
command line with --prog, the hand-over of SIGINT, SIGTERM, and SIGHUP
to nvx-rs while it ran, the wait that outlasted a KeyboardInterrupt, and
the mapping of nvx-rs's exit status to nvx.py's. Its queries, which
compile the egress policies of the scenarios, take their command line
from command_line, as the scenarios' runs do. nvx.py describes itself as
building, testing, benchmarking, and packaging NVX, since it no longer
runs microVMs.

When download finds that a release ships no nvx-rs built from the
checkout's sources, it now gives the Cargo command that builds one,
since no nvx.py command builds it on demand any more; only the microVM
tests still do.

The tests of the delegation go with it: the recording of options in
command-line order, abbreviations, the usage errors that argparse kept,
the shared standard streams and statuses, and the signal handling. The
query tests take their command lines from command_line, the test of an
unstartable nvx-rs runs a query, and the test that nvx-rs run leaves
OpenVMM a terminal to restore runs the nvx-rs that NVX_RS names. Two
tests that read nvx.py's help now run that nvx-rs: one checks that the
nvx-rs chapter of doc/usage.md documents every option of nvx-rs run and
sandbox, as the test of nvx.py's commands checked those sections
before, and the other that the help of nvx-rs run states the memory
default that guests.py gives test-microvm for each guest. The usage test
keeps the check of those defaults in doc/usage.md.

The command table of doc/usage.md drops run and sandbox. doc/ci.md,
doc/project-structure.md, the CI workflow, the nvx-rs README, and the
docs and help of the nvx-rs CLI no longer say that nvx.py runs nvx-rs
for those commands or passes it its name, and the notes on the Copilot
setup say that the microVM tests build nvx-rs when they need it. The
README's differences from nvx.py say that nvx-rs takes no abbreviated
options, which argparse took, and drop the order in which nvx.py and
nvx-rs checked a command line.

Part of #725.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
nvx-rs no longer reproduces nvx.py's program name or argparse's layout
of usages, which it kept only so that nvx.py run and nvx.py sandbox
printed what they printed before they delegated to it. The previous
commit removed those commands, and nothing passes --prog any more.

The top-level parser drops --prog, which callers could set to any name
without square brackets or control characters, so every usage and error
names the program nvx-rs, as in `nvx-rs run: error[CODE]: ...`. A
command line that still passes --prog fails with status 2, as any
unknown option does.

Syntax drops the emulation of HelpFormatter._format_usage, which wrapped
each usage at its parts for 80 columns, and holds the text that follows
the parser's name instead: a usage is one line, `usage: nvx-rs COMMAND
ARGUMENTS`. The top-level, artifacts, and egress parsers, which take few
options, keep naming them, as in `usage: nvx-rs egress compile [-h]
PATH`, while run and sandbox, which take 29 and 35, name them
[OPTIONS], as in `usage: nvx-rs run [OPTIONS]` and `usage: nvx-rs
sandbox [{run,provision,start,exec,stop,deprovision}] [OPTIONS]`. Their
help still lists every option, with its value and description, after
the usage, and usage errors still exit with status 2 with their codes.

The tests that pinned argparse's layout go: the unit test of the
wrapping, the help of run that tests/fixtures/run-help.txt pinned byte
for byte, and the tests of --prog and of the usage that nvx.py sandbox
printed. A unit test checks that each parser's usage is one line and
that its help lists exactly the options that the parser takes, another
that the help of run states the memory default of each guest that
Guest::default_memory_mib applies, and the CLI tests check the usages of
usage errors and help, and that --prog is refused. The test that Ctrl-C
ends a help that fills a pipe now checks that the sandbox help, whose
usage takes one line, still overflows the pipe's page.

The nvx-rs README and the docs of the CLI describe the one-line usages,
and call run and sandbox ports of the former nvx.py commands. The tests
of nvx.py's reader of nvx-rs errors use the one-line usages, and drop
the program name with colons that only --prog could give.

Part of #725.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/fix-issue-725-validate-remote-server branch from 26ffcfc to f53e154 Compare October 25, 2026 16:15
Copilot AI balanced review requested due to automatic review settings October 25, 2026 16:15

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

The breaking cross-platform CLI migration and process-exit timing changes warrant final human review of the reported validation.

No open issues 🎉

2 resolved since last review

Review effort: Balanced

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.

nvx.py: retire the sandbox and run commands in favor of nvx-rs

3 participants