8000 T59 — Create a named VM with its own state, socket and daemon, leaving the default VM's paths unchanged by minimal-foundry[bot] · Pull Request #1702 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

T59 — Create a named VM with its own state, socket and daemon, leaving the default VM's paths unchanged - #1702

Merged
norrietaylor merged 13 commits into
mainfrom
agent/1626-create-a-named-vm-with-its-own-state-socket-and
Sep 25, 2026
Merged

norrietaylor merged 13 commits into
mainfrom
agent/1626-create-a-named-vm-with-its-own-state-socket-and

Conversation

@minimal-foundry
Copy link
Copy Markdown
Contributor

T59 — Create a named VM with its own state, socket and daemon, leaving the default VM's paths unchanged

slice: S15
kind: task
covers: NET-052, NET-053, NET-054, NET-055, NET-056
size: L
est: 400 code, 420 tests
executor: agent
difficulty: needs care
model: strongest
diagnostics: the bundle lists every VM by name with its state directory and socket
observability: one info log line per VM start and stop naming the VM and its state directory
layers:
- vmhost:
- crates/minvmd/src/state.rs: a VM name selects a per-name subdirectory of the provider directory; the default stays where it is
- crates/minvmd/src/main.rs: a global VM-name argument
- crates/minvmd/src/cmd/run.rs: forwards the name on re-exec
- crates/minvmd/src/cmd/boot.rs: forwards the name on re-exec
- scripts/reap-vms.sh: reaps only this checkout's VMs, by name
test: cargo nextest run -p minvmd named_vm_has_own_state_socket_daemon
test: cargo nextest run -p minvmd default_vm_paths_unchanged
test: cargo nextest run -p minvmd named_vm_under_per_name_subdirectory
test: cargo nextest run -p minvmd stop_one_vm_leaves_other_running
test: cargo nextest run -p minvmd reap_scoped_per_checkout
- paths:
- crates/paths/src/lib.rs: a per-name subdirectory helper
test: cargo nextest run -p paths provider_instance_dir_accepts_a_name
- client:
- crates/minimal-client/src/lib.rs: resolve_provider_dir and resolve_socket_path take a VM name
- crates/minimal/src/autospawn.rs: passes the VM name and uses its state directory
- crates/minimal/src/cli.rs: a way to ask for a named VM
- crates/minimal/src/diag/collect.rs: provider_dirs lists named VMs
test: cargo nextest run -p minimal named_vm_reaches_autospawn
produces: none
consumes: none
proof: A second named VM boots with its own state directory, socket and daemon under a per-name subdirectory, the default VM's paths are unchanged, stopping one VM leaves the other running, and reaping touches only this checkout's VMs.

Proof

A second named VM boots with its own state directory, socket and daemon under a per-name subdirectory (minvmd --vm NAME run serves <state>/providers/local-minvmd0/<name>/, and min --vm NAME reads and autospawns that same VM), the default VM's every path is byte-for-byte unchanged, stopping one VM leaves the other running, and reaping (scripts/reap-vms.sh, bare or --vm NAME) touches only this checkout's VMs; the min bug bundle lists every VM by name with its state directory and socket, and each VM start and stop logs one info line naming the VM and its state directory.

Notes from the agent: Layers held, with files outside them to name: (1) crates/minimal/src/main.rs — one hook call, cli.global_args.publish_vm_name() before dispatch, because the parsed --vm must reach minimal-client's process-global before any path resolution; the accessor lives on GlobalArgs in the named layer file cli.rs. (2) crates/minimal/src/loadouts.rs, crates/minimal/src/tests.rs, crates/minimal/tests/bug.rs, crates/minimal/tests/cli.rs, crates/minimal/tests/common/mod.rs — mechanical vm: None added to 20 GlobalArgs test literals (the new field is on a struct a layer names; all 20 sites are test code, found by the compiler). (3) Test files created for the test: lines themselves: crates/minvmd/tests/named_vm_integration.rs (named per the crate's enforced _integration suffix convention) and crates/minimal/tests/named_vm.rs. Also inside layers but worth knowing: minvmd's foreground tracing layer now disables ANSI off-terminal so the start/stop lines are greppable in piped logs. This box has no libkrun, so minvmd's real VM-boot code (run/boot bodies) compiles as runtime-bailing stubs and is not exercised here: the five minvmd tests drive the real binary for status/stop/log-line behavior and use in-process alive-lock holders for daemons, so they pass on any host and stay meaningful under libkrun; a real boot is owed to the KVM lane (just test-vm / just e2e). Also…[truncated]

Tests

