diff --git a/README.md b/README.md index c92a53a..e590a0c 100644 --- a/README.md +++ b/README.md @@ -257,7 +257,7 @@ two tables below are split along exactly that line. | `nomarchy.swaync.enable` | `true` | swaync notifications, themed | | `nomarchy.batteryNotify.enable` | `true` | Low-battery toasts at the bar's thresholds — 25% low, 10% critical (stays up until dismissed); silent no-op on machines without a battery | | `nomarchy.dockAudio.enable` | `true` | On a fresh external-monitor plug, PipeWire/WirePlumber are reprobed after hardware settles and the highest-priority available HDMI/DP/USB sink becomes default, with a toast and journal result. A later manual speaker choice sticks until the next physical plug; unplug falls back to built-in. Manual route: System › Audio | -| `nomarchy.firstBootWelcome.enable` | `true` | One dismissible “you're set” toast on the first session (SUPER+M / SUPER+T / SUPER+? + network pointer); marker is `settings.firstBootShown` in the flake checkout | +| `nomarchy.firstBootWelcome.enable` | `true` | One dismissible “you're set” toast on the first session (SUPER+M / SUPER+T / SUPER+? + network pointer); marker is `settings.firstBootShown` in the flake checkout. Also fires at most one follow-up “Hardware tips” toast pointing at System › Firmware / Fingerprint when `fwupdmgr` / `fprintd-list` are on PATH (`settings.hardwareHintsShown`) | | `nomarchy.idle.enable` | `true` | hyprlock + hypridle (lock 5 min; display off 10 min in every dock/lid state — input wakes it compositor-side, #127; suspend 15 min battery-only via `nomarchy-suspend`) | | `nomarchy.idle.fingerprint` | `false` | Unlock the lock screen with a fingerprint as well as the password, and say so on the input field. Set it alongside `nomarchy.hardware.fingerprint.pam` in system.nix: hyprlock is configured from Home Manager, which cannot read the NixOS option — and hyprlock does **not** take a fingerprint through PAM at all (its PAM stack runs only on submit), so it uses its own fprintd backend that this switch turns on | | `nomarchy.yazi.enable` | `true` | yazi TUI file manager, themed + curated plugins | diff --git a/agent/BACKLOG.md b/agent/BACKLOG.md index 549f596..4fc65be 100644 --- a/agent/BACKLOG.md +++ b/agent/BACKLOG.md @@ -316,17 +316,6 @@ offline ISO's behaviour untouched (`checks.*` for the offline path stay green). 2026-07-10). Keep #59 commented install guidance; no `nomarchy.hardware.nvidia.*` until a hybrid maintainer + queue. -- **Post-install hardware hints** (`VISION § B`) — After the general - “you're set” card (#81), optionally fire **one** additional - self-gated notify when the machine actually has the hardware: - (a) `fwupdmgr` on PATH → “System › Firmware to check LVFS updates”; - (b) `fprintd-list` on PATH → “System › Fingerprint to enroll”. - One-shot markers in `settings.*` (same in-checkout discipline as - `firstBootShown`); never a permanent MOTD nag. Cost: small — extend - `nomarchy-first-boot` or a sibling oneshot + `checks.first-boot` - fixture. Control-center / MOTD already mention these; the gap is the - silent first *graphical* session for people who never open those. - _(#80–#83 + #85–#88 shipped 2026-07-11. Theme A day-2 + neon-glass finish shipped — VISION ✓. Dock/hibernate V3 → HARDWARE-QUEUE. Parallel fingerprint-or-password shipped 2026-07-12 (Bernardo promoted it live; @@ -335,9 +324,9 @@ _(#80–#83 + #85–#88 shipped 2026-07-11. Theme A day-2 + neon-glass finish ### v1.0 pointer -See **VISION**. Open PROPOSED: post-install hardware hints; NVIDIA -deferred past v1; IR portal (b)/(c) need T14s (HARDWARE-QUEUE § T14s). -Standing calls: browser = Chromium; power = PPD. +See **VISION**. Open PROPOSED: NVIDIA deferred past v1; IR portal (b)/(c) +need T14s (HARDWARE-QUEUE § T14s). Standing calls: browser = Chromium; +power = PPD. (Post-install hardware hints shipped 2026-07-17.) ## Decisions `[human]` diff --git a/agent/JOURNAL.md b/agent/JOURNAL.md index 375c1ae..6fac691 100644 --- a/agent/JOURNAL.md +++ b/agent/JOURNAL.md @@ -19,6 +19,22 @@ Template: --- +## 2026-07-17 — Hardware hints: the second (and last) first-session toast +- **Task:** PROPOSED "Post-install hardware hints" (VISION § B), promoted by + Bernardo in-session. +- **Did:** `nomarchy-first-boot` grew a hints stage after the card: + self-gated Firmware/Fingerprint lines (fwupdmgr / fprintd-list on PATH), + one toast, marker `settings.hardwareHintsShown`; marker deliberately unset + while no tooling matches so late-appearing hardware still gets its hint. + README row, HARDWARE.md hint paragraphs, VISION § B checked off. + Implementation by a Sonnet agent from my design; review here. +- **Verified:** V2 — shellcheck clean, flake check green, + `checks.first-boot` green (bare-node silent → fwupdmgr appears → fires + once → silent → live-ISO skip). +- **Pending:** real toast rendering is session-surface, same class as the + proven #81 card; it will fire once for real on the next fresh session. +- **Next suggestion:** #127 close-out (upstream filing pending gh auth). + ## 2026-07-17 — #148: dock intent now enforced without the auto-profiles toggle - **Task:** BACKLOG #148 (promoted from PROPOSED by Bernardo in-session). - **Did:** one-line fix — `enforce_dock_intent` dropped its `auto_on || return` diff --git a/docs/HARDWARE.md b/docs/HARDWARE.md index 8139ddc..6b97438 100644 --- a/docs/HARDWARE.md +++ b/docs/HARDWARE.md @@ -138,7 +138,10 @@ confirms each device and prompts for the reboot a capsule needs; pillar 1 **Hints (#43 / #73):** MOTD cheat-sheet line when `fwupdmgr` is on PATH. Fingerprint / doctor tips follow the same pattern (fingerprint MOTD only -when `nomarchy.hardware.fingerprint.enable`). +when `nomarchy.hardware.fingerprint.enable`). A one-shot toast in the +first graphical session also points at System › Firmware when `fwupdmgr` +is on PATH (`settings.hardwareHintsShown`, `nomarchy-first-boot`) — for +people who never read the MOTD. **Still queued:** a Doctor “updates available” check, and Waybar/updates-panel integration. @@ -191,7 +194,10 @@ happen — sudo, the lock screen, and the greeter only when **Hints (#73):** MOTD line when `fingerprint.enable` is on; first-boot tip when `fprintd-list` is on PATH (`SUPER+M → System › Fingerprint` / -`fprintd-enroll`). No permanent nag without a reader. +`fprintd-enroll`). No permanent nag without a reader. A one-shot toast in +the first graphical session also points at System › Fingerprint when +`fprintd-list` is on PATH (`settings.hardwareHintsShown`, +`nomarchy-first-boot`) — for people who never read the MOTD. ```sh # CLI still works: diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 629dd5e..f489eb9 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -493,6 +493,17 @@ Design/decision records and a running log of shipped work (items marked V2 by render, both heads + the password dialog, via the new `tools/plymouth-preview.sh`. V3 (a real docked boot/shutdown) is queued: the fake heads exist from the start, so the rig cannot resize a canvas. +- ✓ **Post-install hardware hints (VISION § B, 2026-07-17):** after the #81 + "you're set" card, at most one additional "Hardware tips" toast — a + Firmware line when `fwupdmgr` is on PATH, a Fingerprint line when + `fprintd-list` is — with its own one-shot marker + (`settings.hardwareHintsShown`, in-checkout like `firstBootShown`). If no + tooling matches, the marker is deliberately left unset so the hint still + fires once if fwupd/a reader appears later. Lives inside + `nomarchy-first-boot` (the card's exit-1-retries-next-login semantics are + preserved; hints only run once the card has landed). V2: + `checks.first-boot` grew the silent-bare-node → fwupdmgr-appears → + fires-once → silent-again → live-ISO-still-skips flow. - ✓ **Reload re-lit the panel inside a shut lid (#148, 2026-07-17):** every `nomarchy-home` (HM activation → `hyprctl reload`) re-applied the catch-all monitor rule and left eDP-1 burning in the closed clamshell. The #142 tick diff --git a/docs/VISION.md b/docs/VISION.md index bcee40b..ea126e2 100644 --- a/docs/VISION.md +++ b/docs/VISION.md @@ -74,7 +74,7 @@ Reference: [HARDWARE.md](HARDWARE.md) §4–§5, §11 (doctor). | **Silent first-week card** ✓ | One dismissible “you’re set” (menu, themes, keys, network) — not a wizard | ✓ shipped #81 — `settings.firstBootShown` + notify; live ISO keeps its own toast | | **Boreal as default** ✓ | Identity on first boot | ✓ seed `state.json` + install path use Boreal (2026-07-09) | | **Generation readability** ✓ | “What changed last rebuild” in plain language | ✓ shipped #82 — nvd toast + System › What changed? | -| **Post-install hints** | One-shot MOTD/notify for fwupd / fprintd when relevant | Partial: MOTD tips; first-session toast is #81; still refine self-gated hardware hints if needed | +| **Post-install hints** ✓ | One-shot MOTD/notify for fwupd / fprintd when relevant | ✓ shipped 2026-07-17 — self-gated “Hardware tips” toast after the #81 card (`settings.hardwareHintsShown`), fwupd/fprintd lines when the tooling is on PATH | --- @@ -154,13 +154,11 @@ From GOALS non-goals and installer audits — do not “fill the roadmap” with **Still open** (as of 2026-07-11). Shipped items above are *not* open work. -1. `VISION § B` — post-install hardware hints (self-gated first-session - toast when `fwupdmgr` / `fprintd-list` exist; one-shot markers; no nag) -2. `VISION § D` / LATER — wallpapers artifact split (decided deferred; +1. `VISION § D` / LATER — wallpapers artifact split (decided deferred; promote only if theme-switch latency still hurts after measurement) -3. `VISION § H` — HARDWARE-QUEUE burn-down (human-only V3; agents only +2. `VISION § H` — HARDWARE-QUEUE burn-down (human-only V3; agents only append exact steps) -4. `VISION § v1.0` — install P0 re-verify before any `v1` fast-forward +3. `VISION § v1.0` — install P0 re-verify before any `v1` fast-forward (swap=0 / unattended LUKS contracts; human ships the pointer) Do **not** re-open Theme A firmware/fingerprint/doctor/rebuild rows — diff --git a/flake.nix b/flake.nix index 51544fb..a032bf9 100644 --- a/flake.nix +++ b/flake.nix @@ -1773,6 +1773,9 @@ # theme-sync, silent on re-run; skips hostname nomarchy-live. # notify-send + state-sync shimmed on PATH (same pattern as # battery-notify). Real toast rendering stays session/V3. + # Also covers the hardware-hints stage (VISION § B): silent when + # no gated tooling is on PATH, fires once when it appears + # (fwupdmgr shimmed here), silent again after. first-boot = pkgs.testers.runNixOSTest { name = "nomarchy-first-boot"; nodes.machine = { ... }: { @@ -1803,23 +1806,57 @@ "HOME=/root" ) - # First run: toast + marker. + # First run: toast + marker. Node has neither fwupdmgr nor + # fprintd-list, so the hardware-hints stage (VISION § B) + # must stay silent and must NOT set its marker (cheap + # re-check each session — see the script comment). machine.succeed(f"{env} nomarchy-first-boot") out = machine.succeed("cat /tmp/notifications") assert "You're set" in out, f"welcome toast missing:\n{out}" assert "SUPER+M" in out and "SUPER+T" in out, f"key pointers missing:\n{out}" assert "Network" in out or "Wi" in out, f"network pointer missing:\n{out}" + assert "Hardware tips" not in out, f"unwanted hint toast:\n{out}" shown = machine.succeed( f"{env} nomarchy-state-sync get settings.firstBootShown" ).strip() assert shown == "true", f"marker not written: {shown!r}" + hints_marker = machine.succeed( + f"{env} nomarchy-state-sync get settings.hardwareHintsShown 2>/dev/null || true" + ).strip() + assert hints_marker != "true", f"hints marker set too early: {hints_marker!r}" - # Second run: silent (no second toast). + # Second run: silent (no second toast, still no matching + # hardware/tooling). machine.succeed("rm -f /tmp/notifications") machine.succeed(f"{env} nomarchy-first-boot") machine.fail("test -s /tmp/notifications") - # Live ISO hostname: skip even with marker cleared. + # fwupdmgr appears on PATH (e.g. fwupd enabled later): the + # hints stage fires once, mentioning Firmware, and sets its + # own marker without re-touching firstBootShown. + machine.succeed( + "printf '#!/bin/sh\\nexit 0\\n' > /shim/fwupdmgr && " + "chmod +x /shim/fwupdmgr" + ) + machine.succeed(f"{env} nomarchy-first-boot") + out = machine.succeed("cat /tmp/notifications") + assert "Hardware tips" in out, f"hint toast missing:\n{out}" + assert "Firmware" in out, f"firmware hint missing:\n{out}" + hints_marker = machine.succeed( + f"{env} nomarchy-state-sync get settings.hardwareHintsShown" + ).strip() + assert hints_marker == "true", f"hints marker not written: {hints_marker!r}" + + # Run again: silent (hints marker now set). + machine.succeed("rm -f /tmp/notifications") + machine.succeed(f"{env} nomarchy-first-boot") + machine.fail("test -s /tmp/notifications") + + # Live ISO hostname: skip even with markers cleared — + # covers both the welcome card and the hardware-hints stage. + machine.succeed( + f"{env} nomarchy-state-sync --quiet set settings.hardwareHintsShown false --no-switch" + ) machine.succeed( f"{env} nomarchy-state-sync --quiet set settings.firstBootShown false --no-switch" ) diff --git a/pkgs/nomarchy-first-boot/nomarchy-first-boot.sh b/pkgs/nomarchy-first-boot/nomarchy-first-boot.sh index 5c76ec6..272e46c 100644 --- a/pkgs/nomarchy-first-boot/nomarchy-first-boot.sh +++ b/pkgs/nomarchy-first-boot/nomarchy-first-boot.sh @@ -1,53 +1,117 @@ -# nomarchy-first-boot — one-shot "you're set" toast on the first session. -# Marker is settings.firstBootShown in the flake's state.json -# (in-checkout state; never ~/.local/state). notify-send and -# nomarchy-state-sync come from PATH so the VM check can shim them. +# nomarchy-first-boot — one-shot "you're set" toast on the first session, +# plus a second self-gated toast (hardware hints, VISION § B) pointing at +# hardware-specific menu items when the matching tooling is on PATH. +# Markers are settings.firstBootShown / settings.hardwareHintsShown in the +# flake's state.json (in-checkout state; never ~/.local/state). notify-send +# and nomarchy-state-sync come from PATH so the VM check can shim them. # Live ISO already has its own welcome (hosts/live.nix); skip there so # users aren't double-toasted and the live seed doesn't get a sticky -# firstBootShown write every boot. +# firstBootShown write every boot. This also covers the hints stage below. # uname -n is coreutils; avoid depending on a separate hostname package. hn=$(uname -n 2>/dev/null || true) if [ "$hn" = nomarchy-live ]; then exit 0 fi -# Already shown → silent. Missing key / no checkout → treat as not shown -# (state-sync get exits non-zero when the key is absent). +# Already shown → skip straight to the hardware-hints stage below. Missing +# key / no checkout → treat as not shown (state-sync get exits non-zero +# when the key is absent). shown=$(nomarchy-state-sync get settings.firstBootShown 2>/dev/null || true) +just_shown= case "$shown" in - true|1|yes) exit 0 ;; -esac - -# Wait for the notification daemon (swaync). On first login the unit can -# race graphical-session and get "Timeout was reached" from D-Bus — then -# either no toast, or a toast that never lands while we still write the -# marker. Retry notify-send; only persist the marker after a success. -body="SUPER+M menu · SUPER+T themes · SUPER+? keys + true|1|yes) : ;; + *) + # Wait for the notification daemon (swaync). On first login the unit + # can race graphical-session and get "Timeout was reached" from + # D-Bus — then either no toast, or a toast that never lands while we + # still write the marker. Retry notify-send; only persist the marker + # after a success. + body="SUPER+M menu · SUPER+T themes · SUPER+? keys Wi‑Fi: System › Network (or the bar tray) Anything off? System › Doctor" -ok= + ok= + i=0 + while [ "$i" -lt 8 ]; do + if notify-send -a Nomarchy -u normal -t 0 \ + "You're set" \ + "$body"; then + ok=1 + break + fi + i=$((i + 1)) + sleep 2 + done + + if [ -z "$ok" ]; then + # Leave firstBootShown alone so the next login can try again. Don't + # attempt hardware hints either — they're gated on the card landing. + exit 1 + fi + + # Persist in the checkout so re-login is silent. --no-switch: marker + # only. + if ! nomarchy-state-sync --quiet set settings.firstBootShown true --no-switch; then + # No writable flake checkout (or tool missing) — still showed the + # toast; without a marker it may reappear next login. Don't fail + # the unit, and skip hints too (state-sync isn't writable anyway). + exit 0 + fi + just_shown=1 + ;; +esac + +# --- hardware hints (post-install hints, VISION § B) ---------------------- +# At most one additional self-gated toast, once per machine, pointing at +# hardware-specific menu items when the matching tooling is on PATH. +hints_shown=$(nomarchy-state-sync get settings.hardwareHintsShown 2>/dev/null || true) +case "$hints_shown" in + true|1|yes) exit 0 ;; +esac + +hint_lines=() +if command -v fwupdmgr >/dev/null 2>&1; then + hint_lines+=("SUPER+M → System › Firmware to check LVFS updates") +fi +if command -v fprintd-list >/dev/null 2>&1; then + hint_lines+=("SUPER+M → System › Fingerprint to enroll a finger") +fi + +# No matching hardware/tooling: stay quiet and deliberately don't set the +# marker — this re-check is cheap each session, and if the tooling shows +# up later (fwupd enabled, a reader added) the hint still fires once. +if [ "${#hint_lines[@]}" -eq 0 ]; then + exit 0 +fi +hints=$(printf '%s\n' "${hint_lines[@]}") + +# Just showed the welcome card in this run: give swaync a moment so the +# two toasts don't collide. +if [ -n "$just_shown" ]; then + sleep 3 +fi + +hok= i=0 while [ "$i" -lt 8 ]; do if notify-send -a Nomarchy -u normal -t 0 \ - "You're set" \ - "$body"; then - ok=1 + "Hardware tips" \ + "$hints"; then + hok=1 break fi i=$((i + 1)) sleep 2 done -if [ -z "$ok" ]; then - # Leave firstBootShown alone so the next login can try again. +if [ -z "$hok" ]; then exit 1 fi # Persist in the checkout so re-login is silent. --no-switch: marker only. -if ! nomarchy-state-sync --quiet set settings.firstBootShown true --no-switch; then - # No writable flake checkout (or tool missing) — still showed the toast; - # without a marker it may reappear next login. Don't fail the unit. +if ! nomarchy-state-sync --quiet set settings.hardwareHintsShown true --no-switch; then + # No writable flake checkout — still showed the toast; without a marker + # it may reappear next login. Don't fail the unit. exit 0 fi