6 Commits
Author SHA1 Message Date
Frank 2b7b0bc3cd Make the idle-lock watcher survive a clean exit, and outlive a session restart
`swayidle-lock.service` was `enabled`, in `default.target.wants/`, and **dead for eight days**:

    Active: inactive (dead) since Mon 2026-10-12 16:22:23 PDT; 1 week 1 day ago
    Duration: 2d 21h 57min   Main PID: 3181 (code=exited, status=0/SUCCESS)
    Result=success  NRestarts=0

`Restart=on-failure` cannot see a clean exit, and `swayidle -w` exits 0 on SIGTERM. So the
watcher that owns both the 5-minute idle lock and the `before-sleep` lock stopped existing, and
nothing on the box noticed. The consequence is not cosmetic: with `HandleLidSwitch=suspend` and
`HandleLidSwitchExternalPower=""` (which falls back to it), **closing the lid suspended the
machine unlocked**, because the hook that locks on sleep lived inside the unit that had died. No
alternative was running: `pgrep -c -u blankpad 'swayidle|swaylock|hyprlock|gtklock'` = 0, and
neither `autostart.kdl` launches a locker.

Three settings, each with a job:

- `Restart=always` — a supervisor must not assume it can only die badly.
- `StartLimitIntervalSec=0` — with `always`, a fast loop must never wedge the unit into `failed`;
  a lock that keeps trying beats a lock that stopped trying.
- `PartOf=graphical-session.target` — it is wanted by `default.target`, so a session restart
  brought the target back without it; now the watcher is restarted with the session.

What triggered the 16:22 exit is **unattributed**: the user journal has no entries in that window,
so all that can be said is that swayidle exited with status 0 — precisely what the old policy was
blind to. The fix does not depend on knowing the trigger, which is the point.

Verification is behavioural, per the law recorded with it: start the unit, `kill -TERM` the main
PID, and watch `NRestarts` increment with a new `MainPID` — the October event, survived. Not
performed here: starting it arms a 5-minute idle lock on a machine that has gone a week without
one, and that is Frank's call. `systemd-analyze --user verify` clean, `bats tests/` 71/71,
`kb-lint.sh omp-docs` 0, `kb-coverage.py` 0.

Refs #47
2026-10-20 17:24:32 -07:00
Frank 54f5555470 chore: run live-sync heartbeat hourly
Operator decision at review: 15 min is aggressive for config churn; hourly
keeps the stale-window acceptable (<=1 h) with a fifth of the wakeups.
Timer unit, tier law, and failure-table wording aligned.
2026-10-18 16:28:50 -07:00
Frank e52aa6a4a3 feat: live-sync heartbeat commits machine-written paths
The checkout IS the deployment and symlinks make live state (memories,
auto-learned skills, agent config, desktop settings) dirty tracked files
continuously; no commit law covered machine churn, so it accumulated
unpushed for weeks — one disk held the only copy of ~61 managed skills,
a settings fix stayed local-only, and dirty trees refused merges.

scripts/live-sync.sh (systemd user timer, ~15 min) commits ONLY the
allowlisted machine/desktop-live tiers with hooks armed, merges
origin/main, pushes, and reaps wt/* worktrees older than 7 days. Task
dirt outside the allowlist is never staged; conflicts abort and alert
via ntfy; branch guard refuses off main; never --no-verify, never force.
Black-box contract pinned by tests/live-sync.bats (9 tests, suite 36/36).

Refs #11
2026-10-18 16:13:59 -07:00
Frank 49afdfeda4 Noctalia theme migration: Niri IPC, GTK palette, alacritty import
- Switch Niri keybinds from qs-wrapped IPC calls to direct 'noctalia msg'
- Add noctalia.kdl include to niri config
- Replace alacritty theme with import of noctalia.toml
- Ghostty: Retro Legends → noctalia theme
- GTK3/4: accent palette shift from lavender (#bfc2ff) to purple (#b267e4)
- systemd swayidle-lock.service updated
2026-09-11 00:26:56 -07:00
Frank 9a5387b6c0 feat(systemd-user): swayidle idle-lock unit with noctalia IPC fallback chain 2026-09-10 19:42:57 -07:00
Frank a9127a08e9 feat(dotfiles): import env layer as stow-style packages with deploy.sh
Import Dankware's environment layer into the repo as home-relative package
trees (niri, gtk-3/4, ghostty, alacritty, fish, zsh, bash, profile,
systemd-user, dconf). scripts/deploy.sh implements stow-equivalent semantics
(check|adopt|sync modes): identical files adopt to symlinks, new files link,
divergent files quarantine-and-replace under .quarantine/<host>/<ts>/ (gitignored).
zz-<hostname> override packages deploy last with override semantics.

Secrets never enter git: GITEA_TOKEN moved from .zshrc into untracked
~/.config/environment.d/secrets.env (0600); tracked .zshrc sources it
guarded. core.hooksPath armed by deploy.sh so the pre-commit secret gate is
live on every machine that deploys.
2026-09-09 18:09:38 -07:00