line exit tail
cargo nextest run -p minvmd named_vm_has_own_state_socket_daemon 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-022b... Compiling args v0.0.1 (/home/runner/.local/state/minimal/sessions/01a12/tree/crates/args) Compiling ot v0.0.1 (/home/runner/.local/state/minimal/sessions/01a12/tree/crates/ot) C
cargo nextest run -p minvmd default_vm_paths_unchanged 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-05d3... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/c959e/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessio 7438 ns/c959e/tree/crates/
cargo nextest run -p minvmd named_vm_under_per_name_subdirectory 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-d1f9... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/a9c08/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/a9c08/tree/crates/
cargo nextest run -p minvmd stop_one_vm_leaves_other_running 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-fc06... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/4e47b/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/4e47b/tree/crates/
cargo nextest run -p minvmd reap_scoped_per_checkout 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-06ed... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/3f6b5/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/3f6b5/tree/crates/
cargo nextest run -p paths provider_instance_dir_accepts_a_name 0 Applying loadouts: default Running task runtime-implement-test-5 in session task-runtime-implement-test-5-3d7a... Compiling paths v0.0.1 (/home/runner/.local/state/minimal/sessions/7dfa4/tree/crates/paths) Finished 'test' profile [unoptimized] target(s) in 1.46s ──────────── Nextest run ID a
cargo nextest run -p minimal named_vm_reaches_autospawn 0 Applying loadouts: default Running task runtime-implement-test-6 in session task-runtime-implement-test-6-2b60... Compiling args v0.0.1 (/home/runner/.local/state/minimal/sessions/540e7/tree/crates/args) Compiling ot v0.0.1 (/home/runner/.local/state/minimal/sessions/540e7/tree/crates/ot) C

Verify

min task run agent-verify passed ([tasks.verify]).

Base

The base branch moved while the coder worked and was merged in before the tests ran. These files changed on BOTH sides since the branch point — read them first, because a rewrite from a stale reading can undo a merge without a conflict:

  • crates/minimal/tests/cli.rs

Out of scope

These files are outside the task's layers:, so this PR is a draft:

  • crates/minimal/src/loadouts.rs
  • crates/minimal/src/main.rs
  • crates/minimal/src/tests.rs
  • crates/minimal/tests/bug.rs
  • crates/minimal/tests/cli.rs
  • crates/minimal/tests/common/mod.rs
  • crates/minimal/tests/named_vm.rs
  • crates/minvmd/tests/named_vm_integration.rs

Closes #1626

A named VM (NET-052, NET-054) keeps its state, socket and locks under a
per-name subdirectory of the provider dir:
<state>/providers/local-<kind><N>/<vm>/. provider_instance_dir_named
resolves that layout from an explicit VM name; DEFAULT_VM_NAME (default)
resolves to the provider-instance dir itself so the default VM's paths are
byte-for-byte unchanged (NET-053), and validate_vm_name rejects any name
that is not a single path component, so a name can only select a
subdirectory under the provider dir, never escape it.
A --vm NAME global argument selects the VM minvmd acts on (NET-052). The
name is validated at parse time, before any path resolution, and every
path the daemon derives then flows from provider_dir_for: a named VM's
state, locks, bridge socket, config, volume, logs and switch socket all
live under <state>/providers/local-minvmd0/<name>/ (NET-054), while the
default VM resolves to the provider dir itself, byte for byte as before
(NET-053).

Every re-exec (run --detach, the __krun-vmm child) forwards the name via
state::forward_identity, so the detached supervisor and its child keep
serving the same VM — and a named VM's processes carry their name on the
command line, which is what reap-vms.sh --vm matches on. One info line
per VM start and stop names the VM and its state directory; the
foreground layer now disables ANSI off-terminal so the name stays
searchable in piped logs.

scripts/reap-vms.sh gains --vm NAME to reap only that VM's processes,
still pinned to this checkout's path (NET-056): minvmd processes by
their --vm argument, the VM's gvproxy switch by its per-name socket
directory. Bare invocation still reaps every VM this checkout spawned.
`min --vm NAME` now publishes the name at dispatch, so every path the
CLI resolves names the same VM (NET-052):

- minimal-client grows the process-global VM name plus
  `resolve_provider_dir_named`/`resolve_socket_path_named`; a named VM
  nests under `<state>/providers/local-minvmd0/<vm>`, the default VM
  keeps the bare provider dir (NET-053), and the native minimald
  backend — which hosts no VMs — ignores the name. The existing
  two-arg resolvers delegate, so every caller follows the name without
  edits.
- autospawn reads the named VM's state dir and forwards `--vm` to the
  `minvmd run --detach` it spawns, so `min --vm alpha` boots alpha
  rather than a second default VM.
- `min bug` lists every VM as its own `local-minvmd0/<vm>` bundle entry
  (the guest payload subdir is not a VM), and each status.json names
  the VM with its state directory and socket.
- GlobalArgs literals in test code gain the new `vm: None` field; the
  publish hook itself is one call in main.rs before dispatch.
@coderabbitai
coderabbitai Bot commented Sep 25, 2026 •
Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 95527f26-3a48-4175-b21a-d70e75eca49a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@norrietaylor
Copy link
Copy Markdown
Member

Babysitter review of 60175b5 against the T59 plan block on #1626 (NET-052..056). Design fits and every layer intent is met with evidence; all 7 test: lines exited 0 in the status comment and none self-skips (5 minvmd tests plus the paths test reproduced on macOS with libkrun). All 8 out-of-layer touches are in scope (vm: None on test literals, publish_vm_name() before dispatch, the two new test files). Verdict: REVISE, then un-draft.

