# 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). ## Any machine (dev box is fine) - [ ] **#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 7 (undock panel restore, 2026-07-14)** — round 6 (AMD dev box, Bernardo) **passed on the plug side**: the dock docked by itself, audio went straight to the monitor, and the wallpaper painted on the external. One count failed: **unplugging the cable left the laptop panel black**, recoverable only by re-docking. The journal named the cause exactly — across three undocks, two logged `transition=undock internal=eDP-1 result=enable-timeout` and one `result=ok`, i.e. an intermittent race, not a logic error. Both halves are fixed in the commit carrying this entry: `hyprctl keyword monitor eDP-1,preferred,auto,1` is *accepted* (exit 0) while Hyprland is still tearing down the departing external and then silently dropped, so the transition now re-issues it on every poll for 5s rather than trusting one shot; and the watcher no longer treats a failed undock as final — the departure is queued and retried on the 1s tick (up to 6 attempts) instead of being marked handled with `|| true`, which is what turned one lost keyword into a panel dark until re-dock. **The VM harness cannot reach this** (`tools/monitor-fallback.nix`: QEMU's headless backend aborts Hyprland if the last active output is deleted while the DRM output is disabled, so the test must undock *before* removing DP-1 and never collides the two) — hardware is the only place it can be proven. **Re-check:** relogin (mandatory — see below), dock, then unplug the cable **at least five times**, since the old failure only hit ~2 of 3. **Pass:** the panel comes back every single time with workspaces intact, and `journalctl --user -t nomarchy-display-transition -t nomarchy-display-watch --since '-10 min'` shows a `transition=undock result=ok attempts=N` for each undock and **never** `result=gave-up`. `attempts=2` or more is the retry doing its job and is a pass; `gave-up` means the panel could not be enabled at all and is a different fault — capture the artifacts listed in round 4 before changing state. - [ ] **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) — with a finger enrolled and `fingerprint.pam = true` (parallel is the default): `sudo -k true` must show ONE prompt ("Enter Password or Place finger…"); typing the password immediately works, touching the sensor instead works, wrong-finger ×3 falls back to password, and password keeps working with fprintd stopped (`systemctl stop fprintd`). Also check hyprlock and the greeter accept both factors and don't 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. VM already asserts the PAM stack shape + password-only lockout safety with no reader (checks.hardware-toggles). - [ ] **#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; optional Use for login (on) → `nomarchy-rebuild` → login/sudo accept fingerprint. Menu surface is V1/V2 without a reader (self-gate + dry paths). - [ ] **#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 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-theme-sync apply ` 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" = ""; "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 = ""; longitude = ""; }` (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. ## AMD dev box only - [ ] **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. - [ ] **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.) ## 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).