Files
Nomarchy/agent/HARDWARE-QUEUE.md
Bernardo Magri 8e8a142465
All checks were successful
Check / eval (push) Successful in 4m9s
fix(themes): light themes — readable inactive workspaces + notification hover
Two hardware-reported contrast bugs, both the same family: surfaces
that fell back to something designed on dark.

1. Inactive workspace numbers unreadable on every light theme.
   - Generated bar: the @muted dim role is floor-gated at only 2.0:1 —
     fine as "dim" on dark, washed out at number size on light. Light
     mode now promotes inactive buttons to @subtext (3.0 floor,
     typically 4-7:1); dark themes keep @muted.
   - boreal-dawn + kiln-clay whole-swaps: same @muted -> @subtext
     (6.4:1 / 6.6:1).
   - summer-day whole-swap: had NO button color rule at all — the
     container's @fg does not inherit into GTK buttons, so the numbers
     took the GTK default over the cream chip. Explicit @fg_dim
     (= the palette's subtext, 4.0:1) added.
   - executive-ivory already used @subtext; untouched.

2. Notification popup unreadable on hover (bg turns dark, text stays
   dark). Root cause found by probing, not guessing: the popup body is
   one big GTK button (.notification-default-action) and swaync's
   DEFAULT stylesheet — still loaded under our style.css — hovers it to
   rgb(56,56,56). Our sheet never overrode that node (only the
   control-center row), so the default bled through; on light themes
   dark @text sat on it. Now the body + action buttons + close button
   are re-pinned to the palette's alpha(@text) tint construction —
   mode-proof by construction. The first fix attempt (row-level hover)
   was probed, SHOWN insufficient, and replaced.

Verification: V2. nix flake check --no-build, theme-wholeswap,
theme-contrast green. §3 evidence viewed: a purpose-built VM hover
probe under summer-day (notify-send -u critical, cursor parked ON the
popup, screenshot) — before: dark card, drowned text (reproduces the
report exactly, /nix/store/q1qf67fl...notify-shot-summer-day); after:
cream card + faint tint, fully readable (shot-notify2). Bar renders
verified under catppuccin-latte (generated light path incl. the mode
branch) and summer-day (edited whole-swap). An inactive-number visual
needs two live workspaces — VM shows one — so the on-hardware
confirmation is queued (HARDWARE-QUEUE light-theme refits entry);
the ratio change itself is machine-checked.

Bookkeeping riding along: BACKLOG #152 deleted (shipped in d79ad35).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 13:27:43 +01:00

617 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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), force sunrise/sunset around the current time,
let the timer flip once each way. Pass = 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.
- [ ] **#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 ~12 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, 56 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 13 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 46,
2026-07-14)** — rounds 46 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 machines
`~/.nomarchy/flake.nix` has `?ref=main` (not lagging v1). **Pass:**
`nomarchy-rebuild` does not die with
`The option 'nomarchy.hardware' does not exist`.
## 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.
## 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).