test(hibernate): #76 slice 3 — V2 hibernate→resume VM check (PASS)
All checks were successful
Check / eval (push) Successful in 4m11s
All checks were successful
Check / eval (push) Successful in 4m11s
Add checks.hibernate: a runNixOSTest modeled on nixpkgs' hibernate.nix, wired to the actual risk #76 introduces — coexistence with the zram swap that oom.nix now enables by default. It imports oom.nix, puts the disk swap at a lower priority than zram with an explicit boot.resumeDevice, and uses systemd-initrd. The test drives a full hibernate -> power-off -> resume -> crash cycle: a volatile ramfs marker survives resume but NOT a crash-boot (proving genuine resume), and it asserts zram sits at priority 100 while the disk swap is lower, so the hibernate image lands on disk rather than volatile zram. What QEMU genuinely can't do — feed a boot-time resume_offset for a btrfs swapfile (offset only exists after first-boot allocation) or drive a LUKS-initrd unlock — stays a V3 laptop check (HARDWARE-QUEUE, dev box already runs that exact layout), plus the no-swap notify in a real session. With this, all V0/V1/V2 work on #76 is done; #76 collapses to a [blocked:hw] stub and the design record moves to ROADMAP. Verification: V2 PASS — nix build .#checks.x86_64-linux.hibernate built the driver fresh and exited 0. nix flake check --no-build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -28,72 +28,22 @@ in [`docs/ROADMAP.md`](../docs/ROADMAP.md); map in
|
||||
|
||||
## NEXT
|
||||
|
||||
### 76. Hibernation default `[big]`
|
||||
**Product intent (Bernardo 2026-07-10):** hibernation on by default;
|
||||
zram on alongside for live memory pressure. Disk swap ≥ RAM for the
|
||||
hibernate image; zram high-priority for day-to-day swap (not sufficient
|
||||
alone for resume).
|
||||
### 76. Hibernation default — all agent work shipped `[blocked:hw]`
|
||||
Hibernation + zram on by default (product intent, Bernardo 2026-07-10).
|
||||
**All V0/V1/V2 slices done** (2026-07-10); only the V3 laptop check remains
|
||||
(HARDWARE-QUEUE → "AMD dev box"). Design record in **ROADMAP § Hibernation +
|
||||
zram by default**. Shipped:
|
||||
- zram default (`modules/nixos/oom.nix`, zstd/50%/prio 100) — `checks.zram-swap`.
|
||||
- Installer/template already default new installs to a RAM-sized encrypted
|
||||
`@swap` swapfile + `resume_offset` wiring; `swapSize=0` = no-swap opt-out.
|
||||
- `docs/MIGRATION.md` enable-hibernation runbook (existing machines).
|
||||
- `rofi.nix` Power → Hibernate: kept unconditional, **notifies on failure**.
|
||||
- `checks.hibernate` VM test (V2 PASS) — hibernate→resume→crash round-trip;
|
||||
zram (prio 100) doesn't swallow the image, disk swap carries it.
|
||||
|
||||
**✓ zram slice shipped (2026-07-10):** `zramSwap` on by default in
|
||||
`modules/nixos/oom.nix` (zstd, 50% RAM, priority 100 so day-to-day paging
|
||||
stays off the disk swap reserved for the hibernate image); `checks.zram-swap`
|
||||
VM test asserts device/algorithm/priority (V2 pass). **Remainder below is the
|
||||
hibernation half** — disk swapfile default + resume + installer.
|
||||
|
||||
**Reference layout (this machine — already hibernation-capable, no zram
|
||||
yet):** LUKS (`crypto_LUKS` → mapper `crypted`) holds one BTRFS with
|
||||
`@swap` mounted at `/swap`; swapfile `/swap/swapfile` (here 16G);
|
||||
`swapDevices = [{ device = "/swap/swapfile"; }]`;
|
||||
`boot.resumeDevice = "/dev/disk/by-uuid/<LUKS-root-uuid>"`;
|
||||
`boot.kernelParams = [ "resume_offset=…" ]` from
|
||||
`btrfs inspect-internal map-swapfile -r`. The swapfile lives **inside
|
||||
the encrypted volume** (subvolume, not a separate cleartext partition).
|
||||
Installer path already sketches this (`@swap` + resume_offset patch in
|
||||
`nomarchy-install` / `patch-template.py`) when `swapSize` ≥ RAM-ish.
|
||||
|
||||
**✓ Design settled (Bernardo 2026-07-10):**
|
||||
- **Swap sizing** — keep **exactly RAM** (rounded up to whole GiB), as the
|
||||
installer already does. Hibernate image ≤ RAM; zram (priority 100) takes
|
||||
day-to-day paging so the file stays reserved for the image. No change.
|
||||
- **`swapSize=0`** — stays a no-swap / no-resume opt-out (already handled by
|
||||
installer + patch-template: no `@swap`, no `swapDevices`/resume wiring).
|
||||
- **Migration (existing machines)** — **docs runbook in `docs/MIGRATION.md`**,
|
||||
not a tool: create `@swap` subvol + swapfile, `map-swapfile -r` offset,
|
||||
add `swapDevices`/`boot.resumeDevice`/`resume_offset` to `system.nix`.
|
||||
- **No-swap Hibernate UX** — **keep the Power-menu Hibernate row**; when
|
||||
hibernate fails (no swap), show a desktop **notification** explaining no
|
||||
swap is configured (rather than hiding the row or a silent no-op).
|
||||
|
||||
**Already shipped for NEW installs** (verify, don't rebuild): installer
|
||||
defaults `NOMARCHY_SWAP_GB=RAM` → hibernation-ready `@swap` swapfile encrypted
|
||||
with root; `patch-template.py` writes `swapDevices` + `boot.resumeDevice` +
|
||||
`resume_offset`; Power menu has a Hibernate row; hibernate+LUKS+hyprlock
|
||||
interplay handled in `modules/nixos/default.nix` / `modules/home/idle.nix`.
|
||||
|
||||
**Remaining slices:**
|
||||
|
||||
1. ~~**Docs** — `docs/MIGRATION.md` hibernation-enable runbook (V0).~~ ✓
|
||||
shipped 2026-07-10 (§ "Enabling hibernation on an existing machine";
|
||||
commands verified live against the dev machine's LUKS+@swap layout).
|
||||
2. ~~**Menu** — Hibernate row: notify-on-failure when no swap configured.~~
|
||||
✓ shipped 2026-07-10 (`rofi.nix` power arm: `systemctl hibernate ||
|
||||
notify-send …`; success blocks until resume so the notify only fires on
|
||||
real failure). V1 (HM generation builds; `writeShellScriptBin` bash -n) +
|
||||
control-flow proof (fail→notify, success→no notify). Row kept unconditional.
|
||||
The session-level "no-swap pick surfaces a mako notification" is folded
|
||||
into the V3 laptop check below.
|
||||
3. **Verify (required)**
|
||||
- **V0:** eval / option / disko contracts.
|
||||
- **V2 (mandatory, agent):** exercise hibernation in the **VM harness**
|
||||
(`runNixOSTest` or install-test style) — encrypted root + BTRFS
|
||||
`@swap` swapfile + resume_offset; prove hibernate→resume path at
|
||||
least as far as QEMU allows (fail the item if only “config
|
||||
evaluates”).
|
||||
- **V3:** real laptop (this LUKS+@swap layout): Hibernate → power off
|
||||
→ resume session; queue HARDWARE-QUEUE.
|
||||
|
||||
**Out of scope:** TLP; formatter. Prefer boring NixOS knobs; extend the
|
||||
existing installer swapfile story rather than inventing a second layout.
|
||||
**V3 remaining:** real hibernate→resume on the LUKS+@swap swapfile+offset
|
||||
layout (QEMU can't feed a boot-time offset / drive LUKS-initrd) + the no-swap
|
||||
notify in a real session. Steps queued in HARDWARE-QUEUE. Close #76 on PASS.
|
||||
|
||||
## LATER
|
||||
|
||||
|
||||
Reference in New Issue
Block a user