Skip to content

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

Merged
Pedro Henrique Penna (ppenna) merged 4 commits into
devfrom
agents/issue-897-acyclic-host-graph
Oct 27, 2026
Merged

Pedro Henrique Penna (ppenna) merged 4 commits into
devfrom
agents/issue-897-acyclic-host-graph

Conversation

@ppenna

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

Copy link
Copy Markdown
Contributor

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

#909 and the follow-up: #909 fixed #897 independently in another session and was closed as a duplicate of this PR. This PR merged at 71a68acf, before its fold-in of #909's improvements was pushed. The fold-in follows in #919, which lists what it takes from #909:

  • nvx-rs: make the host module's dependency graph acyclic #909's guard model: the whole crate, the imports of every module outside the children, the extern prelude, macro inputs and templates, and list elements;
  • its report of the shortest cycle through each dependency, with the layers;
  • its tests, beside a check that each of its 22 review findings, reproduced against the new guard, passes;
  • its wording for the moved artifact checks, spawn, the layering of host, and the README's testing paragraph.

The red-on-dev output below is that of the guard that merged here; #919 shows the new report.

The direct children of nvx::host now depend on one another without cycles, and a std-only integration test, nvx-rs/tests/host_modules.rs, keeps them so. Three items move down, each re-exported at its old public path, so neither behavior nor the public API changes.

