# Dotfiles - Stow, Generated Configs, and Drift
A dotfiles repo built on Stow and generated configuration fails in predictable ways: symlinks break, generated files drift, and drift checks have blind spots. Edit canonical sources, render, check, install. Keep runtime state out of symlinked paths.
## Key points
- Edit the base settings file, then render, check, and install. Hand-edited per-environment settings fail the check as base-plus-layer drift.
- The sync target owns the settings format. Run it after every manual settings edit so the change reaches the base file and siblings.
- MCP servers live in three scopes: user (global registry top level), local (per project, the CLI default), and project (`.mcp.json`). User-scope-only tracking hides default-added servers.
- "Missing from dotfiles" can be scope coverage, not drift. A check excluded from the aggregate target never runs. Run it manually after adding servers.
- Export must capture live user-scope servers into the tracked file and report local-scope servers, not a false all-clear.
- Sorted-key export against insertion-order storage yields a key-reorder diff: tool noise, not drift. Restore the committed file and apply the exact normalization the check validates.
- Extensions that open settings with `O_NOFOLLOW` refuse symlinks. Deploy their config as a copied real file. Verify content parity, not symlink parity.
- Tool allowlists rot silently and self-confirm. A hyphenated-prefix typo survives because the ownership regex encodes it and the checker validates it.
- Stash a tool's uncommitted runtime churn before verifying a committed tree, or the check fails on pre-existing "generated configuration is stale".
- "Live link is not the Stow link" failures in a worktree are positional: live symlinks point at the main checkout. Compare against a clean worktree at `HEAD`.
- Idempotent shell rc injection: a named marker block that `awk` deletes and re-appends. No duplicates on reprovisioning.
- JSON normalization checks catch trailing-newline and key-sort drift after a tool rewrites a settings file it manages.
- Put shfmt style in `.editorconfig` and drop explicit flags from make, pre-commit, and editor hooks. Every invocation then agrees.
- Dry-run stow first: `stow --simulate -v` or `stow -nv <package>` shows which symlinks would be created or skipped.
- Stow generates relative symlinks. A different `--dir` path creates absolute links that break portability. Always stow from the canonical root.
- `stow -R` (or `--restow`) unlinks and relinks every managed file, not only new additions. One step repairs stale symlinks and recreates removed links.
- Stow gives no directory name special semantics. `meta/` and `shared/` are package roots. Contents must mirror the target tree relative to the package root.
- `stow --adopt` copies live files into the repo and symlinks them atomically, bypassing repo-as-source-of-truth. Diff live versus repo per file first.
- Stow symlinks each file individually. Files added after the initial stow deploy only after you add the missing links in Stow's form.
- A runtime tool that rewrites a stowed config replaces the symlink with a real file. The next install collides, and a repo-only sync cannot recover the drift.
- Keep runtime state out of dotfiles symlinks. `O_NOFOLLOW` opens fail with `ELOOP` and fall back to defaults. Some tools refuse non-regular state files.
- A self-updating tool writes a relative front-door symlink that dangles when the parent bin directory is a repo symlink. Resolve the chain inside the layout: `ln -s ~/.local/state <repo>/.local/state`.
- Node's ESM loader resolves a symlinked entry point to its real path, so relative imports look in the source tree, not the deployment directory. Co-locate dependencies with the entry point.
- A live settings file symlinked or hardlinked into the repo makes a live edit a source edit. Every machine picks up the merged change without a re-stow.
- Hand edits to a generated live file fail the check and vanish on the next render. Land changes in the base and layer sources. Overrides go outside managed blocks, which regenerate on update.
- A catalog refresh is a regeneration, not a merge. Omitted fields come back as tool defaults, not pinned values. Repair with a pin file the renderer re-applies.
- A renderer that derives values from the repo's git identity leaks the wrong identity into generated entries of other layers. Pin the value or fix it upstream.
- Keep the config check strict: `jq -S` sorted keys plus a trailing newline, so drift is caught mechanically, not by review.
## How to apply
1. Dry-run first: `stow --simulate -v -d <dotfiles-root> -t ~`.
2. Stow only from the canonical root. After package files change, run `stow -R <package>`.
3. Diff live versus repo per file before `stow --adopt <package>`.
4. Generated settings: edit base and layer sources, render, check, install. Normalize with `jq -S` plus a trailing newline. Run excluded checks manually.
5. Verify committed trees against a clean worktree at `HEAD`, runtime churn stashed.
## Observations
- [normalization] A pure key reorder in exported config is tool noise. Restore the committed file. #drift (source: agent sessions, 2026-08)
- [runtime-state] `O_NOFOLLOW` tools fail on symlinked state files and fall back to defaults. #symlinks (source: agent sessions, 2026-09)
- [regeneration] A catalog refresh is a regeneration, not a merge. The renderer must re-apply pins. #generated-configs (source: agent sessions, 2026-10)
## Relations
- relates_to [[resources/documentation/Zsh Configuration]] (shell rc marker-block injection)
- relates_to [[resources/documentation/Tmux Configuration]] (multiplexer front-door symlink)
- relates_to [[resources/documentation/Git Configuration]] (git identity in generated configs)
- relates_to [[resources/documentation/Agents/Agents - Model Selection and Catalogs]] (catalog refresh with pins)
## Sources
- Personal agent-session notes, March 2026 to November 2026