feat(doctor): read-only hardware health section (#44)
All checks were successful
Check / eval (push) Successful in 3m4s
All checks were successful
Check / eval (push) Successful in 3m4s
Six self-gating hardware checks in nomarchy-doctor, same ok/bad/warn/skip + one-fix-command contract: NetworkManager connectivity, default PipeWire sink, VA-API/OpenGL smoke (only if vainfo/glxinfo present), fprintd unit + enroll nudge, fwupd daemon + a soft warn when get-updates is non-empty, and the laptop charge_control_end_threshold when a limit is set. Each skips cleanly when its tool/service/device is absent, so the section shrinks to the machine and never hard-fails on a missing feature. Implemented by a worktree-isolated agent in parallel with the #46 install VM; diff reviewed and cherry-picked here. Verified V0 (flake check) + V1 (nix build .#…pkgs.nomarchy-doctor — build-time bash -n + shellcheck), re-run on main after cherry-pick; plus a read-only run on real hardware (all six checks fired + self-gated). V2 (checks.doctor fakes) pending. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -43,14 +43,14 @@ Core shipped 2026-07-09: self-gated **System ▸ Firmware** rofi row
|
|||||||
Waybar/updates-panel integration. (The doctor "updates available"
|
Waybar/updates-panel integration. (The doctor "updates available"
|
||||||
check is item #44.) Spec: docs/HARDWARE.md §4.
|
check is item #44.) Spec: docs/HARDWARE.md §4.
|
||||||
|
|
||||||
### 44. Doctor hardware section — read-only (`VISION § A`)
|
### 44. Doctor hardware section — V2 checks.doctor extension
|
||||||
Extend `nomarchy-doctor` with the same pass/fail + one-fix-command
|
Six read-only hardware checks shipped 2026-07-09 (NetworkManager,
|
||||||
contract for: NetworkManager connectivity, default PipeWire sink,
|
PipeWire sink, VA-API/GL smoke, fprintd, fwupd + get-updates warn,
|
||||||
optional GPU smoke (`vainfo`/`glxinfo` if present), fprintd unit when
|
charge-threshold), all self-gating; V1 (doctor package builds:
|
||||||
fingerprint was enabled, fwupd active (+ warn if `get-updates`
|
bash -n + shellcheck) + a real-hardware read-only smoke on the dev box.
|
||||||
non-empty), laptop charge-threshold sysfs when a limit is set. Spec:
|
**Remaining V2:** extend the `checks.doctor` nixosTest with fakes to
|
||||||
docs/HARDWARE.md §10. Cost: doctor.sh sections; V1 in existing
|
cover the new rows headlessly; pin the fwupd `get-updates` exit-code
|
||||||
`checks.doctor` where fakes exist, rest V2/V3.
|
semantics (0 = updates) in that fake. Spec: docs/HARDWARE.md §10.
|
||||||
|
|
||||||
### 46. V2 verify: install swap=0 + unattended LUKS fail-closed (`208b8d4`)
|
### 46. V2 verify: install swap=0 + unattended LUKS fail-closed (`208b8d4`)
|
||||||
Pure `nix-instantiate` on disko subvols passed V0; **full install path
|
Pure `nix-instantiate` on disko subvols passed V0; **full install path
|
||||||
|
|||||||
@@ -17,6 +17,25 @@ Template:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-07-09 — #44 Doctor hardware section (delegated to worktree agent)
|
||||||
|
- **Task:** NEXT #44 — read-only hardware checks in nomarchy-doctor
|
||||||
|
(HARDWARE.md §10). Delegated to a worktree-isolated opus agent so it ran
|
||||||
|
in parallel with the #46 install VM without KVM contention.
|
||||||
|
- **Did:** Agent added six self-gating checks to `nomarchy-doctor.sh`
|
||||||
|
(NetworkManager, default PipeWire sink, VA-API/GL smoke, fprintd unit +
|
||||||
|
enroll nudge, fwupd active + get-updates warn, laptop charge-threshold),
|
||||||
|
each skipping cleanly when the tool/service/device is absent — same
|
||||||
|
ok/bad/warn/skip contract + one-fix-command style. Reviewed the diff and
|
||||||
|
cherry-picked onto main (branch `agent/doctor-hw-44`).
|
||||||
|
- **Verified:** V0 flake check green; **V1** `nix build …pkgs.nomarchy-doctor`
|
||||||
|
(writeShellApplication runs bash -n + shellcheck at build) — re-run by me
|
||||||
|
on main after cherry-pick. Bonus: the agent ran the built binary on the
|
||||||
|
AMD dev box (read-only) — all six checks fired + self-gated correctly.
|
||||||
|
- **Pending:** V2 — extend the `checks.doctor` nixosTest with fakes for the
|
||||||
|
new rows (VM; deferred while #46 held KVM). fwupd get-updates exit-code
|
||||||
|
semantics worth pinning in that fake.
|
||||||
|
- **Next suggestion:** V2-verify #43+#44 together on the next VM pass.
|
||||||
|
|
||||||
## 2026-07-09 — #43 System ▸ Firmware fwupd menu (core shipped, V2 pending)
|
## 2026-07-09 — #43 System ▸ Firmware fwupd menu (core shipped, V2 pending)
|
||||||
- **Task:** NEXT #43 — surface fwupd/LVFS updates in the menu (VISION § A).
|
- **Task:** NEXT #43 — surface fwupd/LVFS updates in the menu (VISION § A).
|
||||||
Done while the VM was busy with #46, so V0/V1 only by design.
|
Done while the VM was busy with #46, so V0/V1 only by design.
|
||||||
|
|||||||
@@ -127,6 +127,129 @@ else
|
|||||||
skip "snapper (not enabled on this machine)"
|
skip "snapper (not enabled on this machine)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ══ hardware ═════════════════════════════════════════════════════════
|
||||||
|
# Every check below self-gates: it skips cleanly when the tool, service,
|
||||||
|
# or device isn't present, so the section shrinks to fit the machine and
|
||||||
|
# never fails just because a feature is absent. Still read-only.
|
||||||
|
|
||||||
|
# ── network (NetworkManager) ─────────────────────────────────────────
|
||||||
|
if command -v nmcli >/dev/null 2>&1; then
|
||||||
|
nmstate=$(nmcli -t -f STATE general status 2>/dev/null || true)
|
||||||
|
case "$nmstate" in
|
||||||
|
connected*) ok "NetworkManager: $nmstate" ;;
|
||||||
|
"") skip "NetworkManager (no state reported)" ;;
|
||||||
|
*) warn "NetworkManager state: $nmstate" \
|
||||||
|
"connect in System › Network (or: nmcli device)" ;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
skip "NetworkManager (nmcli not present)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── default audio sink (PipeWire) ────────────────────────────────────
|
||||||
|
# wpctl/pactl talk to the user's PipeWire session — only meaningful with
|
||||||
|
# a user bus (not over bare SSH/root).
|
||||||
|
if [ -n "$(systemctl --user is-system-running 2>/dev/null || true)" ]; then
|
||||||
|
if command -v wpctl >/dev/null 2>&1; then
|
||||||
|
if wpctl inspect @DEFAULT_AUDIO_SINK@ >/dev/null 2>&1; then
|
||||||
|
ok "default audio sink present"
|
||||||
|
else
|
||||||
|
warn "no default PipeWire sink" "pick one in System › Audio (or: wpctl status)"
|
||||||
|
fi
|
||||||
|
elif command -v pactl >/dev/null 2>&1; then
|
||||||
|
defsink=$(pactl get-default-sink 2>/dev/null || true)
|
||||||
|
if [ -n "$defsink" ] && [ "$defsink" != "@DEFAULT_SINK@" ]; then
|
||||||
|
ok "default audio sink: $defsink"
|
||||||
|
else
|
||||||
|
warn "no default audio sink" "pick one in System › Audio"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
skip "audio sink (no wpctl/pactl)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
skip "audio sink (no user session bus here)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── GPU acceleration smoke (only if the probes are installed) ────────
|
||||||
|
if command -v vainfo >/dev/null 2>&1; then
|
||||||
|
if vainfo >/dev/null 2>&1; then
|
||||||
|
ok "VA-API acceleration works (vainfo)"
|
||||||
|
else
|
||||||
|
warn "vainfo present but VA-API probe failed" \
|
||||||
|
"check mkFlake.hardwareProfile / GPU drivers (or run: vainfo)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
skip "VA-API smoke (vainfo not installed)"
|
||||||
|
fi
|
||||||
|
if command -v glxinfo >/dev/null 2>&1; then
|
||||||
|
if [ -n "${WAYLAND_DISPLAY:-}${DISPLAY:-}" ]; then
|
||||||
|
if glxinfo -B >/dev/null 2>&1; then
|
||||||
|
ok "OpenGL renderer responds (glxinfo)"
|
||||||
|
else
|
||||||
|
warn "glxinfo present but GL probe failed" \
|
||||||
|
"check mkFlake.hardwareProfile / GPU drivers (or run: glxinfo -B)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
skip "OpenGL smoke (no display attached)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
skip "OpenGL smoke (glxinfo not installed)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── fingerprint (only when fprintd is enabled) ───────────────────────
|
||||||
|
if [ -n "$(systemctl list-unit-files fprintd.service --no-legend --plain 2>/dev/null || true)" ]; then
|
||||||
|
enrolled=""
|
||||||
|
if command -v fprintd-list >/dev/null 2>&1 && [ -n "${USER:-}" ]; then
|
||||||
|
enrolled=$(fprintd-list "$USER" 2>/dev/null || true)
|
||||||
|
fi
|
||||||
|
if [ -n "$enrolled" ] && ! printf '%s' "$enrolled" | grep -qi 'no fingers'; then
|
||||||
|
ok "fprintd enabled, fingerprint(s) enrolled for ${USER:-user}"
|
||||||
|
elif [ -n "$enrolled" ]; then
|
||||||
|
warn "fprintd enabled but no fingerprints enrolled" "enroll one: fprintd-enroll"
|
||||||
|
else
|
||||||
|
ok "fprintd unit installed (fingerprint enabled)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
skip "fingerprint (fprintd not enabled)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── firmware updates (fwupd) ─────────────────────────────────────────
|
||||||
|
# fwupd is D-Bus-activated, so an idle daemon is normal, not a fault.
|
||||||
|
# get-updates reads local metadata (no flashing); a hit is a soft warn.
|
||||||
|
if command -v fwupdmgr >/dev/null 2>&1 \
|
||||||
|
&& [ -n "$(systemctl list-unit-files fwupd.service --no-legend --plain 2>/dev/null || true)" ]; then
|
||||||
|
if systemctl is-active --quiet fwupd.service; then
|
||||||
|
ok "fwupd daemon active"
|
||||||
|
else
|
||||||
|
skip "fwupd daemon (idle — D-Bus-activated on demand)"
|
||||||
|
fi
|
||||||
|
if timeout 20 fwupdmgr get-updates >/dev/null 2>&1; then
|
||||||
|
warn "firmware updates are available" "review, then apply: fwupdmgr update"
|
||||||
|
else
|
||||||
|
ok "firmware up to date (no pending updates)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
skip "fwupd (not enabled / fwupdmgr absent)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── laptop battery charge threshold (only when a limit is set) ───────
|
||||||
|
bat_seen=0; bat_limited=0
|
||||||
|
for bat in /sys/class/power_supply/BAT*; do
|
||||||
|
f="$bat/charge_control_end_threshold"
|
||||||
|
[ -r "$f" ] || continue
|
||||||
|
bat_seen=1
|
||||||
|
lim=$(cat "$f" 2>/dev/null || true)
|
||||||
|
case "$lim" in
|
||||||
|
''|*[!0-9]*) continue ;;
|
||||||
|
esac
|
||||||
|
if [ "$lim" -lt 100 ]; then
|
||||||
|
bat_limited=1
|
||||||
|
ok "$(basename "$bat") charge limit active at ${lim}%"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$bat_seen" -eq 1 ] && [ "$bat_limited" -eq 0 ]; then
|
||||||
|
skip "battery charge limit (none set — charges to 100%)"
|
||||||
|
fi
|
||||||
|
|
||||||
# ── verdict ──────────────────────────────────────────────────────────
|
# ── verdict ──────────────────────────────────────────────────────────
|
||||||
echo
|
echo
|
||||||
if [ "$fails" -eq 0 ]; then
|
if [ "$fails" -eq 0 ]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user