Classify one loginctl invocation. Ok(outcome) ends the probe;
Err(detail) means โthis candidate failed, try the nextโ and
carries the failure detail โ including loginctlโs stderr, which
the old code dropped, so a bug report now pins the exact cause
(#191). Pure over the raw output pieces so the success classification
and stderr capture are testable without spawning loginctl.
Pure decision: from the prior (failure streak, already-logged)
and a fresh probe outcome, compute the next state, the re-probe
interval, and whether to emit a one-time health-transition log.
Split out so the back-off growth and log-once behaviour are
unit-testable without a real loginctl.
Re-probe interval after consecutive_failures failures. 0 (a
healthy probe) uses HEALTHY_TTL; failures grow it exponentially
from 30 s, capped at FAILED_BACKOFF_MAX, so a permanently-broken
probe settles at one attempt every five minutes instead of every
five seconds.
Ordered loginctl show-session identifiers to try. The callerโs own
XDG_SESSION_ID is the most specific, but itโs frequently unset for
GUI apps launched detached from the logind session (D-Bus
activation, some autostart paths) โ and there the old literal self
fallback resolved to nothing, so loginctl exited non-zero and lock
detection disabled itself (#191). auto is logindโs lenient
resolver โ the callerโs session if it has one, otherwise the userโs
display session โ so it succeeds where self canโt. Pure so the
fallback order is testable without a logind session.