High — ci-macos-success is red because of this PR's tests

  • crates/minvmd/tests/named_vm_integration.rs:27 spawns the daemon via env!("CARGO_BIN_EXE_minvmd") only. The macOS e2e job runs from a nextest archive on a testbed and exports MINVMD_BIN for exactly this; default_vm_paths_unchanged panics spawning minvmd: NotFound (job 108004002656) and nextest cancels the rest. Every sibling harness reads std::env::var_os("MINVMD_BIN").unwrap_or_else(|| env!("CARGO_BIN_EXE_minvmd").into()) (boot_integration.rs:38, config_cli_integration.rs:16).
  • 8000 reap_scoped_per_checkout (:418) locates the script through concat!(env!("CARGO_MANIFEST_DIR"), ...), another compile-time path; read CARGO_MANIFEST_DIR at runtime.

Medium

  • --vm is silently ignored on the native backend: resolve_provider_dir_named (crates/minimal-client/src/lib.rs:1138) falls back to the plain provider dir when the kind is not Minvmd, so min --vm alpha ... on Linux without minvmd talks to the default daemon. Asking for a named VM there should fail with a message, not do something else quietly.
  • validate_vm_name (crates/paths/src/lib.rs:219) rejects only empty, ., .. and /. Accepted today: whitespace and control chars, a leading -, names equal to default-VM entries (ssh.sock, minvmd.toml, data-vol.raw, boot.log) which fail later with EEXIST/ENOTDIR, guest (diag drops it at collect.rs:591, so that VM vanishes from the bundle), and unbounded length: on macOS the 104-byte sun_path leaves about 24 bytes for the name under .../local-minvmd0/<name>/gvproxy-switch.sock, and the failure surfaces only at bind with advice that does not apply. Suggest an allowlist like [a-z0-9][a-z0-9-]{0,23} with guest reserved, tested.
  • New client tests: native_backend_ignores_the_vm_name fails on macOS (client_provider_kind forces Minvmd there, lib.rs:1076), needs #[cfg(target_os = "linux")]; set_vm_name_steers_the_default_resolvers sets the process-global VM_NAME, which under plain cargo test -p minimal-client breaks socket_path_selects_the_minvmd_dir (3 of 3 runs). Test through the _named variants or run the global test in a subprocess.

Low

  • All VMs write to one <state>/logs/minvmd.log (main.rs:171); only the start/stop lines carry vm=. A vm span field on supervisor events would make the file attributable.
  • The "stopping VM" line is emitted only by the minvmd stop CLI (main.rs:134-144), not by min stop or a supervisor exit, short of the plan's "one info line per VM start and stop". The autospawn.rs hint "run minvmd stop" omits --vm NAME.
  • Any min --vm <typo> <cmd> creates a state dir (StateDir::new → create_dir_all) that diag then lists as a VM; there is no teardown for a named VM and each provisions its own 256 GiB sparse data volume. Follow-up material.
  • scripts/reap-vms.sh --vm default reaps nothing (default processes carry no --vm); the reap test checks pkill pattern strings only, never a real re-exec argv where forward_identity appends --vm NAME after the subcommand (state.rs:136).
  • min --vm alpha dirs still prints the default socket (dirs.rs:151), outside the layers, follow-up.

Round 1 is in flight on the failed check suite. A /revise for whatever it leaves follows once it lands.

The macOS and KVM lanes replay every `*_integration` harness from a
nextest archive built on another machine (`--workspace-remap`), so no
path baked into the binary at build time exists there. The new
named_vm_integration harness ignored that contract: it spawned
`env!("CARGO_BIN_EXE_minvmd")` and copied
`CARGO_MANIFEST_DIR/../../scripts/reap-vms.sh`, both of which point at
the build runner. Every test in the harness panicked with ENOENT, which
is what took the macOS e2e job (and with it ci-macos-success) red —
`unit` runs `-E 'not binary(/_integration$/)'` and so never saw it.

Follow the contract every other harness already documents: take the
binary from `MINVMD_BIN`, and embed the reap script in the harness with
`include_str!` so it travels with the archive. Verified by replaying
the archive with both build-time paths removed and `MINVMD_BIN` set,
the exact shape of the lanes' harness step: 0/5 before, 5/5 after.
The run helper's doc still read as if the binary were always the one
cargo built beside the harness; since the previous commit it comes from
MINVMD_BIN on the VM lanes' split build/test runners.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 1 of 10 on a failed check suite, pushed as 452097f7dec1.

