Skip to content

nvx-rs: make the host module's dependency graph acyclic - #909

Closed
Pedro Henrique Penna (ppenna) wants to merge 4 commits into
devfrom
agents/issue-897-logical-commits-fix
Closed

Pedro Henrique Penna (ppenna) wants to merge 4 commits into
devfrom
agents/issue-897-logical-commits-fix

Conversation

@ppenna

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

Copy link
Copy Markdown
Contributor

Fixes #897. Part of #896.

The production references between the children of nvx::host formed one strongly connected component of eight children: artifacts, filesystem, launch, platform, process, sandbox, store, and supervisor. This moves the three items that closed it below their users, so the graph is acyclic, and adds a guard test that keeps it acyclic. Behavior and the public API don't change.

Commits

  1. nvx-rs: define Inherited in host::platform::inherit. Inherited, its Borrowed alias, and their impls move unchanged from supervisor::child into a new crate-visible host::platform::inherit. The Linux, Windows, and other platform modules import it from there, so platform no longer references supervisor. supervisor re-exports it, so its only public path is still nvx::host::supervisor::Inherited.
  2. nvx-rs: move the spawn of a VMM to a private host::spawn. supervisor/child.rs moves to host/spawn.rs with git mv (97% similarity). store imports super::spawn::Launched, and supervisor re-exports Launched, VmmCommand, and spawn as before. From the new file, the link [launch](super::launch) would silently point at the module host::launch, so it is now crate::host::supervisor::launch (nvx-rs: break the host module into finer-grained modules #896, invariant 6). The link to spawn in Inherited's documentation became a crate:: path in commit 1.
  3. nvx-rs: move the checks of required files to host::artifacts. require_artifact and require_existing move unchanged from sandbox into artifacts as pub(crate). The public sandbox::require_file stays and calls artifacts::require_existing. cli/run.rs and cli/sandbox/mod.rs import require_artifact from artifacts. sandbox no longer needs host::paths, so the sandbox's tests, which reached it through sandbox, import it themselves.
  4. nvx-rs: guard the dependency graph between host's children. nvx-rs/tests/host_modules.rs is a std-only integration test. It reads the crate's sources, following the module declarations from src/lib.rs, and builds the graph of references as nvx-rs: break the host module into finer-grained modules #896 defines them: crate::, super::, and self:: paths, and paths from a name that extern crate self as NAME; gives the crate root, in use trees, expressions, attributes, macro inputs, and the string arguments of serde attributes such as with; plus paths through names that modules outside host's children import, by name or with a glob (crate::Artifacts, crate::guest_path, crate::resolve_guest_path). Such a name stands for every child that its imports name, under any configuration and through other such names and aliases, and an import of such a module stands for every child that the module's names and modules stand for. It skips comments, literals, and test-only code: the items, statements, fields, variants, match arms, and parameters that cfg(test) marks, and the rest of a module or item after an inner #![cfg(test)]. Where it can't tell type arguments from comparisons, it may count test-only code, but it never skips production code. It expands no macros: a macro's input counts as production code, whatever its cfg attributes, with the paths that its strings hold. It fails loudly on what it can't follow instead of missing references: macro_rules! templates that declare or import, that name or may join a path from a module, a metavariable, or a repetition, or hold one in a string, that expand a token tree, or that complete a serde attribute with a metavariable; #[path] attributes, conditional or not; glob imports of host's children; and imports that bind the crate root or host itself, under any name, in host's children. On a cycle, it lists the shortest cycle through each edge that lies on one, with the first reference along each edge. Ten self-tests on synthetic crates pin what counts as a reference, where test-only code ends, the children that imports and aliases stand for, the refusals, and the report format. The test runs in check-nvx-rs's existing cargo test steps, with no new dependency and no workflow change. The nvx-rs README and doc/ci.md mention it.

Each commit passes fmt, Clippy, the Linux cross-check, and rustdoc with -D warnings on its own.

Resulting layers

cargo test --test host_modules -- --nocapture prints these layers, the same on Windows and Linux. They match the table in #897, with paths in place of python, which #911 deleted.

Layer Children
0 network, paths, report, seal, sys
1 platform
2 filesystem, nvxs, process
3 session, spawn
4 store
5 artifacts
6 launch
7 supervisor
8 instance, sandbox
9 cli

Validation

  • The guard is red on dev and green here. With the guard added to dev (12b56130), it fails and lists 13 cycles through the three back-edges (output below). After the three moves, it passes.
  • The public API diff is zero (nvx-rs: break the host module into finer-grained modules #896, invariant 1). I generated rustdoc JSON (RUSTC_BOOTSTRAP=1 cargo rustdoc --lib --all-features -- -Z unstable-options --output-format json) for dev and this branch on x86_64-pc-windows-msvc and x86_64-unknown-linux-gnu. A script walked it from the crate root through public modules and re-exports, globs included. For each reachable path it recorded the item's signature, generics, public fields, variants, inherent methods, and trait impls, with item IDs replaced by canonical paths and spans and docs ignored. The output is identical: 453 paths on Windows and 583 on Linux, 0 differences.
  • Test names (nvx-rs: break the host module into finer-grained modules #896, invariant 2). I compared cargo test -- --list with all features and with default features between dev and this branch. On Windows, the only change is the guard's eleven new tests. On Linux, the four Linux-only tests host::supervisor::child::tests::* become host::spawn::tests::*, and the guard adds its eleven tests. Nothing else changes. The coverage families and the artifacts bundling filter select the same tests.
  • check-nvx-rs, run locally. On Windows: fmt; Clippy with all features, --lib, and --no-default-features, and with all features for x86_64-unknown-linux-gnu; tests with all features (700 library tests), with default features, and with no default features, all passing; rustdoc with -D warnings; the 1.89 MSRV check; and the macOS cross-check. On Linux (WSL): the library tests with all features, 1,237 passing, including the four host::spawn tests.
  • check-aci-edge-sandboxes and the runtime adapter (protocol-only and host-only), run locally on Windows. All passed.
  • python scripts/check_markdown_links.py passed.
The guard on dev (12b56130)
host's children reference each other in cycles. Break each one, for example by moving the items that one of its references names into a child below the others:

artifacts -> sandbox -> artifacts
    artifacts -> sandbox: src/host/artifacts.rs:41: crate::host::sandbox::require_artifact
    sandbox -> artifacts: src/host/sandbox/distro.rs:49: crate::host::artifacts::DISTRO_MANIFEST_SUFFIX

platform -> supervisor -> platform
    platform -> supervisor: src/host/platform/linux.rs:12: crate::host::supervisor::Inherited
    supervisor -> platform: src/host/supervisor/child.rs:24: crate::host::platform

store -> supervisor -> store
    store -> supervisor: src/host/store/mod.rs:242: super::supervisor::Launched
    supervisor -> store: src/host/supervisor/mod.rs:110: super::store::IdentityCheck

platform -> supervisor -> process -> platform
    platform -> supervisor: src/host/platform/linux.rs:12: crate::host::supervisor::Inherited
    supervisor -> process: src/host/supervisor/child.rs:26: crate::host::process::POLL_INTERVAL
    process -> platform: src/host/process.rs:31: super::platform

platform -> supervisor -> store -> platform
    platform -> supervisor: src/host/platform/linux.rs:12: crate::host::supervisor::Inherited
    supervisor -> store: src/host/supervisor/mod.rs:110: super::store::IdentityCheck
    store -> platform: src/host/store/files.rs:8: crate::host::platform

artifacts -> platform -> supervisor -> launch -> artifacts
    artifacts -> platform: src/host/artifacts.rs:40: crate::host::platform
    platform -> supervisor: src/host/platform/linux.rs:12: crate::host::supervisor::Inherited
    supervisor -> launch: src/host/supervisor/mod.rs:107: super::launch::timeout_problem
    launch -> artifacts: src/host/launch.rs:12: super::artifacts::Artifacts

artifacts -> sandbox -> supervisor -> launch -> artifacts
    artifacts -> sandbox: src/host/artifacts.rs:41: crate::host::sandbox::require_artifact
    sandbox -> supervisor: src/host/sandbox/prepared.rs:67: crate::host::supervisor::Inherited
    supervisor -> launch: src/host/supervisor/mod.rs:107: super::launch::timeout_problem
    launch -> artifacts: src/host/launch.rs:12: super::artifacts::Artifacts

artifacts -> store -> supervisor -> launch -> artifacts
    artifacts -> store: src/host/artifacts.rs:42: crate::host::store::ArtifactIdentity
    store -> supervisor: src/host/store/mod.rs:242: super::supervisor::Launched
    supervisor -> launch: src/host/supervisor/mod.rs:107: super::launch::timeout_problem
    launch -> artifacts: src/host/launch.rs:12: super::artifacts::Artifacts

filesystem -> platform -> supervisor -> launch -> filesystem
    filesystem -> platform: src/host/filesystem.rs:43: super::platform
    platform -> supervisor: src/host/platform/linux.rs:12: crate::host::supervisor::Inherited
    supervisor -> launch: src/host/supervisor/mod.rs:107: super::launch::timeout_problem
    launch -> filesystem: src/host/launch.rs:13: super::filesystem::ExportPlan

filesystem -> platform -> supervisor -> store -> filesystem
    filesystem -> platform: src/host/filesystem.rs:43: super::platform
    platform -> supervisor: src/host/platform/linux.rs:12: crate::host::supervisor::Inherited
    supervisor -> store: src/host/supervisor/mod.rs:110: super::store::IdentityCheck
    store -> filesystem: src/host/store/files.rs:8: crate::host::filesystem

platform -> supervisor -> store -> process -> platform
    platform -> supervisor: src/host/platform/linux.rs:12: crate::host::supervisor::Inherited
    supervisor -> store: src/host/supervisor/mod.rs:110: super::store::IdentityCheck
    store -> process: src/host/store/mod.rs:241: super::process::BootId
    process -> platform: src/host/process.rs:31: super::platform

artifacts -> sandbox -> platform -> supervisor -> launch -> artifacts
    artifacts -> sandbox: src/host/artifacts.rs:41: crate::host::sandbox::require_artifact
    sandbox -> platform: src/host/sandbox/mount.rs:36: crate::host::platform
    platform -> supervisor: src/host/platform/linux.rs:12: crate::host::supervisor::Inherited
    supervisor -> launch: src/host/supervisor/mod.rs:107: super::launch::timeout_problem
    launch -> artifacts: src/host/launch.rs:12: super::artifacts::Artifacts

artifacts -> sandbox -> filesystem -> platform -> supervisor -> launch -> artifacts
    artifacts -> sandbox: src/host/artifacts.rs:41: crate::host::sandbox::require_artifact
    sandbox -> filesystem: src/host/sandbox/mod.rs:69: crate::host::filesystem::ExportPlan
    filesystem -> platform: src/host/filesystem.rs:43: super::platform
    platform -> supervisor: src/host/platform/linux.rs:12: crate::host::supervisor::Inherited
    supervisor -> launch: src/host/supervisor/mod.rs:107: super::launch::timeout_problem
    launch -> artifacts: src/host/launch.rs:12: super::artifacts::Artifacts

Coordination with the template stack

At its head, the template and clone stack (#879 … #888) adds host::template. Even with these moves, store → template (template::Lease) and sandbox → template (template::BlockGeometry) close a cycle, as #897 notes. If this PR lands first, the guard names those edges when the stack rebases.

Copilot AI balanced review requested due to automatic review settings October 26, 2026 20:07

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

The dependency guard can miss production cycles through imports and incorrectly count test-only references.

4 open issues
What changed in this PR

This PR removes dependency cycles between nvx::host modules, preparing for the finer-grained module split in #896 while preserving behavior and public API paths.

Changes:

  • Moves inherited handles, VMM spawning, and required-file checks below their callers.
  • Preserves public re-exports and corrects documentation links for moved items.
  • Adds and documents a source-based dependency guard with synthetic self-tests.
File Description
nvx-rs/​tests/​host_modules.rs Adds dependency scanning, cycle reporting, and self-tests.
nvx-rs/​src/​host/​supervisor/​mod.rs Preserves public re-exports of moved items.
nvx-rs/​src/​host/​store/​mod.rs Imports Launched directly from spawn.
nvx-rs/​src/​host/​spawn.rs Houses VMM spawning outside supervisor.
nvx-rs/​src/​host/​sandbox/​plan.rs Imports file validation from artifacts.
nvx-rs/​src/​host/​sandbox/​mod.rs Delegates file checks to artifacts.
nvx-rs/​src/​host/​platform/​windows.rs Uses the relocated Inherited type.
nvx-rs/​src/​host/​platform/​other.rs Uses the relocated Inherited type.
nvx-rs/​src/​host/​platform/​mod.rs Declares the crate-visible inherit module.
nvx-rs/​src/​host/​platform/​linux.rs Uses the relocated Inherited type.
nvx-rs/​src/​host/​platform/​inherit.rs Houses inherited descriptor and handle support.
nvx-rs/​src/​host/​mod.rs Declares and documents private spawn.
nvx-rs/​src/​host/​cli/​sandbox/​mod.rs Redirects artifact-check imports.
nvx-rs/​src/​host/​cli/​run.rs Redirects artifact-check imports.
nvx-rs/​src/​host/​artifacts.rs Houses shared required-file checks.
nvx-rs/​README.md Documents the dependency guard.
doc/​ci.md Includes the guard in CI documentation.

Review effort: Balanced

Comment thread nvx-rs/tests/host_modules.rs Outdated
Comment thread nvx-rs/tests/host_modules.rs Outdated
Comment thread nvx-rs/tests/host_modules.rs Outdated
Comment thread nvx-rs/tests/host_modules.rs Outdated
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/issue-897-logical-commits-fix branch from 7677559 to cbaa574 Compare October 26, 2026 20:27
Copilot AI balanced review requested due to automatic review settings October 26, 2026 20:27

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 nvx-rs/tests/host_modules.rs Outdated
Comment thread nvx-rs/tests/host_modules.rs Outdated
Comment thread nvx-rs/tests/host_modules.rs
Comment thread nvx-rs/tests/host_modules.rs Outdated
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/issue-897-logical-commits-fix branch from cbaa574 to b563477 Compare October 26, 2026 20:42
Copilot AI balanced review requested due to automatic review settings October 26, 2026 20:42

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 nvx-rs/tests/host_modules.rs
Comment thread nvx-rs/tests/host_modules.rs Outdated
Comment thread nvx-rs/tests/host_modules.rs Outdated
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/issue-897-logical-commits-fix branch from b563477 to 2a6478d Compare October 26, 2026 20:54
Copilot AI balanced review requested due to automatic review settings October 26, 2026 20:54

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

The new dependency guard still has cases that silently omit production references and allow cycles to pass.

2 open issues
3 resolved since last review

Review effort: Balanced

Comment thread nvx-rs/tests/host_modules.rs Outdated
Comment thread nvx-rs/tests/host_modules.rs Outdated
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/issue-897-logical-commits-fix branch from 2a6478d to 3181fc8 Compare October 26, 2026 22:37
Copilot AI balanced review requested due to automatic review settings October 26, 2026 22:37
Inherited names a descriptor (Unix) or handle (Windows) of the caller
that a VMM inherits. supervisor::child defined it, so the Linux,
Windows, and other platform modules, whose spawns take the allowed
descriptors or handles as Inherited values, imported it from
crate::host::supervisor. That import made platform depend on
supervisor, which depends on platform in turn, and is one of the three
back-edges that tie eight of host's children into one cycle.

Inherited, the alias Borrowed of what it borrows on each host, and
their impls move unchanged into a new crate-visible module,
host::platform::inherit, from which the platform modules import it, so
that platform no longer references supervisor. Inherited stays public,
and supervisor re-exports it, so that its only public path is still
nvx::host::supervisor::Inherited, where the sandbox's prepared launches
and the CLI keep naming it. supervisor::child, which spawns with it,
imports it from its new module.

The documentation of Inherited links to File and spawn by their paths,
since neither is in scope in the new module. The Linux tests of
supervisor::child import the descriptor types that the module imported
for the moved impls.

Part of #897.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
supervisor::child held the detached spawn of a VMM: VmmCommand, spawn,
and the Launched child that spawn returns. The store records a launched
VMM's identity from its Launched, so store imported
super::supervisor::Launched, while supervisor runs its launches through
the store's journal and imports store: another back-edge of the cycle
between host's children. The spawn itself needs neither of them; it
imports only platform, process, and protocol.

supervisor/child.rs moves with git mv to host/spawn.rs, a private child
of host, and store imports Launched from it, so that store no longer
references supervisor. supervisor re-exports VmmCommand, spawn, and
Launched as it did, so that their public paths stay
nvx::host::supervisor::{VmmCommand, spawn, Launched}. The overview of
host names the new private module.

The documentation of spawn linked to supervisor's launch as
super::launch, which from host/spawn.rs would name the module
host::launch instead, without a warning from cargo doc, so it links to
crate::host::supervisor::launch. Its links to Launch::record_process
were crate paths already, and stay documentation rather than references
from spawn to store. The four Linux tests of the spawn move with it,
from host::supervisor::child::tests to host::spawn::tests.

Part of #897.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
artifacts checks each artifact that it locates with require_artifact,
which sandbox defined beside its public require_file, together with the
helper that both share, require_existing. So artifacts imported
crate::host::sandbox::require_artifact, while the sandbox's distro
layers import artifacts::DISTRO_MANIFEST_SUFFIX: the last back-edge of
the cycle between host's children.

The two helpers move unchanged into artifacts, as pub(crate), so that
artifacts no longer references sandbox. Only the documentation of
require_existing, which described the check through require_file, now
describes it itself. The public sandbox::require_file stays where it
is, as it is, and calls artifacts::require_existing. The run and
sandbox commands of the CLI import require_artifact from artifacts.
sandbox no longer needs host::paths, which require_existing used, so
the sandbox's tests, which reached paths through sandbox, import it
themselves.

With this and the previous two commits, the production references
between host's children form no cycle, and the children stack in
layers, each above the children that it references: network, paths,
report, seal, and sys; platform; filesystem, nvxs, and process;
session and spawn; store; artifacts; launch; supervisor; instance and
sandbox; and cli.

Part of #897.

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

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 nvx-rs/tests/host_modules.rs
Comment thread nvx-rs/tests/host_modules.rs Outdated
Comment thread nvx-rs/tests/host_modules.rs
Comment thread nvx-rs/tests/host_modules.rs
Comment thread nvx-rs/tests/host_modules.rs
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/issue-897-logical-commits-fix branch from 3181fc8 to 12becfb Compare October 26, 2026 22:53
Copilot AI balanced review requested due to automatic review settings October 26, 2026 22:53

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 guard can miss dependency cycles through wrapper re-exports and macro-generated paths, while incorrectly scoped local imports can cause it to reject acyclic code.

5 open issues

Review effort: Balanced

@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/issue-897-logical-commits-fix branch from 12becfb to 93510dc Compare October 26, 2026 23:06
Copilot AI balanced review requested due to automatic review settings October 26, 2026 23:06
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
tests/host_modules.rs, which #910 added, keeps the direct children of
nvx::host free of cycles. #909, an independent fix of #897 that #910
superseded, had a test that follows more of the code through which a
child may name another, and that reports a cycle as #897 asks, with one
reference for each of its dependencies. Fold it in, so that the test
does what both did.

The test now reads the crate's sources from src/lib.rs through every
module that the crate declares, in a file of its own or inline, where it
read src/lib.rs and the files beneath src/host/ alone, so it sees every
module through whose imports a child may name another. A file beneath
src/host/ that no mod declares still fails it. It still reads text, with
std alone, and blanks comments and literals.

It leaves out test-only code: what a cfg that requires test guards,
directly, through a cfg_attr that applies attributes, not one in what
another attribute takes as its arguments, or as the inner attribute of a
module or a file, with the files of the modules that such code declares,
and #[test] functions. It no longer takes an inline module for test-only
code by its name, tests, alone. It leaves out guarded code only as far
as its tokens show where that code ends, as before, and now also leaves
out an element of a list, such as a field of a struct expression or a
parameter of generics or of a closure, up to its first comma, >, |, or
the end of the list, which it counted whole. The input of a macro, which
need not be Rust, and whose attributes the macro may keep or drop, is
production code throughout: no cfg in it leaves anything out, every path
in its tokens counts, however the test reads the rest of it, as in a use
tree that the macro takes apart, and so does every path in its strings,
even one after doc =, which the macro need not take for documentation;
and the test finds there what it refuses wherever it is, as an include!
in a use tree. So the test may find a dependency in test-only code, but
never misses one in production code.

A child depends on another when its production code names a path into
the other: a crate::, super::, self::, or self::super:: path, or, now,
one from a name that extern crate self as NAME gives the crate root in
the extern prelude, unless the module, defined once, declares a module
or crate of that name, or imports one of the crate's modules under it,
through modules and imports by name, in every configuration. Such a
declaration in the crate root, which the test refused, is now followed;
a child's import of the crate root or host itself still fails it. The
path may be in a use tree, an expression, an attribute, or, with its
escapes decoded, a string of an attribute or of a macro's input, but not
in documentation that an attribute applies outside the input of a macro.
It may go through the names that any module outside the children, not
only the crate root and host, binds with its imports, by name, under an
alias, or with a glob, which the test reads to a fixed point under every
configuration, through a module's import of itself, under any alias, as
often as a path goes through it, and a child's import of such a module
depends on every child that the module's names stand for. A macro that a
child exports is still an item of the crate root, in the namespace of
macros.

Code whose dependencies the test cannot follow fails it: a #[path]
attribute anywhere in the crate, where only host's failed it; include!,
under any name that imports may give it, unless a macro_rules! of that
name hides it, in the file or in a block, and through a path, which may
name it whatever the crate defines; an import of host or the crate root
itself in a child; a glob import of a child into a module outside them;
a path through imports that alias one another in a cycle that lengthens
it, which imports of different configurations may make; an inline module
in the input of a macro, which may put the module's code in another
module; a #[macro_use] attribute in host; and the template of a
macro_rules! that declares or imports, that names or may join a path
from a module or from the macro's input, or that holds one in a string,
which resolve where the macro expands, that puts another file's code in
with include!, that invokes a macro that its input names, which may be
include!, that may make a keyword that declares of an identifier of its
input, after a visibility of its input or none, as $vis $kind $name {
... } may be mod name { ... }, or that invokes a macro by name under
local_inner_macros.

A cycle fails the test, which now lists, for each dependency on one, the
shortest cycle through it, with the first reference along each of its
dependencies, under how to break it, where it listed each strongly
connected component. Without a cycle, the children stack in layers, each
above those that it depends on, which the test prints with --nocapture.
On the dev that #910 fixed, at 12b5613, the test lists 13 cycles, each
through one of the three dependencies that #910 removed. On this tree,
it finds the same 51 dependencies as #909's test, in the same ten layers
as before.

The ten tests of #909 run beside the test's twelve, with their fixtures
unchanged, in place of the test's previous thirteen: its two tests of
the report, of strongly connected components and of one reference for
each dependency, give way to one of the shortest cycle through each
dependency, and its test of guarded code whose end the tokens do not
show is renamed for what it now checks, that such code is test-only no
further than its tokens show. Each of the 22 findings of #909's reviews
reproduces as a fixture against the test, and passes; eight failed on
the test before, and #909's tests pin them. A separate review of the
fold, in five rounds, found nineteen more ways to misread code, and its
fixes two more, each with a regression fixture that fails on the test
before its fix. Two stay on purpose: a path-qualified include! fails the
test, whatever it names, and a name that a glob brings in hides no name
of the extern prelude, since what a glob brings in depends on
visibility, which the test does not read.

The module documentation of host and the README's testing section
describe the test as it now is.

Validation, on dev. The test's 22 tests pass. On Windows, the
check-nvx-rs steps that run there pass: rustfmt; Clippy with warnings
denied for all targets with all features, for the default-feature
library, and for the library without default features; the tests with
all features (831), with default features (686), and of the library
without default features (201); the artifact-bundling test (18); rustdoc
with warnings denied; the Rust 1.89 check; and the runtime adapter's
protocol-only (39 tests) and host-only (84 tests) configurations. So do
the check-aci-edge-sandboxes steps that run on Windows, with 168 and 89
tests and agent bundling, and the Markdown link check. On Linux, under
WSL 2, the Linux steps of both actions pass too: the nvx tests with all
features (1405), with default features (670), and of the library without
default features (201), artifact bundling (19), the macOS check, the
guest binaries' lint, tests (829), and build, the runtime adapter's
guest-only configuration (106 tests) besides the others, and
aci_edge_sandboxes's tests and its macOS check. Against dev, the lists
of tests change only in this test's.

Part of #897.
Part of #896.
Supersedes #909.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
The fix of #897 in #910 moved require_artifact and require_existing from
host::sandbox into host::artifacts, and the spawn of a VMM from
host::supervisor into the private host::spawn. Their documentation still
described them from where they had been: require_artifact and
require_existing described themselves by sandbox::require_file, which
itself calls require_existing, and the module documentation of host
described spawn only in its paragraph on the layering of host. The
independent fix of #897 in #909, which #910 superseded, worded both more
precisely. Take its wording.

require_artifact now refers to require_existing, beside it.
require_existing states what it checks and how it fails: that the path
is a regular file, following links, as require_file of nvx.py checks it,
or else the error code that its caller passes and the message
`{description} not found: {path}`, with the path spelled with its
components alone, as Path::components reads them.

The supervisor's entry in the module documentation of host now describes
spawn: a private module, whose items supervisor re-exports, that starts
the VMM detached, with no other descriptor or handle of the caller's.
The paragraph on the layering of host no longer repeats this, and says
only that the store builds on spawn.

Only documentation changes: the public API, the messages, the error
codes, and the tests stay the same.

Validation at this commit, on dev: rustdoc with warnings denied; Clippy
with warnings denied for all targets with all features, for Windows and
Linux; cargo check for macOS; and the tests with all features on Windows
(822), and the library's tests with all features on Linux (1237), pass.

Part of #897.
Part of #896.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
tests/host_modules.rs, which #910 added, keeps the direct children of
nvx::host free of cycles. #909, an independent fix of #897 that #910
superseded, had a test that follows more of the code through which a
child may name another, and that reports a cycle as #897 asks, with one
reference for each of its dependencies. Fold it in, so that the test
does what both did.

The test now reads the crate's sources from src/lib.rs through every
module that the crate declares, in a file of its own or inline, where it
read src/lib.rs and the files beneath src/host/ alone, so it sees every
module through whose imports a child may name another. A file beneath
src/host/ that no mod declares still fails it. It still reads text, with
std alone, and blanks comments and literals.

It leaves out test-only code: what a cfg that requires test guards,
directly, through a cfg_attr that applies attributes, not one in what
another attribute takes as its arguments, or as the inner attribute of a
module or a file, with the files of the modules that such code declares,
and #[test] functions. It no longer takes an inline module for test-only
code by its name, tests, alone. It leaves out guarded code only as far
as its tokens show where that code ends, as before, and now also leaves
out an element of a list, such as a field of a struct expression or a
parameter of generics or of a closure, up to its first comma, >, |, or
the end of the list, which it counted whole. The input of a macro, which
need not be Rust, and whose attributes the macro may keep or drop, is
production code throughout: no cfg in it leaves anything out, every path
in its tokens counts, from any of them, past a :: before it, however the
test reads the rest of it, as in a use tree that the macro takes apart,
and so does every path in its strings, even one after doc =, which the
macro need not take for documentation; and the test finds there what it
refuses wherever it is, as an include! in a use tree, and every module
that the input declares in a file of its own. So the test may find a
dependency in test-only code, but never misses one in production code.

A child depends on another when its production code names a path into
the other: a crate::, super::, self::, or self::super:: path, or, now,
one from a name that extern crate self as NAME gives the crate root in
the extern prelude, unless the module, defined once, declares a module
or crate of that name, or imports one of the crate's modules under it,
through modules and imports by name, in every configuration. Such a
declaration in the crate root, which the test refused, is now followed;
a child's import of the crate root or host itself still fails it. The
path may be in a use tree, an expression, an attribute, or, with its
escapes decoded, a string of an attribute or of a macro's input, but not
in documentation that an attribute applies outside the input of a macro.
It may go through the names that any module outside the children, not
only the crate root and host, binds with its imports, by name, under an
alias, or with a glob, which the test reads to a fixed point under every
configuration, through a module's import of itself, under any alias, as
often as a path goes through it, and a child's import of such a module
depends on every child that the module's names stand for. A macro that a
child exports is still an item of the crate root, in the namespace of
macros.

Code whose dependencies the test cannot follow fails it: a #[path]
attribute anywhere in the crate, where only host's failed it; include!,
under any name that imports may give it, unless a macro_rules! of that
name hides it, in the file or in a block, and through a path, which may
name it whatever the crate defines; an import of host or the crate root
itself in a child; a glob import of a child into a module outside them;
a path through imports that alias one another in a cycle that lengthens
it, which imports of different configurations may make; an inline module
in the input of a macro, which may put the module's code in another
module; a #[macro_use] attribute in host; and the template of a
macro_rules! that declares or imports, that names or may join a path
from a module or from the macro's input, or that holds one in a string,
which resolve where the macro expands, that puts another file's code in
with include!, that invokes a macro that its input names, which may be
include!, that puts extern before an identifier or a token tree of its
input, which may complete extern crate, that may make a keyword that
declares of an identifier of its input, after a visibility of its input
or none, or at the end of a repetition, as $vis $kind $name { ... } may
be mod name { ... }, or that invokes a macro by name under
local_inner_macros.

A cycle fails the test, which now lists, for each dependency on one, the
shortest cycle through it, with the first reference along each of its
dependencies, under how to break it, where it listed each strongly
connected component. Without a cycle, the children stack in layers, each
above those that it depends on, which the test prints with --nocapture.
On the dev that #910 fixed, at 12b5613, the test lists 13 cycles, each
through one of the three dependencies that #910 removed. On this tree,
it finds the same 51 dependencies as #909's test, in the same ten layers
as before.

The ten tests of #909 run beside the test's twelve, with their fixtures
unchanged, in place of the test's previous thirteen: its two tests of
the report, of strongly connected components and of one reference for
each dependency, give way to one of the shortest cycle through each
dependency, and its test of guarded code whose end the tokens do not
show is renamed for what it now checks, that such code is test-only no
further than its tokens show. Each of the 22 findings of #909's reviews
reproduces as a fixture against the test, and passes; eight failed on
the test before, and #909's tests pin them. A separate review of the
fold, in five rounds, found nineteen more ways to misread code, and its
fixes two more, each with a regression fixture that fails on the test
before its fix. Two stay on purpose: a path-qualified include! fails the
test, whatever it names, and a name that a glob brings in hides no name
of the extern prelude, since what a glob brings in depends on
visibility, which the test does not read.

The module documentation of host and the README's testing section
describe the test as it now is.

Validation, on dev. The test's 22 tests pass. On Windows, the
check-nvx-rs steps that run there pass: rustfmt; Clippy with warnings
denied for all targets with all features, for the default-feature
library, and for the library without default features; the tests with
all features (831), with default features (686), and of the library
without default features (201); the artifact-bundling test (18); rustdoc
with warnings denied; the Rust 1.89 check; and the runtime adapter's
protocol-only (39 tests) and host-only (84 tests) configurations. So do
the check-aci-edge-sandboxes steps that run on Windows, with 168 and 89
tests and agent bundling, and the Markdown link check. On Linux, under
WSL 2, the Linux steps of both actions pass too: the nvx tests with all
features (1405), with default features (670), and of the library without
default features (201), artifact bundling (19), the macOS check, the
guest binaries' lint, tests (829), and build, the runtime adapter's
guest-only configuration (106 tests) besides the others, and
aci_edge_sandboxes's tests and its macOS check. Against dev, the lists
of tests change only in this test's.

Part of #897.
Part of #896.
Supersedes #909.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
The fix of #897 in #910 moved require_artifact and require_existing from
host::sandbox into host::artifacts, and the spawn of a VMM from
host::supervisor into the private host::spawn. Their documentation still
described them from where they had been: require_artifact and
require_existing described themselves by sandbox::require_file, which
itself calls require_existing, and the module documentation of host
described spawn only in its paragraph on the layering of host. The
independent fix of #897 in #909, which #910 superseded, worded both more
precisely. Take its wording.

require_artifact now refers to require_existing, beside it.
require_existing states what it checks and how it fails: that the path
is a regular file, following links, as require_file of nvx.py checks it,
or else the error code that its caller passes and the message
`{description} not found: {path}`, with the path spelled with its
components alone, as Path::components reads them.

The supervisor's entry in the module documentation of host now describes
spawn: a private module, whose items supervisor re-exports, that starts
the VMM detached, with no other descriptor or handle of the caller's.
The paragraph on the layering of host no longer repeats this, and says
only that the store builds on spawn.

Only documentation changes: the public API, the messages, the error
codes, and the tests stay the same.

Validation at this commit, on dev: rustdoc with warnings denied; Clippy
with warnings denied for all targets with all features, for Windows and
Linux; cargo check for macOS; and the tests with all features on Windows
(822), and the library's tests with all features on Linux (1237), pass.

Part of #897.
Part of #896.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
tests/host_modules.rs, which #910 added, keeps the direct children of
nvx::host free of cycles. #909, an independent fix of #897 that #910
superseded, had a test that follows more of the code through which a
child may name another, and that reports a cycle as #897 asks, with one
reference for each of its dependencies. Fold it in, so that the test
does what both did.

The test now reads the crate's sources from src/lib.rs through every
module that the crate declares, in a file of its own or inline, where it
read src/lib.rs and the files beneath src/host/ alone, so it sees every
module through whose imports a child may name another. A file beneath
src/host/ that no mod declares still fails it. It still reads text, with
std alone, and blanks comments and literals.

It leaves out test-only code: what a cfg that requires test guards,
directly, through a cfg_attr that applies attributes, not one in what
another attribute takes as its arguments, or as the inner attribute of a
module or a file, with the files of the modules that such code declares,
and #[test] functions. It no longer takes an inline module for test-only
code by its name, tests, alone. It leaves out guarded code only as far
as its tokens show where that code ends, as before, and now also leaves
out an element of a list, such as a field of a struct expression or a
parameter of generics or of a closure, up to its first comma, >, |, or
the end of the list, which it counted whole. The input of a macro, which
need not be Rust, and whose attributes the macro may keep or drop, is
production code throughout: no cfg in it leaves anything out, every path
in its tokens counts, from any of them, past a :: before it, however the
test reads the rest of it, as in a use tree that the macro takes apart,
and so does every path in its strings, even one after doc =, which the
macro need not take for documentation; and the test finds there what it
refuses wherever it is, as an include! in a use tree, and every module
that the input declares in a file of its own. So the test may find a
dependency in test-only code, but never misses one in production code.

A child depends on another when its production code names a path into
the other: a crate::, super::, self::, or self::super:: path, or, now,
one from a name that extern crate self as NAME, at the top level of the
crate root's file, gives the crate root in the extern prelude, unless
the module, defined once, declares a module or crate of that name, or
imports one of the crate's modules under it, through modules and imports
by name, in every configuration. Such a declaration in the crate root,
which the test refused, is now followed; a child's import of the crate
root or host itself still fails it. The path may be in a use tree, an
expression, an attribute, or, with its escapes decoded, a string of an
attribute or of a macro's input, but not in documentation that an
attribute applies outside the input of a macro. It may go through the
names that any module outside the children, not only the crate root and
host, binds with its imports, by name, under an alias, or with a glob,
which the test reads to a fixed point under every configuration, through
a module's import of itself, under any alias, as often as a path goes
through it, and a child's import of such a module depends on every child
that the module's names stand for. A macro that a child exports is still
an item of the crate root, in the namespace of macros.

Code whose dependencies the test cannot follow fails it: a #[path]
attribute anywhere in the crate, where only host's failed it; include!,
under any name that imports may give it, unless a macro_rules! of that
name hides it, in the file or in a block, and through a path, which may
name it whatever the crate defines; an import of host or the crate root
itself in a child; a glob import of a child into a module outside them;
a path through imports that alias one another in a cycle that lengthens
it, which imports of different configurations may make; an inline module
in the input of a macro, which may put the module's code in another
module, or an alias of the crate root in the input of one in the crate
root, which may join the extern prelude; in the input of a macro that
declares a module in a file of its own, a path = or a macro_use, of
which a template may make an attribute of the module; a #[macro_use]
attribute in host; and the template of a macro_rules! that declares or
imports, that holds an attribute that sets a module's file or brings
macros into scope, that names or may join a path from a module or from
the macro's input, or that holds one in a string, which resolve where
the macro expands, that puts another file's code in with include!, that
invokes a macro that its input names, which may be include!, that puts
extern before an identifier or a token tree of its input, which may
complete extern crate, that may make a keyword that declares of an
identifier of its input, after a visibility of its input or none, or an
item, a statement, or a block of its input, or at the end of a
repetition, as $vis $kind $name { ... } may be mod name { ... }, or that
invokes a macro by name under local_inner_macros.

A cycle fails the test, which now lists, for each dependency on one, the
shortest cycle through it, with the first reference along each of its
dependencies, under how to break it, where it listed each strongly
connected component. Without a cycle, the children stack in layers, each
above those that it depends on, which the test prints with --nocapture.
On the dev that #910 fixed, at 12b5613, the test lists 13 cycles, each
through one of the three dependencies that #910 removed. On this tree,
it finds the same 51 dependencies as #909's test, in the same ten layers
as before.

The ten tests of #909 run beside the test's twelve, with their fixtures
unchanged, in place of the test's previous thirteen: its two tests of
the report, of strongly connected components and of one reference for
each dependency, give way to one of the shortest cycle through each
dependency, and its test of guarded code whose end the tokens do not
show is renamed for what it now checks, that such code is test-only no
further than its tokens show. Each of the 22 findings of #909's reviews
reproduces as a fixture against the test, and passes; eight failed on
the test before, and #909's tests pin them. A separate review of the
fold, in five rounds, found nineteen more ways to misread code, and its
fixes two more, each with a regression fixture that fails on the test
before its fix. Two stay on purpose: a path-qualified include! fails the
test, whatever it names, and a name that a glob brings in hides no name
of the extern prelude, since what a glob brings in depends on
visibility, which the test does not read.

The module documentation of host and the README's testing section
describe the test as it now is.

Validation, on dev. The test's 22 tests pass. On Windows, the
check-nvx-rs steps that run there pass: rustfmt; Clippy with warnings
denied for all targets with all features, for the default-feature
library, and for the library without default features; the tests with
all features (831), with default features (686), and of the library
without default features (201); the artifact-bundling test (18); rustdoc
with warnings denied; the Rust 1.89 check; and the runtime adapter's
protocol-only (39 tests) and host-only (84 tests) configurations. So do
the check-aci-edge-sandboxes steps that run on Windows, with 168 and 89
tests and agent bundling, and the Markdown link check. On Linux, under
WSL 2, the Linux steps of both actions pass too: the nvx tests with all
features (1405), with default features (670), and of the library without
default features (201), artifact bundling (19), the macOS check, the
guest binaries' lint, tests (829), and build, the runtime adapter's
guest-only configuration (106 tests) besides the others, and
aci_edge_sandboxes's tests and its macOS check. Against dev, the lists
of tests change only in this test's.

Part of #897.
Part of #896.
Supersedes #909.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
The fix of #897 in #910 moved require_artifact and require_existing from
host::sandbox into host::artifacts, and the spawn of a VMM from
host::supervisor into the private host::spawn. Their documentation still
described them from where they had been: require_artifact and
require_existing described themselves by sandbox::require_file, which
itself calls require_existing, and the module documentation of host
described spawn only in its paragraph on the layering of host. The
independent fix of #897 in #909, which #910 superseded, worded both more
precisely. Take its wording.

require_artifact now refers to require_existing, beside it.
require_existing states what it checks and how it fails: that the path
is a regular file, following links, as require_file of nvx.py checks it,
or else the error code that its caller passes and the message
`{description} not found: {path}`, with the path spelled with its
components alone, as Path::components reads them.

The supervisor's entry in the module documentation of host now describes
spawn: a private module, whose items supervisor re-exports, that starts
the VMM detached, with no other descriptor or handle of the caller's.
The paragraph on the layering of host no longer repeats this, and says
only that the store builds on spawn.

Only documentation changes: the public API, the messages, the error
codes, and the tests stay the same.

Validation at this commit, on dev: rustdoc with warnings denied; Clippy
with warnings denied for all targets with all features, for Windows and
Linux; cargo check for macOS; and the tests with all features on Windows
(822), and the library's tests with all features on Linux (1237), pass.

Part of #897.
Part of #896.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
tests/host_modules.rs, which #910 added, keeps the direct children of
nvx::host free of cycles. #909, an independent fix of #897 that #910
superseded, had a test that follows more of the code through which a
child may name another, and that reports a cycle as #897 asks, with one
reference for each of its dependencies. Fold it in, so that the test
does what both did.

The test now reads the crate's sources from src/lib.rs through every
module that the crate declares, in a file of its own or inline, where it
read src/lib.rs and the files beneath src/host/ alone, so it sees every
module through whose imports a child may name another. A file beneath
src/host/ that no mod declares still fails it. It still reads text, with
std alone, and blanks comments and literals.

It leaves out test-only code: what a cfg that requires test guards,
directly, through a cfg_attr that applies attributes, not one in what
another attribute takes as its arguments, or as the inner attribute of a
module or a file, with the files of the modules that such code declares,
and #[test] functions. It no longer takes an inline module for test-only
code by its name, tests, alone. It leaves out guarded code only as far
as its tokens show where that code ends, as before, and now also leaves
out an element of a list, such as a field of a struct expression or a
parameter of generics or of a closure, up to its first comma, >, |, or
the end of the list, which it counted whole. The input of a macro, which
need not be Rust, and whose attributes the macro may keep or drop, is
production code throughout: no cfg in it leaves anything out, every path
in its tokens counts, from any of them, past a :: before it, however the
test reads the rest of it, as in a use tree that the macro takes apart,
and so does every path in its strings, even one after doc =, which the
macro need not take for documentation; and the test finds there what it
refuses wherever it is, as an include! in a use tree, and every module
that the input declares in a file of its own. So the test may find a
dependency in test-only code, but never misses one in production code.

A child depends on another when its production code names a path into
the other: a crate::, super::, self::, or self::super:: path, or, now,
one from a name that extern crate self as NAME, at the top level of the
crate root's file, gives the crate root in the extern prelude, unless
the module, defined once, declares a module or crate of that name, or
imports one of the crate's modules under it, through modules and imports
by name, in every configuration. Such a declaration in the crate root,
which the test refused, is now followed; a child's import of the crate
root or host itself still fails it. The path may be in a use tree, an
expression, an attribute, or, with its escapes decoded, a string of an
attribute or of a macro's input, but not in documentation that an
attribute applies outside the input of a macro. It may go through the
names that any module outside the children, not only the crate root and
host, binds with its imports, by name, under an alias, or with a glob,
which the test reads to a fixed point under every configuration, through
a module's import of itself, under any alias, as often as a path goes
through it, and a child's import of such a module depends on every child
that the module's names stand for. A macro that a child exports is still
an item of the crate root, in the namespace of macros.

Code whose dependencies the test cannot follow fails it: a #[path]
attribute anywhere in the crate, where only host's failed it; include!,
under any name that imports may give it, unless a macro_rules! of that
name hides it, in the file or in a block, and through a path, which may
name it whatever the crate defines; an import of host or the crate root
itself in a child; a glob import of a child into a module outside them;
a path through imports that alias one another in a cycle that lengthens
it, which imports of different configurations may make; an inline
module in the input of a macro, which may put the module's code in
another module, or an alias of the crate root in the input of one in
the crate root, which may join the extern prelude; in the input of a
macro that declares a module in a file of its own, a path = or a
macro_use, of which a template may make an attribute of the module, and
in that of one that defines a macro, a macro_export outside its own
attributes; a #[macro_use] attribute in host; and the template of a
macro_rules! that declares or imports, that holds an attribute that
sets a module's file, brings macros into scope, or exports a macro,
that aliases the crate root or a module above it, as $kind {crate as
this} may, that names or may join a path from a module or from the
macro's input, or that holds one in a string, which resolve where the
macro expands, that puts another file's code in with include!, that
invokes a macro that its input names, which may be include!, that puts
extern before an identifier or a token tree of its input, which may
complete extern crate, that may make a keyword that declares of an
identifier of its input, after a visibility of its input or none, or an
item, a statement, or a block of its input, or at the end of a
repetition, as $vis $kind $name { ... } may be mod name { ... }, or
that invokes a macro by name under local_inner_macros.

A cycle fails the test, which now lists, for each dependency on one, the
shortest cycle through it, with the first reference along each of its
dependencies, under how to break it, where it listed each strongly
connected component. Without a cycle, the children stack in layers, each
above those that it depends on, which the test prints with --nocapture.
On the dev that #910 fixed, at 12b5613, the test lists 13 cycles, each
through one of the three dependencies that #910 removed. On this tree,
it finds the same 51 dependencies as #909's test, in the same ten layers
as before.

The ten tests of #909 run beside the test's twelve, with their fixtures
unchanged, in place of the test's previous thirteen: its two tests of
the report, of strongly connected components and of one reference for
each dependency, give way to one of the shortest cycle through each
dependency, and its test of guarded code whose end the tokens do not
show is renamed for what it now checks, that such code is test-only no
further than its tokens show. Each of the 22 findings of #909's reviews
reproduces as a fixture against the test, and passes; eight failed on
the test before, and #909's tests pin them. A separate review of the
fold, in five rounds, found nineteen more ways to misread code, and its
fixes two more, each with a regression fixture that fails on the test
before its fix. Two stay on purpose: a path-qualified include! fails the
test, whatever it names, and a name that a glob brings in hides no name
of the extern prelude, since what a glob brings in depends on
visibility, which the test does not read.

The module documentation of host and the README's testing section
describe the test as it now is.

Validation, on dev. The test's 22 tests pass. On Windows, the
check-nvx-rs steps that run there pass: rustfmt; Clippy with warnings
denied for all targets with all features, for the default-feature
library, and for the library without default features; the tests with
all features (831), with default features (686), and of the library
without default features (201); the artifact-bundling test (18); rustdoc
with warnings denied; the Rust 1.89 check; and the runtime adapter's
protocol-only (39 tests) and host-only (84 tests) configurations. So do
the check-aci-edge-sandboxes steps that run on Windows, with 168 and 89
tests and agent bundling, and the Markdown link check. On Linux, under
WSL 2, the Linux steps of both actions pass too: the nvx tests with all
features (1405), with default features (670), and of the library without
default features (201), artifact bundling (19), the macOS check, the
guest binaries' lint, tests (829), and build, the runtime adapter's
guest-only configuration (106 tests) besides the others, and
aci_edge_sandboxes's tests and its macOS check. Against dev, the lists
of tests change only in this test's.

Part of #897.
Part of #896.
Supersedes #909.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
The fix of #897 in #910 moved require_artifact and require_existing from
host::sandbox into host::artifacts, and the spawn of a VMM from
host::supervisor into the private host::spawn. Their documentation still
described them from where they had been: require_artifact and
require_existing described themselves by sandbox::require_file, which
itself calls require_existing, and the module documentation of host
described spawn only in its paragraph on the layering of host. The
independent fix of #897 in #909, which #910 superseded, worded both more
precisely. Take its wording.

require_artifact now refers to require_existing, beside it.
require_existing states what it checks and how it fails: that the path
is a regular file, following links, as require_file of nvx.py checks it,
or else the error code that its caller passes and the message
`{description} not found: {path}`, with the path spelled with its
components alone, as Path::components reads them.

The supervisor's entry in the module documentation of host now describes
spawn: a private module, whose items supervisor re-exports, that starts
the VMM detached, with no other descriptor or handle of the caller's.
The paragraph on the layering of host no longer repeats this, and says
only that the store builds on spawn.

Only documentation changes: the public API, the messages, the error
codes, and the tests stay the same.

Validation at this commit, on dev: rustdoc with warnings denied; Clippy
with warnings denied for all targets with all features, for Windows and
Linux; cargo check for macOS; and the tests with all features on Windows
(822), and the library's tests with all features on Linux (1237), pass.

Part of #897.
Part of #896.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
tests/host_modules.rs, which #910 added, keeps the direct children of
nvx::host free of cycles. #909, an independent fix of #897 that #910
superseded, had a test that follows more of the code through which a
child may name another, and that reports a cycle as #897 asks, with one
reference for each of its dependencies. Fold it in, so that the test
does what both did.

The test now reads the crate's sources from src/lib.rs through every
module that the crate declares, in a file of its own or inline, where it
read src/lib.rs and the files beneath src/host/ alone, so it sees every
module through whose imports a child may name another. A file beneath
src/host/ that no mod declares still fails it. It still reads text, with
std alone, and blanks comments and literals.

It leaves out test-only code: what a cfg that requires test guards,
directly, through a cfg_attr that applies attributes, not one in what
another attribute takes as its arguments, or as the inner attribute of a
module or a file, with the files of the modules that such code declares,
and #[test] functions. It no longer takes an inline module for test-only
code by its name, tests, alone. It leaves out guarded code only as far
as its tokens show where that code ends, as before, and now also leaves
out an element of a list, such as a field of a struct expression or a
parameter of generics or of a closure, up to its first comma, >, |, or
the end of the list, which it counted whole. The input of a macro, which
need not be Rust, and whose attributes the macro may keep or drop, is
production code throughout: no cfg in it leaves anything out, every path
in its tokens counts, from any of them, past a :: before it, however the
test reads the rest of it, as in a use tree that the macro takes apart,
and so does every path in its strings, even one after doc =, which the
macro need not take for documentation; and the test finds there what it
refuses wherever it is, as an include! in a use tree, and every module
that the input declares in a file of its own. So the test may find a
dependency in test-only code, but never misses one in production code.

A child depends on another when its production code names a path into
the other: a crate::, super::, self::, or self::super:: path, or, now,
one from a name that extern crate self as NAME, at the top level of the
crate root's file, gives the crate root in the extern prelude, unless
the module, defined once, declares a module or crate of that name, or
imports one of the crate's modules under it, through modules and imports
by name, in every configuration. Such a declaration in the crate root,
which the test refused, is now followed; a child's import of the crate
root or host itself still fails it. The path may be in a use tree, an
expression, an attribute, or, with its escapes decoded, a string of an
attribute or of a macro's input, but not in documentation that an
attribute applies outside the input of a macro. It may go through the
names that any module outside the children, not only the crate root and
host, binds with its imports, by name, under an alias, or with a glob,
which the test reads to a fixed point under every configuration, through
a module's import of itself, under any alias, as often as a path goes
through it, and a child's import of such a module depends on every child
that the module's names stand for. A macro that a child exports is still
an item of the crate root, in the namespace of macros.

Code whose dependencies the test cannot follow fails it: a #[path]
attribute anywhere in the crate, where only host's failed it; include!,
under any name that imports may give it, unless a macro_rules! of that
name hides it, in the file or in a block, and through a path, which may
name it whatever the crate defines; an import of host or the crate root
itself in a child; a glob import of a child into a module outside them;
a path through imports that alias one another in a cycle that lengthens
it, which imports of different configurations may make; an inline
module in the input of a macro, which may put the module's code in
another module, or an alias of the crate root in the input of one in
the crate root, which may join the extern prelude; in the input of a
macro that declares a module in a file of its own, a path or a
macro_use, of which a template may make an attribute of the module, and
in that of one that defines a macro, a macro_export outside its own
attributes; a #[macro_use] attribute in host; and the template of a
macro_rules! that declares or imports, that holds an attribute that
sets a module's file, brings macros into scope, or exports a macro, or
one that its input completes before a macro_rules!, that aliases the
crate root or a module above it, as $kind {crate as this} may, that
names or may join a path from a module or from the macro's input, or
that holds one in a string, but documentation outside the input of a
macro that it invokes, which resolve where the macro expands, that puts
another file's code in with include!, that invokes a macro that its
input names, which may be include!, that puts extern before an
identifier or a token tree of its input, which may complete extern
crate, that may make a keyword that declares of an identifier of its
input, after a visibility of its input or none, or an item, a
statement, or a block of its input, or at the end of a repetition, as
$vis $kind $name { ... } may be mod name { ... }, and before a group of
an import, as $kind { self as root } may be, or that invokes a macro by
name under local_inner_macros.

A cycle fails the test, which now lists, for each dependency on one, the
shortest cycle through it, with the first reference along each of its
dependencies, under how to break it, where it listed each strongly
connected component. Without a cycle, the children stack in layers, each
above those that it depends on, which the test prints with --nocapture.
On the dev that #910 fixed, at 12b5613, the test lists 13 cycles, each
through one of the three dependencies that #910 removed. On this tree,
it finds the same 51 dependencies as #909's test, in the same ten layers
as before.

The ten tests of #909 run beside the test's twelve, with their fixtures
unchanged, in place of the test's previous thirteen: its two tests of
the report, of strongly connected components and of one reference for
each dependency, give way to one of the shortest cycle through each
dependency, and its test of guarded code whose end the tokens do not
show is renamed for what it now checks, that such code is test-only no
further than its tokens show. Each of the 22 findings of #909's reviews
reproduces as a fixture against the test, and passes; eight failed on
the test before, and #909's tests pin them. A separate review of the
fold, in five rounds, found nineteen more ways to misread code, and its
fixes two more, each with a regression fixture that fails on the test
before its fix. Two stay on purpose: a path-qualified include! fails the
test, whatever it names, and a name that a glob brings in hides no name
of the extern prelude, since what a glob brings in depends on
visibility, which the test does not read.

The module documentation of host and the README's testing section
describe the test as it now is.

Validation, on dev. The test's 22 tests pass. On Windows, the
check-nvx-rs steps that run there pass: rustfmt; Clippy with warnings
denied for all targets with all features, for the default-feature
library, and for the library without default features; the tests with
all features (831), with default features (686), and of the library
without default features (201); the artifact-bundling test (18); rustdoc
with warnings denied; the Rust 1.89 check; and the runtime adapter's
protocol-only (39 tests) and host-only (84 tests) configurations. So do
the check-aci-edge-sandboxes steps that run on Windows, with 168 and 89
tests and agent bundling, and the Markdown link check. On Linux, under
WSL 2, the Linux steps of both actions pass too: the nvx tests with all
features (1405), with default features (670), and of the library without
default features (201), artifact bundling (19), the macOS check, the
guest binaries' lint, tests (829), and build, the runtime adapter's
guest-only configuration (106 tests) besides the others, and
aci_edge_sandboxes's tests and its macOS check. Against dev, the lists
of tests change only in this test's.

Part of #897.
Part of #896.
Supersedes #909.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
The fix of #897 in #910 moved require_artifact and require_existing from
host::sandbox into host::artifacts, and the spawn of a VMM from
host::supervisor into the private host::spawn. Their documentation still
described them from where they had been: require_artifact and
require_existing described themselves by sandbox::require_file, which
itself calls require_existing, and the module documentation of host
described spawn only in its paragraph on the layering of host. The
independent fix of #897 in #909, which #910 superseded, worded both more
precisely. Take its wording.

require_artifact now refers to require_existing, beside it.
require_existing states what it checks and how it fails: that the path
is a regular file, following links, as require_file of nvx.py checks it,
or else the error code that its caller passes and the message
`{description} not found: {path}`, with the path spelled with its
components alone, as Path::components reads them.

The supervisor's entry in the module documentation of host now describes
spawn: a private module, whose items supervisor re-exports, that starts
the VMM detached, with no other descriptor or handle of the caller's.
The paragraph on the layering of host no longer repeats this, and says
only that the store builds on spawn.

Only documentation changes: the public API, the messages, the error
codes, and the tests stay the same.

Validation at this commit, on dev: rustdoc with warnings denied; Clippy
with warnings denied for all targets with all features, for Windows and
Linux; cargo check for macOS; and the tests with all features on Windows
(822), and the library's tests with all features on Linux (1237), pass.

Part of #897.
Part of #896.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
tests/host_modules.rs, which #910 added, keeps the direct children of
nvx::host free of cycles. #909, an independent fix of #897 that #910
superseded, had a test that follows more of the code through which a
child may name another, and that reports a cycle as #897 asks, with one
reference for each of its dependencies. Fold it in, so that the test
does what both did.

The test now reads the crate's sources from src/lib.rs through every
module that the crate declares, in a file of its own or inline, where it
read src/lib.rs and the files beneath src/host/ alone, so it sees every
module through whose imports a child may name another. A file beneath
src/host/ that no mod declares still fails it. It still reads text, with
std alone, and blanks comments and literals.

It leaves out test-only code: what a cfg that requires test guards,
directly, through a cfg_attr that applies attributes, not one in what
another attribute takes as its arguments, or as the inner attribute of a
module or a file, with the files of the modules that such code declares,
and #[test] functions. It no longer takes an inline module for test-only
code by its name, tests, alone. It leaves out guarded code only as far
as its tokens show where that code ends, as before, and now also leaves
out an element of a list, such as a field of a struct expression or a
parameter of generics or of a closure, up to its first comma, >, |, or
the end of the list, which it counted whole. The input of a macro, which
need not be Rust, and whose attributes the macro may keep or drop, is
production code throughout: no cfg in it leaves anything out, every path
in its tokens counts, from any of them, past a :: before it, however the
test reads the rest of it, as in a use tree that the macro takes apart,
and so does every path in its strings, even one after doc =, which the
macro need not take for documentation; and the test finds there what it
refuses wherever it is, as an include! in a use tree, and every module
that the input declares in a file of its own. So the test may find a
dependency in test-only code, but never misses one in production code.

A child depends on another when its production code names a path into
the other: a crate::, super::, self::, or self::super:: path, or, now,
one from a name that extern crate self as NAME, at the top level of the
crate root's file, gives the crate root in the extern prelude, unless
the module, defined once, declares a module or crate of that name, or
imports one of the crate's modules under it, through modules and imports
by name, in every configuration. Such a declaration in the crate root,
which the test refused, is now followed; a child's import of the crate
root or host itself still fails it. The path may be in a use tree, an
expression, an attribute, or, with its escapes decoded, a string of an
attribute or of a macro's input, but not in documentation that an
attribute applies outside the input of a macro. It may go through the
names that any module outside the children, not only the crate root and
host, binds with its imports, by name, under an alias, or with a glob,
which the test reads to a fixed point under every configuration, through
a module's import of itself, under any alias, as often as a path goes
through it, and a child's import of such a module depends on every child
that the module's names stand for. A macro that a child exports is still
an item of the crate root, in the namespace of macros.

Code whose dependencies the test cannot follow fails it: a #[path]
attribute anywhere in the crate, where only host's failed it; include!,
under any name that imports may give it, unless a macro_rules! of that
name hides it, in the file or in a block, and through a path, which may
name it whatever the crate defines; an import of host or the crate root
itself in a child; a glob import of a child into a module outside them;
a path through imports that alias one another in a cycle that lengthens
it, which imports of different configurations may make; an inline
module in the input of a macro, which may put the module's code in
another module, or an alias of the crate root in the input of one in
the crate root, which may join the extern prelude; in the input of a
macro that declares a module in a file of its own, a path or a
macro_use, of which a template may make an attribute of the module, and
in that of one that defines a macro, a macro_export outside its own
attributes; a #[macro_use] attribute in host; and the template of a
macro_rules! that declares or imports, that holds an attribute that
sets a module's file, brings macros into scope, or exports a macro, or
one that its input completes before a macro_rules!, that aliases the
crate root or a module above it, as $kind {crate as this} may, that
names or may join a path from a module or from the macro's input, or
that holds one in a string, but documentation outside the input of a
macro that it invokes, which resolve where the macro expands, that puts
another file's code in with include!, that invokes a macro that its
input names, which may be include!, that puts extern before an
identifier or a token tree of its input, which may complete extern
crate, that may make a keyword that declares of an identifier of its
input, after a visibility of its input or none, or an item, a
statement, or a block of its input, or at the end of a repetition, as
$vis $kind $name { ... } may be mod name { ... }, and before a group
that an import may hold, any but an empty one or one with a colon or a
dot at its top level, which no import holds, as $kind { $path } may be
use { crate::host }, or before the token that follows it when a
repetition that it ends repeats, as $($kind)* may be use nvx, or that
invokes a macro by name under local_inner_macros.

A cycle fails the test, which now lists, for each dependency on one, the
shortest cycle through it, with the first reference along each of its
dependencies, under how to break it, where it listed each strongly
connected component. Without a cycle, the children stack in layers, each
above those that it depends on, which the test prints with --nocapture.
On the dev that #910 fixed, at 12b5613, the test lists 13 cycles, each
through one of the three dependencies that #910 removed. On this tree,
it finds the same 51 dependencies as #909's test, in the same ten layers
as before.

The ten tests of #909 run beside the test's twelve, with their fixtures
unchanged, in place of the test's previous thirteen: its two tests of
the report, of strongly connected components and of one reference for
each dependency, give way to one of the shortest cycle through each
dependency, and its test of guarded code whose end the tokens do not
show is renamed for what it now checks, that such code is test-only no
further than its tokens show. Each of the 22 findings of #909's reviews
reproduces as a fixture against the test, and passes; eight failed on
the test before, and #909's tests pin them. A separate review of the
fold, in five rounds, found nineteen more ways to misread code, and its
fixes two more, each with a regression fixture that fails on the test
before its fix. Two stay on purpose: a path-qualified include! fails the
test, whatever it names, and a name that a glob brings in hides no name
of the extern prelude, since what a glob brings in depends on
visibility, which the test does not read.

The module documentation of host and the README's testing section
describe the test as it now is.

Validation, on dev. The test's 22 tests pass. On Windows, the
check-nvx-rs steps that run there pass: rustfmt; Clippy with warnings
denied for all targets with all features, for the default-feature
library, and for the library without default features; the tests with
all features (831), with default features (686), and of the library
without default features (201); the artifact-bundling test (18); rustdoc
with warnings denied; the Rust 1.89 check; and the runtime adapter's
protocol-only (39 tests) and host-only (84 tests) configurations. So do
the check-aci-edge-sandboxes steps that run on Windows, with 168 and 89
tests and agent bundling, and the Markdown link check. On Linux, under
WSL 2, the Linux steps of both actions pass too: the nvx tests with all
features (1405), with default features (670), and of the library without
default features (201), artifact bundling (19), the macOS check, the
guest binaries' lint, tests (829), and build, the runtime adapter's
guest-only configuration (106 tests) besides the others, and
aci_edge_sandboxes's tests and its macOS check. Against dev, the lists
of tests change only in this test's.

Part of #897.
Part of #896.
Supersedes #909.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
The fix of #897 in #910 moved require_artifact and require_existing from
host::sandbox into host::artifacts, and the spawn of a VMM from
host::supervisor into the private host::spawn. Their documentation still
described them from where they had been: require_artifact and
require_existing described themselves by sandbox::require_file, which
itself calls require_existing, and the module documentation of host
described spawn only in its paragraph on the layering of host. The
independent fix of #897 in #909, which #910 superseded, worded both more
precisely. Take its wording.

require_artifact now refers to require_existing, beside it.
require_existing states what it checks and how it fails: that the path
is a regular file, following links, as require_file of nvx.py checks it,
or else the error code that its caller passes and the message
`{description} not found: {path}`, with the path spelled with its
components alone, as Path::components reads them.

The supervisor's entry in the module documentation of host now describes
spawn: a private module, whose items supervisor re-exports, that starts
the VMM detached, with no other descriptor or handle of the caller's.
The paragraph on the layering of host no longer repeats this, and says
only that the store builds on spawn.

Only documentation changes: the public API, the messages, the error
codes, and the tests stay the same.

Validation at this commit, on dev: rustdoc with warnings denied; Clippy
with warnings denied for all targets with all features, for Windows and
Linux; cargo check for macOS; and the tests with all features on Windows
(822), and the library's tests with all features on Linux (1237), pass.

Part of #897.
Part of #896.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
tests/host_modules.rs, which #910 added, keeps the direct children of
nvx::host free of cycles. #909, an independent fix of #897 that #910
superseded, had a test that follows more of the code through which a
child may name another, and that reports a cycle as #897 asks, with one
reference for each of its dependencies. Fold it in, so that the test
does what both did.

The test now reads the crate's sources from src/lib.rs through every
module that the crate declares, in a file of its own or inline, where it
read src/lib.rs and the files beneath src/host/ alone, so it sees every
module through whose imports a child may name another. A file beneath
src/host/ that no mod declares still fails it. It still reads text, with
std alone, and blanks comments and literals.

It leaves out test-only code: what a cfg that requires test guards,
directly, through a cfg_attr that applies attributes, not one in what
another attribute takes as its arguments, or as the inner attribute of a
module or a file, with the files of the modules that such code declares,
and #[test] functions. It no longer takes an inline module for test-only
code by its name, tests, alone. It leaves out guarded code only as far
as its tokens show where that code ends, as before, and now also leaves
out an element of a list, such as a field of a struct expression or a
parameter of generics or of a closure, up to its first comma, >, |, or
the end of the list, which it counted whole. The input of a macro, which
need not be Rust, and whose attributes the macro may keep or drop, is
production code throughout: no cfg in it leaves anything out, every path
in its tokens counts, from any of them, past a :: before it, however the
test reads the rest of it, as in a use tree that the macro takes apart,
and so does every path in its strings, even one after doc =, which the
macro need not take for documentation; and the test finds there what it
refuses wherever it is, as an include! in a use tree, and every module
that the input declares in a file of its own. So the test may find a
dependency in test-only code, but never misses one in production code.

A child depends on another when its production code names a path into
the other: a crate::, super::, self::, or self::super:: path, or, now,
one from a name that extern crate self as NAME, at the top level of the
crate root's file, gives the crate root in the extern prelude, unless
the module, defined once, declares a module or crate of that name, or
imports one of the crate's modules under it, through modules, imports by
name, and names that such a declaration without a cfg gives, in every
configuration. Such a declaration in the crate root, which the test
refused, is now followed; a child's import of the crate root or host
itself still fails it. The path may be in a use tree, an expression, an
attribute, or, with its escapes decoded, a string of an attribute or of
a macro's input, but not in documentation that an attribute applies
outside the input of a macro. It may go through the names that any
module outside the children, not only the crate root and host, binds
with its imports, by name, under an alias, or with a glob, which the
test reads to a fixed point under every configuration, through a
module's import of itself, under any alias, as often as a path goes
through it, and a child's import of such a module depends on every child
that the module's names stand for. A macro that a child exports is still
an item of the crate root, in the namespace of macros.

Code whose dependencies the test cannot follow fails it: a #[path]
attribute anywhere in the crate, where only host's failed it; include!,
under any name that imports may give it, unless a macro_rules! of that
name hides it, in the file or in a block, and through a path, which may
name it whatever the crate defines; an import of host or the crate root
itself in a child; a glob import of a child into a module outside them;
a path through imports that alias one another in a cycle that lengthens
it, which imports of different configurations may make; an inline module
in the input of a macro, which may put the module's code in another
module, or an alias of the crate root in the input of one in the crate
root, which may join the extern prelude; in the input of a macro that
declares a module in a file of its own, a path or a macro_use, of which
a template may make an attribute of the module; a macro_export anywhere
in the input of a macro or in a template, of which a template may make
an attribute that exports a macro at the crate root, from where the test
cannot tell; a #[macro_use] attribute in host; and the template of a
macro_rules! that declares or imports, that holds an attribute that sets
a module's file or brings macros into scope, that aliases the crate root
or a module above it, as $kind {crate as this} may, that names or may
join a path from a module or from the macro's input, or that holds one
in a string, but documentation outside the input of a macro that it
invokes, which resolve where the macro expands, that puts another file's
code in with include!, that invokes a macro that its input names, which
may be include!, that puts extern before an identifier or a token tree
of its input, which may complete extern crate, that may make a keyword
that declares of an identifier of its input, after a visibility of its
input or none, or an item, a statement, or a block of its input, or at
the end of a repetition, as $vis $kind $name { ... } may be mod name {
... }, and before a group that an import may hold, any but an empty one
or one that holds at its top level a token that no import holds, such as
a colon, a dot, or a semicolon, as $kind { $path } may be use {
crate::host }, or before the token that follows it when a repetition
that it ends repeats, as $($kind)* may be use nvx, or that invokes a
macro by name under local_inner_macros.

A cycle fails the test, which now lists, for each dependency on one, the
shortest cycle through it, with the first reference along each of its
dependencies, under how to break it, where it listed each strongly
connected component. Without a cycle, the children stack in layers, each
above those that it depends on, which the test prints with --nocapture.
On the dev that #910 fixed, at 12b5613, the test lists 13 cycles, each
through one of the three dependencies that #910 removed. On this tree,
it finds the same 51 dependencies as #909's test, in the same ten layers
as before.

The ten tests of #909 run beside the test's twelve, with their fixtures
unchanged, in place of the test's previous thirteen: its two tests of
the report, of strongly connected components and of one reference for
each dependency, give way to one of the shortest cycle through each
dependency, and its test of guarded code whose end the tokens do not
show is renamed for what it now checks, that such code is test-only no
further than its tokens show. Each of the 22 findings of #909's reviews
reproduces as a fixture against the test, and passes; eight failed on
the test before, and #909's tests pin them. A separate review of the
fold, in five rounds, found nineteen more ways to misread code, and its
fixes two more, each with a regression fixture that fails on the test
before its fix. Two stay on purpose: a path-qualified include! fails the
test, whatever it names, and a name that a glob brings in hides no name
of the extern prelude, since what a glob brings in depends on
visibility, which the test does not read.

The module documentation of host and the README's testing section
describe the test as it now is.

Validation, on dev. The test's 22 tests pass. On Windows, the
check-nvx-rs steps that run there pass: rustfmt; Clippy with warnings
denied for all targets with all features, for the default-feature
library, and for the library without default features; the tests with
all features (831), with default features (686), and of the library
without default features (201); the artifact-bundling test (18); rustdoc
with warnings denied; the Rust 1.89 check; and the runtime adapter's
protocol-only (39 tests) and host-only (84 tests) configurations. So do
the check-aci-edge-sandboxes steps that run on Windows, with 168 and 89
tests and agent bundling, and the Markdown link check. On Linux, under
WSL 2, the Linux steps of both actions pass too: the nvx tests with all
features (1405), with default features (670), and of the library without
default features (201), artifact bundling (19), the macOS check, the
guest binaries' lint, tests (829), and build, the runtime adapter's
guest-only configuration (106 tests) besides the others, and
aci_edge_sandboxes's tests and its macOS check. Against dev, the lists
of tests change only in this test's.

Part of #897.
Part of #896.
Supersedes #909.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
The fix of #897 in #910 moved require_artifact and require_existing from
host::sandbox into host::artifacts, and the spawn of a VMM from
host::supervisor into the private host::spawn. Their documentation still
described them from where they had been: require_artifact and
require_existing described themselves by sandbox::require_file, which
itself calls require_existing, and the module documentation of host
described spawn only in its paragraph on the layering of host. The
independent fix of #897 in #909, which #910 superseded, worded both more
precisely. Take its wording.

require_artifact now refers to require_existing, beside it.
require_existing states what it checks and how it fails: that the path
is a regular file, following links, as require_file of nvx.py checks it,
or else the error code that its caller passes and the message
`{description} not found: {path}`, with the path spelled with its
components alone, as Path::components reads them.

The supervisor's entry in the module documentation of host now describes
spawn: a private module, whose items supervisor re-exports, that starts
the VMM detached, with no other descriptor or handle of the caller's.
The paragraph on the layering of host no longer repeats this, and says
only that the store builds on spawn.

Only documentation changes: the public API, the messages, the error
codes, and the tests stay the same.

Validation at this commit, on dev: rustdoc with warnings denied; Clippy
with warnings denied for all targets with all features, for Windows and
Linux; cargo check for macOS; and the tests with all features on Windows
(822), and the library's tests with all features on Linux (1237), pass.

Part of #897.
Part of #896.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
tests/host_modules.rs, which #910 added, keeps the direct children of
nvx::host free of cycles. #909, an independent fix of #897 that #910
superseded, had a test that follows more of the code through which a
child may name another, and that reports a cycle as #897 asks, with one
reference for each of its dependencies. Fold it in, so that the test
does what both did.

The test now reads the crate's sources from src/lib.rs through every
module that the crate declares, in a file of its own or inline, where it
read src/lib.rs and the files beneath src/host/ alone, so it sees every
module through whose imports a child may name another. A file beneath
src/host/ that no mod declares still fails it. It still reads text, with
std alone, and blanks comments and literals.

It leaves out test-only code: what a cfg that requires test guards,
directly, through a cfg_attr that applies attributes, not one in what
another attribute takes as its arguments, or as the inner attribute of a
module or a file, with the files of the modules that such code declares,
and #[test] functions. It no longer takes an inline module for test-only
code by its name, tests, alone. It leaves out guarded code only as far
as its tokens show where that code ends, as before, and now also leaves
out an element of a list, such as a field of a struct expression or a
parameter of generics or of a closure, up to its first comma, >, |, or
the end of the list, which it counted whole. The input of a macro, which
need not be Rust, and whose attributes the macro may keep or drop, is
production code throughout: no cfg in it leaves anything out, every path
in its tokens counts, from any of them, past a :: before it, however the
test reads the rest of it, as in a use tree that the macro takes apart,
and so does every path in its strings, even one after doc =, which the
macro need not take for documentation; and the test finds there what it
refuses wherever it is, as an include! in a use tree, and every module
that the input declares in a file of its own. So the test may find a
dependency in test-only code, but never misses one in production code.

A child depends on another when its production code names a path into
the other: a crate::, super::, self::, or self::super:: path, or, now,
one from a name that extern crate self as NAME, at the top level of the
crate root's file, gives the crate root in the extern prelude, unless
the module, defined once, declares a module or crate of that name, or
imports one of the crate's modules under it, through modules, imports by
name, and names that such a declaration without a cfg gives, in every
configuration. Such a declaration in the crate root, which the test
refused, is now followed; a child's import of the crate root or host
itself still fails it. The path may be in a use tree, an expression, an
attribute, or, with its escapes decoded, a string of an attribute or of
a macro's input, but not in documentation that an attribute applies
outside the input of a macro. It may go through the names that any
module outside the children, not only the crate root and host, binds
with its imports, by name, under an alias, or with a glob, which the
test reads to a fixed point under every configuration, through a
module's import of itself, under any alias, as often as a path goes
through it, and a child's import of such a module depends on every child
that the module's names stand for. A macro that a child exports is still
an item of the crate root, in the namespace of macros.

Code whose dependencies the test cannot follow fails it: a #[path]
attribute anywhere in the crate, where only host's failed it; include!,
under any name that imports may give it, unless a macro_rules! of that
name hides it, in the file or in a block, and through a path, which may
name it whatever the crate defines; an import of host or the crate root
itself in a child; a glob import of a child into a module outside them;
a path through imports that alias one another in a cycle that lengthens
it, which imports of different configurations may make; an inline module
in the input of a macro, which may put the module's code in another
module, or an alias of the crate root in the input of one in the crate
root, which may join the extern prelude; a module that the input of a
macro declares in a file of its own, where the input of a macro or a
template holds a path or a macro_use anywhere, of which a template,
maybe through other macros, may make an attribute that sets the module's
file or brings its macros into scope; a macro_export anywhere in the
input of a macro or in a template, of which a template may make an
attribute that exports a macro at the crate root, from where the test
cannot tell; a #[macro_use] attribute in host; and the template of a
macro_rules! that declares or imports, that holds an attribute that sets
a module's file or brings macros into scope, that aliases the crate root
or a module above it, as $kind {crate as this} may, that names or may
join a path from a module or from the macro's input, or that holds one
in a string, but documentation outside the input of a macro that it
invokes, which resolve where the macro expands, that puts another file's
code in with include!, that invokes a macro that its input names, which
may be include!, that puts extern before an identifier or a token tree
of its input, which may complete extern crate, that may make a keyword
that declares of an identifier of its input, after a visibility of its
input or none, or an item, a statement, or a block of its input, or at
the end of a repetition, as $vis $kind $name { ... } may be mod name {
... }, and before a group that an import may hold, any but an empty one
or one that holds at its top level a token that no import holds, such as
a colon, a dot, a semicolon, or a keyword but as, as $kind { $path } may
be use { crate::host }, or before the token that follows it when a
repetition that it ends repeats, as $($kind)* may be use nvx, or that
invokes a macro by name under local_inner_macros.

A cycle fails the test, which now lists, for each dependency on one, the
shortest cycle through it, with the first reference along each of its
dependencies, under how to break it, where it listed each strongly
connected component. Without a cycle, the children stack in layers, each
above those that it depends on, which the test prints with --nocapture.
On the dev that #910 fixed, at 12b5613, the test lists 13 cycles, each
through one of the three dependencies that #910 removed. On this tree,
it finds the same 51 dependencies as #909's test, in the same ten layers
as before.

The ten tests of #909 run beside the test's twelve, with their fixtures
unchanged, in place of the test's previous thirteen: its two tests of
the report, of strongly connected components and of one reference for
each dependency, give way to one of the shortest cycle through each
dependency, and its test of guarded code whose end the tokens do not
show is renamed for what it now checks, that such code is test-only no
further than its tokens show. Each of the 22 findings of #909's reviews
reproduces as a fixture against the test, and passes; eight failed on
the test before, and #909's tests pin them. A separate review of the
fold, in five rounds, found nineteen more ways to misread code, and its
fixes two more, each with a regression fixture that fails on the test
before its fix. Two stay on purpose: a path-qualified include! fails the
test, whatever it names, and a name that a glob brings in hides no name
of the extern prelude, since what a glob brings in depends on
visibility, which the test does not read.

The module documentation of host and the README's testing section
describe the test as it now is.

Validation, on dev. The test's 22 tests pass. On Windows, the
check-nvx-rs steps that run there pass: rustfmt; Clippy with warnings
denied for all targets with all features, for the default-feature
library, and for the library without default features; the tests with
all features (831), with default features (686), and of the library
without default features (201); the artifact-bundling test (18); rustdoc
with warnings denied; the Rust 1.89 check; and the runtime adapter's
protocol-only (39 tests) and host-only (84 tests) configurations. So do
the check-aci-edge-sandboxes steps that run on Windows, with 168 and 89
tests and agent bundling, and the Markdown link check. On Linux, under
WSL 2, the Linux steps of both actions pass too: the nvx tests with all
features (1405), with default features (670), and of the library without
default features (201), artifact bundling (19), the macOS check, the
guest binaries' lint, tests (829), and build, the runtime adapter's
guest-only configuration (106 tests) besides the others, and
aci_edge_sandboxes's tests and its macOS check. Against dev, the lists
of tests change only in this test's.

Part of #897.
Part of #896.
Supersedes #909.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
The fix of #897 in #910 moved require_artifact and require_existing from
host::sandbox into host::artifacts, and the spawn of a VMM from
host::supervisor into the private host::spawn. Their documentation still
described them from where they had been: require_artifact and
require_existing described themselves by sandbox::require_file, which
itself calls require_existing, and the module documentation of host
described spawn only in its paragraph on the layering of host. The
independent fix of #897 in #909, which #910 superseded, worded both more
precisely. Take its wording.

require_artifact now refers to require_existing, beside it.
require_existing states what it checks and how it fails: that the path
is a regular file, following links, as require_file of nvx.py checks it,
or else the error code that its caller passes and the message
`{description} not found: {path}`, with the path spelled with its
components alone, as Path::components reads them.

The supervisor's entry in the module documentation of host now describes
spawn: a private module, whose items supervisor re-exports, that starts
the VMM detached, with no other descriptor or handle of the caller's.
The paragraph on the layering of host no longer repeats this, and says
only that the store builds on spawn.

Only documentation changes: the public API, the messages, the error
codes, and the tests stay the same.

Validation at this commit, on dev: rustdoc with warnings denied; Clippy
with warnings denied for all targets with all features, for Windows and
Linux; cargo check for macOS; and the tests with all features on Windows
(822), and the library's tests with all features on Linux (1237), pass.

Part of #897.
Part of #896.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
tests/host_modules.rs, which #910 added, keeps the direct children of
nvx::host free of cycles. #909, an independent fix of #897 that #910
superseded, had a test that follows more of the code through which a
child may name another, and that reports a cycle as #897 asks, with one
reference for each of its dependencies. Fold it in, so that the test
does what both did.

The test now reads the crate's sources from src/lib.rs through every
module that the crate declares, in a file of its own or inline, where it
read src/lib.rs and the files beneath src/host/ alone, so it sees every
module through whose imports a child may name another. A file beneath
src/host/ that no mod declares still fails it. It still reads text, with
std alone, and blanks comments and literals.

It leaves out test-only code: what a cfg that requires test guards,
directly, through a cfg_attr that applies attributes, not one in what
another attribute takes as its arguments, or as the inner attribute of a
module or a file, with the files of the modules that such code declares,
and #[test] functions. It no longer takes an inline module for test-only
code by its name, tests, alone. It leaves out guarded code only as far
as its tokens show where that code ends, as before, and now also leaves
out an element of a list, such as a field of a struct expression or a
parameter of generics or of a closure, up to its first comma, >, |, or
the end of the list, which it counted whole. The input of a macro, which
need not be Rust, and whose attributes the macro may keep or drop, is
production code throughout: no cfg in it leaves anything out, every path
in its tokens counts, from any of them, past a :: before it, however the
test reads the rest of it, as in a use tree that the macro takes apart,
and so does every path in its strings, even one after doc =, which the
macro need not take for documentation; and the test finds there what it
refuses wherever it is, as an include! in a use tree, and every module
that the input declares in a file of its own. So the test may find a
dependency in test-only code, but never misses one in production code.

A child depends on another when its production code names a path into
the other: a crate::, super::, self::, or self::super:: path, or, now,
one from a name that extern crate self as NAME, at the top level of the
crate root's file, gives the crate root in the extern prelude, unless
the module, defined once, declares a module or crate of that name, or
imports one of the crate's modules under it, through modules, imports by
name, and names that such a declaration without a cfg gives, in every
configuration. Such a declaration in the crate root, which the test
refused, is now followed; a child's import of the crate root or host
itself still fails it. The path may be in a use tree, an expression, an
attribute, or, with its escapes decoded, a string of an attribute or of
a macro's input, but not in documentation that an attribute applies
outside the input of a macro. It may go through the names that any
module outside the children, not only the crate root and host, binds
with its imports, by name, under an alias, or with a glob, which the
test reads to a fixed point under every configuration, through a
module's import of itself, under any alias, as often as a path goes
through it, and a child's import of such a module depends on every child
that the module's names stand for. A macro that a child exports is still
an item of the crate root, in the namespace of macros.

Code whose dependencies the test cannot follow fails it: a #[path]
attribute anywhere in the crate, where only host's failed it; include!,
under any name that imports may give it, unless a macro_rules! of that
name hides it, in the file or in a block, and through a path, which may
name it whatever the crate defines; an import of host or the crate root
itself in a child; a glob import of a child into a module outside them;
a path through imports that alias one another in a cycle that lengthens
it, which imports of different configurations may make; an inline module
in the input of a macro, which may put the module's code in another
module, or an alias of the crate root in the input of one in the crate
root, which may join the extern prelude; a module that the input of a
macro declares in a file of its own, where the input of a macro or a
template holds a path or a macro_use anywhere, of which a template,
maybe through other macros, may make an attribute that sets the module's
file or brings its macros into scope; a macro_export anywhere in the
input of a macro or in a template, of which a template may make an
attribute that exports a macro at the crate root, from where the test
cannot tell; a #[macro_use] attribute in host; and the template of a
macro_rules! that declares or imports, that holds an attribute that sets
a module's file or brings macros into scope, that aliases the crate root
or a module above it, as $kind {crate as this} may, that names or may
join a path from a module or from the macro's input, or that holds one
in a string, but documentation outside the input of a macro that it
invokes, which resolve where the macro expands, that puts another file's
code in with include!, that invokes a macro that its input names, which
may be include!, that puts extern before an identifier or a token tree
of its input, which may complete extern crate, that may make a keyword
that declares of an identifier of its input, after a visibility of its
input or none, or an item, a statement, or a block of its input, or at
the end of a repetition, or anywhere in the input of a macro that it
invokes, which may take any part of it for an item, as $vis $kind $name
{ ... } may be mod name { ... }, and before a group that an import may
hold, any but an empty one or one that holds at its top level a token
that no import holds, such as a colon, a dot, a semicolon, or a keyword
but as, as $kind { $path } may be use { crate::host }, or before the
token that follows it when a repetition that it ends repeats, as
$($kind)* may be use nvx, or that invokes a macro by name under
local_inner_macros.

A cycle fails the test, which now lists, for each dependency on one, the
shortest cycle through it, with the first reference along each of its
dependencies, under how to break it, where it listed each strongly
connected component. Without a cycle, the children stack in layers, each
above those that it depends on, which the test prints with --nocapture.
On the dev that #910 fixed, at 12b5613, the test lists 13 cycles, each
through one of the three dependencies that #910 removed. On this tree,
it finds the same 51 dependencies as #909's test, in the same ten layers
as before.

The ten tests of #909 run beside the test's twelve, with their fixtures
unchanged, in place of the test's previous thirteen: its two tests of
the report, of strongly connected components and of one reference for
each dependency, give way to one of the shortest cycle through each
dependency, and its test of guarded code whose end the tokens do not
show is renamed for what it now checks, that such code is test-only no
further than its tokens show. Each of the 22 findings of #909's reviews
reproduces as a fixture against the test, and passes; eight failed on
the test before, and #909's tests pin them. A separate review of the
fold, in five rounds, found nineteen more ways to misread code, and its
fixes two more, each with a regression fixture that fails on the test
before its fix. Two stay on purpose: a path-qualified include! fails the
test, whatever it names, and a name that a glob brings in hides no name
of the extern prelude, since what a glob brings in depends on
visibility, which the test does not read.

The module documentation of host and the README's testing section
describe the test as it now is.

Validation, on dev. The test's 22 tests pass. On Windows, the
check-nvx-rs steps that run there pass: rustfmt; Clippy with warnings
denied for all targets with all features, for the default-feature
library, and for the library without default features; the tests with
all features (831), with default features (686), and of the library
without default features (201); the artifact-bundling test (18); rustdoc
with warnings denied; the Rust 1.89 check; and the runtime adapter's
protocol-only (39 tests) and host-only (84 tests) configurations. So do
the check-aci-edge-sandboxes steps that run on Windows, with 168 and 89
tests and agent bundling, and the Markdown link check. On Linux, under
WSL 2, the Linux steps of both actions pass too: the nvx tests with all
features (1405), with default features (670), and of the library without
default features (201), artifact bundling (19), the macOS check, the
guest binaries' lint, tests (829), and build, the runtime adapter's
guest-only configuration (106 tests) besides the others, and
aci_edge_sandboxes's tests and its macOS check. Against dev, the lists
of tests change only in this test's.

Part of #897.
Part of #896.
Supersedes #909.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
The fix of #897 in #910 moved require_artifact and require_existing from
host::sandbox into host::artifacts, and the spawn of a VMM from
host::supervisor into the private host::spawn. Their documentation still
described them from where they had been: require_artifact and
require_existing described themselves by sandbox::require_file, which
itself calls require_existing, and the module documentation of host
described spawn only in its paragraph on the layering of host. The
independent fix of #897 in #909, which #910 superseded, worded both more
precisely. Take its wording.

require_artifact now refers to require_existing, beside it.
require_existing states what it checks and how it fails: that the path
is a regular file, following links, as require_file of nvx.py checks it,
or else the error code that its caller passes and the message
`{description} not found: {path}`, with the path spelled with its
components alone, as Path::components reads them.

The supervisor's entry in the module documentation of host now describes
spawn: a private module, whose items supervisor re-exports, that starts
the VMM detached, with no other descriptor or handle of the caller's.
The paragraph on the layering of host no longer repeats this, and says
only that the store builds on spawn.

Only documentation changes: the public API, the messages, the error
codes, and the tests stay the same.

Validation at this commit, on dev: rustdoc with warnings denied; Clippy
with warnings denied for all targets with all features, for Windows and
Linux; cargo check for macOS; and the tests with all features on Windows
(822), and the library's tests with all features on Linux (1237), pass.

Part of #897.
Part of #896.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
tests/host_modules.rs, which #910 added, keeps the direct children of
nvx::host free of cycles. #909, an independent fix of #897 that #910
superseded, had a test that follows more of the code through which a
child may name another, and that reports a cycle as #897 asks, with one
reference for each of its dependencies. Fold it in, so that the test
does what both did.

The test now reads the crate's sources from src/lib.rs through every
module that the crate declares, in a file of its own or inline, where it
read src/lib.rs and the files beneath src/host/ alone, so it sees every
module through whose imports a child may name another. A file beneath
src/host/ that no mod declares still fails it. It still reads text, with
std alone, and blanks comments and literals.

It leaves out test-only code: what a cfg that requires test guards,
directly, through a cfg_attr that applies attributes, not one in what
another attribute takes as its arguments, or as the inner attribute of a
module or a file, with the files of the modules that such code declares,
and #[test] functions. It no longer takes an inline module for test-only
code by its name, tests, alone. It leaves out guarded code only as far
as its tokens show where that code ends, as before, and now also leaves
out an element of a list, such as a field of a struct expression or a
parameter of generics or of a closure, up to its first comma, >, |, or
the end of the list, which it counted whole. The input of a macro, which
need not be Rust, and whose attributes the macro may keep or drop, is
production code throughout: no cfg in it leaves anything out, every path
in its tokens counts, from any of them, past a :: before it, however the
test reads the rest of it, as in a use tree that the macro takes apart,
and so does every path in its strings, even one after doc =, which the
macro need not take for documentation; and the test finds there what it
refuses wherever it is, as an include! in a use tree, and every module
that the input declares in a file of its own. So the test may find a
dependency in test-only code, but never misses one in production code.

A child depends on another when its production code names a path into
the other: a crate::, super::, self::, or self::super:: path, or, now,
one from a name that extern crate self as NAME, at the top level of the
crate root's file, gives the crate root in the extern prelude, unless
the module, defined once, declares a module or crate of that name, or
imports one of the crate's modules under it, through modules, imports by
name, and names that such a declaration without a cfg gives, in every
configuration. Such a declaration in the crate root, which the test
refused, is now followed; a child's import of the crate root or host
itself still fails it. The path may be in a use tree, an expression, an
attribute, or, with its escapes decoded, a string of an attribute or of
a macro's input, but not in documentation that an attribute applies
outside the input of a macro. It may go through the names that any
module outside the children, not only the crate root and host, binds
with its imports, by name, under an alias, or with a glob, which the
test reads to a fixed point under every configuration, through a
module's import of itself, under any alias, as often as a path goes
through it, and a child's import of such a module depends on every child
that the module's names stand for. A macro that a child exports is still
an item of the crate root, in the namespace of macros.

Code whose dependencies the test cannot follow fails it: a #[path]
attribute anywhere in the crate, where only host's failed it; include!,
under any name that imports may give it, unless a macro_rules! of that
name hides it, in the file or in a block, and through a path, which may
name it whatever the crate defines; an import of host or the crate root
itself in a child; a glob import of a child into a module outside them;
a path through imports that alias one another in a cycle that lengthens
it, which imports of different configurations may make; an inline module
in the input of a macro, which may put the module's code in another
module, or an alias of the crate root in the input of one in the crate
root, which may join the extern prelude; a module that the input of a
macro declares in a file of its own, where the input of a macro or a
template holds a path or a macro_use anywhere, of which a template,
maybe through other macros, may make an attribute that sets the module's
file or brings its macros into scope; a macro_export anywhere in the
input of a macro or in a template, of which a template may make an
attribute that exports a macro at the crate root, from where the test
cannot tell; a #[macro_use] attribute in host; and the template of a
macro_rules! that declares or imports, that holds an attribute that sets
a module's file or brings macros into scope, that aliases the crate root
or a module above it, as $kind {crate as this} may, that names or may
join a path from a module or from the macro's input, or that holds one
in a string, but documentation outside the input of a macro that it
invokes, which resolve where the macro expands, that puts another file's
code in with include!, that invokes a macro that its input names, which
may be include!, that puts extern before an identifier or a token tree
of its input, which may complete extern crate, that may make a keyword
that declares of an identifier of its input, after a visibility of its
input or none, or an item, a statement, or a block of its input, or at
the end of a repetition, or anywhere in the input of a macro that it
invokes, which may take any part of it for an item, as $vis $kind $name
{ ... } may be mod name { ... }, and before a group that may expand to
the group of an import, as $kind { $path } may to use { crate::host },
unlike $name { field: value } or $name { $($field: $value),* }, or
before the token that follows it when a repetition that it ends repeats,
as $($kind)* may be use nvx, or that invokes a macro by name under
local_inner_macros.

A cycle fails the test, which now lists, for each dependency on one, the
shortest cycle through it, with the first reference along each of its
dependencies, under how to break it, where it listed each strongly
connected component. Without a cycle, the children stack in layers, each
above those that it depends on, which the test prints with --nocapture.
On the dev that #910 fixed, at 12b5613, the test lists 13 cycles, each
through one of the three dependencies that #910 removed. On this tree,
it finds the same 51 dependencies as #909's test, in the same ten layers
as before.

The ten tests of #909 run beside the test's twelve, with their fixtures
unchanged, in place of the test's previous thirteen: its two tests of
the report, of strongly connected components and of one reference for
each dependency, give way to one of the shortest cycle through each
dependency, and its test of guarded code whose end the tokens do not
show is renamed for what it now checks, that such code is test-only no
further than its tokens show. Each of the 22 findings of #909's reviews
reproduces as a fixture against the test, and passes; eight failed on
the test before, and #909's tests pin them. A separate review of the
fold, in five rounds, found nineteen more ways to misread code, and its
fixes two more, each with a regression fixture that fails on the test
before its fix. Two stay on purpose: a path-qualified include! fails the
test, whatever it names, and a name that a glob brings in hides no name
of the extern prelude, since what a glob brings in depends on
visibility, which the test does not read.

The module documentation of host and the README's testing section
describe the test as it now is.

Validation, on dev. The test's 22 tests pass. On Windows, the
check-nvx-rs steps that run there pass: rustfmt; Clippy with warnings
denied for all targets with all features, for the default-feature
library, and for the library without default features; the tests with
all features (831), with default features (686), and of the library
without default features (201); the artifact-bundling test (18); rustdoc
with warnings denied; the Rust 1.89 check; and the runtime adapter's
protocol-only (39 tests) and host-only (84 tests) configurations. So do
the check-aci-edge-sandboxes steps that run on Windows, with 168 and 89
tests and agent bundling, and the Markdown link check. On Linux, under
WSL 2, the Linux steps of both actions pass too: the nvx tests with all
features (1405), with default features (670), and of the library without
default features (201), artifact bundling (19), the macOS check, the
guest binaries' lint, tests (829), and build, the runtime adapter's
guest-only configuration (106 tests) besides the others, and
aci_edge_sandboxes's tests and its macOS check. Against dev, the lists
of tests change only in this test's.

Part of #897.
Part of #896.
Supersedes #909.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
The fix of #897 in #910 moved require_artifact and require_existing from
host::sandbox into host::artifacts, and the spawn of a VMM from
host::supervisor into the private host::spawn. Their documentation still
described them from where they had been: require_artifact and
require_existing described themselves by sandbox::require_file, which
itself calls require_existing, and the module documentation of host
described spawn only in its paragraph on the layering of host. The
independent fix of #897 in #909, which #910 superseded, worded both more
precisely. Take its wording.

require_artifact now refers to require_existing, beside it.
require_existing states what it checks and how it fails: that the path
is a regular file, following links, as require_file of nvx.py checks it,
or else the error code that its caller passes and the message
`{description} not found: {path}`, with the path spelled with its
components alone, as Path::components reads them.

The supervisor's entry in the module documentation of host now describes
spawn: a private module, whose items supervisor re-exports, that starts
the VMM detached, with no other descriptor or handle of the caller's.
The paragraph on the layering of host no longer repeats this, and says
only that the store builds on spawn.

Only documentation changes: the public API, the messages, the error
codes, and the tests stay the same.

Validation at this commit, on dev: rustdoc with warnings denied; Clippy
with warnings denied for all targets with all features, for Windows and
Linux; cargo check for macOS; and the tests with all features on Windows
(822), and the library's tests with all features on Linux (1237), pass.

Part of #897.
Part of #896.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
tests/host_modules.rs, which #910 added, keeps the direct children of
nvx::host free of cycles. #909, an independent fix of #897 that #910
superseded, had a test that follows more of the code through which a
child may name another, and that reports a cycle as #897 asks, with one
reference for each of its dependencies. Fold it in, so that the test
does what both did.

The test now reads the crate's sources from src/lib.rs through every
module that the crate declares, in a file of its own or inline, where it
read src/lib.rs and the files beneath src/host/ alone, so it sees every
module through whose imports a child may name another. A file beneath
src/host/ that no mod declares still fails it. It still reads text, with
std alone, and blanks comments and literals.

It leaves out test-only code: what a cfg that requires test guards,
directly, through a cfg_attr that applies attributes, not one in what
another attribute takes as its arguments, or as the inner attribute of a
module or a file, with the files of the modules that such code declares,
and #[test] functions. It no longer takes an inline module for test-only
code by its name, tests, alone. It leaves out guarded code only as far
as its tokens show where that code ends, as before, and now also leaves
out an element of a list, such as a field of a struct expression or a
parameter of generics or of a closure, up to its first comma, >, |, or
the end of the list, which it counted whole. The input of a macro, which
need not be Rust, and whose attributes the macro may keep or drop, is
production code throughout: no cfg in it leaves anything out, every path
in its tokens counts, from any of them, past a :: before it, however the
test reads the rest of it, as in a use tree that the macro takes apart,
and so does every path in its strings, even one after doc =, which the
macro need not take for documentation; and the test finds there what it
refuses wherever it is, as an include! in a use tree, and every module
that the input declares in a file of its own. So the test may find a
dependency in test-only code, but never misses one in production code.

A child depends on another when its production code names a path into
the other: a crate::, super::, self::, or self::super:: path, or, now,
one from a name that extern crate self as NAME, at the top level of the
crate root's file, gives the crate root in the extern prelude, unless
the module, defined once, declares a module or crate of that name, or
imports one of the crate's modules under it, through modules, imports by
name, and names that such a declaration without a cfg gives, in every
configuration. Such a declaration in the crate root, which the test
refused, is now followed; a child's import of the crate root or host
itself still fails it. The path may be in a use tree, an expression, an
attribute, or, with its escapes decoded, a string of an attribute or of
a macro's input, but not in documentation that an attribute applies
outside the input of a macro. It may go through the names that any
module outside the children, not only the crate root and host, binds
with its imports, by name, under an alias, or with a glob, which the
test reads to a fixed point under every configuration, through a
module's import of itself, under any alias, as often as a path goes
through it, and a child's import of such a module depends on every child
that the module's names stand for. A macro that a child exports is still
an item of the crate root, in the namespace of macros.

Code whose dependencies the test cannot follow fails it: a #[path]
attribute anywhere in the crate, where only host's failed it; include!,
under any name that imports may give it, unless a macro_rules! of that
name hides it, in the file or in a block, and through a path, which may
name it whatever the crate defines; an import of host or the crate root
itself in a child; a glob import of a child into a module outside them;
a path through imports that alias one another in a cycle that lengthens
it, which imports of different configurations may make; an inline module
in the input of a macro, which may put the module's code in another
module, or an alias of the crate root in the input of one in the crate
root, which may join the extern prelude; a module that the input of a
macro declares in a file of its own, where the input of a macro or a
template holds a path or a macro_use anywhere, of which a template,
maybe through other macros, may make an attribute that sets the module's
file or brings its macros into scope; a macro_export anywhere in the
input of a macro or in a template, of which a template may make an
attribute that exports a macro at the crate root, from where the test
cannot tell; a #[macro_use] attribute in host; and the template of a
macro_rules! that declares or imports, that holds an attribute that sets
a module's file or brings macros into scope, that aliases the crate root
or a module above it, as $kind {crate as this} may, that names or may
join a path from a module or from the macro's input, past any :: before
it in the input of a macro that it invokes, or that holds one in a
string, but documentation outside the input of a macro that it invokes,
which resolve where the macro expands, that puts another file's code in
with include!, that invokes a macro that its input names, which may be
include!, that puts extern before an identifier or a token tree of its
input, which may complete extern crate, even past a visibility of its
input, which may be empty, that may make a keyword that declares of an
identifier of its input, after a visibility of its input or none, or an
item, a statement, or a block of its input, or at the end of a
repetition, or anywhere in the input of a macro that it invokes, which
may take any part of it for an item, as $vis $kind $name { ... } may be
mod name { ... }, and before a group that may expand to the group of an
import, as $kind { $path } may to use { crate::host }, unlike $name {
field: value } or $name { $($field: $value),* }, or before the token
that follows it when a repetition that it ends repeats, as $($kind)* may
be use nvx, or that invokes a macro by name under local_inner_macros.

A cycle fails the test, which now lists, for each dependency on one, the
shortest cycle through it, with the first reference along each of its
dependencies, under how to break it, where it listed each strongly
connected component. Without a cycle, the children stack in layers, each
above those that it depends on, which the test prints with --nocapture.
On the dev that #910 fixed, at 12b5613, the test lists 13 cycles, each
through one of the three dependencies that #910 removed. On this tree,
it finds the same 51 dependencies as #909's test, in the same ten layers
as before.

The ten tests of #909 run beside the test's twelve, with their fixtures
unchanged, in place of the test's previous thirteen: its two tests of
the report, of strongly connected components and of one reference for
each dependency, give way to one of the shortest cycle through each
dependency, and its test of guarded code whose end the tokens do not
show is renamed for what it now checks, that such code is test-only no
further than its tokens show. Each of the 22 findings of #909's reviews
reproduces as a fixture against the test, and passes; eight failed on
the test before, and #909's tests pin them. A separate review of the
fold, in five rounds, found nineteen more ways to misread code, and its
fixes two more, each with a regression fixture that fails on the test
before its fix. Two stay on purpose: a path-qualified include! fails the
test, whatever it names, and a name that a glob brings in hides no name
of the extern prelude, since what a glob brings in depends on
visibility, which the test does not read.

The module documentation of host and the README's testing section
describe the test as it now is.

Validation, on dev. The test's 22 tests pass. On Windows, the
check-nvx-rs steps that run there pass: rustfmt; Clippy with warnings
denied for all targets with all features, for the default-feature
library, and for the library without default features; the tests with
all features (831), with default features (686), and of the library
without default features (201); the artifact-bundling test (18); rustdoc
with warnings denied; the Rust 1.89 check; and the runtime adapter's
protocol-only (39 tests) and host-only (84 tests) configurations. So do
the check-aci-edge-sandboxes steps that run on Windows, with 168 and 89
tests and agent bundling, and the Markdown link check. On Linux, under
WSL 2, the Linux steps of both actions pass too: the nvx tests with all
features (1405), with default features (670), and of the library without
default features (201), artifact bundling (19), the macOS check, the
guest binaries' lint, tests (829), and build, the runtime adapter's
guest-only configuration (106 tests) besides the others, and
aci_edge_sandboxes's tests and its macOS check. Against dev, the lists
of tests change only in this test's.

Part of #897.
Part of #896.
Supersedes #909.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
The fix of #897 in #910 moved require_artifact and require_existing from
host::sandbox into host::artifacts, and the spawn of a VMM from
host::supervisor into the private host::spawn. Their documentation still
described them from where they had been: require_artifact and
require_existing described themselves by sandbox::require_file, which
itself calls require_existing, and the module documentation of host
described spawn only in its paragraph on the layering of host. The
independent fix of #897 in #909, which #910 superseded, worded both more
precisely. Take its wording.

require_artifact now refers to require_existing, beside it.
require_existing states what it checks and how it fails: that the path
is a regular file, following links, as require_file of nvx.py checks it,
or else the error code that its caller passes and the message
`{description} not found: {path}`, with the path spelled with its
components alone, as Path::components reads them.

The supervisor's entry in the module documentation of host now describes
spawn: a private module, whose items supervisor re-exports, that starts
the VMM detached, with no other descriptor or handle of the caller's.
The paragraph on the layering of host no longer repeats this, and says
only that the store builds on spawn.

Only documentation changes: the public API, the messages, the error
codes, and the tests stay the same.

Validation at this commit, on dev: rustdoc with warnings denied; Clippy
with warnings denied for all targets with all features, for Windows and
Linux; cargo check for macOS; and the tests with all features on Windows
(822), and the library's tests with all features on Linux (1237), pass.

Part of #897.
Part of #896.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
tests/host_modules.rs, which #910 added, keeps the direct children of
nvx::host free of cycles. #909, an independent fix of #897 that #910
superseded, had a test that follows more of the code through which a
child may name another, and that reports a cycle as #897 asks, with one
reference for each of its dependencies. Fold it in, so that the test
does what both did.

The test now reads the crate's sources from src/lib.rs through every
module that the crate declares, in a file of its own or inline, where it
read src/lib.rs and the files beneath src/host/ alone, so it sees every
module through whose imports a child may name another. A file beneath
src/host/ that no mod declares still fails it. It still reads text, with
std alone, and blanks comments and literals.

It leaves out test-only code: what a cfg that requires test guards,
directly, through a cfg_attr that applies attributes, not one in what
another attribute takes as its arguments, or as the inner attribute of a
module or a file, with the files of the modules that such code declares,
and #[test] functions. It no longer takes an inline module for test-only
code by its name, tests, alone. It leaves out guarded code only as far
as its tokens show where that code ends, as before, and now also leaves
out an element of a list, such as a field of a struct expression or a
parameter of generics or of a closure, up to its first comma, >, |, or
the end of the list, which it counted whole. The input of a macro, which
need not be Rust, and whose attributes the macro may keep or drop, is
production code throughout: no cfg in it leaves anything out, every path
in its tokens counts, from any of them, past a :: before it, however the
test reads the rest of it, as in a use tree that the macro takes apart,
and so does every path in its strings, even one after doc =, which the
macro need not take for documentation; and the test finds there what it
refuses wherever it is, as an include! in a use tree, and every module
that the input declares in a file of its own. So the test may find a
dependency in test-only code, but never misses one in production code.

A child depends on another when its production code names a path into
the other: a crate::, super::, self::, or self::super:: path, or, now,
one from a name that extern crate self as NAME, at the top level of the
crate root's file, gives the crate root in the extern prelude, unless
the module, defined once, declares a module or crate of that name, or
imports one of the crate's modules under it, through modules, imports by
name, and names that such a declaration without a cfg gives, in every
configuration. Such a declaration in the crate root, which the test
refused, is now followed; a child's import of the crate root or host
itself still fails it. The path may be in a use tree, an expression, an
attribute, or, with its escapes decoded, a string of an attribute or of
a macro's input, but not in documentation that an attribute applies
outside the input of a macro. It may go through the names that any
module outside the children, not only the crate root and host, binds
with its imports, by name, under an alias, or with a glob, which the
test reads to a fixed point under every configuration, through a
module's import of itself, under any alias, as often as a path goes
through it, and a child's import of such a module depends on every child
that the module's names stand for. A macro that a child exports is still
an item of the crate root, in the namespace of macros.

Code whose dependencies the test cannot follow fails it: a #[path]
attribute anywhere in the crate, where only host's failed it; include!,
under any name that imports may give it, unless a macro_rules! of that
name hides it, in the file or in a block, and through a path, which may
name it whatever the crate defines; an import of host or the crate root
itself in a child; a glob import of a child into a module outside them;
a path through imports that alias one another in a cycle that lengthens
it, which imports of different configurations may make; an inline module
in the input of a macro, which may put the module's code in another
module, or an alias of the crate root in the input of one in the crate
root, which may join the extern prelude; a module that the input of a
macro declares in a file of its own, where the input of a macro or a
template holds a path or a macro_use anywhere, of which a template,
maybe through other macros, may make an attribute that sets the module's
file or brings its macros into scope; a macro_export anywhere in the
input of a macro or in a template, of which a template may make an
attribute that exports a macro at the crate root, from where the test
cannot tell; a #[macro_use] attribute in host; and the template of a
macro_rules! that declares or imports, that holds an attribute that sets
a module's file or brings macros into scope, that aliases the crate root
or a module above it, as $kind {crate as this} may, that names or may
join a path from a module or from the macro's input, past any :: before
it in the input of a macro that it invokes, or that holds one in a
string, but documentation outside the input of a macro that it invokes,
which resolve where the macro expands, that puts another file's code in
with include!, that invokes a macro that its input names, which may be
include!, that puts extern before an identifier or a token tree of its
input, which may complete extern crate, even past a visibility of its
input, which may be empty, that may make a keyword that declares of an
identifier of its input, after a visibility of its input or none, or an
item, a statement, or a block of its input, or at the end of a
repetition, or anywhere in the input of a macro that it invokes, which
may take any part of it for an item, as $vis $kind $name { ... } may be
mod name { ... }, and before a group that may expand to the group of an
import, as $kind { $path } may to use { crate::host }, unlike $name {
field: value } or $name { $($field: $value),* }, or before the token
that follows it when a repetition that it ends repeats, as $($kind)* may
be use nvx, or that invokes a macro by name under local_inner_macros.

A cycle fails the test, which now lists, for each dependency on one, the
shortest cycle through it, with the first reference along each of its
dependencies, under how to break it, where it listed each strongly
connected component. Without a cycle, the children stack in layers, each
above those that it depends on, which the test prints with --nocapture.
On the dev that #910 fixed, at 12b5613, the test lists 13 cycles, each
through one of the three dependencies that #910 removed. On this tree,
it finds the same 51 dependencies as #909's test, in the same ten layers
as before.

The ten tests of #909 run beside the test's twelve, with their fixtures
unchanged, in place of the test's previous thirteen: its two tests of
the report, of strongly connected components and of one reference for
each dependency, give way to one of the shortest cycle through each
dependency, and its test of guarded code whose end the tokens do not
show is renamed for what it now checks, that such code is test-only no
further than its tokens show. Each of the 22 findings of #909's reviews
reproduces as a fixture against the test, and passes; eight failed on
the test before, and #909's tests pin them. A separate review of the
fold, in five rounds, found nineteen more ways to misread code, and its
fixes two more, each with a regression fixture that fails on the test
before its fix. Two stay on purpose: a path-qualified include! fails the
test, whatever it names, and a name that a glob brings in hides no name
of the extern prelude, since what a glob brings in depends on
visibility, which the test does not read.

The module documentation of host and the README's testing section
describe the test as it now is.

Validation, on dev. The test's 22 tests pass. On Windows, the
check-nvx-rs steps that run there pass: rustfmt; Clippy with warnings
denied for all targets with all features, for the default-feature
library, and for the library without default features; the tests with
all features (831), with default features (686), and of the library
without default features (201); the artifact-bundling test (18); rustdoc
with warnings denied; the Rust 1.89 check; and the runtime adapter's
protocol-only (39 tests) and host-only (84 tests) configurations. So do
the check-aci-edge-sandboxes steps that run on Windows, with 168 and 89
tests and agent bundling, and the Markdown link check. On Linux, under
WSL 2, the Linux steps of both actions pass too: the nvx tests with all
features (1405), with default features (670), and of the library without
default features (201), artifact bundling (19), the macOS check, the
guest binaries' lint, tests (829), and build, the runtime adapter's
guest-only configuration (106 tests) besides the others, and
aci_edge_sandboxes's tests and its macOS check. Against dev, the lists
of tests change only in this test's.

Part of #897.
Part of #896.
Supersedes #909.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
The fix of #897 in #910 moved require_artifact and require_existing from
host::sandbox into host::artifacts, and the spawn of a VMM from
host::supervisor into the private host::spawn. Their documentation still
described them from where they had been: require_artifact and
require_existing described themselves by sandbox::require_file, which
itself calls require_existing, and the module documentation of host
described spawn only in its paragraph on the layering of host. The
independent fix of #897 in #909, which #910 superseded, worded both more
precisely. Take its wording.

require_artifact now refers to require_existing, beside it.
require_existing states what it checks and how it fails: that the path
is a regular file, following links, as require_file of nvx.py checks it,
or else the error code that its caller passes and the message
`{description} not found: {path}`, with the path spelled with its
components alone, as Path::components reads them.

The supervisor's entry in the module documentation of host now describes
spawn: a private module, whose items supervisor re-exports, that starts
the VMM detached, with no other descriptor or handle of the caller's.
The paragraph on the layering of host no longer repeats this, and says
only that the store builds on spawn.

Only documentation changes: the public API, the messages, the error
codes, and the tests stay the same.

Validation at this commit, on dev: rustdoc with warnings denied; Clippy
with warnings denied for all targets with all features, for Windows and
Linux; cargo check for macOS; and the tests with all features on Windows
(822), and the library's tests with all features on Linux (1237), pass.

Part of #897.
Part of #896.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
tests/host_modules.rs, which #910 added, keeps the direct children of
nvx::host free of cycles. #909, an independent fix of #897 that #910
superseded, had a test that follows more of the code through which a
child may name another, and that reports a cycle as #897 asks, with one
reference for each of its dependencies. Fold it in, so that the test
does what both did.

The test now reads the crate's sources from src/lib.rs through every
module that the crate declares, in a file of its own or inline, where it
read src/lib.rs and the files beneath src/host/ alone, so it sees every
module through whose imports a child may name another. A file beneath
src/host/ that no mod declares still fails it. It still reads text, with
std alone, and blanks comments and literals.

It leaves out test-only code: what a cfg that requires test guards,
directly, through a cfg_attr that applies attributes, not one in what
another attribute takes as its arguments, or as the inner attribute of a
module or a file, with the files of the modules that such code declares,
and #[test] functions. It no longer takes an inline module for test-only
code by its name, tests, alone. It leaves out guarded code only as far
as its tokens show where that code ends, as before, and now also leaves
out an element of a list, such as a field of a struct expression or a
parameter of generics or of a closure, up to its first comma, >, |, or
the end of the list, which it counted whole. The input of a macro, which
need not be Rust, and whose attributes the macro may keep or drop, is
production code throughout: no cfg in it leaves anything out, every path
in its tokens counts, from any of them, past a :: before it, however the
test reads the rest of it, as in a use tree that the macro takes apart,
and so does every path in its strings, even one after doc =, which the
macro need not take for documentation; and the test finds there what it
refuses wherever it is, as an include! in a use tree, and every module
that the input declares in a file of its own. So the test may find a
dependency in test-only code, but never misses one in production code.

A child depends on another when its production code names a path into
the other: a crate::, super::, self::, or self::super:: path, or, now,
one from a name that extern crate self as NAME, at the top level of the
crate root's file, gives the crate root in the extern prelude, unless
the module, defined once, declares a module or crate of that name, or
imports one of the crate's modules under it, through modules, imports by
name, and names that such a declaration without a cfg gives, in every
configuration. Such a declaration in the crate root, which the test
refused, is now followed; a child's import of the crate root or host
itself still fails it. The path may be in a use tree, an expression, an
attribute, or, with its escapes decoded, a string of an attribute or of
a macro's input, or a doc comment in a macro's input, of which Rust
makes such a string, but not in documentation that an attribute applies
outside the input of a macro. It may go through the names that any
module outside the children, not only the crate root and host, binds
with its imports, by name, under an alias, or with a glob, which the
test reads to a fixed point under every configuration, through a
module's import of itself, under any alias, as often as a path goes
through it, and a child's import of such a module depends on every child
that the module's names stand for. A macro that a child exports is still
an item of the crate root, in the namespace of macros.

Code whose dependencies the test cannot follow fails it: a #[path]
attribute anywhere in the crate, where only host's failed it; an
identifier that is not ASCII, which Rust takes for any spelling that
Unicode's NFC normalization makes alike; include!, under any name that
imports may give it, unless a macro_rules! of that name hides it, in the
file or in a block, and through a path, which may name it whatever the
crate defines; an import of host or the crate root itself in a child; a
glob import of a child into a module outside them; a path through
imports that alias one another in a cycle that lengthens it, which
imports of different configurations may make; an inline module in the
input of a macro, which may put the module's code in another module, or
an alias of the crate root in the input of one in the crate root, which
may join the extern prelude; a module that the input of a macro declares
in a file of its own, where the input of a macro or a template holds a
path or a macro_use anywhere, of which a template, maybe through other
macros, may make an attribute that sets the module's file or brings its
macros into scope; a macro_export anywhere in the input of a macro or in
a template, of which a template may make an attribute that exports a
macro at the crate root, from where the test cannot tell; a #[macro_use]
attribute in host; and the template of a macro_rules! that declares or
imports, that holds an attribute that sets a module's file or brings
macros into scope, that aliases the crate root or a module above it, as
$kind {crate as this} may, that names or may join a path from a module
or from the macro's input, past any :: before it in the input of a macro
that it invokes, or that holds one in a string, or in a doc comment in
the input of a macro that it invokes, but documentation outside the
input of a macro that it invokes, which resolve where the macro expands,
that puts another file's code in with include!, that invokes a macro
that its input names, which may be include!, that puts extern before an
identifier or a token tree of its input, which may complete extern
crate, even past a visibility of its input, which may be empty, that may
make a keyword that declares of an identifier of its input, after a
visibility of its input or none, or an item, a statement, a block, or an
expression of its input, or at the end of a repetition, or anywhere in
the input of a macro that it invokes, which may take any part of it for
an item, as $vis $kind $name { ... } may be mod name { ... }, and before
a group that may expand to the group of an import, as $kind { $path }
may to use { crate::host }, unlike $name { field: value } or $name {
$($field: $value),* }, or before the token that follows it when a
repetition that it ends repeats, as $($kind)* may be use nvx, or that
invokes a macro by name under local_inner_macros.

A cycle fails the test, which now lists, for each dependency on one, the
shortest cycle through it, with the first reference along each of its
dependencies, under how to break it, where it listed each strongly
connected component. Without a cycle, the children stack in layers, each
above those that it depends on, which the test prints with --nocapture.
On the dev that #910 fixed, at 12b5613, the test lists 13 cycles, each
through one of the three dependencies that #910 removed. On this tree,
it finds the same 51 dependencies as #909's test, in the same ten layers
as before.

The ten tests of #909 run beside the test's twelve, with their fixtures
unchanged, in place of the test's previous thirteen: its two tests of
the report, of strongly connected components and of one reference for
each dependency, give way to one of the shortest cycle through each
dependency, and its test of guarded code whose end the tokens do not
show is renamed for what it now checks, that such code is test-only no
further than its tokens show. Each of the 22 findings of #909's reviews
reproduces as a fixture against the test, and passes; eight failed on
the test before, and #909's tests pin them. A separate review of the
fold, in five rounds, found nineteen more ways to misread code, and its
fixes two more, each with a regression fixture that fails on the test
before its fix. Two stay on purpose: a path-qualified include! fails the
test, whatever it names, and a name that a glob brings in hides no name
of the extern prelude, since what a glob brings in depends on
visibility, which the test does not read.

The module documentation of host and the README's testing section
describe the test as it now is.

Validation, on dev. The test's 22 tests pass. On Windows, the
check-nvx-rs steps that run there pass: rustfmt; Clippy with warnings
denied for all targets with all features, for the default-feature
library, and for the library without default features; the tests with
all features (831), with default features (686), and of the library
without default features (201); the artifact-bundling test (18); rustdoc
with warnings denied; the Rust 1.89 check; and the runtime adapter's
protocol-only (39 tests) and host-only (84 tests) configurations. So do
the check-aci-edge-sandboxes steps that run on Windows, with 168 and 89
tests and agent bundling, and the Markdown link check. On Linux, under
WSL 2, the Linux steps of both actions pass too: the nvx tests with all
features (1405), with default features (670), and of the library without
default features (201), artifact bundling (19), the macOS check, the
guest binaries' lint, tests (829), and build, the runtime adapter's
guest-only configuration (106 tests) besides the others, and
aci_edge_sandboxes's tests and its macOS check. Against dev, the lists
of tests change only in this test's.

Part of #897.
Part of #896.
Supersedes #909.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
The fix of #897 in #910 moved require_artifact and require_existing from
host::sandbox into host::artifacts, and the spawn of a VMM from
host::supervisor into the private host::spawn. Their documentation still
described them from where they had been: require_artifact and
require_existing described themselves by sandbox::require_file, which
itself calls require_existing, and the module documentation of host
described spawn only in its paragraph on the layering of host. The
independent fix of #897 in #909, which #910 superseded, worded both more
precisely. Take its wording.

require_artifact now refers to require_existing, beside it.
require_existing states what it checks and how it fails: that the path
is a regular file, following links, as require_file of nvx.py checks it,
or else the error code that its caller passes and the message
`{description} not found: {path}`, with the path spelled with its
components alone, as Path::components reads them.

The supervisor's entry in the module documentation of host now describes
spawn: a private module, whose items supervisor re-exports, that starts
the VMM detached, with no other descriptor or handle of the caller's.
The paragraph on the layering of host no longer repeats this, and says
only that the store builds on spawn.

Only documentation changes: the public API, the messages, the error
codes, and the tests stay the same.

Validation at this commit, on dev: rustdoc with warnings denied; Clippy
with warnings denied for all targets with all features, for Windows and
Linux; cargo check for macOS; and the tests with all features on Windows
(822), and the library's tests with all features on Linux (1237), pass.

Part of #897.
Part of #896.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
tests/host_modules.rs, which #910 added, keeps the direct children of
nvx::host free of cycles. #909, an independent fix of #897 that #910
superseded, had a test that follows more of the code through which a
child may name another, and that reports a cycle as #897 asks, with one
reference for each of its dependencies. Fold it in, so that the test
does what both did.

The test now reads the crate's sources from src/lib.rs through every
module that the crate declares, in a file of its own or inline, where it
read src/lib.rs and the files beneath src/host/ alone, so it sees every
module through whose imports a child may name another. A file beneath
src/host/ that no mod declares still fails it. It still reads text, with
std alone, and blanks comments and literals.

It leaves out test-only code: what a cfg that requires test guards,
directly, through a cfg_attr that applies attributes, not one in what
another attribute takes as its arguments, or as the inner attribute of a
module or a file, with the files of the modules that such code declares,
and #[test] functions. It no longer takes an inline module for test-only
code by its name, tests, alone. It leaves out guarded code only as far
as its tokens show where that code ends, as before, and now also leaves
out an element of a list, such as a field of a struct expression or a
parameter of generics or of a closure, up to its first comma, >, |, or
the end of the list, which it counted whole. The input of a macro, which
need not be Rust, and whose attributes the macro may keep or drop, is
production code throughout: no cfg in it leaves anything out, every path
in its tokens counts, from any of them, past a :: before it, however the
test reads the rest of it, as in a use tree that the macro takes apart,
and so does every path in its strings, even one after doc =, which the
macro need not take for documentation; and the test finds there what it
refuses wherever it is, as an include! in a use tree, and every module
that the input declares in a file of its own. So the test may find a
dependency in test-only code, but never misses one in production code.

A child depends on another when its production code names a path into
the other: a crate::, super::, self::, or self::super:: path, or, now,
one from a name that extern crate self as NAME, at the top level of the
crate root's file, gives the crate root in the extern prelude, unless
the module, defined once, declares a module or crate of that name, or
imports one of the crate's modules under it, through modules, imports by
name, and names that such a declaration without a cfg gives, in every
configuration. Such a declaration in the crate root, which the test
refused, is now followed; a child's import of the crate root or host
itself still fails it. The path may be in a use tree, an expression, an
attribute, or, with its escapes decoded, a string of an attribute or of
a macro's input, or a doc comment in a macro's input, of which Rust
makes such a string, but not in documentation that an attribute applies
outside the input of a macro. It may go through the names that any
module outside the children, not only the crate root and host, binds
with its imports, by name, under an alias, or with a glob, which the
test reads to a fixed point under every configuration, through a
module's import of itself, under any alias, as often as a path goes
through it, and a child's import of such a module depends on every child
that the module's names stand for. A macro that a child exports is still
an item of the crate root, in the namespace of macros.

Code whose dependencies the test cannot follow fails it: a #[path]
attribute anywhere in the crate, where only host's failed it; an
identifier that is not ASCII, in code or in a path of a string or a doc
comment, which Rust takes for any spelling that Unicode's NFC
normalization makes alike; include!, under any name that imports may
give it, unless a macro_rules! of that name hides it, in the file or in
a block, and through a path, which may name it whatever the crate
defines; an import of host or the crate root itself in a child; a glob
import of a child into a module outside them; a path through imports
that alias one another in a cycle that lengthens it, which imports of
different configurations may make; an inline module in the input of a
macro, which may put the module's code in another module, or an alias of
the crate root in the input of one in the crate root, which may join the
extern prelude; a module that the input of a macro declares in a file of
its own, where the input of a macro or a template holds a path or a
macro_use anywhere, of which a template, maybe through other macros, may
make an attribute that sets the module's file or brings its macros into
scope; a macro_export anywhere in the input of a macro or in a template,
of which a template may make an attribute that exports a macro at the
crate root, from where the test cannot tell; a #[macro_use] attribute in
host; and the template of a macro_rules! that declares or imports, that
holds an attribute that sets a module's file or brings macros into
scope, that aliases the crate root or a module above it, as $kind {crate
as this} may, that names or may join a path from a module or from the
macro's input, past any :: before it in the input of a macro that it
invokes, or that holds one in a string, or in a doc comment in the input
of a macro that it invokes, but documentation outside the input of a
macro that it invokes, which resolve where the macro expands, that puts
another file's code in with include!, that invokes a macro that its
input names, which may be include!, that puts extern before an
identifier or a token tree of its input, which may complete extern
crate, even past a visibility of its input, which may be empty, that may
make a keyword that declares of an identifier of its input, after a
visibility of its input or none, or an item, a statement, a block, or an
expression of its input, or at the end of a repetition, or anywhere in
the input of a macro that it invokes, which may take any part of it for
an item, as $vis $kind $name { ... } may be mod name { ... }, and before
a group that may expand to the group of an import, as $kind { $path }
may to use { crate::host }, unlike $name { field: value } or $name {
$($field: $value),* }, or before the token that follows it when a
repetition that it ends repeats, as $($kind)* may be use nvx, or that
invokes a macro by name under local_inner_macros.

A cycle fails the test, which now lists, for each dependency on one, the
shortest cycle through it, with the first reference along each of its
dependencies, under how to break it, where it listed each strongly
connected component. Without a cycle, the children stack in layers, each
above those that it depends on, which the test prints with --nocapture.
On the dev that #910 fixed, at 12b5613, the test lists 13 cycles, each
through one of the three dependencies that #910 removed. On this tree,
it finds the same 51 dependencies as #909's test, in the same ten layers
as before.

The ten tests of #909 run beside the test's twelve, with their fixtures
unchanged, in place of the test's previous thirteen: its two tests of
the report, of strongly connected components and of one reference for
each dependency, give way to one of the shortest cycle through each
dependency, and its test of guarded code whose end the tokens do not
show is renamed for what it now checks, that such code is test-only no
further than its tokens show. Each of the 22 findings of #909's reviews
reproduces as a fixture against the test, and passes; eight failed on
the test before, and #909's tests pin them. A separate review of the
fold, in five rounds, found nineteen more ways to misread code, and its
fixes two more, each with a regression fixture that fails on the test
before its fix. Two stay on purpose: a path-qualified include! fails the
test, whatever it names, and a name that a glob brings in hides no name
of the extern prelude, since what a glob brings in depends on
visibility, which the test does not read.

The module documentation of host and the README's testing section
describe the test as it now is.

Validation, on dev. The test's 22 tests pass. On Windows, the
check-nvx-rs steps that run there pass: rustfmt; Clippy with warnings
denied for all targets with all features, for the default-feature
library, and for the library without default features; the tests with
all features (831), with default features (686), and of the library
without default features (201); the artifact-bundling test (18); rustdoc
with warnings denied; the Rust 1.89 check; and the runtime adapter's
protocol-only (39 tests) and host-only (84 tests) configurations. So do
the check-aci-edge-sandboxes steps that run on Windows, with 168 and 89
tests and agent bundling, and the Markdown link check. On Linux, under
WSL 2, the Linux steps of both actions pass too: the nvx tests with all
features (1405), with default features (670), and of the library without
default features (201), artifact bundling (19), the macOS check, the
guest binaries' lint, tests (829), and build, the runtime adapter's
guest-only configuration (106 tests) besides the others, and
aci_edge_sandboxes's tests and its macOS check. Against dev, the lists
of tests change only in this test's.

Part of #897.
Part of #896.
Supersedes #909.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
The fix of #897 in #910 moved require_artifact and require_existing from
host::sandbox into host::artifacts, and the spawn of a VMM from
host::supervisor into the private host::spawn. Their documentation still
described them from where they had been: require_artifact and
require_existing described themselves by sandbox::require_file, which
itself calls require_existing, and the module documentation of host
described spawn only in its paragraph on the layering of host. The
independent fix of #897 in #909, which #910 superseded, worded both more
precisely. Take its wording.

require_artifact now refers to require_existing, beside it.
require_existing states what it checks and how it fails: that the path
is a regular file, following links, as require_file of nvx.py checks it,
or else the error code that its caller passes and the message
`{description} not found: {path}`, with the path spelled with its
components alone, as Path::components reads them.

The supervisor's entry in the module documentation of host now describes
spawn: a private module, whose items supervisor re-exports, that starts
the VMM detached, with no other descriptor or handle of the caller's.
The paragraph on the layering of host no longer repeats this, and says
only that the store builds on spawn.

Only documentation changes: the public API, the messages, the error
codes, and the tests stay the same.

Validation at this commit, on dev: rustdoc with warnings denied; Clippy
with warnings denied for all targets with all features, for Windows and
Linux; cargo check for macOS; and the tests with all features on Windows
(822), and the library's tests with all features on Linux (1237), pass.

Part of #897.
Part of #896.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pedro Henrique Penna (ppenna) added a commit that referenced this pull request Oct 27, 2026
tests/host_modules.rs, which #910 added, keeps the direct children of
nvx::host free of cycles. #909, an independent fix of #897 that #910
superseded, had a test that follows more of the code through which a
child may name another, and that reports a cycle as #897 asks, with one
reference for each of its dependencies. Fold it in, so that the test
does what both did.

The test now reads the crate's sources from src/lib.rs through every
module that the crate declares, in a file of its own or inline, where it
read src/lib.rs and the files beneath src/host/ alone, so it sees every
module through whose imports a child may name another. A file beneath
src/host/ that no mod declares still fails it. It still reads text, with
std alone, and blanks comments and literals.

It leaves out test-only code: what a cfg that requires test guards,
directly, through a cfg_attr that applies attributes, not one in what
another attribute takes as its arguments, or as the inner attribute of a
module or a file, with the files of the modules that such code declares,
and #[test] functions. It no longer takes an inline module for test-only
code by its name, tests, alone. It leaves out guarded code only as far
as its tokens show where that code ends, as before, and now also leaves
out an element of a list, such as a field of a struct expression or a
parameter of generics or of a closure, up to its first comma, >, |, or
the end of the list, which it counted whole. The input of a macro, which
need not be Rust, and whose attributes the macro may keep or drop, is
production code throughout: no cfg in it leaves anything out, every path
in its tokens counts, from any of them, past a :: before it, however the
test reads the rest of it, as in a use tree that the macro takes apart,
and so does every path in its strings, even one after doc =, which the
macro need not take for documentation; and the test finds there what it
refuses wherever it is, as an include! in a use tree, and every module
that the input declares in a file of its own. So the test may find a
dependency in test-only code, but never misses one in production code.

A child depends on another when its production code names a path into
the other: a crate::, super::, self::, or self::super:: path, or, now,
one from a name that extern crate self as NAME, at the top level of the
crate root's file, gives the crate root in the extern prelude, unless
the module, defined once, declares a module or crate of that name, or
imports one of the crate's modules under it, through modules, imports by
name, and names that such a declaration without a cfg gives, in every
configuration. Such a declaration in the crate root, which the test
refused, is now followed; a child's import of the crate root or host
itself still fails it. The path may be in a use tree, an expression, an
attribute, or, with its escapes decoded, a string of an attribute or of
a macro's input, or a doc comment in a macro's input, of which Rust
makes such a string, but not in documentation that an attribute applies
outside the input of a macro. It may go through the names that any
module outside the children, not only the crate root and host, binds
with its imports, by name, under an alias, or with a glob, which the
test reads to a fixed point under every configuration, through a
module's import of itself, under any alias, as often as a path goes
through it, and a child's import of such a module depends on every child
that the module's names stand for. A macro that a child exports is still
an item of the crate root, in the namespace of macros.

Code whose dependencies the test cannot follow fails it: a #[path]
attribute anywhere in the crate, where only host's failed it; an
identifier that is not ASCII, in code or in a path that the test reads
in a string or a doc comment, which Rust takes for any spelling that
Unicode's NFC normalization makes alike; include!, under any name that
imports may give it, unless a macro_rules! of that name hides it, in the
file or in a block, and through a path, which may name it whatever the
crate defines; an import of host or the crate root itself in a child; a
glob import of a child into a module outside them; a path through
imports that alias one another in a cycle that lengthens it, which
imports of different configurations may make; an inline module in the
input of a macro, which may put the module's code in another module, or
an alias of the crate root in the input of one in the crate root, which
may join the extern prelude; a module that the input of a macro declares
in a file of its own, where the input of a macro or a template holds a
path or a macro_use anywhere, of which a template, maybe through other
macros, may make an attribute that sets the module's file or brings its
macros into scope; a macro_export anywhere in the input of a macro or in
a template, of which a template may make an attribute that exports a
macro at the crate root, from where the test cannot tell; a #[macro_use]
attribute in host; and the template of a macro_rules! that declares or
imports, that holds an attribute that sets a module's file or brings
macros into scope, that aliases the crate root or a module above it, as
$kind {crate as this} may, that names or may join a path from a module
or from the macro's input, past any :: before it in the input of a macro
that it invokes, or that holds one in a string, or in a doc comment in
the input of a macro that it invokes, but documentation outside the
input of a macro that it invokes, which resolve where the macro expands,
that puts another file's code in with include!, that invokes a macro
that its input names, which may be include!, that puts extern before an
identifier or a token tree of its input, which may complete extern
crate, even past a visibility of its input, which may be empty, that may
make a keyword that declares of an identifier of its input, after a
visibility of its input or none, or an item, a statement, a block, or an
expression of its input, or at the end of a repetition, or anywhere in
the input of a macro that it invokes, which may take any part of it for
an item, as $vis $kind $name { ... } may be mod name { ... }, and before
a group that may expand to the group of an import, as $kind { $path }
may to use { crate::host }, unlike $name { field: value } or $name {
$($field: $value),* }, or before the token that follows it when a
repetition that it ends repeats, as $($kind)* may be use nvx, or that
invokes a macro by name under local_inner_macros.

A cycle fails the test, which now lists, for each dependency on one, the
shortest cycle through it, with the first reference along each of its
dependencies, under how to break it, where it listed each strongly
connected component. Without a cycle, the children stack in layers, each
above those that it depends on, which the test prints with --nocapture.
On the dev that #910 fixed, at 12b5613, the test lists 13 cycles, each
through one of the three dependencies that #910 removed. On this tree,
it finds the same 51 dependencies as #909's test, in the same ten layers
as before.

The ten tests of #909 run beside the test's twelve, with their fixtures
unchanged, in place of the test's previous thirteen: its two tests of
the report, of strongly connected components and of one reference for
each dependency, give way to one of the shortest cycle through each
dependency, and its test of guarded code whose end the tokens do not
show is renamed for what it now checks, that such code is test-only no
further than its tokens show. Each of the 22 findings of #909's reviews
reproduces as a fixture against the test, and passes; eight failed on
the test before, and #909's tests pin them. A separate review of the
fold, in five rounds, found nineteen more ways to misread code, and its
fixes two more, each with a regression fixture that fails on the test
before its fix. Two stay on purpose: a path-qualified include! fails the
test, whatever it names, and a name that a glob brings in hides no name
of the extern prelude, since what a glob brings in depends on
visibility, which the test does not read.

The module documentation of host and the README's testing section
describe the test as it now is.

Validation, on dev. The test's 22 tests pass. On Windows, the
check-nvx-rs steps that run there pass: rustfmt; Clippy with warnings
denied for all targets with all features, for the default-feature
library, and for the library without default features; the tests with
all features (831), with default features (686), and of the library
without default features (201); the artifact-bundling test (18); rustdoc
with warnings denied; the Rust 1.89 check; and the runtime adapter's
protocol-only (39 tests) and host-only (84 tests) configurations. So do
the check-aci-edge-sandboxes steps that run on Windows, with 168 and 89
tests and agent bundling, and the Markdown link check. On Linux, under
WSL 2, the Linux steps of both actions pass too: the nvx tests with all
features (1405), with default features (670), and of the library without
default features (201), artifact bundling (19), the macOS check, the
guest binaries' lint, tests (829), and build, the runtime adapter's
guest-only configuration (106 tests) besides the others, and
aci_edge_sandboxes's tests and its macOS check. Against dev, the lists
of tests change only in this test's.

Part of #897.
Part of #896.
Supersedes #909.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ppenna
Pedro Henrique Penna (ppenna) deleted the agents/issue-897-logical-commits-fix branch October 27, 2026 15:34
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-rs: make the host module's dependency graph acyclic

2 participants