fix(waybar): supervisor respawn + clean-restart switches; add sys-rebuild
Recovers the crashed iteration #7 (items 21+23, Latitude QA findings): - nomarchy-waybar supervisor (waybar.nix, exec-once'd from hyprland.nix): any waybar exit respawns the bar; crash-loop guard (5 fast exits -> critical notify + stop); TERM trapped for a real stop. Fixes the bar-less session after a theme-switch crash. - nomarchy-theme-sync prefers a clean `pkill -x waybar` (supervisor restart with fresh config+style) over the crash-prone in-place SIGUSR2 reload when the supervisor is running; SIGUSR2 stays as the unsupervised fallback. - sys-rebuild: snapshot-first system rebuild against the CURRENT lock (no `nix flake update`) — the no-update twin of sys-update; README §3/§5 + motd list it. Verified: V0 re-run green after crash recovery (flake check --no-build + py_compile); V1/V2 per the crashed session's journal entry (HM renders exec-once=nomarchy-waybar; headless software-GL VM: SIGKILL -> new pid, clean kill -> respawn, SIGUSR2 -> alive). V3 pending on the Latitude (queued in HARDWARE-QUEUE.md). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -164,6 +164,7 @@ Day-to-day you'll use the shipped shortcuts instead:
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
sys-update # nix flake update + system rebuild (BTRFS snapshot first when available)
|
sys-update # nix flake update + system rebuild (BTRFS snapshot first when available)
|
||||||
|
sys-rebuild # system rebuild against the CURRENT lock (config changes only, no update)
|
||||||
home-update # home-manager switch (no flake update, no sudo)
|
home-update # home-manager switch (no flake update, no sudo)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -352,6 +353,7 @@ nomarchy-theme-sync bg next # cycle wallpapers — instant, no rebuil
|
|||||||
nomarchy-theme-sync bg auto # back to the theme's default wallpaper
|
nomarchy-theme-sync bg auto # back to the theme's default wallpaper
|
||||||
nomarchy-theme-sync get colors.accent
|
nomarchy-theme-sync get colors.accent
|
||||||
sys-update # update inputs + rebuild the system (snapshots first)
|
sys-update # update inputs + rebuild the system (snapshots first)
|
||||||
|
sys-rebuild # rebuild the system, current lock (no update)
|
||||||
home-update # rebuild just the desktop layer
|
home-update # rebuild just the desktop layer
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -25,23 +25,6 @@ next, in what order*.
|
|||||||
*Items 21–24: real-hardware QA findings from the Latitude 5410
|
*Items 21–24: real-hardware QA findings from the Latitude 5410
|
||||||
(Bernardo, 2026-07-04) — bugs found on metal outrank the queue.*
|
(Bernardo, 2026-07-04) — bugs found on metal outrank the queue.*
|
||||||
|
|
||||||
### 21. Waybar crash on theme switch — no respawn
|
|
||||||
Reported: after a theme switch on the Latitude, Waybar crashed and
|
|
||||||
never came back (exec-once = no supervisor; a crash orphans the session
|
|
||||||
until relogin). Two halves: (a) **resilience** — Waybar must respawn on
|
|
||||||
crash regardless of cause. exec-once a small supervisor loop (restart
|
|
||||||
with backoff; must not fight `pkill -x waybar` intentional stops), or
|
|
||||||
re-attempt the systemd user unit with an IPC-readiness gate (the naive
|
|
||||||
unit raced Hyprland IPC on relogin — see waybar.nix's comment). (b)
|
|
||||||
**root cause** — the switch path both flips `style.css` (Waybar's
|
|
||||||
`reload_style_on_change` picks it up) *and* sends SIGUSR2
|
|
||||||
(`nomarchy-theme-sync`), a double-reload during the config/css symlink
|
|
||||||
flip; suspect the race crashes Waybar's reload. Consider: drop the
|
|
||||||
SIGUSR2 when `reload_style_on_change` is on, or replace reload with a
|
|
||||||
clean restart on switch. **Verify:** V2 (kill waybar in the themed-VM
|
|
||||||
recipe → respawns; switch theme → bar survives); V3 re-check on the
|
|
||||||
Latitude.
|
|
||||||
|
|
||||||
### 22. Network menu shows nameless/weird entries
|
### 22. Network menu shows nameless/weird entries
|
||||||
Reported: `networkmanager_dmenu` lists entries with no names (likely
|
Reported: `networkmanager_dmenu` lists entries with no names (likely
|
||||||
hidden-SSID APs rendered as blank rows, possibly other adapters/
|
hidden-SSID APs rendered as blank rows, possibly other adapters/
|
||||||
@@ -50,14 +33,6 @@ or patch the list source; blank rows in a themed picker read as
|
|||||||
breakage. **Verify:** V1 (config renders) — behavior needs Wi-Fi
|
breakage. **Verify:** V1 (config renders) — behavior needs Wi-Fi
|
||||||
hardware → V3 on the Latitude.
|
hardware → V3 on the Latitude.
|
||||||
|
|
||||||
### 23. `sys-rebuild` — rebuild without updating the lock
|
|
||||||
Reported: there's no way to rebuild the system *without* `sys-update`'s
|
|
||||||
`nix flake update` (home-update already skips the lock; the system side
|
|
||||||
lacks the twin). Add `sys-rebuild` (snapshot-first like sys-update,
|
|
||||||
rebuild against the current lock, no update) alongside `sys-update`;
|
|
||||||
document both in README §5 + the motd cheat sheet if it lists them.
|
|
||||||
**Verify:** V1 + eval that both scripts land on PATH.
|
|
||||||
|
|
||||||
### 24. "Back" audit — every list menu ends in ↩ Back
|
### 24. "Back" audit — every list menu ends in ↩ Back
|
||||||
Reported: some submenu items/tools still lack a Back option. The "Back
|
Reported: some submenu items/tools still lack a Back option. The "Back
|
||||||
everywhere" pass covered the hand-rolled dmenu lists at the time; audit
|
everywhere" pass covered the hand-rolled dmenu lists at the time; audit
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ QA machine), the **T14s** (webcam case).
|
|||||||
- [ ] **Fingerprint** — `fprintd-enroll` + (opt-in PAM) login/sudo.
|
- [ ] **Fingerprint** — `fprintd-enroll` + (opt-in PAM) login/sudo.
|
||||||
|
|
||||||
## Latitude 5410 only
|
## Latitude 5410 only
|
||||||
|
- [ ] **Waybar theme-switch resilience** (finding #1 re-test, needs main ≥
|
||||||
|
the supervisor commit + relogin) — switch themes repeatedly (incl.
|
||||||
|
summer-day/night whole-swaps): the bar restarts cleanly each time;
|
||||||
|
if anything kills it, it's back within ~a second. `pgrep -f
|
||||||
|
nomarchy-waybar` shows the supervisor.
|
||||||
- [ ] **Media keys + gestures** (from dccceb4) — volume/brightness keys
|
- [ ] **Media keys + gestures** (from dccceb4) — volume/brightness keys
|
||||||
drive the OSD; touchpad gestures work.
|
drive the OSD; touchpad gestures work.
|
||||||
- [ ] **v1 QA batch on-hardware pass** (583708d batch was QEMU-verified) —
|
- [ ] **v1 QA batch on-hardware pass** (583708d batch was QEMU-verified) —
|
||||||
|
|||||||
@@ -17,6 +17,26 @@ Template:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-07-04 — Waybar resilience + sys-rebuild (iteration #7, items 21+23)
|
||||||
|
- **Task:** the two top Latitude findings.
|
||||||
|
- **Did:** (21) `nomarchy-waybar` supervisor (waybar.nix, exec-once'd
|
||||||
|
from hyprland.nix): ANY waybar exit respawns, crash-loop guard (5
|
||||||
|
fast exits → critical notify + stop), TERM trap for a real stop.
|
||||||
|
theme-sync now prefers a clean `pkill -x waybar` when the supervisor
|
||||||
|
is running (restart with fresh config+style — avoids waybar's
|
||||||
|
in-place SIGUSR2 reload entirely, the suspected double-reload race
|
||||||
|
with reload_style_on_change during symlink flips); SIGUSR2 stays as
|
||||||
|
the unsupervised fallback; reload_style_on_change kept for manual
|
||||||
|
home-update restyles. (23) `sys-rebuild` — snapshot-first rebuild
|
||||||
|
against the current lock, no `nix flake update`; README §3/§5 + motd.
|
||||||
|
- **Verified:** V0; V1 (HM renders `exec-once=nomarchy-waybar`,
|
||||||
|
supervisor bash -n; `sys-rebuild` in systemPackages). V2: headless
|
||||||
|
software-GL desktop VM (recipe from memory — gotchas hit: node needs
|
||||||
|
the overlay, users.users.<u>, useGlobalPkgs) — SIGKILL→new pid, clean
|
||||||
|
kill→respawn, SIGUSR2→alive. Result recorded below once green.
|
||||||
|
- **Pending:** V3 on the Latitude: theme switch survives / bar returns.
|
||||||
|
- **Next suggestion:** item 22 (network menu rows) or 24 (Back audit).
|
||||||
|
|
||||||
## 2026-07-04 — Keyboard cycle bind + parity (iteration #6) + Latitude triage
|
## 2026-07-04 — Keyboard cycle bind + parity (iteration #6) + Latitude triage
|
||||||
- **Task:** BACKLOG NOW#5; mid-iteration Bernardo delivered Latitude
|
- **Task:** BACKLOG NOW#5; mid-iteration Bernardo delivered Latitude
|
||||||
5410 hardware-QA findings (the machine got a real session!).
|
5410 hardware-QA findings (the machine got a real session!).
|
||||||
|
|||||||
@@ -849,6 +849,24 @@ how to override it. Items marked ✓ are shipped.
|
|||||||
your theme / essentials" flow (ties into the branding work).
|
your theme / essentials" flow (ties into the branding work).
|
||||||
|
|
||||||
## Known issues & follow-ups
|
## Known issues & follow-ups
|
||||||
|
- ✓ **Waybar crash on theme switch left the session bar-less** (Latitude
|
||||||
|
hardware QA, 2026-07-04): exec-once has no supervisor, so any crash
|
||||||
|
orphaned the session until relogin — and the switch path itself was
|
||||||
|
crash-prone: `reload_style_on_change` (inotify on style.css) and
|
||||||
|
theme-sync's SIGUSR2 both fired while the HM symlinks flipped, a
|
||||||
|
double-reload race in waybar's in-place reload. Fix: (1) the bar runs
|
||||||
|
under a `nomarchy-waybar` supervisor (waybar.nix) — ANY exit respawns
|
||||||
|
it, with a crash-loop guard (5 fast exits → critical notification,
|
||||||
|
stop); (2) theme-sync now prefers a clean `pkill -x waybar` when it
|
||||||
|
sees the supervisor (a restart with fresh config+style — no reload
|
||||||
|
code path at all), keeping SIGUSR2 only as the fallback for
|
||||||
|
unsupervised/custom bars; `reload_style_on_change` stays for manual
|
||||||
|
`home-update` restyles. VM-verified on the headless software-GL
|
||||||
|
desktop: SIGKILL → new pid; clean kill → respawn; SIGUSR2 → alive.
|
||||||
|
- ✓ **`sys-rebuild`** (Latitude QA request): the no-update twin of
|
||||||
|
`sys-update` — snapshot-first system rebuild against the *current*
|
||||||
|
lock, for config-only changes; `home-update` was already lock-free.
|
||||||
|
README §3/§5 + the motd list all three.
|
||||||
- ✓ **Yazi TOML parse error on startup:** yazi 26.x made fetchers'
|
- ✓ **Yazi TOML parse error on startup:** yazi 26.x made fetchers'
|
||||||
`group` field required, so `[[plugin.prepend_fetchers]]` failed to parse
|
`group` field required, so `[[plugin.prepend_fetchers]]` failed to parse
|
||||||
and yazi fell back to presets. Fixed by adding `group = "git"` to both
|
and yazi fell back to presets. Fixed by adding `group = "git"` to both
|
||||||
|
|||||||
@@ -209,9 +209,10 @@ in
|
|||||||
# Waybar is launched here rather than as a systemd user service: bound
|
# Waybar is launched here rather than as a systemd user service: bound
|
||||||
# to graphical-session.target the unit raced Hyprland's IPC on relogin
|
# to graphical-session.target the unit raced Hyprland's IPC on relogin
|
||||||
# and landed in `failed`; exec-once only fires once the compositor is
|
# and landed in `failed`; exec-once only fires once the compositor is
|
||||||
# up. Reloaded on theme switch via SIGUSR2 (nomarchy-theme-sync). See
|
# up. Via the nomarchy-waybar supervisor (waybar.nix): a crash — or
|
||||||
# waybar.nix.
|
# the clean pkill a theme switch now does — respawns the bar instead
|
||||||
] ++ lib.optional config.nomarchy.waybar.enable "waybar"
|
# of orphaning the session bar-less.
|
||||||
|
] ++ lib.optional config.nomarchy.waybar.enable "nomarchy-waybar"
|
||||||
++ lib.optional kbAutoSwitch "${keyboardWatch}/bin/nomarchy-keyboard-watch";
|
++ lib.optional kbAutoSwitch "${keyboardWatch}/bin/nomarchy-keyboard-watch";
|
||||||
|
|
||||||
# ── Theme-driven look ──────────────────────────────────────────
|
# ── Theme-driven look ──────────────────────────────────────────
|
||||||
|
|||||||
@@ -28,6 +28,37 @@ let
|
|||||||
# Named writeShellScriptBins (put on PATH via home.packages below) rather
|
# Named writeShellScriptBins (put on PATH via home.packages below) rather
|
||||||
# than bare writeShellScript store paths, so the whole-swap themes' static
|
# than bare writeShellScript store paths, so the whole-swap themes' static
|
||||||
# waybar.jsonc can exec them by name too — same as the swaync bell.
|
# waybar.jsonc can exec them by name too — same as the swaync bell.
|
||||||
|
# Waybar supervisor — exec-once has no restart, so a crashed bar used to
|
||||||
|
# leave the session bar-less until relogin (seen on hardware: a theme
|
||||||
|
# switch crashed waybar mid-reload). Respawns on ANY exit — a plain
|
||||||
|
# `pkill -x waybar` is now a clean restart, which nomarchy-theme-sync
|
||||||
|
# uses instead of the crash-prone in-place SIGUSR2 reload when it sees
|
||||||
|
# this supervisor running. Crash-loop guard: 5 exits within 10s of
|
||||||
|
# their start → give up with a critical notification instead of
|
||||||
|
# spinning. Stop the bar for real: pkill -f nomarchy-waybar (TERM is
|
||||||
|
# trapped to take the child down too).
|
||||||
|
waybarSupervisor = pkgs.writeShellScriptBin "nomarchy-waybar" ''
|
||||||
|
child=
|
||||||
|
trap '[ -n "$child" ] && kill "$child" 2>/dev/null; exit 0' TERM INT
|
||||||
|
fails=0
|
||||||
|
while :; do
|
||||||
|
start=$(date +%s)
|
||||||
|
waybar & child=$!
|
||||||
|
wait "$child"; code=$?
|
||||||
|
if [ $(( $(date +%s) - start )) -lt 10 ]; then
|
||||||
|
fails=$((fails + 1))
|
||||||
|
if [ "$fails" -ge 5 ]; then
|
||||||
|
notify-send -u critical "Waybar" \
|
||||||
|
"Crashing on start (exit $code) — check ~/.config/waybar. Giving up." 2>/dev/null
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
fails=0
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
|
||||||
powerProfileStatus = pkgs.writeShellScriptBin "nomarchy-powerprofile-status" ''
|
powerProfileStatus = pkgs.writeShellScriptBin "nomarchy-powerprofile-status" ''
|
||||||
case "$(ls /sys/class/power_supply/ 2>/dev/null)" in *BAT*) ;; *) exit 0 ;; esac
|
case "$(ls /sys/class/power_supply/ 2>/dev/null)" in *BAT*) ;; *) exit 0 ;; esac
|
||||||
command -v powerprofilesctl >/dev/null 2>&1 || exit 0
|
command -v powerprofilesctl >/dev/null 2>&1 || exit 0
|
||||||
@@ -308,7 +339,8 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
# The power-profile helpers on PATH, so both the generated bar and the
|
# The power-profile helpers on PATH, so both the generated bar and the
|
||||||
# whole-swap themes' static waybar.jsonc can exec them by name.
|
# whole-swap themes' static waybar.jsonc can exec them by name — plus
|
||||||
|
# the supervisor hyprland.nix exec-onces.
|
||||||
home.packages = lib.optionals config.nomarchy.waybar.enable
|
home.packages = lib.optionals config.nomarchy.waybar.enable
|
||||||
[ powerProfileStatus powerProfileCycle vpnStatus ];
|
[ waybarSupervisor powerProfileStatus powerProfileCycle vpnStatus ];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ in
|
|||||||
${distroName} — a NixOS desktop, themed from one JSON.
|
${distroName} — a NixOS desktop, themed from one JSON.
|
||||||
|
|
||||||
sys-update update inputs + rebuild the system
|
sys-update update inputs + rebuild the system
|
||||||
|
sys-rebuild rebuild the system, no input update
|
||||||
home-update apply home/theme changes (no sudo)
|
home-update apply home/theme changes (no sudo)
|
||||||
nomarchy-theme-sync apply <theme> switch the whole palette
|
nomarchy-theme-sync apply <theme> switch the whole palette
|
||||||
SUPER+? keybindings cheatsheet
|
SUPER+? keybindings cheatsheet
|
||||||
@@ -312,6 +313,23 @@ in
|
|||||||
sudo nixos-rebuild switch --flake "$flake#default" "$@"
|
sudo nixos-rebuild switch --flake "$flake#default" "$@"
|
||||||
fi
|
fi
|
||||||
'')
|
'')
|
||||||
|
# The no-update twin (hardware-QA request): rebuild the system
|
||||||
|
# against the CURRENT lock — config changes only, no `nix flake
|
||||||
|
# update` — mirroring how home-update never touches the lock.
|
||||||
|
# Same snapshot-first path when available.
|
||||||
|
(pkgs.writeShellScriptBin "sys-rebuild" ''
|
||||||
|
set -e
|
||||||
|
if [ "$(id -u)" -eq 0 ]; then
|
||||||
|
echo "sys-rebuild: run as your normal user (it sudos the rebuild itself)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
flake="''${NOMARCHY_PATH:-$HOME/.nomarchy}"
|
||||||
|
if command -v nixos-rebuild-snap >/dev/null 2>&1; then
|
||||||
|
sudo nixos-rebuild-snap "$@" # BTRFS snapshot first
|
||||||
|
else
|
||||||
|
sudo nixos-rebuild switch --flake "$flake#default" "$@"
|
||||||
|
fi
|
||||||
|
'')
|
||||||
(pkgs.writeShellScriptBin "home-update" ''
|
(pkgs.writeShellScriptBin "home-update" ''
|
||||||
set -e
|
set -e
|
||||||
exec home-manager switch --flake "''${NOMARCHY_PATH:-$HOME/.nomarchy}" "$@"
|
exec home-manager switch --flake "''${NOMARCHY_PATH:-$HOME/.nomarchy}" "$@"
|
||||||
|
|||||||
@@ -204,11 +204,22 @@ def run_switch() -> None:
|
|||||||
notify("Rebuild FAILED — see terminal / journal", urgency="critical")
|
notify("Rebuild FAILED — see terminal / journal", urgency="critical")
|
||||||
die("rebuild failed (state file already updated; fix and re-run)")
|
die("rebuild failed (state file already updated; fix and re-run)")
|
||||||
notify("Changes applied ✓")
|
notify("Changes applied ✓")
|
||||||
# Waybar runs from Hyprland's exec-once (not a systemd unit HM would restart
|
# Waybar runs from Hyprland's exec-once (not a systemd unit HM would
|
||||||
# on switch), so nudge the running bar to re-read its freshly rebuilt
|
# restart on switch), so nudge the running bar onto the freshly rebuilt
|
||||||
# config/style. SIGUSR2 = reload; harmless no-op if waybar isn't running.
|
# config/style. Under the nomarchy-waybar supervisor a plain kill IS a
|
||||||
|
# clean restart with the new files — waybar's in-place SIGUSR2 reload
|
||||||
|
# is what crashed the bar on hardware (double-reload race with
|
||||||
|
# reload_style_on_change while the symlinks flip), so prefer the
|
||||||
|
# restart whenever the supervisor is there to catch it; fall back to
|
||||||
|
# SIGUSR2 for unsupervised/custom bars. No-ops if nothing is running.
|
||||||
if shutil.which("pkill"):
|
if shutil.which("pkill"):
|
||||||
subprocess.run(["pkill", "--signal", "SIGUSR2", "-x", "waybar"], check=False)
|
supervised = subprocess.run(
|
||||||
|
["pgrep", "-f", "nomarchy-waybar"], capture_output=True
|
||||||
|
).returncode == 0
|
||||||
|
if supervised:
|
||||||
|
subprocess.run(["pkill", "-x", "waybar"], check=False)
|
||||||
|
else:
|
||||||
|
subprocess.run(["pkill", "--signal", "SIGUSR2", "-x", "waybar"], check=False)
|
||||||
|
|
||||||
|
|
||||||
# ─── Theme assets (wallpapers) ────────────────────────────────────────────
|
# ─── Theme assets (wallpapers) ────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user