Commits

  1. nvx-rs: move the handles that a VMM inherits into platform. Inherited, with its Borrowed alias and impls, moves from supervisor/child.rs into a new crate-visible host::platform::inherit, which the Linux, Windows, and other-host platform modules import. supervisor re-exports it, so nvx::host::supervisor::Inherited stays its only public path. This removes platform → supervisor.
  2. nvx-rs: move the spawn of a VMM beneath the store. supervisor/child.rs moves, with git mv, to the private host::spawn. supervisor re-exports Launched, VmmCommand, and spawn, and store imports super::spawn::Launched. The doc link to supervisor::launch now uses a crate:: path, since super::launch would silently name the host::launch module from the new file (nvx-rs: break the host module into finer-grained modules #896, invariant 6). This removes store → supervisor.
  3. nvx-rs: check for artifacts in the artifacts module. require_artifact and require_existing move from sandbox into artifacts as pub(crate). require_existing still spells a missing path with host::paths::normalized, so artifacts now imports the std-only paths module, which sits beneath both, and the sandbox tests import paths themselves. The public sandbox::require_file stays and calls artifacts::require_existing, and cli/run.rs and cli/sandbox/mod.rs import require_artifact from artifacts. This removes artifacts → sandbox.
  4. nvx-rs: keep the host's module dependencies acyclic. This adds the guard, states the layering in host's module docs, and describes the test in the nvx-rs README and doc/ci.md.

The guard

tests/host_modules.rs reads src/lib.rs and the files under src/host/ as text, with std alone. It needs no new dependency, and it runs in check-nvx-rs's existing cargo test steps without a workflow change. It:

  • blanks comments and literals: line, doc, and nested block comments, plain, byte, C, and raw strings, and character literals, but not lifetimes or labels;
  • follows mod declarations and inline modules, and leaves out test-only code: code under a cfg that requires test (cfg(test) or cfg(all(test, unix)), but not cfg(not(test)) or cfg(any(test, feature = "cli"))), the files of the modules that such code declares, #[test] functions, and inline tests modules. It excludes guarded code only as far as bracket structure shows where that code ends: an item, with angle brackets tracked in its header; a statement ending in ;; a macro invocation; a block, if (with its else branches), match, or loop; or a field, parameter, variant, or arm of the list that declares it, where an arm whose expression has a block needs no comma. In ordinary code, only the start of the file or a {, }, or ; before the attributes shows that an item or statement starts. A scan for such an end stops at the start of another item, for example after an item kind the guard doesn't know. Other guarded code, such as a field of a struct expression, or a generic or closure parameter, counts. The guard may therefore report a dependency from test-only code, but it never misses one from production code;
  • resolves every crate::, super::, and self:: path, in use trees and elsewhere, including attributes, such as a derive, and their strings, such as serde's with = "...", to the child of host that holds what it names, through the module-level imports of the crate root and host, such as crate::Artifacts. It reads those imports to a fixed point, since one may name what another imports, in either file and order, or rename what its own module imports by the name alone, and a name that cfgs import from several children, or that a module also has under other cfgs, depends on each of them. A macro that a child exports with #[macro_export], directly or through cfg_attr, and from its file or an inline module, is a crate-root item held by that child. An outer attribute's paths are test-only where what it applies to is, and an inner attribute's where its region is. A raw identifier, such as r#type, names what type would. cfg(unix) and cfg(windows) code both count.

A cycle fails the test with each strongly connected component and one reference for each dependency in it. The test also fails on code whose dependencies it cannot follow: a #[path] attribute, a file that no mod declares, an import of host or the crate root itself, including extern crate self as this;, a #[macro_use] attribute in host (directly or through cfg_attr), and a glob import from a child into either. This also covers #[path] and #[macro_use] in a host that the crate root declares inline.

Red on dev, with only the test file added:

artifacts, filesystem, launch, platform, process, sandbox, store, supervisor:
  artifacts -> platform: src/host/artifacts.rs:40 uses crate::host::platform
  artifacts -> sandbox: src/host/artifacts.rs:41 uses crate::host::sandbox::require_artifact
  artifacts -> store: src/host/artifacts.rs:42 uses crate::host::store::ArtifactIdentity
  filesystem -> platform: src/host/filesystem.rs:43 uses super::platform
  launch -> artifacts: src/host/launch.rs:12 uses super::artifacts::Artifacts
  launch -> filesystem: src/host/launch.rs:13 uses super::filesystem::ExportPlan
  platform -> supervisor: src/host/platform/linux.rs:12 uses crate::host::supervisor::Inherited
  process -> platform: src/host/process.rs:31 uses super::platform
  sandbox -> artifacts: src/host/sandbox/distro.rs:49 uses crate::host::artifacts::DISTRO_MANIFEST_SUFFIX
  sandbox -> filesystem: src/host/sandbox/mod.rs:69 uses crate::host::filesystem::ExportPlan
  sandbox -> platform: src/host/sandbox/mount.rs:36 uses crate::host::platform
  sandbox -> supervisor: src/host/sandbox/prepared.rs:67 uses crate::host::supervisor::Inherited
  store -> filesystem: src/host/store/files.rs:8 uses crate::host::filesystem
  store -> platform: src/host/store/files.rs:8 uses crate::host::platform
  store -> process: src/host/store/mod.rs:241 uses super::process::BootId
  store -> supervisor: src/host/store/mod.rs:242 uses super::supervisor::Launched
  supervisor -> launch: src/host/supervisor/mod.rs:107 uses super::launch::timeout_problem
  supervisor -> platform: src/host/supervisor/child.rs:24 uses crate::host::platform
  supervisor -> process: src/host/supervisor/child.rs:26 uses crate::host::process::POLL_INTERVAL
  supervisor -> store: src/host/supervisor/mod.rs:110 uses super::store::IdentityCheck

the_graph_holds_well_known_dependencies_of_the_sources fails there too, since dev has no host::spawn. After commit 1, the cycle shrinks to artifacts, launch, sandbox, store, supervisor; after commit 2, to artifacts, launch, sandbox, supervisor; after commit 3, it is gone, and all thirteen tests pass.

Resulting layers, lowest first, as the issue predicted:

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

Coordination with the template stack

host::template, from the template and clone stack (#879 to #888), is not on dev yet. At the head of #888, store → template (template::Lease) and sandbox → template (template::BlockGeometry) would close a cycle again. If this PR lands first, the guard flags both edges, with a reference for each, when the stack rebases, and the stack resolves them as #897 describes.

Validation

I ran the checks locally on Windows 11 and on Linux under WSL 2 (Ubuntu 26.04, kernel 6.18), with Rust 1.95.0 and the 1.89 MSRV, on dev at 12b56130. No check needs a hypervisor.

Rebase: dev moved from ff3cedf6 to 12b56130, whose #911 deletes host::python and has sandbox::require_existing spell paths with a new std-only host::paths module. Commits 1 and 2 applied cleanly, and commit 4 needed only the README's list of tests merged. I redid commit 3 on the new code: require_pure_file is gone, so only require_artifact and require_existing move. On the new dev, the guard finds the same eight-child cycle with the same three back-edges, and the branch has the same layers, with paths in place of python. The graph now has 51 dependencies, since #911 also adds two downward ones, sandbox → platform and store → seal.

  • Public API (nvx-rs: break the host module into finer-grained modules #896, invariant 1): I generated rustdoc JSON with all features (RUSTC_BOOTSTRAP=1 cargo rustdoc --lib --all-features -- -Z unstable-options --output-format json) for x86_64-pc-windows-msvc and x86_64-unknown-linux-gnu. I walked it from the crate root through public paths, re-exports, fields, variants, inherent methods, and trait impls, including synthetic and blanket ones. Each of the four commits has 0 differences from dev, over 9,632 entries on Windows and 13,779 on Linux. The moved items appear only at nvx::host::supervisor::{Inherited, Launched, VmmCommand, spawn} and nvx::host::sandbox::require_file; no platform::inherit or host::spawn path is public.
  • Test names (nvx-rs: break the host module into finer-grained modules #896, invariant 2): I compared cargo test -- --list with dev, with all features and with default ones. On Windows, the only change is the thirteen new host_modules tests. On Linux, the moved module's four Linux-only tests are also renamed from host::supervisor::child::tests::* to host::spawn::tests::*. The artifacts bundling filter selects the same tests as before.
  • check-nvx-rs, Windows steps: rustfmt; Clippy with -D warnings for all targets with all features, the lib, and the lib without default features; tests with all features (822 passed), default features (677), and the lib without default features (201); artifact bundling (18); rustdoc with -D warnings; the 1.89 check; and the runtime adapter's protocol-only (36) and host-only (76) configurations with their MSRV checks.
  • check-nvx-rs, Linux steps: the same, with 1,396, 661, and 201 tests and bundling (19), plus the macOS cross-check, the guest binaries' lint and tests (820), their build and checks (33), and the runtime adapter's guest-only configuration (96).
  • check-aci-edge-sandboxes: formatting, Clippy, tests (168 with all features and 89 with default features), agent bundling (4), rustdoc, and the MSRV check on both platforms, plus the macOS check on Linux.
  • Each intermediate commit: Clippy with -D warnings for Windows and Linux, cargo check for macOS, and the Windows tests with all features (809) pass. The Linux library tests (1,237) pass at commits 1 and 2.
  • Markdown link check: passed.

Test counts are each test binary's own test result totals. Some tests run their own binary again as a child, whose nested totals are not counted.

The hypervisor-backed tests are left to CI.

Review

Copilot's ten reviews found twenty-five ways for the guard to misread a reference. The guard commit now handles each one, with regression fixtures. Each fixture fails on the guard before the fix and passes now. No fix changes the graph of the real sources: 49 dependencies on the old base, and 51, in the same layers, on the new one.

First review, covered by imports_of_the_root_and_host_resolve_through_chains_and_every_alternative:

  • Chained re-exports: host/mod.rs sorts before lib.rs, so a single pass dropped host's pub use crate::Item; of what the root imports from a child. The imports are now read to a fixed point.
  • Conditional re-exports: #[cfg(unix)] pub use host::a::Thing; and #[cfg(windows)] pub use host::b::Thing; overwrote each other. Each imported name now keeps every child that it may come from, and a path through it depends on each one.

Second review:

  • Bare-name aliases: the root's pub use Item as Alias; after pub use host::a::Item; was skipped, since Item is an imported name rather than a module. Imports may now start with a name that their module imports. The fixture, added to the test above, gave [] before the fix.
  • Raw identifiers: super::r#b::X was split at r# and resolved outside host, and mod r#b; declared nothing. Raw identifiers are now single tokens that name what they would without r#. The new raw_identifiers_name_what_their_names_do test checks the cycle that such a path closes.
  • Generic commas in test-only fields: #[cfg(test)] value: Result<super::b::X, super::b::Y>, ended at the comma inside <...>, so Y counted as production. A test-only named field or parameter now ends at the first comma outside its type's angle brackets, and an arrow's >, as in fn(u8) -> T, closes none. Cases in test_only_code_is_not_a_dependency cover fields and parameters. Before the fix, they gave a spurious a -> b.

Third review:

  • A module and an import of the same name: with #[cfg(unix)] pub mod backend { ... } and #[cfg(windows)] pub use host::b as backend; in lib.rs, the module hid the import, so crate::backend::run() lost a -> b. The imports of the crate root and host along a path now count beside any module of the same name. A fixture in the import test covers that cycle. Before the fix, it gave only b -> a.
  • else branches of a test-only if: the guard of #[cfg(test)] if ... { ... } else { ... } ended with the first block, so the else code counted as production. A } followed by else now keeps the guard for the next block. test_only_code_is_not_a_dependency covers an if, else if, and else chain, followed by a production call that still counts. Before the fix, it gave a spurious a -> b.

Fourth review: three findings showed the same weakness in the earlier field and else fixes. When the guard guessed where test-only code ends, a wrong guess could extend the exclusion over production code and hide a dependency. The guard now uses the extent rule above instead, so an exclusion can end too early but never too late:

  • Struct-expression fields: Data { #[cfg(test)] debug: 1 < 2, value: super::b::helper() } hid value, because the earlier field fix read < as a generic bracket. A guarded field of a struct expression now counts.
  • Qualified macros: #[cfg(test)] crate::make_tests! {} left the guard open over the next function. Macro invocations through a path now end after their arguments.
  • Braces in generic arguments: #[cfg(test)] fn helper<T: Bound<{1}>>() { ... } ended at {1}. Items now end at the body after their generics. The other example, #[cfg(test)] if { true } { ... }, still ends at its block condition, so its if block counts. A new test, guarded_code_whose_end_the_tokens_do_not_show_counts, covers it and the struct-expression field. Taking the second block as the body would hide production code after if c { a } { b }.
  • Generic tuple fields: pub struct S(#[cfg(test)] Result<X, Y>); ended at the generic comma. Fields and parameters of declared structs, unions, variants, and functions now end at a comma outside their type's angle brackets.
  • Local imports: fn helper() { use crate::host::b::Item; } in lib.rs or host was treated as a re-export. Only module-level imports now name items for the crate root and host.

Each of the five cases failed on the guard before this round and passes now. All earlier fixtures still pass, and the real graph is unchanged. Fifth review: two more ways for an exclusion to end too late.

  • Arms without commas: #[cfg(test)] 0 => loop { break } needs no comma, so the guard scanned to the comma of the next, production arm. An arm whose body is a block expression (a block, if, match, loop, or unsafe or const block) now ends after that block and any else chain. test_only_code_is_not_a_dependency covers a guarded loop arm before a production arm, which still counts.
  • Brackets in discriminants: in enum E { A = Data { ... }.value, B = pick(...) }, the brackets after Data and pick were read as variant field lists. They now count as variant fields only when they follow a variant name that starts a variant, after {, ,, or an attribute. guarded_code_whose_end_the_tokens_do_not_show_counts covers both discriminants.

For defense in depth, scans for the end of an item header or an expression statement now stop at tokens that can only start another item or attribute, such as pub, struct, use, let, or #. Lifetimes and labels, such as 'static, are now single tokens, so -> &'static str doesn't stop a header scan. A guarded auto trait Marker {}, a kind the guard doesn't know, can no longer extend over the next function. A fixture covers it, along with a test-only function returning &'static str.

Sixth review:

  • Async closures: async move || ... was taken for an async move { ... } block, so a guarded arm with such a closure ran to the next arm's block. A block now needs a { after async move.
  • Generic parameters: in fn f<#[cfg(test)] 'a, T>() { ... }, the lifetime was taken for a label, and #[cfg(test)] const N: usize scanned to a later ;. Guarded code that follows a < or a , in ordinary code is a list element whose end the guard can't see, so it counts. guarded_code_whose_end_the_tokens_do_not_show_counts covers both, and the body and the following use still count.
  • extern crate self as this;: this crate-root alias was ignored, so crate::this::host::b::... resolved outside host. It now fails the guard as an import of the crate root itself.

I also closed the remaining way for children to depend on one another without a path: macros. A #[macro_export] macro of a child, which is at the crate root, now resolves to that child, and #[macro_use] in host fails the guard. There are fixtures for both. host uses neither today. Expression-statement scans also stop at union and macro, which start items.

Seventh review:

  • cfg_attr: #[cfg_attr(unix, macro_export)] and #[cfg_attr(unix, macro_use)] were ignored. Attribute checks now look inside cfg_attr under any predicate, as the #[path] check already did. The first form registers the export, and the second fails the guard.
  • Inline children: an exported macro in mod a { ... } inside host/mod.rs was attributed to host, not to a. Each export now records the module that encloses it. I applied the same rule to #[macro_use] and #[path], which an inline host in lib.rs could otherwise bypass. Fixtures cover the inline cycle and both inline-host failures.

Eighth review:

  • Paths in attributes: attributes were skipped whole, so #[derive(crate::host::b::Serialize)] in a, while b uses a's type, hid a -> b. The crate::, super::, and self:: paths in an attribute's tokens now count, and so do those in its strings, such as serde's with = "super::b::codec", which a derive turns into code. Each counts as what the attribute applies to does: an outer attribute's paths are test-only only where its item, field, or other code is, and an inner attribute's only where its region is. A new test, paths_in_attributes_count_where_what_the_attributes_apply_to_does, covers the derive cycle, cfg_attr, string paths with their lines, and test-only items, fields, and modules.
  • Closure parameters: in |#[cfg(test)] _unused: ()| { super::b::helper(); };, the guard ran through the closure body to the statement's ;. Rather than adding | to the < and , that left guarded code counted, I inverted the rule: in ordinary code, a guard now leaves code out only after the start of the file or a {, }, or ;, where an item or statement starts. This also covers cfg_if!'s if #[cfg(test)] { ... } else { ... }, whose production else branch the guard had left out. guarded_code_whose_end_the_tokens_do_not_show_counts covers both.

Four of the five cases that I compared failed on the previous guard: the derive cycle, a serde string path, the closure, and cfg_if!. The fifth, a derive on a #[cfg(test)] item, passes on both, and checks that the new code doesn't count test-only attributes.

Ninth review:

  • Documentation: #[doc = "[spawn](crate::host::supervisor::spawn)"] would add platform → supervisor, though the same link in a /// comment, which is blanked, adds nothing. The strings of a doc = ... value or a doc(...) list, also within cfg_attr, now name no dependency, while helper attributes' strings, such as serde's, still count.
  • Escaped strings: "crate::host::\x62::codec" was read as crate::host. Strings that are not raw are now decoded first, with \x, \u{...}, the simple escapes, and line continuations, and each decoded byte keeps the line of its source.

Tenth review:

  • Raw attribute names: #[r#path = "c.rs"], #[r#macro_use], and #[r#macro_export], also within cfg_attr, bypassed the attribute checks. They now compare names without r#, as do the test, cfg, and doc checks.
  • Macro names: a macro named host, exported by a, shared the crate root's import map with the module host, so crate::host::b::h() in b gained b → a. Exports now register as NAME!, a path names a macro only with the ! and delimiter of an invocation, and an import takes its name in both namespaces.

Each case failed on the guard before its round: four of the five that I compared in the ninth, and five of the six in the tenth. The others pass on both, and check that serde paths beside a doc comment, and macro paths through host beside an X != 1 comparison, still resolve.

The counts above are from the rebased branch with every fix. CI was fully green on a1682c05; later heads were superseded before their runs finished.

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

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 silently miss cycles through chained or conditional re-exports.

2 open issues
What changed in this PR

This PR removes dependency cycles between nvx::host’s direct children, establishing the layering needed for the module refactoring in #896 while preserving existing public paths.

Changes:

  • Moves inherited handles, VMM spawning, and artifact checks into lower-level modules.
  • Adds a standard-library-only dependency guard with parser and cycle-detection tests.
  • Documents the layering rule and its existing CI integration.
File Description
nvx-rs/​tests/​host_modules.rs Adds dependency analysis and cycle-detection tests.
nvx-rs/​src/​host/​supervisor/​mod.rs Preserves public paths through re-exports.
nvx-rs/​src/​host/​store/​mod.rs Imports Launched directly from spawn.
nvx-rs/​src/​host/​spawn.rs Relocates VMM spawning and corrects its documentation link.
nvx-rs/​src/​host/​sandbox/​plan.rs Uses artifact-module file validation.
nvx-rs/​src/​host/​sandbox/​mod.rs Delegates file checks while retaining require_file.
nvx-rs/​src/​host/​platform/​windows.rs Imports inherited handles from the platform layer.
nvx-rs/​src/​host/​platform/​other.rs Uses the relocated inherited-handle type.
nvx-rs/​src/​host/​platform/​mod.rs Declares the crate-visible inherit module.
nvx-rs/​src/​host/​platform/​linux.rs Imports inherited descriptors from the platform layer.
nvx-rs/​src/​host/​platform/​inherit.rs Holds inherited descriptor and handle abstractions.
nvx-rs/​src/​host/​mod.rs Declares private spawning and documents layering.
nvx-rs/​src/​host/​cli/​sandbox/​mod.rs Imports artifact checks from their new location.
nvx-rs/​src/​host/​cli/​run.rs Uses the relocated artifact check.
nvx-rs/​src/​host/​artifacts.rs Owns the shared artifact and file checks.
nvx-rs/​README.md Describes the dependency guard.
doc/​ci.md Documents the guard’s inclusion in CI.

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-acyclic-host-graph branch from efc0292 to 5a8fe93 Compare October 26, 2026 20:29
Copilot AI balanced review requested due to automatic review settings October 26, 2026 20:29

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 genuine cycles and report false cycles for valid Rust syntax.

3 open issues
2 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
Comment thread nvx-rs/tests/host_modules.rs Outdated
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/issue-897-acyclic-host-graph branch from 5a8fe93 to 1992556 Compare October 26, 2026 20:44
Copilot AI balanced review requested due to automatic review settings October 26, 2026 20:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The dependency guard can miss cycles through conditional aliases and falsely report cycles from test-only branches.

2 open issues
3 resolved since last review

Review effort: Balanced

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-acyclic-host-graph branch from 1992556 to 05c10ac Compare October 26, 2026 20:55
Copilot AI balanced review requested due to automatic review settings October 26, 2026 20:55

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 Outdated
Comment thread nvx-rs/tests/host_modules.rs Outdated
Comment thread nvx-rs/tests/host_modules.rs
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/issue-897-acyclic-host-graph branch from 05c10ac to 682046d Compare October 26, 2026 21:18
Copilot AI balanced review requested due to automatic review settings October 26, 2026 21:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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-acyclic-host-graph branch from 682046d to a1682c0 Compare October 26, 2026 21:36
Copilot AI balanced review requested due to automatic review settings October 26, 2026 21:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The dependency guard can still omit production dependencies and allow cycles to pass undetected.

3 open issues
2 resolved since last review

Review effort: Balanced

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
@ppenna
Pedro Henrique Penna (ppenna) force-pushed the agents/issue-897-acyclic-host-graph branch from a1682c0 to 9234af4 Compare October 26, 2026 21:53
Copilot AI balanced review requested due to automatic review settings October 26, 2026 21: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.

🟡 Changes recommended

The dependency guard still misses macro dependencies from inline children and conditional attributes, allowing production cycles to pass undetected.

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-acyclic-host-graph branch from 9234af4 to 87dc0a7 Compare October 26, 2026 22:08
Copilot AI balanced review requested due to automatic review settings October 26, 2026 22:08
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, 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;
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, as $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, 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>
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