feat(power): suspend-then-hibernate 1h on battery (#115)
All checks were successful
Check / eval (push) Successful in 6m16s

Human decision: 1h delay, battery only, Preferences enable/disable.
Wire systemd suspend-then-hibernate + HibernateDelaySec=1h +
HibernateOnACPower=false when settings.power.suspendThenHibernate is on
and boot.resumeDevice is set. Lid: s2h undocked, plain suspend on AC.
nomarchy-suspend for hypridle + Power menu (live state). LUKS unlock
before encrypted hibernate so s2h→disk is not double-password.
state-bridges assert the policy; HARDWARE-QUEUE #115 + suggested order.

V1: nix flake check --no-build. V3: bag-carry on hardware.
This commit is contained in:
2026-07-15 12:13:23 +01:00
parent e9dd3d14c5
commit 52d1581131
13 changed files with 304 additions and 51 deletions

View File

@@ -123,36 +123,6 @@ the measured number, both ISOs built from one tree), installs successfully with
a network in a QEMU run, says clearly at boot that it needs one, and leaves the
offline ISO's behaviour untouched (`checks.*` for the offline path stay green).
### 115. Suspend-then-hibernate, with a way to set it up
Bernardo, 2026-07-14: a suspended laptop should be able to fall through to
hibernate after a while, so a bag-carried machine stops draining, and setting
that up must be easy — not a systemd-sleep man-page trip.
The mechanism is `systemd`'s `suspend-then-hibernate` plus
`HibernateDelaySec`; the work is the surfacing, and the constraints are
already in the tree. Hibernate needs the resume offset + swap the installer
detects (`resumeOffset`/`rootUuid` in patch-template.py) — on a machine
without them this must self-gate, not fail at suspend time, the way Battery
limit gates on a battery. `modules/nixos/default.nix` already declines to
hyprlock before an encrypted hibernate (the LUKS resume gates it); check that
reasoning still holds when the suspend leads to hibernate. Menu placement: it
belongs with the power settings, not the root (§ menu placement convention).
Settle first (`[human]`): whether the delay is a preset list (30 min / 1 h /
2 h / never) or a free-form entry, and whether it applies on battery only or
always — the same on-AC question `modules/home/idle.nix`'s suspend listener
already answers with `${onAc} ||`.
In-flake state like the rest (`settings.*`), menu-writable, and read via
`state-read.nix` on the NixOS side — NOT `config.nomarchy.settings`,
which does not exist there (ROADMAP § *NixOS-side state bridges (#116)*; copy
the `bluetooth.enable` shape in `modules/nixos/default.nix` and add a case to
`checks.state-bridges`). Pass = the toggle survives a rebuild,
a suspended machine hibernates after the configured delay and resumes, and a
machine without hibernate support hides the row instead of offering a
suspend that never wakes.
## LATER
- **Wallpapers artifact split** (ROADMAP § Faster switches — decided,

View File

@@ -9,8 +9,39 @@ git history is the archive. Machines: the **AMD dev box** (Ryzen AI
laptop: AMD + fingerprint + NPU), the **Latitude 5310/5410** (Intel QA),
the **T14s** (webcam case).
## Suggested order (sweep 2026-07-15)
Run these first when you have a laptop session (AMD dev box unless noted):
| # | Item | Why first |
|---|------|-----------|
| 1 | **#115** suspend-then-hibernate | Just shipped; quick path is minutes (battery vs AC + `nomarchy-suspend`) |
| 2 | **#127** docked idle blackout | Real incident; capture path before it bit-rots |
| 3 | **Docking recovery round 8** + headphone jack-follow | Same dock session; relogin once, then undock ×5 + jack |
| 4 | **#104** airplane mode | Fast radio smoke after `nomarchy-home` |
| 5 | **#101** charge-limit USB-C burst | Dock plug/unplug storm; pairs with #3 |
| 6 | **Parallel fingerprint** hyprlock/greeter residual | sudo path already PASSED 2026-07-14 |
| 7 | **#95** Kitty-only on Acer | Validates terminal stack on oldest GPU |
| 8 | **#123** / **#124** install bake + flake pin | Next full reinstall window |
Everything else below stays open; order is convenience, not a gate.
## Any machine (dev box is fine)
- [ ] **#115 suspend-then-hibernate (laptop with hibernate/resume wired)** —
after system rebuild: Preferences shows **Suspend then hibernate (on)**
(hidden if `CanHibernate=no`). Confirm:
`grep -E 'HibernateDelaySec|HibernateOnACPower' /etc/systemd/sleep.conf`
`1h` / false; `grep HandleLidSwitch /etc/systemd/logind.conf`
undocked s2h, ExternalPower=suspend, Docked=ignore.
**Quick path:** on battery, `nomarchy-suspend` then
`systemctl list-jobs` / journal should show suspend-then-hibernate
(not plain suspend). On AC, same command → plain suspend.
**Full path (optional, long):** battery, lid close or idle past 15m,
leave ~1h+, open → single LUKS unlock (encrypted) or lock screen
(unencrypted), session intact. Toggle **off** + rebuild → plain
suspend only. **Pass:** battery s2h + 1h hibernate resume works;
AC never plans hibernate; no row without hibernate support.
- [ ] **#104 airplane mode radios (any laptop with Wi-Fi + BT)** —
after `nomarchy-home` + waybar restart: System **Airplane mode (off)**
(or SUPER+CTRL+R). **Pass on:** Wi-Fi and Bluetooth both drop (nm-applet /
@@ -241,13 +272,13 @@ the **T14s** (webcam case).
a no-op (charge-limit re-apply passed on `BAT0` — Latitude
2026-07-10).
- [ ] **btop theme fidelity (#52 residual)** — softGL theme-shot cannot
open Ghostty, so the hand `btop.theme` assets were only guest-file
asserted (main_bg / inactive_fg keys) + desktop/bar rendered for
rose-pine, everforest, summer-night, vantablack, catppuccin. On a
real session: `nomarchy-state-sync apply <slug>` then `btop` and
confirm backgrounds/text match the theme (esp. rose-pine Dawn light
bg `#faf4ed`, vantablack near-black `#0d0d0d`, catppuccin Mocha
`#1E1E2E`). One pass cycling those five is enough.
open a real terminal UI, so the hand `btop.theme` assets were only
guest-file asserted (main_bg / inactive_fg keys) + desktop/bar
rendered for rose-pine, everforest, summer-night, vantablack,
catppuccin. On a real session: `nomarchy-state-sync apply <slug>`
then `btop` and confirm backgrounds/text match the theme (esp.
rose-pine Dawn light bg `#faf4ed`, vantablack near-black `#0d0d0d`,
catppuccin Mocha `#1E1E2E`). One pass cycling those five is enough.
- [ ] **SSH_AUTH_SOCK for GUI clients** — after relogin, launch a GUI git
client (or `rofi`-launched terminal-less app) and confirm it reaches
gpg-agent's SSH socket without an interactive shell parent.

View File

@@ -19,6 +19,16 @@ Template:
---
## 2026-07-15 — #115 suspend-then-hibernate (1h, battery-only + menu)
- **Task:** Human settled delay=1h, battery only, menu enable/disable.
- **Did:** `nomarchy-suspend` + logind/sleep bridge from
`settings.power.suspendThenHibernate` (default on; needs resumeDevice).
Preferences toggle (rebuild). Unlock-before-encrypted-hibernate for s2h→disk.
HARDWARE-QUEUE: #115 steps + suggested-order sweep (top 8).
- **Verified:** V0/V1 `nix flake check --no-build` incl. state-bridges s2h cases.
- **Pending:** V3 bag-carry / 1h on battery (HARDWARE-QUEUE #115).
- **Next suggestion:** human runs HARDWARE-QUEUE suggested order, or #120.
## 2026-07-15 — #98 GTK dialog button black-on-dark (Boreal)
- **Task:** Bernardo: GTK dialog button labels black vs dark Boreal chrome.
- **Did:** Root cause — Stylix pins light `adw-gtk3` for all polarities.