utils/vmbox/NEXT_SESSION_PROMPT.md
Krzysztof kuhy Rudnicki 25ba142f94
Some checks are pending
file length / file-length (push) Waiting to run
palette drift / palette (push) Waiting to run
vmbox: handoff gains the fix-it mandate, sandbox reuse, and a build-speed job
Three changes from kuhy's follow-up:

- An explicit standing instruction that when vmbox breaks the next session
  FIXES it -- not works around it, not reports it as a limitation, not quietly
  falls back to testing on the host. Bugs found by real use are the return on
  having built this.
- Guidance to attach to an existing sandbox or create one as appropriate,
  rather than tearing down and rebuilding per command (`vm reset` is ~2s).
- A build-speed job with measurements rather than guesses: the last build
  pulled 197 packages / 396 MiB over the internet and spent 45s waiting on
  cloud-init, while the host's 114 GB pacman cache already holds every one of
  the packages checked. Exposing that cache read-only is the obvious first
  move; trimming base-devel and shortening the cloud-init wait follow.

The smoke gate stays regardless -- a faster build that fails open would be a
bad trade.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdyMBGCrGVGDWtJ9xe48f3
2026-08-22 21:34:26 +02:00

8.5 KiB

Next session: make vmbox the default place scripts get tested

Paste everything below into a fresh Claude session.


~/utils/vmbox is a working disposable-Arch-VM sandbox (built and verified 2026-08-22, pushed to github.com/kuhyx/utils). Read ~/utils/vmbox/README.md first — it documents the design and the traps.

Three jobs this session, in order.

Job 1: harden it by using it for real

vmbox has only ever been driven by one session, against a handful of scripts. testsAndMisc/linux_configuration is ~1090 files, ~170 of which need root. Expect it to break. That is the point of this job: find the breakages by running real work through it, and fix them.

Standing instruction for the whole session: when vmbox breaks, FIX IT. Do not work around it, do not report it as a limitation and move on, do not switch to testing on the host instead. A bug found here is the whole return on building the thing. Fix, commit, push, carry on with the list.

Reuse a sandbox when one is already up and suitable (vm list, then vm run <name> ... — it starts the VM if stopped); create a fresh one when you need a clean system or a different pinned clock. Do not tear down and rebuild a sandbox for every command: vm reset is ~2s and is usually what you actually want.

Work through this list, fixing vmbox whenever it gets in the way:

  1. The 8 Arch-only tests the repo's CI already names in .github/workflows/shell-tests.yml (arch-tests job) — test_hosts_*, test_makepkg_*, test_pacman_wrapper_security.sh, test_security_hardening.sh, test_shutdown_timer_monitor.sh. These are the ones its comments say "cannot run on an Ubuntu runner". Run them in a sandbox and report which pass.
  2. test_security_hardening.sh specifically: on a bare guest it reports its host checks as skipped. Install the real system first (install_core_system.sh), then re-run it, so those checks become real.
  3. setup_night_lockdown.sh — writes /etc/systemd/system units and an i2c module config. The i2c RGB part is hardware and will not work; confirm everything else does.
  4. boot_recovery/install.sh — pacman hooks + mkinitcpio. Highest blast radius of anything in the repo, and the best argument for the sandbox.
  5. The hosts guard (periodic_background/hosts/install.sh) — rewrites /etc/hosts and makes it immutable with chattr +i. Verify the immutable flag really lands in the guest, then that vm reset clears it.

For each: vm share what it needs, clone into the guest, run it, report what happened. When vmbox itself is the thing that broke, fix vmbox and commit.

Known-good invocation shape

vm share ~/testsAndMisc            # bind mount, NOT a symlink (9p won't follow one)
vm share ~/utils                   # guard-lib lives here; several scripts need guardctl
vm new t1 --rtc 2026-08-22T20:55:00   # pin the clock: shutdown scripts gate on 21:00-05:00
vm run t1 'git clone --no-hardlinks -q /mnt/hostrepo/testsAndMisc ~/tam'
vm run t1 'echo y | sudo bash ~/tam/path/to/script.sh enable'   # many prompt; stdin is closed
vm reset t1                        # ~2s back to pristine

Traps already paid for — do not rediscover these

  • A guest that boots but refuses ssh is almost never a network problem. Run systemctl list-jobs on the guest via vm ssh or the serial console. A unit that is enabled, has satisfied dependencies, and neither starts nor fails is QUEUED behind something. That cost most of a session.
  • Never grep a bash -x trace for the failure — you find where your pattern matched, not where execution stopped. Read the tail.
  • pkill -f <pattern> kills the Bash-tool shell when the pattern appears in the command running it (exit 144). Use pgrep -f | xargs -r kill.
  • Full list: ~/.claude/projects/-home-kuhy/memory/reference-vmbox-guest-image-traps.md and reference-qemu-shutdown-verification.md.

