# 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