# Hardware queue — V3 checks only a human can run

Everything shipped at V1/V2 whose final verification needs real hardware.
Agents **append** (newest at the bottom of a section) with exact steps;
Bernardo runs them and either checks off (`[x]` + date + verdict) or files
the failure as a NOW bug in BACKLOG.md. Checked-off entries are **pruned**
on the next sync sweep (LOOP.md §5) — outcomes live in the journal/ROADMAP;
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** the brick's *cause* (reopened) | Wake path fixed + fully verified 2026-07-16; cause unknown — start at the dead Ctrl+Alt+F3, not the wake path |
| 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)

- [ ] **Theme pairs V3 — the four new themes on real GL** — after pulling
      the pair commits: `nomarchy-state-sync apply <slug>` for each of
      boreal-dawn, executive-ivory, kiln, kiln-clay. Pass = all of:
      (1) the bar renders true — boreal-dawn's frosted pills actually blur
      what's behind them (softGL VM can't prove blur), kiln's plank shows
      its copper edge, no font-fallback boxes (GeistMono / JetBrainsMono /
      CaskaydiaCove); (2) wallpaper paints at native res, no banding on the
      kiln ember gradient; (3) Kitty + btop under each theme — btop colors
      match (the VM never started the GL terminal; btop.theme is
      file-asserted only); (4) boreal-dawn app launcher (SUPER+Space) lays
      out its icon grid; (5) an nm-applet/tray menu is readable under all
      four (the `*`-reset counter-rules). If a VM-rendered preview looks
      off in the theme-picker grid next to the hardware-captured ones,
      recapture that preview on hardware into themes/<slug>/preview.png.
- [ ] **Light-theme contrast refits (2026-07-18 report)** — under any
      light theme: (1) with two workspaces open, the inactive number is
      readable on the bar (subtext, not the washed-out grey); (2) hover a
      notification popup — background stays the theme's light card with a
      faint tint, text readable (the swaync default's dark
      .notification-default-action hover is now overridden); close
      button matches the palette. VM-verified with a hover probe under
      summer-day; this is the on-hardware confirmation of the report.
- [ ] **Auto-theme pair flip on hardware** — set
      `settings.autoTheme.{day,night}` to one of the new pairs (e.g.
      kiln-clay / kiln), set sunset (or sunrise) a few minutes ahead via
      Look & Feel › Auto theme (the time edit itself rebuilds — since the
      exact-time revision the timer fires AT the configured minute, no
      15-min poll), let the timer flip once each way. Pass = (1)
      `systemctl --user list-timers nomarchy-auto-theme` shows the two
      configured times as the trigger; (2) the switch lands within ~a
      minute of the configured time; (3) the whole desktop switches in
      one generation, and nothing leaks from the previous theme — fonts,
      rounding, terminal opacity all reset (the per-theme appearance
      block), wallpaper follows; (4) with autoCommit on, `git -C
      ~/.nomarchy log` shows an `apply theme` commit and state.json is
      not left dirty (the 0.5.1 pathspec fix).
- [ ] **#148 dock-intent enforcement without auto-profiles (dev box, docked)** —
      after pulling the #148 commit: **log out and back in first** — the
      watcher is `exec-once`, so `nomarchy-home` alone leaves the old one
      running. Then, docked clamshell (lid closed, panel off, dockMode set
      from the Display menu or a prior auto-dock), run `nomarchy-home` or a
      bare `hyprctl reload`. **Pass:** eDP-1 re-lights for at most ~1–2 s and
      goes back off by itself; `journalctl -t nomarchy-display-watch
      --since -5min` shows `dock-intent=true … action=re-dock`; a "Docked —"
      toast appears. Same relogin answers #149's question (does the Waybar
      unit still race Hyprland IPC on a cold relogin without linger?).
      **Fail:** the panel stays lit inside the shut lid (the #148 symptom).
- [ ] **#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 /
      blueman confirm); Waybar shows a plane glyph; click glyph or menu again
      restores **only** the radios that were on before. If Wi-Fi was already
      off, it must stay off after disengage. Glyph must **not** appear when
      airplane is off.
- [ ] **#96 battery-limit row on threshold-less firmware (Acer M5-481T)** —
      on the Acer (live or installed session with the commit carrying this
      entry): open System (`SUPER+CTRL+I`). **Pass:** a "Battery limit" row
      IS listed (battery present); picking it shows the notification
      explaining the firmware exposes no charge-stop control (not a raw
      error, and the row never silently disappears). On the T14s the row
      must still open the working preset picker.
- [ ] **#101 charge-limit service survives USB-C power-event bursts**
      (round 2) — round 1 FAILED on TuringMachine 2026-07-13 18:16: a
      *spaced* storm (each run finished before the next AC event) landed 5
      successful starts in 10s → `start-limit-hit`, unit marked failed,
      doctor badge — although every run succeeded. Fixed by exempting the
      unit from start rate limiting (`StartLimitIntervalSec=0`) + a
      spaced-storm guard in `checks.battery-charge-limit` (the commit
      carrying this entry). **Re-check:** after pulling/rebuilding this
      commit, `sudo systemctl reset-failed nomarchy-battery-charge-limit
      .service`, then plug/unplug the powered USB-C dock several times in
      quick succession. **Pass:** `systemctl is-failed
      nomarchy-battery-charge-limit.service` stays false, `Result=success`,
      BAT0's `charge_control_end_threshold` still matches the limit, and the
      journal has no new `start-limit-hit`/`Failed with result` lines.
