Skip to content

Fix silently broken dotfiles, then modernise conventions - #106

Open
michaelbarton wants to merge 12 commits into
masterfrom
worktree-dotfiles-plan-impl
Open

Fix silently broken dotfiles, then modernise conventions#106
michaelbarton wants to merge 12 commits into
masterfrom
worktree-dotfiles-plan-impl

Conversation

@michaelbarton

Copy link
Copy Markdown
Owner

Summary

Implements the encapsulated-cooking-puffin review: fix the things that were silently dead, then bring the rest of the setup in line with current practice.

  • Stop GIT_AUTHOR_*/GIT_COMMITTER_* from overriding every gitconfig level, so a work identity can be layered in via an untracked ~/.config/git/config-local without naming an employer in this public repo.
  • Fix treesitter updates (TSUpdateSync is gone on nvim-treesitter main and was exiting 0), install ansible/requirements.yml from make galaxy/apply, drop openssl@1.1 and two dead atuin keys, and remove a dangling git autocommit symlink.
  • Cut non-interactive fish from ~630ms to ~100ms (MISE_FISH_AUTO_ACTIVATE=0, interactive-only prompt/history, fish_add_path --move so the keyring PATH invariant holds in nested shells).
  • Pin formatter versions in mise.toml, Actions to SHAs, and TPM plugins to tags/commits. Move git/tmux to XDG paths, narrow the global gitignore, and finish the PEP 723/uv run --script migration so standalone scripts actually run.
  • Split machine-local Claude keys (model, work marketplace/plugins) out of the tracked public settings.json. Lefthook runs make fmt_check locally. Ghostty now follows system light/dark and binds the quick terminal.

Commit signing (§13) is still skipped on purpose. macos-option-as-alt (§21c) is left unset pending an interactive Option+h check.

Test plan

  • make fmt_check (already green on this branch)
  • After merge/apply: make galaxy && make apply is idempotent on a second run
  • Create ~/.config/git/config-local + config-work, then git var GIT_AUTHOR_IDENT is the work address in a KariusDx checkout and the personal address in ~/.dotfiles
  • ./zettel/format_zettel_page.py --help, ./bioinformatics/get_multimapping_reads.py --help, ./duckdb/dump_schema.py --help print usage
  • After apply: fish -c true is under 100ms; fish -l -c 'which git keyring' is Homebrew git and ~/.local/bin/keyring
  • Temp repo: touch a.png && git status shows ?? a.png (global ignore no longer swallows PNGs)
  • New Ghostty window: tmux auto-attaches, starship/atuin/Ctrl+S/Ctrl+G still work; Option+h switches windows (if not, add macos-option-as-alt = left); system appearance toggles the terminal theme
  • Both CI workflows green, including the macOS bootstrap job

- Stop exporting GIT_AUTHOR_*/GIT_COMMITTER_* from fish, which silently
  overrode every gitconfig level (including includeIf) and made per-repo
  identity impossible. git/gitconfig now includes an optional, untracked
  ~/.config/git/config-local so a work identity can be layered in via
  hasconfig without naming any employer in this public repo.
- Fix ansible/tasks/neovim.yml: TSUpdateSync no longer exists on
  nvim-treesitter main, so the task was silently exiting 0 without updating
  parsers. Call the Lua update API synchronously and fail on error.
- Drop openssl@1.1 from Brewfile (disabled upstream 2024-09-11).
- Remove two atuin config keys that don't exist in current atuin, and enable
  the daemon (removes an ~50ms subprocess from shell startup).
- Wire ansible/requirements.yml into `make galaxy` (now a dependency of
  `apply`) and both CI workflows — previously installed by nothing.
- Remove a dangling ~/.config/git/autocommit.json symlink left over from a
  removed feature.
Gate the expensive interactive-only setup (starship, tmux auto-attach,
zoxide, full mise activation, atuin, fzf keybindings, abbrs) behind
`status is-interactive`, so scripts and hooks spawning `fish -c` don't pay
for prompt/history/keybinding machinery they never use.