line exit tail
cargo nextest run -p minvmd named_vm_has_own_state_socket_daemon 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-f558... Compiling ot v0.0.1 (/home/runner/.local/state/minimal/sessions/36f64/tree/crates/ot) Compiling args v0.0.1 (/home/runner/.local/state/minimal/sessions/36f64/tree/crates/args) C
cargo nextest run -p minvmd default_vm_paths_unchanged 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-2f7d... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/5a5d2/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/5a5d2/tree/crates/
cargo nextest run -p minvmd named_vm_under_per_name_subdirectory 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-f3f2... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/de55b/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/de55b/tree/crates/
cargo nextest run -p minvmd stop_one_vm_leaves_other_running 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-c8ad... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/86a77/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/86a77/tree/crates/
cargo nextest run -p minvmd reap_scoped_per_checkout 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-0608... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/61431/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/61431/tree/crates/
cargo nextest run -p paths provider_instance_dir_accepts_a_name 0 Applying loadouts: default Running task runtime-implement-test-5 in session task-runtime-implement-test-5-aac6... Compiling paths v0.0.1 (/home/runner/.local/state/minimal/sessions/65ad5/tree/crates/paths) Finished 'test' profile [unoptimized] target(s) in 5.08s ──────────── Nextest run ID a
cargo nextest run -p minimal named_vm_reaches_autospawn 0 Applying loadouts: default Running task runtime-implement-test-6 in session task-runtime-implement-test-6-431c... Compiling args v0.0.1 (/home/runner/.local/state/minimal/sessions/77062/tree/crates/args) Compiling ot v0.0.1 (/home/runner/.local/state/minimal/sessions/77062/tree/crates/ot) C

Verify min task run agent-verify passed.

Outside the task's layers::

  • crates/minimal/src/loadouts.rs
  • crates/minimal/src/main.rs
  • crates/minimal/src/tests.rs
  • crates/minimal/tests/bug.rs
  • crates/minimal/tests/cli.rs
  • crates/minimal/tests/common/mod.rs
  • crates/minimal/tests/named_vm.rs
  • crates/minvmd/tests/named_vm_integration.rs

Converted to a draft: something above needs a person before review.

The named-VM proof stands at this head — a second VM named with --vm gets its own state directory, socket and daemon under providers/local-minvmd0/<name>/, the default VM keeps every path it had, stopping one VM leaves the other running, and scripts/reap-vms.sh --vm <NAME> reaps only this checkout's named VM — and the harness that proves all of it now runs where the VM lanes run it: on the split build/test machines, where it previously panicked on the build runner's paths and red the macOS e2e job.

Notes from the agent: Revise round: the two failed checks were ci-macos-success and e2e, and neither the macOS unit job nor any Linux lane was red — which points at the e2e job's binary(/_integration$/) harness step, the one step that replays minvmd test binaries on a machine that did not build them. I could not read the CI logs (no gh access here), so I reproduced the failure locally: named_vm_integration ignored the contract every other harness documents in ci-macos.yml/ci-linux-kvm.yml (MINVMD_BIN, --workspace-remap — 'their compile-time CARGO_BIN_EXE path does not exist on the test runner'), so all five of its tests panicked with ENOENT on the replay machine: four on env!("CARGO_BIN_EXE_minvmd"), the fifth on copying CARGO_MANIFEST_DIR/../../scripts/reap-vms.sh. Fix: take the binary from MINVMD_BIN (CARGO_BIN_EXE fallback) and embed the reap script with include_str!. Verified by replaying a cargo nextest archive of the crate from /tmp with --workspace-remap and --profile vm --run-ignored all, MINVMD_BIN set, and both build-time paths removed from the filesystem: 0/5 before the fix, 5/5 after; all seven test lines also pass in-tree. The Linux/KVM lane replays this harness under the same contract, so it owes the same fix even though it is not in the failed list. Not covered here: the e2e job's hypervisor-backed steps after the harness step (daemon lifecycle, VM-backed session e2e) need a Mac with libkrun or KVM, which this bo…[truncated]

@norrietaylor
Copy link
Copy Markdown
Member

/revise Round 1 fixed the archive-lane failure (MINVMD_BIN fallback, embedded reap script) and both VM lanes are green at 452097f. The remaining findings from the review at 60175b5 still stand:

  1. --vm on the native backend. resolve_provider_dir_named (crates/minimal-client/src/lib.rs:1138) silently falls back to the plain provider dir when the client provider kind is not Minvmd, so on Linux without minvmd min --vm alpha ... talks to the default daemon. Return an error there ("named VMs need the minvmd provider") instead; make native_backend_ignores_the_vm_name assert the refusal and gate it #[cfg(target_os = "linux")], since client_provider_kind forces Minvmd on macOS (lib.rs:1076).

  2. VM name validation. validate_vm_name (crates/paths/src/lib.rs:219) only rejects empty, ., .. and /. Replace it with an allowlist: ASCII lowercase letters, digits and -, starting with a letter or digit, at most 24 bytes (the 104-byte macOS sun_path leaves about that much under .../local-minvmd0/<name>/gvproxy-switch.sock), and reserve guest (diag drops that directory at crates/minvmd/src/diag/collect.rs:591). Point the error at the rule. Extend provider_instance_dir_accepts_a_name (or add a sibling) to cover a rejected name from each class: whitespace, uppercase, leading -, over-long, guest, ssh.sock.

  3. Test isolation. set_vm_name_steers_the_default_resolvers sets the process-global VM_NAME and breaks socket_path_selects_the_minvmd_dir under plain cargo test -p minimal-client (3 of 3 runs on macOS). Test the steering through the _named variants with an explicit name, or run the global-setting assertion in a spawned subprocess, so the crate's tests pass under both cargo test and nextest.

  4. Low, fix if cheap: add --vm NAME to the minvmd stop hint in crates/minimal/src/autospawn.rs when a named VM is in use; tag the supervisor's tracing events with a vm field so <state>/logs/minvmd.log is attributable per VM; log the "stopping VM" info line on the min stop path too (crates/minvmd/src/main.rs:134-144 only covers the CLI), matching the plan's one-line-per-start-and-stop.