- [ ] **Docking recovery round 8 (undock panel restore, 2026-07-14)** —
      round 7 **FAILED** on the AMD dev box (Bernardo, 5–6 consecutive
      unplugs, none recovered). Its whole diagnosis was wrong, and the way it
      was wrong is the lesson: round 6 read `result=enable-timeout` ×2 +
      `result=ok` ×1 as an intermittent race and retried the keyword. But the
      keyword is **inert, not raced** — with ZERO enabled outputs (panel
      disabled by the dock, external gone) Hyprland 0.55.4 accepts
      `keyword monitor` (prints `ok`, exits 0) and never flushes it until a
      DRM event arrives. Retrying an inert command 25×/poll for 6 polls just
      bought 30s of black screen. Every `result=ok` in that journal was
      **Bernardo plugging the cable back in** — his hotplug flushed the queued
      rule and the next poll took the credit. Probed live 2026-07-14: keyword
      inert across 4s and 5s in two runs, `dispatch forcerendererreload` also
      inert, and only `hyprctl reload` worked — 99ms and 289ms, cable out.
      The transition now escalates to `reload` when the keyword proves inert,
      re-asserts the rule, and restores per-device keyboard layouts (a reload
      drops runtime `device[…]:kb_layout` keywords).
      **Already proven on hardware** (2026-07-14, two real unplugs invoking
      the fixed `nomarchy-display-transition undock eDP-1` directly with the
      watcher paused): panel on and workspaces 1–3 home in 1.8s, journal
      `keyword=inert escalate=reload` → `enable=via-reload` → `result=ok`; and
      the keyboard-restore path with a Logitech K400 whose receiver is in the
      laptop — it kept its remembered `us` across the undock, while a control
      `hyprctl reload` with no restore dropped it to the session's `gb`
      (so `restore_keyboards` is load-bearing, not insurance).
      **What is NOT proven, and is this round:** the *watcher-driven* path
      (the running watcher calls the transition by baked store path, so it
      only picks the fix up at relogin — the round-5 trap), and repetition.
      **Re-check:** relogin (mandatory — see below), dock, then unplug the
      cable **at least five times**, plus once with the lid shut.
      **Pass:** the panel comes back every time with workspaces intact, and
      `journalctl --user
      -t nomarchy-display-transition -t nomarchy-display-watch --since
      '-10 min'` shows `result=ok` for each undock with **no**
      `result=enable-failed`. `keyword=inert escalate=reload` on every undock
      is EXPECTED — that is the fix firing, not a fault. A `result=ok`
      **without** a preceding `keyword=inert` line means the keyword flushed
      on its own and the reload was never needed: interesting, worth
      reporting, still a pass.
- [ ] **Headphone jack-follow (last open count from rounds 4–6,
      2026-07-14)** — rounds 4–6 opened five counts on the AMD dev box; four
      are now **confirmed fixed on hardware** by round 6 (Bernardo): wallpaper
      on the external, automatic dock mode, audio reaching the monitor, and
      the external keyboard prompting **exactly once** (was four times). The
      undock panel is round 7 above. This is the one count **never tested**:
      plugging headphones had stopped moving audio to them, because a pinned
      `default.configured.audio.sink` outranks the priority rules, which kills
      jack-follow on UCM cards where the headphones are their own sink (fixed
      in ce480f3's lineage, unverified). **Re-check:** docked and undocked,
      plug and unplug headphones — audio must move to them and back each time,
      and `journalctl --user -t nomarchy-dock-audio` must show `trigger=jack
      selected=…` on each plug. Worth folding into the round 7 session: the
      dock is already in hand and the two don't interfere.

      **Historical detail for the four settled counts** (kept only until
      round 7 closes; the fixes themselves are in the log):
      — round 4 was run on the AMD dev box and **failed on four counts**
      (Bernardo): no wallpaper on the external monitor, no automatic dock
      mode (System › Display had to be used by hand), audio staying on the
      laptop speakers while docked, and one dock + one external keyboard
      asking for a layout **four times**. Separately, plugging headphones had
      stopped moving audio to them. Causes and fixes in the commit carrying
      this entry: the display watcher's `socat -T 1` closed Hyprland's IPC
      socket after a second of idle, so a dock plugged into an idle desktop
      lost `monitoradded` in the reconnect gap (wallpaper + dock mode + audio
      all hang off that one event); Hyprland lists every key-capable evdev
      node as a "keyboard"; and a pinned `default.configured.audio.sink`
      outranks the priority rules, which kills jack-follow on UCM cards where
      the headphones are their own sink. **Re-run round 4 below in full**,
      plus: (a) leave the desktop untouched for ~30s, then plug the dock —
      wallpaper, dock mode, and audio must all follow with no manual step;
      (b) the external keyboard must prompt **exactly once**, and never for
      the monitor or the lid/power buttons; (c) docked and undocked, plug and
      unplug headphones — audio must move to them and back each time.
      `journalctl --user -t nomarchy-display-watch` should show
      `outputs-changed added=…`, and `-t nomarchy-dock-audio`
      `trigger=jack selected=…` on a headphone plug.

      **Round 5 was itself run and failed (2026-07-14); this entry now covers
      round 6.** Two of the four reports were never actually testing the fix:
      both watchers are Hyprland `exec-once`, so `nomarchy-home` swaps the
      config but leaves the *old* processes running — only `nomarchy-dock-audio`
      (a user service) had picked up new code. **Relogin is mandatory before
      testing either watcher**; confirm with `tr '\0' '\n' < /proc/$(pgrep -f
      bin/nomarchy-display-profile-watch)/cmdline | sed -n 2p` and grep the
      script for `outputs-changed`. The other two were real and are fixed in
      the commit carrying this entry: automatic dock mode did not exist at all
      (the watcher auto-*undocked* but only ever matched display profiles on
      plug, and this box has none saved with `displayProfileAuto` unset, so
      nothing happened — `Dock mode` was interactive-only), and the BenQ's
      audio was unreachable because WirePlumber had `alsa_card.pci-0000_c3_00.1`
      pinned to `pro-audio`, whose raw `pro-output-N` sinks have no ports and
      no routing. The BenQ's own USB card is input-only (all profiles
      `sinks: 0`), so DisplayPort is the only path to its speakers.
      **Also check:** no display profile is saved, yet plugging the BenQ must
      now dock by itself; and `journalctl --user -t nomarchy-dock-audio` shows
      `repaired-card=alsa_card.pci-0000_c3_00.1 profile=HiFi` on the first
      plug, after which audio reaches the monitor's speakers. A saved+matching
      display profile must still win over auto-dock.
