Files
command-center/omp-docs/tools/dotfiles-deploy.md
Frank 9ebf5fe85f Untrack a retired niri config that was committed as trash; correct two census rows
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
2026-10-20 17:00:20 -07:00

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.
dotfiles
deployment
systemd
noctalia
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.