Keep the seven plan test lines green.

Review round 2 on the named-VM PR, three findings:

- validate_vm_name now holds VM names to an allowlist instead of a
  four-case blocklist: ASCII lowercase letters, digits and `-`,
  starting with a letter or digit, at most 24 bytes so the per-name
  socket path still fits the 104-byte macOS sun_path, and `guest`
  reserved for the diag bundle's in-guest provider subdirectory. The
  error names the rule it enforces.
- A named VM against the native `local-minimald` backend is refused
  with "named VMs need the minvmd provider" instead of silently
  falling back to the default daemon's directory, which would have
  `min --vm alpha` talking to the default VM while claiming to talk
  to alpha. The default VM still resolves - it is the only VM that
  backend has. The refusal test is Linux-only, since
  client_provider_kind forces Minvmd on macOS.
- The default-resolver steering test no longer leaks the
  process-global VM name into the rest of the crate under plain
  `cargo test`: the real assertion runs in a spawned child behind an
  env marker, so both runners see the same green crate.
Review round 2, the low finding: a VM's start/stop pair must be
attributable in the logs.

- Every stop funnels through one helper that logs "stopping VM" with
  the VM name and state directory: the `minvmd stop` CLI before it
  signals, the run supervisor at VMM-child exit - which is where a
  `min stop` surfaces - and foreground `boot`.
- The detached supervisor now runs inside a `vm` info span, so its
  records carry the VM they belong to even in the shared
  <state>/logs/minvmd.log every VM writes; the JSON layer flattens
  span fields onto the record, matching minimald's `conn` span.
- The autospawn shutdown hint names the VM when one is in use:
  `minvmd --vm alpha stop`.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 2 of 10 on a /revise note, pushed as 81b0431eab01.

line exit tail
cargo nextest run -p minvmd named_vm_has_own_state_socket_daemon 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-5908... Compiling args v0.0.1 (/home/runner/.local/state/minimal/sessions/3c0d4/tree/crates/args) Compiling ot v0.0.1 (/home/runner/.local/state/minimal/sessions/3c0d4/tree/crates/ot) C
cargo nextest run -p minvmd default_vm_paths_unchanged 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-ac8d... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/65dbc/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/65dbc/tree/crates/
cargo nextest run -p minvmd named_vm_under_per_name_subdirectory 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-1478... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/3fccd/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/3fccd/tree/crates/
cargo nextest run -p minvmd stop_one_vm_leaves_other_running 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-a627... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/f830e/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/f830e/tree/crates/
cargo nextest run -p minvmd reap_scoped_per_checkout 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-1240... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/fd6ee/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/fd6ee/tree/crates/
cargo nextest run -p paths provider_instance_dir_accepts_a_name 0 Applying loadouts: default Running task runtime-implement-test-5 in session task-runtime-implement-test-5-2b82... Compiling paths v0.0.1 (/home/runner/.local/state/minimal/sessions/f12bd/tree/crates/paths) Finished 'test' profile [unoptimized] target(s) in 1.37s ──────────── Nextest run ID 5
cargo nextest run -p minimal named_vm_reaches_autospawn 0 Applying loadouts: default Running task runtime-implement-test-6 in session task-runtime-implement-test-6-1a48... Compiling ot v0.0.1 (/home/runner/.local/state/minimal/sessions/8c7ff/tree/crates/ot) Compiling args v0.0.1 (/home/runner/.local/state/minimal/sessions/8c7ff/tree/crates/args) C

Verify min task run agent-verify passed.

Outside the task's layers::

  • crates/minimal/src/loadouts.rs
  • crates/minimal/src/main.rs
  • crates/minimal/src/tests.rs
  • crates/minimal/tests/bug.rs
  • crates/minimal/tests/cli.rs
  • crates/minimal/tests/common/mod.rs
  • crates/minimal/tests/named_vm.rs
  • crates/minvmd/src/cmd/mod.rs
  • crates/minvmd/tests/named_vm_integration.rs

Converted to a draft: something above needs a person before review.

A named VM still boots with its own state directory, socket and daemon under a per-name subdirectory while the default VM's paths stay unchanged — and a reviewer can now also see that min --vm alpha on the native local-minimald backend is refused with "named VMs need the minvmd provider" instead of silently talking to the default daemon, that a VM name outside the allowlist (whitespace, uppercase, leading -, over 24 bytes, guest) is rejected with the rule spelled out, that the minvmd stop shutdown hint names the VM in use (minvmd --vm alpha stop), and that every VM stop — min stop, minvmd stop, guest poweroff, crash — logs one "stopping VM" line carrying the VM name and state directory, with the detached supervisor's records tagged by a vm span so the shared minvmd.log stays attributable once a second VM runs.