- [ ] **Docking recovery round 4 (#100, closed-lid BenQ sequence)** — after
      updating to the commit carrying this entry, run `nomarchy-home` and
      relogin. With the lid open, put visible windows on at least workspaces
      1, 2, and 3 and start audio; plug the BenQ GW2790QT. **Pass on plug:**
      wallpaper appears, audio toasts and moves to a BenQ/GPU HDMI sink, and
      `journalctl --user -t nomarchy-dock-audio -n 10` shows
      `trigger=monitoradded` plus `selected=…` (not only a card event). Open
      System › Display › **Dock mode**. **Pass on dock:** every workspace and
      window is on the BenQ, focus follows, eDP is disabled in `hyprctl
      monitors all -j`, and `systemd-inhibit --list` shows Nomarchy holding
      `handle-lid-switch` for “Safe dock/undock display transition”; there is
      no dangling laptop workspace and no application exits. Close the lid;
      audio/session must stay awake. While still docked, manually choose the
      speakers and change volume/mute: it must stay on speakers. Unplug the
      BenQ **with the lid still closed**, wait 10 seconds, then open it.
      **Pass on undock:** no suspend/crash/hard reset, eDP is already active,
      workspaces 1/2/3 and their windows are intact on it, and the inhibitor
      disappears only after lid-open. `journalctl -b --since '-5 min'` must
      contain no cable-removal `systemd-logind: Suspending...`. Finally close
      the undocked lid normally: the laptop must suspend, proving policy was
      restored. Wake it, replug the BenQ: that fresh plug must move audio back
      to HDMI (manual speakers do not survive a physical replug). If any step
      fails, capture `journalctl --user -t nomarchy-display-watch -t
      nomarchy-display-transition -t nomarchy-dock-audio --since '-10 min'`,
      `systemd-inhibit --list`, `hyprctl monitors all -j`, `hyprctl workspaces
      -j`, and `pactl --format=json list sinks` before changing state again.
- [ ] **Parallel fingerprint-or-password on the real reader** (AMD dev
      box, 2026-07-12) — **sudo path PASSED 2026-07-14** (Bernardo, dev box,
      gen 422, `fingerprint.pam = true`, parallel default): `sudo -k true`
      showed ONE prompt, the typed password worked, touching the sensor
      instead worked, and wrong-finger ×3 fell back to password.
      **Still open:** hyprlock and the greeter must accept both factors and
      not wedge on a leftover prompt after a fingerprint win (known cosmetic
      quirk of the hack — pthread_cancel'd prompt); `fingerprint.parallel =
      false` must restore the old sequential behavior.
      **Do NOT "check password still works with `systemctl stop fprintd`"** —
      that check was in this item and it is a trap that cannot fail. fprintd
      is D-Bus activated, so PAM asks D-Bus and D-Bus starts it straight back
      up: Bernardo ran it and *the fingerprint still authenticated*, which is
      the tell that the daemon never stayed down. It proves nothing about a
      dead reader. The property it was reaching for is structural anyway —
      password is `auth sufficient` at order 11700, independent of the fprintd
      rule at 11400, so no fprintd failure can gate it (verify by eval:
      `nix eval --raw <flake>#nixosConfigurations.<host>.config.security.pam
      .services.sudo.text`), and checks.hardware-toggles asserts that shape in
      the VM with no reader at all. If someone genuinely wants it on hardware
      it needs `systemctl mask` + stop, and masking a working machine's
      authentication daemon to re-prove an evaluated invariant is a bad trade.
- [ ] **#55 fingerprint enroll on real reader** — with
      `nomarchy.hardware.fingerprint.enable` and a physical reader: System ›
      Fingerprint › Enroll a finger; List shows it; Verify succeeds.
      Enroll/List/Verify verified on hardware 2026-07-14 (T14s); the
      remaining unknown is a reader-less machine's self-gate.
- [ ] **fingerprint + auto-login toggles on hardware** (this commit) — on the
      T14s, after removing the pinning lines from `~/.nomarchy/system.nix`
      (`greeter.autoLogin`, `hardware.fingerprint.pam`):
      1. System › Fingerprint shows **Fingerprint (on)** (state already true).
         Toggle it off → terminal opens, sudo system rebuild + home switch →
         toast "Fingerprint off". Expect: sudo now refuses the finger and asks
         only for a password; the lock screen (SUPER+CTRL+L) shows no
         "or scan your finger" line. Toggle back on → both return.
      2. With fingerprint OFF and no finger enrolled, the toggle must REFUSE
         with "Enroll a finger first" and write nothing.
      3. System › Auto-login shows **(on)**. Toggle off → sudo rebuild →
         reboot → tuigreet asks, and accepts password OR finger (fingerprint
         on). Toggle back on → reboot → straight to the session, no prompt.
      Expected throughout: the two are independent, and auto-login on means
      no boot prompt regardless of the fingerprint switch.
- [ ] **#60 non-BAT* battery name (if available)** — on a machine whose
      system battery is **not** named `BAT*` (e.g. `CMB0`): confirm
      charge-limit oneshot writes the threshold, System › Battery limit
      and Power profile rows appear, Waybar power-profile module shows,
      doctor charge-limit section runs. On `BAT0`-only machines this is
      a no-op (charge-limit re-apply passed on `BAT0` — Latitude
      2026-07-10).
- [ ] **btop theme fidelity (#52 residual)** — softGL theme-shot cannot
      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.
- [ ] **Notification inhibited-state glyph** (item 28 color-only sweep,
      iteration #69) — have an app hold a notification inhibitor (e.g. run
      a fullscreen video, or `busctl --user call org.freedesktop.Notifications
      … Inhibit`); the Waybar bell must switch to the muted **bell-off** 󰂛
      (same glyph as DND), NOT the normal bell 󰂜/󰂚. Then release it → the
      bell returns. Repeat on a whole-swap bar (summer-day/night use their
      own bell-off glyph, executive-slate/boreal use 󰂛). The glyph itself
      already renders (DND uses it); this only confirms swaync emits the
      `inhibited-*` class and the bar routes it.
- [ ] **Keyboard layout cycle bind** — with a comma layout (e.g.
      `nomarchy.keyboard.layout = "us,de"`), SUPER+SHIFT+K cycles the
      focused keyboard's layout, the Waybar `󰌌` indicator follows, and
      the row shows in the SUPER+? cheatsheet. — 2026-07-04 attempt:
      no comma layout was configured, so the bind wasn't rendered (the
      gate working as designed, not a failure) — retest after setting a
      comma layout + rebuild + relogin. The SUPER+? no-op found en
      route was a real bug → BACKLOG item 26 (fix shipped; SUPER+?
      cheatsheet PASS on Latitude 2026-07-10).
- [ ] **swaync readable on summer-day** (item 25 fix) — on summer-day
      after `home-update`: `notify-send "title" "body text"` shows
      readable body text; open the control centre (SUPER+N), hover a
      notification row and check the Clear button — all text legible
      (body/buttons now @text on tinted chips, not subtext/surface).
- [ ] **Bar + rofi legible on flexoki-light** (item 27 fix) — switch to
      flexoki-light: every Waybar module readable (window title, tray
      row, dimmed inactive workspaces / muted volume visible-but-dim),
      rofi inputbar/alternate rows show text. Spot-check one dark theme
      (e.g. tokyo-night) for no visual regression in the same spots.
- [ ] **Stub-bar themes got the generated bar** (item 28, 90a5104) —
      switch to catppuccin, lumon, nord and retro-82: each now shows
      the generated styled bar in its own palette (workspace pill,
      padded right cluster) instead of the old raw default Waybar.
      Their rofi menus should look unchanged.
- [ ] **Back-audit spot-check** (item 24) — the keybinds cheatsheet
      (SUPER+? / root menu › Keybindings) now ends in ↩ Back and
      returns to the root picker; spot a couple of submenus (Display,
      VPN › Tailscale) still Back correctly after the audit pass.
- [ ] **Per-device keyboard menu + hotplug restore (re-verify after in-flake
      graduation)** — update to the current `main`, run `nomarchy-home`, and
      relogin with an external keyboard already connected. Open System ›
      Keyboard: both the built-in and external boards must be listed with
      their current layouts. Pick a different layout for the external board;
      it must apply only there and persist in `settings.keyboard.devices`.
      Unplug/replug it: the saved layout must restore without another prompt
      and graduate into a `device{}` block on the next rebuild. Finally plug
      a previously unseen keyboard in after login: the automatic picker must
      appear once and remember that choice too.
- [ ] **Snapshots restore + rollback exercise** (residual — the
      GUI/polkit half passed on hardware 2026-07-04, a47aa3a): in
      `sudo nomarchy-snapshots`, restore a single file (`undochange`)
      and walk a root-config rollback up to (or through) the
      typed-`yes` gate.
- [ ] **Rollback menu (item 9b)** — after a couple of theme changes,
      menu › System › Rollback: recent desktop generations listed
      (newest marked current); picking an older one opens a terminal,
      activates it, and the theme visibly reverts; picking the newer one
      again rolls forward. The two System rows: Snapshots opens the
      snapshot flow, "boot an older generation (how)" fires an
      instruction notification.
- [ ] **Open-a-file smoke (viewers + mime defaults, item 8)** — after
      `nomarchy-home`: from yazi/Thunar, open a PDF (→ zathura, themed to
      the palette), an image (→ imv, NOT GIMP), a video (→ mpv);
      `xdg-open .` on a directory → Thunar; a link → **Chromium**
      (`chromium-browser.desktop` — template default as of 2026-07-10).
- [ ] **Update awareness** — with `nomarchy.updates.enable`, let the timer
      fire (or start the unit): indicator appears only when inputs are
      behind, notification only on count growth, click opens the upgrade
      flow.
- [ ] **VPN menu live paths** — import a real WireGuard `.conf` and an
      `.ovpn` via System → VPN, toggle up/down (● / ○ state), and the
      Tailscale block: up/down + exit-node without sudo (operator grant).
      Exit-node rows now show "hostname — Country City" (2026-07-11,
      header-offset parsing): confirm Mullvad-style nodes carry their
      location, a locationless self-hosted node stays a bare hostname,
      and picking either still sets the node (`tailscale status` shows it).
- [ ] **Printer menu** — with `nomarchy.services.printing`, System →
      Printers opens system-config-printer; add a printer, test page.
- [ ] **GRUB UEFI ISO theme render** — boot the ISO on UEFI hardware:
      composed splash background, palette menu in the lower third, accent
      timeout bar.
- [ ] **Visual theme pass** — live ISO: all six identity themes (bars) +
      the four authored rofi `.rasi` (nord/retro-82/lumon/kanagawa) look
      right, not just parse.
- [ ] **Display profiles, slice a (item 15)** — declare two
      `nomarchy.displayProfiles` (e.g. docked disables eDP-1 + arranges
      the externals; undocked re-enables it), `home-update`, then:
      System › Display › Profiles lists them (● marks active); applying
      is instant (no rebuild) and survives a relogin (the baked
      overlay); "Base layout" restores; a menu resolution pick made
      earlier must NOT re-enable a profile-disabled panel after the
      next rebuild.
- [ ] **Display profiles workspace pins, slice c (item 15)** — give the
      docked profile `workspaces = { "1" = "<ext>"; "9" = "eDP-1"; }`:
      applying the profile moves open workspaces 1/9 to those outputs
      immediately, new visits land there too, and the pins survive a
      relogin (baked `workspace` rules). Switching to a profile without
      pins leaves workspaces where they are (stale session pins are
      expected until reload/rebuild — noted in the applier).
- [ ] **Display profiles auto-switch, slice b (item 15)** — same setup,
      then menu › Profiles › Auto-switch on: plugging the dock/monitor
      applies the matching profile within ~3s (toast names it);
      unplugging switches back; Auto-switch off stops that; with two
      profiles naming the same outputs, no flapping (ties do nothing).
- [ ] **P2 retunes eyeball (item 28b)** — spot-check on top of the P1
      entry below: gruvbox/nord/lumon/everforest/retro-82/white dimmed
      text (inactive workspaces, muted volume) now visible-but-dim;
      latte + rose-pine(dawn) + summer-day warn/battery tint reads on
      the light bases; flexoki-light statuses are the deeper canonical
      600s; latte selected-row/alt-accent pink darker. Anything that
      lost its "dim" feel → reopen 28b.
- [ ] **Retuned palettes eyeball (item 28b P1)** — switch through
      summer-day, flexoki-light, kanagawa, miasma: chips/menus/toasts
      now draw on a *raised* surface (summer-day's was slate-on-cream,
      kanagawa's near-black, miasma's pure black), secondary text
      (tooltips, fastfetch labels) is visible on summer-day +
      flexoki-light, kanagawa floats are lighter than the bg (upstream
      sumiInk4). Anything that reads worse than before → reopen 28b.
- [ ] **Doctor bar tripwire (LATER item)** — with everything healthy
      the bar shows nothing; `systemctl --user start doomed`-style
      induced failure → within ~5 min a red  appears (tooltip lists
      the ✖ lines), clicking opens the sheet in a terminal;
      reset-failed → it disappears on the next poll. Also visible on
      both summer bars.
- [ ] **OCR region (LATER item)** — Tools › Capture › "OCR region →
      clipboard": select a region with visible text → toast reports
      the word count and `wl-paste` yields the text; Esc in slurp
      cancels silently; a text-free region toasts "No text recognized"
      without clobbering the clipboard.
- [ ] **Look & Feel submenu (item 19)** — root menu: "Look & Feel"
      (replacing Theme, root still six rows) → Theme grid opens, Next
      wallpaper cycles instantly, Night light toggles (and is GONE
      from System). SUPER+T / SUPER+SHIFT+T direct binds unchanged.
- [ ] **Launch-or-focus (item 17)** — uncomment the template's
      `nomarchy.launchOrFocus` firefox example (with firefox
      installed), `home-update` + reload: SUPER+B launches firefox
      when closed, FOCUSES the existing window when open (also from
      another workspace); with firefox removed the bind fires a
      "not installed" toast; the row shows under SUPER+?.
- [ ] **Themed greeter + console (item 16)** — after `sys-rebuild` +
      logout: tuigreet renders in theme colors (dark container, accent
      border, themed prompt/time) and a raw tty (CTRL+ALT+F2) shows the
      theme's ANSI palette; on a LUKS machine the passphrase prompt
      follows too. Then switch theme + `sys-rebuild` + logout → the
      greeter follows the new palette. (tuigreet can't run under the
      VM harness — see MEMORY.md — so rendering is hardware-tier.)
- [ ] **Color picker (item 13, final slice)** — SUPER+CTRL+P (or Tools ›
      Color picker): hyprpicker's zoom loupe appears; click a pixel →
      toast shows the hex and `wl-paste` yields it; Esc cancels with no
      toast. Row present in the SUPER+? cheatsheet.
- [ ] **Low-battery toasts (item 13 slice)** — on battery, drain past
      25%: one "Battery low" toast; past 10%: a critical "Battery
      critical" toast that stays up until dismissed (swaync); plug in,
      drain again → it re-notifies; no repeat toasts while it just keeps
      draining. (Crossing logic VM-verified — this checks the real
      swaync rendering in a session.)

- [ ] **Audio opens in Amberol** (iteration #60, item 37) — after
      `nomarchy-home`: double-click an mp3/flac/ogg (or `xdg-open song.mp3`)
      → it opens in Amberol, not mpv; video files still open in mpv.
      (`xdg-mime query default audio/mpeg` → io.bassi.Amberol.desktop.)
- [ ] **rofi menu polish** (iteration #58, item 39 + #56/#57 34/40) —
      after `nomarchy-home`: (a) menu › System › Power profile shows a
      colored icon per profile (performance/balanced/power-saver), not
      just text — icons *render* (not blank); (b) every submenu's ↩ Back
      shows a single arrow, no double; (c) on this single-monitor box,
      System › Display → pick a resolution → Back returns to System
      (not back into the same resolution list).
- [ ] **Screenshot annotation (satty)** (iteration #73) — after `nomarchy-home`:
      hit SUPER+SHIFT+Print (or Tools ▸ Capture ▸ Annotate region) → a region
      select (slurp) appears, then the `satty` UI opens in fullscreen with the
      screenshot loaded. Check that the UI draws on the current theme palette
      (tools colored properly) and hitting save places the screenshot in
      `~/Pictures/Screenshots/` while hitting copy places it in the clipboard.
- [ ] **#76 no-swap Hibernate notify** (#76 itself is closed — the
      hibernate→resume power-cycle **PASSED on TuringMachine 2026-07-12**:
      Bernardo ran the full hibernate → power off → single LUKS unlock →
      session-restored cycle "flawlessly"; this notify check is the one
      remaining leftover) — on a **Nomarchy** machine installed
      with `swap = 0` (or temporarily `sudo swapoff -a` on one): `nomarchy-menu
      → Power → Hibernate` must surface a desktop notification ("Couldn't
      hibernate — likely no swap is configured. See docs/MIGRATION.md →
      Enabling hibernation."), **not** a silent no-op. (`swapon` after, if you
      swapoff'd for the test.)
- [ ] **Night-light geo mode (2026-07-11)** — in `home.nix` set
      `nomarchy.nightlight = { enable = true; latitude = "<lat>";
      longitude = "<long>"; }` (your real coordinates), `nomarchy-home`,
      relogin. Then: `systemctl --user status wlsunset` is running (and
      `hyprsunset` is NOT); after local sunset the screen visibly warms
      (or test by setting coordinates where it's currently night); the
      Waybar moon + menu toggle still flip it instantly and an *off*
      survives relogin (the ExecCondition gate on the swapped unit —
      the eval check proves the wiring, not the runtime gate). **Pass** =
      warm shift at the location's night + toggle/persistence intact.
- [ ] **#103 live-ISO baseline apps actually launch** (this commit) — the
      half no headless check can reach: `checks.live-baseline-apps` proves the
      binaries and `.desktop` entries are in the exact HM generation the ISO
      ships, but not that a GUI app opens on real hardware (the agent may not
      drive a graphical VM, and the live ISO has no SSH to script one). On the
      **Acer M5-481T** (the machine that found this — its GPU is the oldest
      shipped, so it is the honest test), boot the new live ISO and, from the
      launcher only — no terminal, that's the point: open **Chromium**,
      **LibreOffice** (Writer), **Text Editor**, **Amberol**, **Snapshot**.
      **Pass** = all five are *listed in the launcher* and each opens a window.
      Also: a `.html` file opens in Chromium (the HTTPS mime default now names
      a browser that is present — #94's exact bug), and a `.txt` file opens in
      **Text Editor** (`text/plain` falls through to
      `org.gnome.TextEditor.desktop` after #119; vscode remains preferred on
      template installs). Firefox is deliberately absent; its absence is
      correct, not a miss.
- [ ] **#123 install bake: first boot fully themed without manual HM switch**
      — re-install from a main ISO (this commit+) on **either** the Acer
      M5-481T or the Dell XPS 9350. **Pass:** install ends with "Desktop
      pre-activated", no `dconf-CRITICAL` in
      `/var/log/nomarchy-hm-preactivate.log`, first graphical login has
      Stylix GTK + nm-applet **without** hand `home-manager switch`.
- [ ] **#95 Kitty-only terminal on Acer M5-481T** — after rebuild/install
      from main: SUPER+Return opens **themed Kitty**; System › Doctor opens
      a floating classed Kitty window. No Ghostty on PATH required.
- [ ] **#124 flake pin after main-ISO install** — installed machine’s
      `~/.nomarchy/flake.nix` has `?ref=main` (not lagging v1). **Pass:**
      `nomarchy-rebuild` does not die with
      `The option 'nomarchy.hardware' does not exist`.
- [ ] **Relogin session recovery (2026-07-18 incident)** — after pulling
      the session-recovery commit and rebuilding: from a full Hyprland
      session (bar up, a browser open so easyeffects/tray are busy), log
      out (SUPER+SHIFT+E) and log straight back in at the greeter — do
      **not** reboot between. **Pass:** `nomarchy-doctor` shows *no failed
      user units*; `systemctl --user status cliphist swaync
      xdg-desktop-portal-hyprland swayosd` are all `active (running)`; the
      bar, notifications, and clipboard history (SUPER+V) work. This is
      the V3 close for the stale `graphical-session.target` /
      `start-limit-hit` relogin breakage (BACKLOG #149 note).
- [ ] **#150 hypridle wake path now uses the FULL undock** — after pulling
      the #150 commit + `nomarchy-home`: the hyprland-side transition/keyboard
      tools are byte-identical (proven by store-path equality), so only the
      hypridle wake behaviour changed. Two checks: **(a) suspend/resume while
      docked** (lid closed, external on) — suspend, resume; pass = you land
      back docked on the external with the panel still off and remembered
      external-keyboard layouts intact (the wake now runs the full undock,
      which `restore_keyboards` after any reload; the old mini did not). **(b)
      zero-output rescue** — the #127 case (dock mode, external gone/black,
      eDP disabled): pass = the internal panel comes back lit, workspaces move
      to it, and `~/nomarchy-display-dump-*.txt` is written iff it stays dark.
      Watch for a regression: the full undock clears dock-intent, so confirm a
      *transient* zero-output blip on resume while genuinely still docked does
      not leave you undocked with the laptop panel on (the external re-add
      should re-dock via the watcher — verify it does).

## AMD dev box only
- [ ] **#118 smartd still runs where drives DO have SMART** (this commit) — the
      half a VM cannot answer: QEMU exposes no SMART, so `checks.smartd-gate`
      proves the skip but has to drive the *with-device* path through a stub.
      On the dev box (real NVMe), after `nomarchy-rebuild` + reboot:
      `systemctl status smartd` is **active/running**, and
      `systemctl show -p ExecCondition --value smartd` names
      `smartd-any-smart-device`. **Pass** = smartd is running, exactly as
      before this commit — i.e. the gate skips nothing on real hardware.
      **Fail** = inactive/skipped, which would mean the gate is silently
      disabling drive-health monitoring: revert it, don't tune it.
      Cheap bonus while you are there: `nomarchy-doctor` reports no failed
      units (the red-icon symptom that started #118).
- [ ] **AMD runtime bits** — VA-API (`vainfo` → radeonsi), amd-pstate EPP
      active and PPD switching governors; opt-ins: ROCm (`rocminfo`, a GPU
      PyTorch/Ollama smoke) and the XDNA NPU driver loading.
- [ ] **Fingerprint** — `fprintd-enroll` + (opt-in PAM) login/sudo.
- [ ] **#137/#145 splash on a real docked boot** — the one thing the render rig
      cannot do: its fake heads exist from the start, so the canvas never
      resizes, which *is* the bug. **Docked**, reboot: pass = the logo is centred
      on **both** panels at boot **and** at shutdown (it was off-centre on the
      external), and the LUKS prompt shows the padlock, the entry, and beneath it
      a keyboard icon + your layout (`us`). Then reboot **undocked** — unchanged.
      Rendering is already proven (`tools/plymouth-preview.sh`, both heads +
      `ask-for-password`), so what is unproven is only a canvas that changes
      under a real DRM boot. If the splash is ever blank, the passphrase prompt
      is invisible but still live — type it blind, or pick the previous
      generation in the boot menu, which carries the old theme.
- [ ] **#142 dock mode survives a rebuild** — **relogin first** (the watcher is
      `exec-once`; `nomarchy-home` alone leaves the old one running and you
      would be testing nothing — round 6's lesson). Then, docked: run
      `nomarchy-home`. Pass = the laptop panel stays off, no workspace lands on
      it, no menu trip; `journalctl --user -t nomarchy-display-watch` shows
      `dock-intent=true panel=eDP-1 state=re-enabled action=re-dock` (a rebuild
      re-lights the panel and the watcher takes it back within ~1s, so a brief
      flicker is expected — the panel staying *up* is the failure).
      Then the three gates that must NOT fire, since this code can disable a
      panel: (a) Menu ▸ Display ▸ **Screen on** → the panel stays on (intent
      cleared; if it snaps off again within a second, that is the bug);
      (b) **undock** → panel returns and stays; (c) boot/relogin **undocked**
      with `settings.display.dockMode` still `true` in state.json → panel stays
      on (no external ⇒ never re-dock). (c) is the one that matters: it is the
      #127 brick if it is wrong. Gates are unit-tested against stubs, but only
      hardware proves the wiring.
- [ ] **#138 dock audio does not break a running browser** — the plug event
      itself is the only thing V2 could not do (the tool was driven by hand;
      Hyprland calls the same entry point). **Relogin first** — the watcher is
      `exec-once`, so `nomarchy-home` alone leaves the old process running and
      you would be testing nothing (round 6's lesson). Then, with Chromium
      **already open** on a Meet call: dock/undock. Pass = output still follows
      to the monitor, **Meet keeps its mic and speakers without restarting the
      browser**, and `journalctl --user -t nomarchy-dock-audio` shows
      `selected=…` with **no** `action=graph-restart-fallback`, while
      `systemctl --user show -p MainPID --value pipewire.service` is unchanged
      across the plug. If a `graph-restart-fallback` line does appear, rung 3
      fired on a healthy plug — that is a bug, not the recovery working: file
      it with the surrounding journal.
- [ ] **System ▸ Firmware menu on real LVFS hardware** (item #43,
      `nomarchy-menu firmware`) — on a machine whose firmware/SSD/dock is
      on LVFS: open Menu ▸ System ▸ **Firmware**; confirm the terminal runs
      `fwupdmgr refresh` → lists real `get-updates` → the y/N confirm gates
      correctly → `fwupdmgr update` applies and prints the reboot note when a
      capsule needs one. Verify **no** flash happens on "N"/cancel. (VM only
      proves the menu row + flow renders; a real capsule write is
      hardware-only.)

## Acer Aspire M5-481T only (1366×768 — the narrow-panel case)
- [ ] **#139 sheets on a small panel** — Waybar clock → calendar, System ▸
      Doctor, and the Waybar updates click. Pass = each opens floating and
      centred at roughly 60%×65% / 55%×70% / 45%×50% of *that* screen (they are
      computed from the focused monitor now, so this is checking the fallback
      path and the font, not the arithmetic), fully on-screen, none clipped by
      the bar. Dev box (2560×1440) already measured exact.
- [ ] **#131 menu width on the real narrow panel** — open Menu ▸ Recovery.
      Pass = every label complete (no ellipsis) and the picker visibly *not*
      hogging the screen (≤65%, the cap). The geometry was already reproduced
      pixel-exactly on the dev box (888px + JetBrainsMono 14 = what 65% of 1366
      produces) and passed, so what is genuinely unproven here is only the
      Acer's own fontconfig/DPI resolving `ch` the same way — try it under a
      **JetBrainsMono 14 theme** (summer-day/night, kanagawa), the widest case.

- [ ] **Theme-switch bar round-trip (2026-07-19 waybar-poke + gtk-pin fix)** —
      after `nomarchy-pull` + `nomarchy-home`, switch dark → light → dark
      (e.g. boreal → kiln-clay → kiln). Pass = the bar visibly restarts on
      each switch and the workspace digits land per theme (dark-on-accent
      active pill, dimmed inactives on dark themes) with no color carried
      over from the previous theme; then start/stop a screen recording and
      toggle airplane mode — the supervisor must survive both pokes
      (`pgrep -lx nomarchy-waybar` still lists it).

## Latitude 5310 / 5410 only
- [ ] **v1 QA batch on-hardware pass** (583708d batch was QEMU-verified) —
      general smoke before the next `main → v1` promotion (broader than
      the 2026-07-10 session; include theme/ISO/greeter leftovers).

## T14s only
- [ ] **Webcam IR-hide end-to-end on Nomarchy** — installer detects the
      RGB+IR pair, bakes `hardware.camera.hideIrSensor`; `wpctl status`
      shows one colour source; an app picker lists one camera; Howdy-style
      direct `/dev/video2` reads still work.
- [ ] **Portal/Flatpak libcamera IR (b)/(c)** — after #71 docs: on hardware,
      confirm a Flatpak/portal picker still lists the internal IR node;
      only then investigate (b) WirePlumber libcamera GREY-only monitor
      rule or (c) libcamera/udev-layer hide. Do not ship either without a
      live dual-sensor check (see HARDWARE.md §7 / ROADMAP § Webcam).
