feat(first-boot): post-install hardware hints — one self-gated toast, once
All checks were successful
Check / eval (push) Successful in 3m39s

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 (VISION § B). One-shot marker settings.hardwareHintsShown,
in-checkout like firstBootShown. When no tooling matches, the marker is
deliberately left unset — the re-check costs a command -v per session,
and a later-enabled fwupd or newly-added reader still gets its hint once.
Never a permanent nag; live-ISO gate covers the new stage too.

Lives inside nomarchy-first-boot: the card's semantics are unchanged
(retry loop, exit 1 so a failed notify retries next login, marker only
after success); hints run only once the card has landed. Docs: README
option row, HARDWARE.md hint paragraphs, VISION § B checked off.

Verification: V2 — shellcheck clean, nix flake check --no-build green,
nix build .#checks.x86_64-linux.first-boot green (bare node silent →
fwupdmgr appears → fires once → silent again → live-ISO still skips).
Real toast rendering is the same session surface as the proven #81 card.
Implementation by a Sonnet subagent; design and review on Fable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 07:56:03 +01:00
parent 5479ade702
commit bbdc329ed6
8 changed files with 171 additions and 50 deletions

View File

@@ -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
WiFi: 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