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

169 lines
8.5 KiB
Markdown

# 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
```bash
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.