The repo still tracked `dotfiles/niri/.config/niri/cfg/.AGENTTRASH/display.kdl.superseded` — a retired output config committed 2026-10-09, five days before `.gitignore` grew the `.AGENTTRASH/` rule. Tracked-then-ignored: an ignore rule never reaches an already-tracked path, so `git rm --cached` is the only repair. The working copy joins the rest of today's tracked debt. Found while answering "does this repo still track my noctalia and niri settings", which also falsified two rows of the census committed yesterday: niri is 9 tracked files, not 12; noctalia is 3 authored files against 2672 files of app state and downloaded catalog, not 2/0. The page's own classifier command reproduces the new numbers — it never reproduced the old ones, which is the only reason they were wrong and not merely different. Two laws recorded alongside, both from the same probe: `config.kdl`'s `include "noctalia.kdl"` is not `optional=true`, so a fresh box cannot start niri until the theme pipeline has run once; and `cfg/display.kdl -> desk-1-home.kdl` is desk state a fresh box does not receive — by design, but nowhere written down. Refs #40
4.9 KiB
type, title, description, tags, timestamp, verified
| type | title | description | tags | timestamp | verified | ||||
|---|---|---|---|---|---|---|---|---|---|
| reference | Dotfiles deployment tier | How dotfiles/ packages become live configuration — stow-equivalent symlinks, host override packages, and the two laws about theme media and systemd enable symlinks. |
|
2026-10-20 | 2026-10-20 |
Dotfiles deployment tier
dotfiles/ holds home-relative trees and scripts/deploy.sh is a stow-equivalent: the
repo checkout is the deployment, and deploying replaces live files with symlinks into
the repo, so git pull + deploy.sh is the whole propagation story. One directory per
tool (niri, ghostty, gtk-3.0, systemd-user, …); a package named zz-$(hostname)
is stowed last and its files replace symlinks coming from base packages.
| Mode | Effect |
|---|---|
deploy.sh --check |
dry run — adopts identicals, reports what would be linked, lists divergents, writes nothing. Run this first, always |
deploy.sh (sync) |
adopt identicals, link new, quarantine divergents into .quarantine/ and replace them |
deploy.sh --adopt |
like sync, but divergents are reported and skipped rather than quarantined |
Two package shapes are not symlinks: dconf/dump.txt is loaded with dconf load /, and
systemd-user units get daemon-reload plus enable --now only when their ExecStart
binary resolves on the current machine, so a unit belonging to another box stays inert
instead of crash-looping.
The tracked unit is the file, not the directory. A live directory can be a real
directory and still be fully tracked, because every file inside it is a symlink into the
repo (niri is 9 tracked / 1 box-only; gtk-3.0 is 3/0). Any audit that classifies by
directory mislabels the tree — classify per file with readlink -f. The live census and
its classifier command are in home-config-census.
Law: media the theme pipeline consumes is content, not config
Wallpapers live at dotfiles/noctalia/.local/share/noctalia/wallpapers/ and are
gitignored. They were tracked until 2026-10-20 — 23 files, 80 MiB — which meant every
clone of a configuration repo carried a media library no diff can review, and the
payload only ever grows because deleting a tracked blob from the tree does not delete it
from history. Noctalia reads
~/.local/share/noctalia/wallpapers/, which is a set of symlinks into that directory, so
the files stay on disk and untracking them broke nothing on this box.
Restore on a fresh box: every filename is wallhaven_<id>.<ext> and the ID is the whole
address — curl -L -o <name> "https://wallhaven.cc/w/<id>" puts the library back, then
deploy.sh relinks it. Losing the set is an acceptable outcome; 80 MB per clone forever
is not.
Law: never systemctl --user enable a unit from a worktree
enable writes ~/.config/systemd/user/default.target.wants/<unit> as a symlink to the
unit path it was handed, and that path is absolute. The heartbeat reaps wt/* worktrees
older than seven days (live-sync), so a unit enabled from a worktree turns
into a dangling symlink while systemctl --user is-enabled still answers enabled — the
unit is dead and the status lies about it. Found on 2026-10-20:
blankpad-desk-autoswitch.service pointed into the reaped wt/de-redesign-20261009, so
desk auto-switching had been silently off since the reap.
Check the link target before the unit file:
ls -l ~/.config/systemd/user/default.target.wants/
# every target must resolve; a dangling one is a reaped worktree, not a config bug
Fix is to relink into the deployment checkout (~/.omp/dotfiles/…), systemctl --user daemon-reload, then confirm with is-active — is-enabled proves nothing.
Law: a generated file must never be a required include
~/.config/niri/config.kdl ends with include "noctalia.kdl" — no optional=true.
That file is noctalia's palette output (#ff8c00, #f43841, one re-stamp
2026-10-18 17:36:27), so it is machine state and correctly untracked. But on a fresh
box deploy.sh links config.kdl first, and niri then refuses to start until noctalia
has run once and written the file — the compositor is bricked by the order of operations,
not by a bad config. Close it at the include, which survives someone skipping the theme
pipeline: mark generated includes optional=true, as the display.kdl and
switch-events.kdl lines in the same file already are.
Desk choice is live state, never config
~/.config/niri/cfg/display.kdl is a relative symlink to whichever profile is active
(desk-1-home.kdl, desk-2-portable.kdl), written by desk.sh. Both profiles are
tracked in dotfiles/zz-blankpad/; the pointer is not, because which desk the box sits on
is not a fact about the repo. A fresh box therefore starts with no output configuration —
config.kdl includes display.kdl with optional=true, so niri comes up usable but with
default monitors until desk.sh is run once.