Notes from the agent: Round 2 (directive): all four findings addressed; no threads section in the directive, so no thread verdicts. (1) The native-backend refusal lives at the top of resolve_provider_dir_named, keyed on the resolved provider kind and vm != DEFAULT_VM_NAME, so it holds on both platforms; its test is #[cfg(target_os = "linux")] because client_provider_kind forces Minvmd on macOS — the default VM still resolves on the native backend, which is the only VM it has. (2) The allowlist is in paths::validate_vm_name: ASCII lowercase/digits/-, first byte not -, at most 24 bytes (the per-name socket must fit the 104-byte macOS sun_path), guest reserved for the diag bundle's in-guest subdirectory; resolve_socket_path under the name is what bounds it, so the cap is documented against that. One clap detail the tests encode: a separate -alpha token is parsed as flags before the validator runs, so the leading-dash rejection is covered through the value form --vm=-alpha. (3) The steering test now runs its real assertion in a spawned child behind the MINIMAL_CLIENT_VM_STEER_CHILD marker, so the process-g…[truncated]

@norrietaylor
norrietaylor marked this pull request as ready for review September 25, 2026 11:11
@norrietaylor
Copy link
Copy Markdown
Member

Babysitter verification of round 2 (3353f9c, deb65f3; head 81b0431) against the /revise note: all four items done.

  1. Native backend refuses a named VM (crates/minimal-client/src/lib.rs:1145), test native_backend_refuses_a_named_vm gated to Linux as asked.
  2. validate_vm_name is the allowlist with the rule in the error text; the paths, client and minvmd integration tests each reject one name per class, and the 24-byte cap is tested inclusive.
  3. The steering test runs its global-setting assertion in a spawned child; cargo test -p paths -p minimal-client on macOS: 55 of 56 pass, the one failure (socket_path_honors_override) fails identically on main and is the macOS provider-kind forcing, not this PR's.
  4. --vm in the stop hint, vm on the supervisor span, one log_stopping_vm for every stop path.

test-kvm and ci-macos are green at 81b0431. Out-of-layer files are all in scope for the named-VM plumbing this task adds, so the PR is taken out of draft for the bot review; merge follows minimal-review and CodeRabbit.

@norrietaylor
norrietaylor requested a review from a team as a code owner September 25, 2026 11:11
@minimal-foundry
minimal-foundry Bot commented Sep 25, 2026 •
Copy link
Copy Markdown
Contributor Author

Approved: no findings at 80e64ebf66ce.

Nothing to do.

Verdict APPROVE, posted as a comment: the App cannot review its own pull request; the check and the label carry it.

Clobber: none: the base was merged in and crates/minimal/tests/cli.rs (the only file that moved on both sides) diffed clean since the branch point — the sole change is the mechanical vm: None field addition.

@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

minimal-review — advisory review

Verdict COMMENT — 1 finding, 1 inline.

Comment thread crates/minvmd/src/main.rs Resolved
Review round 3, the low finding: `minvmd stop` logged "stopping VM"
unconditionally, so a stop of a supervised VM put the line in the log
twice - once from the CLI before it signals, once from the `run`
supervisor when the signalled VMM child exits - and an idempotent stop
of an already-stopped VM logged a stop that never happened, the
opposite of the "one line per VM stop" the comment claimed.

The line now comes only from the process that observes the VM die: the
`run` supervisor at VMM-child exit, or a foreground `boot`. That witness
is alive whenever `stop` actually stops something - it holds the alive
lock `stop` requires before it signals - so no stop loses its line, and
a no-op stop (already stopped, stale state) logs none.

`cmd/mod.rs` is outside this task's layers; only its helper doc changed,
because it described the CLI logging this round removes.

The named-VM harness cannot run a supervisor on a libkrun-less host, so
it now pins the CLI-side invariants that keep the line one-per-stop: the
stop CLI emits no "stopping VM" line itself, and a no-op stop logs none.
@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

minimal-review — advisory review

Verdict COMMENT — 1 finding, 1 inline.

Comment thread scripts/reap-vms.sh Resolved
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 3 of 10 on the runtime's review findings, pushed as 646f44b53e31.

line exit tail
cargo nextest run -p minvmd named_vm_has_own_state_socket_daemon 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-df44... Compiling ot v0.0.1 (/home/runner/.local/state/minimal/sessions/98d79/tree/crates/ot) Compiling args v0.0.1 (/home/runner/.local/state/minimal/sessions/98d79/tree/crates/args) C
cargo nextest run -p minvmd default_vm_paths_unchanged 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-d7e3... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/4b534/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/4b534/tree/crates/
cargo nextest run -p minvmd named_vm_under_per_name_subdirectory 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-f517... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/753ed/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/753ed/tree/crates/
cargo nextest run -p minvmd stop_one_vm_leaves_other_running 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-a987... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/fd2f2/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/fd2f2/tree/crates/
cargo nextest run -p minvmd reap_scoped_per_checkout 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-1af9... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/656da/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/656da/tree/crates/
cargo nextest run -p paths provider_instance_dir_accepts_a_name 0 Applying loadouts: default Running task runtime-implement-test-5 in session task-runtime-implement-test-5-0422... Compiling paths v0.0.1 (/home/runner/.local/state/minimal/sessions/bd001/tree/crates/paths) Finished 'test' profile [unoptimized] target(s) in 1.40s ──────────── Nextest run ID 5
cargo nextest run -p minimal named_vm_reaches_autospawn 0 Applying loadouts: default Running task runtime-implement-test-6 in session task-runtime-implement-test-6-9431... Compiling ot v0.0.1 (/home/runner/.local/state/minimal/sessions/cd420/tree/crates/ot) Compiling args v0.0.1 (/home/runner/.local/state/minimal/sessions/cd420/tree/crates/args) C

Verify min task run agent-verify passed.

Outside the task's layers::

  • crates/minimal/src/loadouts.rs
  • crates/minimal/src/main.rs
  • crates/minimal/src/tests.rs
  • crates/minimal/tests/bug.rs
  • crates/minimal/tests/cli.rs
  • crates/minimal/tests/common/mod.rs
  • crates/minimal/tests/named_vm.rs
  • crates/minvmd/src/cmd/mod.rs
  • crates/minvmd/tests/named_vm_integration.rs

Converted to a draft: something above needs a person before review.

Review threads the coder judged:

  • 👍 valid — PRRT_kwDOPdfcxM6l-i-E: Fixed: the stop CLI no longer logs the one-per-stop line — only the supervisor or foreground boot that observes the VMM child exit does — so a supervised stop logs one line, a no-op stop (already stopped / stale state) logs none, and the overstated comment is gone; the harness pins both CLI-side inv (not posted on the thread: not an open thread on the pull request)

A second named VM boots with its own state directory, socket and daemon under a per-name subdirectory, the default VM's paths are unchanged, stopping one VM leaves the other running, and reaping touches only this checkout's VMs; and every VM stop now yields exactly one 'stopping VM' line naming the VM and its state directory, logged by the witness that observes the VM die — a supervised stop is no longer double-logged, and a no-op stop logs nothing.

Notes from the agent: Round 3 addressed the single review thread (the duplicate/false 'stopping VM' line). The stop CLI no longer logs the one-per-stop line at all: the run supervisor (or a foreground boot) that observes the VMM child exit is its only logger, which is alive whenever stop actually stops something (it holds the alive lock stop requires before signalling), so no supervised stop loses its line. Out-of-layers touch, disclosed: crates/minvmd/src/cmd/mod.rs is not in this task's layers; only the log_stopping_vm helper's doc comment changed there, because it stated the CLI logs the line before it signals — behavior this round removes. Coverage note: the libkrun-less integration harness cannot run a supervisor, so the positive line (its vm=/state_dir= content) is observable only where a run supervisor really runs (VM lanes); the harness now pins the CLI-side invariants instead — the stop CLI emits no stop line itself, and a no-op stop logs none while taking the 'minvmd is not running' branch. Extra checks beyond the test lines, both clean: cargo clippy -p minvmd --all-targets, cargo fmt -p minvmd -- --check.

@minimal-foundry
minimal-foundry Bot marked this pull request as draft September 25, 2026 11:58
@norrietaylor
Copy link
Copy Markdown
Member

/revise Round 3 (646f44b) is verified: the supervisor is the stop line's only witness, the CLI logs none, and the integration test pins both the supervised and the no-op case. One item remains, minimal-review's Low on scripts/reap-vms.sh:54-55, which the runtime did not open a round for.

scripts/reap-vms.sh --vm default reaps nothing: the default VM's processes carry no --vm flag (state::reexec_vm_args and autospawn omit it for DEFAULT_VM_NAME) and its switch socket is not under a default/ subdirectory, so both patterns miss every live process and the script exits 0 having done nothing. Treat --vm default as the bare invocation (clear VM when it equals default), so it reaps the default VM like no flag does; keep the script POSIX sh. No test line needed beyond bash -n and shellcheck, which CI's shell lane runs.

Keep the plan's seven test lines green. The PR stays a draft until the round lands and is then un-drafted.

The default VM's processes carry no --vm flag (minvmd's re-exec and the
CLI's autospawn omit it for the default name) and its switch socket sits
in no per-name subdirectory, so the name-pinned patterns matched nothing
and the script exited 0 having reaped no VM. Clearing the name makes
--vm default reap the default VM like the bare invocation does.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 4 of 10 on a /revise note, pushed as 80e64ebf66ce.

