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
93 lines
4.9 KiB
Markdown
93 lines
4.9 KiB
Markdown
---
|
|
type: reference
|
|
title: Dotfiles deployment tier
|
|
description: How dotfiles/ packages become live configuration — stow-equivalent symlinks, host override packages, and the two laws about theme media and systemd enable symlinks.
|
|
tags:
|
|
- dotfiles
|
|
- deployment
|
|
- systemd
|
|
- noctalia
|
|
timestamp: 2026-10-20
|
|
verified: 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](/inventory/home-config-census.md).
|
|
|
|
## 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](noctalia-shell.md) 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](live-sync.md)), 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:
|
|
|
|
```bash
|
|
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.
|