Rebuilding the image

vm build --force takes ~10 min. It ends with a smoke test that boots a real sandbox and refuses to ship an unreachable image. Do not debug the image by rebuilding per hypothesis — boot an overlay off the current base, fix it live over the serial console (root autologin is on ttyS0 and ttyS1), confirm the fix, and only then bake it into guest/provision.sh and rebuild once.

Job 2: make it the default, once Job 1 says it is trustworthy

kuhy's ask: "claude should ALWAYS defer to using this sandbox when testing stuff, ESPECIALLY when testing scripts."

Do Job 2 only after Job 1. A rule that routes work into a tool that breaks is worse than no rule. If Job 1 leaves vmbox flaky, say so and stop.

The rule belongs in ~/.claude/CLAUDE.md. Points to settle with kuhy before writing it — these are real forks, not rhetorical:

  • What triggers it. "Any script" is too broad: it would send a one-line python_pkg unit test through a VM boot. A sharper line is anything that needs root, writes outside $HOME, touches systemd/pacman//etc, or could power the machine off.
  • What it replaces. Today's CLAUDE.md development workflow says "run the actual script to verify it does what it is supposed to do". Does the sandbox become the first run, or an extra step before the host run? Note the standing feedback-verify-real-deployment-path memory: testing somewhere that is not the real deploy target has burned kuhy before, so a sandbox pass must NOT be presented as proof the host is fine.
  • The escape hatch. Anything hardware-bound (adb/phone, GPU, i2c, the Huion tablet) cannot run in a VM. The rule needs a stated exception, or it will be violated on the first phone task and quietly stop being followed.
  • Where it lives. CLAUDE.md is always-on context, so keep it to a few lines and link out. Per token-spend.instructions.md, a long rule there is a permanent per-turn tax.

Draft it, get kuhy's sign-off on those four points, then write it.

Job 3: make vm build faster than ~10 minutes

Measured on the last real build, so this is a known target rather than a guess:

  • 45s waiting for cloud-init on the guest's first boot.
  • 197 packages, 396 MiB downloaded over the internet (1273 MiB installed).
  • The host's own pacman cache is 114 GB and already contains every one of xorg-server i3-wm base-devel git strace jq kcov bats rsync xterm dmenu shellcheck — verified. The build is re-downloading packages that are already on this machine.

Ideas, cheapest first — measure before and after, do not assume:

  1. Expose the host pacman cache to the build VM read-only (-virtfs the way vm share already does) and point the guest's pacman at it (CacheDir, or bind /var/cache/pacman/pkg). Should remove most of the 396 MiB. Careful: the guest must not WRITE to the host cache, and a stale cached package can be older than the guest's mirror expects — -Syu first, or accept --needed semantics.
  2. Trim the package list. base-devel is a large group; check whether the target tests actually need all of it. kcov is only needed for the shell coverage jail.
  3. Cut the cloud-init wait. The build boot spends 45s on datasource probing that the sealed image then disables anyway; a kernel arg or an earlier cloud-init.disabled may shorten it.
  4. Cache the provisioned disk. If provisioning inputs are unchanged (guest/provision.sh hash + package list), a rebuild could reuse the last provisioned image instead of redoing it. Only worth it if 1-3 are not enough.

Note the build already ends with a smoke test that boots a real sandbox; keep that. Making the build fast by making it fail open would be a bad trade.

Definition of done

  • The 5 items in Job 1 have been run in a sandbox, with results reported per item (pass / fail / not-applicable-in-a-VM, with reasons).
  • Every vmbox bug found is fixed, committed, and pushed.
  • bats ~/utils/vmbox/tests/test_vmbox.bats, shellcheck and ruff all clean.
  • Either the CLAUDE.md rule is written and agreed, or there is a clear statement of what still makes vmbox untrustworthy.
  • vm build --force is measurably faster than the ~10 min baseline, with the before/after numbers stated.

State as of handoff

Verified working: the shutdown verdict (5 outcomes, all measured against real guests), destructive demo 7/7, reset ~2s, VM-to-VM ping (0% loss), screenshots, pinned clock, repo share + clone, base-corruption guard, build smoke gate.

Unverified / not attempted: everything in Job 1 above; anything hardware-bound; concurrent sandboxes beyond two.