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

@@ -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"
)