The dominant cost was mise: Homebrew's vendor_conf.d/mise-activate.fish
runs *before* fish/config.fish and unconditionally pays ~200ms per
invocation. Setting MISE_FISH_AUTO_ACTIVATE=0 inside config.fish is too
late to help the process that sets it, but is inherited by every fish
child process spawned from an already-configured interactive shell
(confirmed: nested `fish -c true` drops from 630ms to ~85ms). For the
first fish process of a session (no parent to inherit from), the variable
has to already be in the environment before fish starts — added via
Ghostty's `env` directive, which the profiler confirms drops a fresh
`fish -c true` from ~460ms to ~100ms.

The non-interactive branch also avoids invoking the `mise` binary at all
(even `mise --version` alone costs ~180ms of process startup) — it just
prepends the shims dir to PATH directly, per mise's own guidance for
scripts.

Also: fish_add_path gets --move so it can reorder PATH entries already
inherited from a parent environment (previously a no-op if the entry was
already present anywhere in PATH) — this is what keeps ~/.local/bin ahead
of Homebrew for the `keyring`/keyrings.codeartifact invariant in nested
shells. USER_BIN/LOCAL_BIN/HOMEBREW_BIN/HOMEBREW_SBIN/GHOSTTY_BIN/NPM_BIN
are now locally scoped scratch vars instead of being exported into every
child process.
ruff==0.15.17 was hardcoded in both Makefile and ansible/tasks/neovim.yml
(commit #103 already documents drift between these two spots). Add
mise.toml pinning ruff, stylua, actionlint, prettier, and ansible-lint;
Makefile now calls each via `mise exec --` instead of threading
@$(VERSION) through npx/uvx, and ansible/tasks/neovim.yml reads the ruff
pin back out with `mise config get tools.ruff` instead of duplicating it.

CI installs the pinned versions via jdx/mise-action instead of a bespoke
actionlint curl download and an unpinned `uv tool install ansible-lint`.

Verified: `make fmt_check` passes end-to-end against the mise-resolved
tools, and `mise config get tools.ruff -f mise.toml` / the ansible
playbook syntax-check both resolve correctly.
- tmux: replace the pre-3.2 `terminal-overrides ",xterm-ghostty:Tc"` private
  extension with `terminal-features RGB` (supported since tmux 3.2, we're on
  3.7c). Declare the `extkeys` terminal-feature and extended-keys-format so
  tmux actually requests extended keys from Ghostty, per `man tmux`.
- Pin TPM and its plugins instead of floating HEAD: TPM and
  vim-tmux-navigator (no useful release tags) pinned to their currently
  installed commits; catppuccin/tmux pinned to v2.1.3, which is exactly what
  was already installed. Unpinned plugins were the only dependency in this
  repo without a lockfile-equivalent, and catppuccin/tmux has a history of
  breaking config-API changes between majors.
- Pin GitHub Actions to commit SHAs (with a version comment) instead of
  mutable major tags, and add .github/dependabot.yml to keep them current.
- Move tmux.conf, gitconfig, and the global ignore file to their XDG
  locations (~/.config/tmux, ~/.config/git/{config,ignore}), dropping
  `core.excludesfile` entirely (git's default already points at the XDG
  ignore path). Also drops `core.autocrlf` (vestigial on macOS-only),
  `branch.autosetupmerge` (already git's default), and
  `feature.experimental` (opts into changing behaviour between git
  releases). link_files.yml now removes the old ~/.gitconfig/.gitignore/
  .tmux.conf symlinks first — git and tmux both prefer the legacy path over
  XDG when both exist, so leaving the old links in place would have silently
  un-done the migration.
- Trim the global gitignore to what no project ever wants tracked (OS/editor
  cruft, secret-shaped filenames) per your call to keep it narrow. A global
  excludesfile applies to every repo you touch, so entries like `*.png`,
  `Gemfile.lock`, or `*.[1-9]` were silently dropping files in repos that
  legitimately track them. Removed patterns are listed in the file's footer
  comment for recoverability.

Verified: `make fmt_check`, `tmux -f tmux/tmux.conf` loads cleanly in an
isolated test server, actionlint passes, and an ansible --check --diff dry
run against the new paths confirms the legacy-symlink removal and new XDG
links resolve as intended (no real changes applied — dotfiles_dir still
points at ~/.dotfiles for actual application).
Brewfile: drop pipx (superseded by `uv tool`), iterm2 (Ghostty is the
actual terminal in use), the two legacy powerline fonts (superseded by the
Nerd Font already installed), the duplicate dockutil cask (keep the brew
formula), the standalone npm prettier entry (mise.toml is now the pinned
source, used via `mise exec`), and the no-op appdir arg on font casks
(fonts don't install to an appdir).

Add lefthook so formatting failures are caught before a push instead of a
CI run later — a single Go binary, no Python environment to manage. Scoped
to `make fmt_check` only; ansible-lint/nvim-check stay CI-only since
they're too slow for a commit hook. Installed via a new ansible task
(`lefthook install`, tagged lefthook/setup).
personal_hostname: Michaels-MacBook-Air compared against ansible_hostname,
but this machine's ansible_hostname is now "michael" (confirmed via `ansible
-m setup`) — the machine was renamed at some point and the comparison has
silently never matched since, meaning the paperless launch agent has never
actually been installed by this task despite looking configured.

Replace it with inventory group membership: ansible/inventory.ini's
[personal] group now lists the current hostname, and the task checks
`ansible_fqdn in groups['personal']`. This scales to a second machine (add a
[work] group) without another string literal to keep in sync, and a rename
only requires updating the inventory file in one place.
fsmonitor + untrackedCache pairs with the feature.manyFiles already set —
per `man git-config`, a background daemon watches the working tree so
`git status` skips a full filesystem scan. Also adds fetch.prune/pruneTags,
push.followTags, column.ui, diff.mnemonicPrefix, submodule.recurse, and
help.autocorrect=prompt — small quality-of-life defaults this repo was
missing relative to current git (2.50.1 here, well past the 2.35.1+ fsmonitor
requirement).
- theme now follows the system appearance (light:Catppuccin Latte,
  dark:Catppuccin Frappe), matching the BAT_THEME_DARK/LIGHT pair already
  set in fish/config.fish and delta's detect-dark-light. Previously pinned
  to dark, so the terminal was the one thing that didn't switch, and
  bat/delta would render light themes onto a dark background.
- Bind the quick terminal (cmd+`): all quick-terminal-* options were already
  present but nothing invoked toggle_quick_terminal, in this config or in
  Ghostty's defaults, so the feature was fully configured and unreachable.
- auto-update = off, since Ghostty is installed via the Brewfile cask and
  its own updater would otherwise silently diverge from what `brew bundle
  check` reports as installed.
- confirm-close-surface = false: tmux persists every session, so the
  confirmation prompt guards against nothing.

Not included: macos-option-as-alt for the tmux M-h/M-l binds (§21c) — needs
an interactive Option+h check that can't be verified headlessly, so it's
left as a note rather than a blind change.

Verified: `ghostty +validate-config` passes.
Global instructions were the one part of the agent tooling here that
wasn't version controlled (settings.json, hooks/, and skills/explain
already are). Seeds conventions the repo already demonstrates: uv over
pip/poetry, just/make task runners, ruff at line-length 100, and running
make fmt before committing. Linked into ~/.claude/CLAUDE.md via
agent_hooks.yml alongside the existing settings.json/cursor-rules links.
No indentation convention was enforced before formatters ran; editors were
relying on their own defaults for YAML, Lua, Python, fish, and shell files
in the meantime. Matches the conventions the formatters already enforce:
2-space default, 4-space Python, tabs in Makefile, and no
trailing-whitespace trim in Markdown (hard line breaks).
8 of 11 tracked scripts still could not run on system Python 3.9.6:
inert metadata (dbt), no metadata (zettel, bioinformatics), a Homebrew
python3.10 shebang (dump_schema), or no shebang at all (template_today).
Give each the same `# /// script` + `uv run --script` convention the
three working scripts already use, drop the leftover poetry symlink
from link_files.yml, and run scripts/check_pep723.py from fmt_check so
the convention cannot silently rot again.

Also restyle hammerspoon/init.lua to 2-space indent so stylua --check
agrees with .editorconfig.
Tracked settings.json was a symlink Claude Code writes on every /config
change, so the file churned with local state (model) and published
work-only marketplace entries. Keep shareable defaults (hooks,
permissions, effortLevel, tui, theme) in the repo; model, enabledPlugins,
and extraKnownMarketplaces now belong in ~/.claude/settings.local.json,
which is already gitignored. Document the split in the README.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant