Commands · reference

vmlab dev

10 min read · 2026-09-02 · vmlab 0.9

vmlab dev holds the verbs that mean nothing for a machine that is not @dev: reading and resolving the workspace syncer of a dev machine.

sh
vmlab dev <COMMAND>
SubcommandMeaning
syncThe workspace syncer: what it is doing, and what to do about a halt.
-h, --helpPrint help.

Every verb here that takes an optional machine resolves it through one ladder: the argument, then VMLAB_DEV_MACHINE, then the lab's default @dev machine. A rung that names a machine which is not a dev machine in this lab is an error that says which rung and lists the candidates. When no rung names one and no machine is the default, the error lists the dev machines and the two ways to pick one. A lab with no @dev machine at all says so.

Exit status is 0 on success. The selection ladder and the lab file are checked locally and fail with 1. Requests the verbs send carry the codes in Wire protocol and error codes: not_found (4) when the daemon does not know the machine, and failed (1) for everything the syncer refuses, since the workspace verbs answer every refusal as failed with the reason in the message.

vmlab dev sync

sh
vmlab dev sync <COMMAND>
SubcommandMeaning
statusWhat the syncer last decided: halted paths, warnings, and what it skipped by name.
flushRun a sync pass now and wait for it, rather than for the next edit.
diffShow the guest's copy of a path beside the host's.
resolvePick which side wins at a halted path, and carry it out.
-h, --helpPrint help.

Resolution is host-side, necessarily. The host opens channels and the guest answers, so there is no guest-to-host control path: a vmlab inside the dev machine could not call back. These verbs are typed in the lab directory on the host, not in a shell on the guest. The guest's only signal of a halt is the marker file .vmlab-sync-halt at its workspace root, which lists the halted paths.

All four verbs talk to a lab daemon that is already running and never start one: a syncer exists only while its machine is up, so starting a daemon would boot a lab in order to answer no. A lab that is not running is refused with a pointer to vmlab up. A machine that is up but declares no @dev(workspace = …), or is not up, is refused with has no workspace syncer running.

vmlab dev sync status

sh
vmlab dev sync status [MACHINE]
OptionMeaning
[MACHINE]Which dev machine. Default: VMLAB_DEV_MACHINE, then the lab's default @dev machine.
-h, --helpPrint help.

status reads the syncer's report off the lab status projection, the same value the console shows, and prints it in the order it matters. First the halt, whole, when there is one: the headline names the machine and how many paths conflict, or the bulk-delete guard that tripped, then each halted path with its reason, a count of any not listed, and the routes out. Without a halt the first line says the workspace is in step with its pass count, or is a number of paths behind the canonical copy, or has not completed a pass yet.

Then everything the syncer declined to do or is waiting on, each by name: a waiting line while both directions wait on a stat-walk, a re-seeding line while the bracket after a snapshot restore runs, a volume warning naming a subtree that carried an unusual amount of work, a trouble line when the last pass could not finish, the count of watch discontinuities answered with a full walk, .git paths deferred while a lock is held, paths not yet carried across that a snapshot capture would refuse on, and paths not synced by name with their reasons, such as a socket or a file over the size guard.

sh
$ vmlab dev sync status
the workspace on "dev01" has stopped, both directions, on 2 conflicting paths

  src/main.rs
      both sides changed it since they last agreed
  README.md
      both sides changed it since they last agreed

`vmlab dev sync diff <path>` shows the guest copy host-side; `vmlab dev sync resolve <path> --host` or `--guest` picks a side, and `--all` takes the batch. Making both sides identical by hand needs no verb at all — the next pass adopts them as agreed. Both copies are still where they were: a halt writes neither and deletes neither.

Exit status is 0 on success and 1 when the lab is not running or the machine has no syncer.

vmlab dev sync flush

sh
vmlab dev sync flush [MACHINE]
OptionMeaning
[MACHINE]Which dev machine, through the same ladder as status.
-h, --helpPrint help.

flush asks the syncer to run a pass now, waits for that pass to complete, and prints the same report status prints, so the effect of the flush is visible. It is the pass a snapshot capture runs first. The pass carries every write made on either side before the flush and left alone since, waiting out the 250 ms quiet period for a path still inside it. A file still being written stays pending and is listed as not yet carried. A halted workspace stays halted: a flush carries nothing across a halt. The wait gives up after 120 seconds and says to check vmlab status for whether the machine is still answering.

sh
vmlab dev sync flush dev01

Exit status is 0 on success and failed (1) when the pass does not complete or the machine has no syncer.

vmlab dev sync diff

sh
vmlab dev sync diff [OPTIONS] [PATHS]...
OptionMeaning
[PATHS]...Workspace-relative paths. Default: every halted path.
--machine <MACHINE>Which dev machine, through the same ladder as status.
-h, --helpPrint help.

diff brings the guest's copy of each path to the host and shows it beside the host's. The host copy is a directory on this workstation; only the guest's is behind the seam, which is why the verb exists rather than opening a shell and looking. With no path and no halt it is refused, because there is nothing to name.

The output opens with the host root and the guest root, then one section per path. Two identical copies say so, and that the next pass adopts them with no verb. A path one side lacks says which. Two readable text copies print as a unified diff, - for host lines and + for guest lines, up to 2000 lines a side; past that, or for a binary or a file the guest would not send whole, each side prints its size and digest prefix and why it was not shown.

sh
vmlab dev sync diff
vmlab dev sync diff src/main.rs --machine dev01

Exit status is 0 on success and failed (1) when no path is named and nothing is halted, or the guest copy cannot be read.

vmlab dev sync resolve

sh
vmlab dev sync resolve [OPTIONS] [PATHS]...
OptionMeaning
[PATHS]...Workspace-relative paths. Omit with --all.
--hostThe canonical host copy wins: carry it into the guest.
--guestThe guest's working copy wins: carry it onto the canonical copy.
--allEvery halted path, as the halt currently stands.
--machine <MACHINE>Which dev machine, through the same ladder as status.
-h, --helpPrint help.

resolve records which side wins at the named paths and runs a pass that carries it out, then prints the report. There is no default side: exactly one of --host and --guest is required, and the two conflict. Paths are required unless --all is given, which the daemon expands from the halt it is holding, so a thirty-thousand-file halt needs no list. --all on a workspace that is not halted is refused with there is nothing to resolve. Named paths are recorded as given, and the pass that follows carries the chosen side across at each of them.

The losing copy is overwritten

The copy that loses is overwritten and is not recoverable from vmlab. Run vmlab dev sync diff first. Making both sides identical by hand is a third route that needs no verb: the next pass adopts them as agreed.

sh
vmlab dev sync resolve src/main.rs --guest
vmlab dev sync resolve --all --host

Exit status is 0 on success, 1 when no side or no path is given, and failed (1) when the workspace is not halted or the pass does not complete.