line exit tail
cargo nextest run -p minvmd named_vm_has_own_state_socket_daemon 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-5d13... Compiling ot v0.0.1 (/home/runner/.local/state/minimal/sessions/775c2/tree/crates/ot) Compiling args v0.0.1 (/home/runner/.local/state/minimal/sessions/775c2/tree/crates/args) C
cargo nextest run -p minvmd default_vm_paths_unchanged 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-a91b... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/d5336/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/d5336/tree/crates/
cargo nextest run -p minvmd named_vm_under_per_name_subdirectory 0 Applying loa 10BC8 douts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-6db1... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/e3a34/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/e3a34/tree/crates/
cargo nextest run -p minvmd stop_one_vm_leaves_other_running 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-9b91... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/ac1a5/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/ac1a5/tree/crates/
cargo nextest run -p minvmd reap_scoped_per_checkout 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-fa56... Compiling version v0.6.0 (/home/runner/.local/state/minimal/sessions/9bf22/tree/crates/version) Compiling mlog v0.0.1 (/home/runner/.local/state/minimal/sessions/9bf22/tree/crates/
cargo nextest run -p paths provider_instance_dir_accepts_a_name 0 Applying loadouts: default Running task runtime-implement-test-5 in session task-runtime-implement-test-5-cdcc... Compiling paths v0.0.1 (/home/runner/.local/state/minimal/sessions/60e36/tree/crates/paths) Finished 'test' profile [unoptimized] target(s) in 1.60s ──────────── Nextest run ID 2
cargo nextest run -p minimal named_vm_reaches_autospawn 0 Applying loadouts: default Running task runtime-implement-test-6 in session task-runtime-implement-test-6-1c22... Compiling ot v0.0.1 (/home/runner/.local/state/minimal/sessions/82d21/tree/crates/ot) Compiling args v0.0.1 (/home/runner/.local/state/minimal/sessions/82d21/tree/crates/args) C

Verify min task run agent-verify passed.

Outside the task's layers::

  • crates/minimal/src/loadouts.rs
  • crates/minimal/src/main.rs
  • crates/minimal/src/tests.rs
  • crates/minimal/tests/bug.rs
  • crates/minimal/tests/cli.rs
  • crates/minimal/tests/common/mod.rs
  • crates/minimal/tests/named_vm.rs
  • crates/minvmd/src/cmd/mod.rs
  • crates/minvmd/tests/named_vm_integration.rs

Converted to a draft: something above needs a person before review.

Review threads the coder judged:

  • 👍 valid — scripts/reap-vms.sh:69: Fixed as directed: the script clears the VM name when it equals default, so --vm default runs the bare invocation's patterns and reaps the default VM; the header comment documents it and reap_scoped_per_checkout now pins it against the bare invocation's recorded patterns.

A second named VM boots with its own state directory, socket and daemon under a per-name subdirectory, the default VM's paths are unchanged, stopping one VM leaves the other running, and reaping touches only this checkout's VMs — and as of this round scripts/reap-vms.sh --vm default reaps the default VM exactly like the bare invocation does, where before it matched no live process and exited 0 having reaped nothing.

Notes from the agent: Round 4 addressed the one remaining item: the minimal-review LOW on scripts/reap-vms.sh. The finding's premise was verified against the code (state::reexec_vm_args and minimal's autospawn omit --vm for DEFAULT_VM_NAME; paths::provider_instance_dir_named gives the default VM no per-name subdirectory), so it was fixed as the note directed: the script clears the VM name when it equals default, making the invocation the bare one. Documented consequence, now in the script's header comment: --vm default reaps every VM of this checkout, exactly as the bare invocation does — that is the chosen semantics, not a bug. The fix is pinned in the existing reap_scoped_per_checkout integration test, which now asserts --vm default records byte-for-byte the bare invocation's pkill patterns (the test file is in the layers' scope via the test line that names it). All seven plan test lines re-run green at the new head; no other file changed.

@norrietaylor
norrietaylor marked this pull request as ready for review September 25, 2026 12:47
@norrietaylor
Copy link
Copy Markdown
Member

Babysitter verification of round 4 (a09cb99, head 80e64eb): --vm default now clears the name and takes the bare path, and reap_scoped_per_checkout pins that the recorded patterns equal the bare invocation's. The reap-vms thread is resolved. Everything else in the range came in with the main merge. Out of draft again for the bot review; merge follows build-linux and minimal-review at this head.

@minimal-foundry minimal-foundry Bot left a comment
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

minimal-review — approved

Verdict APPROVE — No findings.

@minimal-foundry minimal-foundry Bot added the review:approved minimal-review approved the current head label Sep 25, 2026
@norrietaylor norrietaylor left a comment
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Babysitter approval: T59 verified against the plan block through four rounds, all lanes green at 80e64eb including test-kvm and ci-macos, minimal-review APPROVE on this head, CodeRabbit passed, no open threads.

@norrietaylor
norrietaylor merged commit 22ad605 into main Sep 25, 2026
33 checks passed
@norrietaylor
norrietaylor deleted the agent/1626-create-a-named-vm-with-its-own-state-socket-and branch September 25, 2026 13:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review:approved minimal-review approved the current head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

T59 — Create a named VM with its own state, socket and daemon, leaving the default VM's paths unchanged

1 participant

0