feat(doctor): #147 — flag machine state the flake never set; linger is the worked example
Some checks failed
Check / eval (push) Has been cancelled

/var/lib survives a migration untouched, and a stray pre-Nomarchy
Linger=yes marker shaped the QA box's session behavior for six months
with nothing in the flake to show for it. Ship the class as guards:

- nomarchy-doctor: new machine-state-drift check after the flake-checkout
  check — loginctl show-user -p Linger; "yes" warns (nothing is broken —
  it is drift) and prints the disable-linger fix; unknown-to-logind skips.
- docs/MIGRATION.md § Post-migration cleanup: "/var/lib machine state
  survives migration" — the worked example and the general suspicion rule.
- checks.doctor: linger sub-case — warn fires under enable-linger, exit
  stays 0, warning clears after disable-linger.

Deliberately NOT hardening the session wiring against the portal-gtk
restart race linger enabled: a clean install cannot reach that state.
The Waybar exec-once loose thread survives as BACKLOG #149.

Verification: V2 — shellcheck clean, nix flake check --no-build green,
nix build .#checks.x86_64-linux.doctor green (both branches exercised).
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:44:43 +01:00
parent 8fc0a9bc29
commit e324876eb7
6 changed files with 80 additions and 57 deletions

View File

@@ -265,64 +265,16 @@ If that count is zero over a few weeks post-linger, **close this unbuilt**. If i
is not, the upstream issue has sat ten months on vaxerski's "that will cause a
segfault no?" and a reproducer would be worth more than a local guard.
### 147. Nomarchy cannot see machine state it never set — and it bit the QA box for 6 months
### 149. `[watch]` Waybar's exec-once workaround may be defending against nothing post-linger
**Rewritten 2026-07-16 after diagnosis; the original filing was wrong and its
wrongness is the point.** It claimed "a re-login leaves session units dead;
the repo never adopted the standard session wiring". The repo *has* the wiring
— HM's `wayland.windowManager.hyprland.systemd.enable` defaults on and is line
1 of the generated `hyprland.conf` — and it fired correctly. Do not go add it.
**What actually happened**, from systemd's own words at the 17:25:54 relogin:
```
Stopped target Hyprland compositor session.
Requested transaction contradicts existing jobs: Transaction for
graphical-session.target/stop is destructive (xdg-desktop-portal-gtk.service
has 'start' job queued, but 'stop' is included in transaction).
graphical-session.target: Failed to enqueue stop job, ignoring
Reached target Hyprland compositor session.
```
`hyprland-session.target` stopped; `StopWhenUnneeded=yes` correctly tried to
take `graphical-session.target` with it; systemd **refused the stop** because
portal-gtk had a `start` job queued from its own post-crash restart loop (3
restart/fail lines in that 13s gap). So graphical-session never went down, 8ms
later hyprland-session came back to find it active, and nothing `WantedBy` it
was re-pulled. cliphist had already burned 5 restarts into `start-limit-hit`;
nm-applet/udiskie have no restart policy and just stayed dead.
**But none of that can happen on a Nomarchy install.** The whole sequence needs
the user manager to survive logout, i.e. `Linger=yes` — and linger came from an
empty marker in `/var/lib/systemd/linger/bernardo` dated **2026-01-24**, which
**pre-dates Bernardo's migration to Nomarchy** (he was on a custom flake then).
Nothing here sets it: not modules, not hosts, not templates, and `git log -S`
says it was never shipped, ever. Removed 2026-07-16 (`loginctl disable-linger`,
no restart needed); nothing wanted it — `nomarchy-updates.timer` is
`Persistent=yes` and catches up on login.
**So the real item is the class, not the bug.** `/var/lib` is machine state; a
migration reconciles the flake and leaves everything else exactly as it was.
Nomarchy therefore ran for six months on its own primary V3 box with a setting
it never chose, never set, and could not see — on the machine whose whole job is
telling us how the distro behaves. That is a direct hit on the in-flake-state
promise (state outside the checkout) *and* on V3's trustworthiness: a QA box
that silently diverges reports on a distro nobody ships.
**Pitch:** a `nomarchy-doctor` check for drift of this class — linger is the
worked example (`loginctl show-user $USER -p Linger` vs. what the flake asks
for), and MIGRATION.md should say plainly that `/var/lib` survives and what to
sweep. Cost is small and it is the only part of this worth shipping. **Do not**
harden the session wiring against the portal-gtk race: a clean install cannot
reach it, and defending against it would be clever code for a state we do not
ship.
**Loose thread for whoever takes this:** `modules/home/hyprland.nix` runs Waybar
from `exec-once` rather than a unit because the unit "raced Hyprland's IPC on a
**warm** relogin ... never retried, so the bar vanished" — and a warm relogin is
exactly what linger creates. That workaround may now be defending against
nothing. Do not rip it out on this note alone; it works, and the claim needs a
real relogin without linger to test.
Spun out of #147 (shipped 2026-07-17) so its loose thread survives the entry:
`modules/home/hyprland.nix` runs Waybar from `exec-once` rather than a systemd
unit because the unit "raced Hyprland's IPC on a **warm** relogin ... never
retried, so the bar vanished" — and a warm relogin is exactly what linger
created. With the stray linger removed (2026-07-16) that workaround may be
defending against nothing. Do not rip it out on this note alone: it works, and
the claim needs a real relogin without linger to test (V3 — the same relogin
that verifies #148's watcher fix would answer it).
### 120. A netinstall ISO, next to the fat offline one