From d5430f8013af6d1b549466a5f7dd314ee275bcc5 Mon Sep 17 00:00:00 2001 From: Seth Messer Date: Tue, 23 Jun 2026 08:58:59 -0400 Subject: [PATCH 01/24] feat(migration): add mise dotfile classifier and reconcile targets --- .tickets/mbm-55qf.md | 44 +++ .tickets/mbm-qkmx.md | 40 +++ lat.md/migration/mise-bootstrap.md | 48 ++++ lat.md/migration/mise-parity-checklist.md | 323 ++++++++++++++++++++++ mise.toml | 302 ++++++++++++++++++++ scripts/mise/doctor | 52 ++++ scripts/mise/dotfile-preflight | 293 ++++++++++++++++++++ 7 files changed, 1102 insertions(+) create mode 100644 .tickets/mbm-55qf.md create mode 100644 .tickets/mbm-qkmx.md create mode 100644 lat.md/migration/mise-bootstrap.md create mode 100644 lat.md/migration/mise-parity-checklist.md create mode 100644 mise.toml create mode 100755 scripts/mise/doctor create mode 100755 scripts/mise/dotfile-preflight diff --git a/.tickets/mbm-55qf.md b/.tickets/mbm-55qf.md new file mode 100644 index 000000000..2d239e436 --- /dev/null +++ b/.tickets/mbm-55qf.md @@ -0,0 +1,44 @@ +--- +id: mbm-55qf +status: closed +deps: [] +links: + [ + mbm-buez, + mbm-c3sd, + mbm-ju5m, + mbm-xqjv, + mbm-m0rs, + mbm-9ov0, + mbm-8afn, + mbm-qkmx, + ] +created: 2026-06-22T21:33:33Z +type: feature +priority: 2 +assignee: Seth Messer +tags: [ready-for-development] +--- + +# Implement mise dotfile symlink classifier preflight + +Build a read-only preflight used by the mise bootstrap migration to inspect each target in mise.toml [dotfiles] before any link/adopt operation. It must follow symlink chains like ~/.config/ghostty -> /nix/store/...home-manager-files -> /nix/store/...hm_ghostty -> ~/.dotfiles/config/ghostty and classify final ownership. File hints: mise.toml, scripts/mise/doctor, scripts/mise/bootstrap-final, lat.md/migration/mise-parity-checklist.md, home/common/lib.nix. + +## Acceptance Criteria + +1. A script or doctor subcommand reads every mise.toml [dotfiles] target and prints a dry-run table with installed path, symlink chain, final target, and class. +2. Classes include repo-final, store-flake-source, store-generated, external-final, real-directory, real-file, missing, and conflict/unknown. +3. The script never mutates filesystem state and exits nonzero only for true conflicts or unreadable paths. +4. The report correctly identifies repo-final paths documented in lat.md/migration/mise-parity-checklist.md, including ghostty, hammerspoon, nvim, tmux, kitty, kanata macbook files, SSH config, and espanso app-support path. +5. lat.md/migration/mise-parity-checklist.md is updated if classification names or behavior change. +6. Existing validation still passes: lat_check and relevant shell lint/format checks. + +## Notes + +**2026-06-22T21:47:36Z** + +Implemented read-only scripts/mise/dotfile-preflight and wired scripts/mise/doctor plus mise task dotfiles:preflight. Classifier reads mise.toml [dotfiles], follows full symlink chains, reports target/source/final/class/status, and exits nonzero only on unknown/conflict. Validation run found safe repo-final targets: ghostty, hammerspoon, kitty, nvim, tmux, ~/bin; missing safe targets: fnox config, ~/.config/ssh, ~/.config/espanso; needs handling: generated mise config, real fish/kanata/.local/bin dirs, store-flake git files. Also confirmed mise targets for SSH and espanso are wrong vs current ~/.ssh/config and ~/Library/Application Support/espanso ownership. Checks: scripts/mise/dotfile-preflight, --json, shellcheck doctor/bootstrap-final, lat_check. scripts/mise/doctor still exits 1 because fnox command missing on this host, unrelated to classifier. + +**2026-06-22T21:48:53Z** + +Follow-up: added inventory-only checks for documented repo-final paths not yet represented correctly in mise.toml: ~/.ssh/config, ~/Library/Application Support/espanso, and ~/.config/kanata/macbook\*.kbd. Latest preflight summary: safe=13, needs-handling=7, conflicts=0. diff --git a/.tickets/mbm-qkmx.md b/.tickets/mbm-qkmx.md new file mode 100644 index 000000000..4ee5db7cb --- /dev/null +++ b/.tickets/mbm-qkmx.md @@ -0,0 +1,40 @@ +--- +id: mbm-qkmx +status: closed +deps: 4:1:deps: [, mbm-55qf] +links: [mbm-buez, mbm-c3sd, mbm-ju5m, mbm-xqjv, mbm-m0rs, mbm-55qf, mbm-9ov0, mbm-8afn] +created: 2026-06-22T21:33:33Z +type: task +priority: 2 +assignee: Seth Messer +tags: [ready-for-development] +--- + +# Reconcile mise dotfile targets with symlink ownership inventory + +Update mise dotfile ownership based on the symlink inventory so mise does not blindly map paths that Home Manager currently generates or maps to different app support locations. This should fix obvious mismatches and document retained/manual/generated cases. File hints: mise.toml, mise/dotfiles/fish, config/espanso, config/kanata, scripts/mise/doctor, lat.md/migration/mise-parity-checklist.md. + +## Acceptance Criteria + +1. mise.toml [dotfiles] is updated so repo-final targets map to the same final source paths used by current Home Manager. +2. Espanso target decision is explicit: either link ~/Library/Application Support/espanso to config/espanso or document and validate a supported XDG path. +3. Fish is not treated as equivalent to current Home Manager generated fish config unless missing snippets/functions are ported or explicitly deferred. +4. Store-generated and store-flake-source paths from the inventory are not silently overwritten by mise dotfiles. +5. mise bootstrap --dry-run or the new preflight reports no unexpected dotfile conflicts for known repo-final paths. +6. lat.md/migration/mise-parity-checklist.md reflects the final dotfile target decisions. + +## Notes + +**2026-06-23T12:28:32Z** + +Reconciled mise dotfile targets per classifier findings (mbm-55qf): + +1. ~/.config/ssh → ~/.ssh/config (individual file, correct path) +2. ~/.config/espanso → ~/Library/Application Support/espanso (app support path) +3. ~/.config/kanata → split into individual .kbd file targets (macbook.kbd, macbook-disabled.kbd) +4. Fish kept as directory target with note about deferred per-file reconciliation (mbm-8afn) +5. Git files kept as-is with store-flake-source note +6. mise config kept with store-generated note +7. mise.toml [dotfiles] section documented with inline comments explaining each group's status and deferral tickets +8. scripts/mise/dotfile-preflight KNOWN_INVENTORY cleared (all now mise.toml targets) + Preflight summary: safe=11, needs-handling=6, conflicts=0. lat_check passed. shellcheck passed. diff --git a/lat.md/migration/mise-bootstrap.md b/lat.md/migration/mise-bootstrap.md new file mode 100644 index 000000000..c2498dd0d --- /dev/null +++ b/lat.md/migration/mise-bootstrap.md @@ -0,0 +1,48 @@ +# Mise bootstrap migration + +Mise bootstrap is the proposed replacement orchestration layer for the current nix-darwin and Home Manager workstation setup. + +The migration keeps Nix installed for legacy flakes, devenv itself, devenv projects, and external Nix repos, but stops treating Nix as the primary owner of macOS user configuration. + +## Ownership model + +Mise owns repeatable user workstation convergence where its experimental bootstrap model is strong enough. + +- `mise.toml` is the workstation bootstrap config. +- `mise/Brewfile` owns GUI casks, Mac App Store apps, and the few Homebrew formulae needed to drive that app layer. +- Mise `[tools]` owns dev CLIs and runtimes, preferring Aqua-backed tools where the mise registry supports them. +- `aube` is the preferred npm package manager and is installed through mise alongside Node. +- `mise/fnox/config.toml` replaces OpNix declarations for 1Password-backed user secrets. +- `scripts/mise/*` owns special cases that need imperative macOS behavior. +- `scripts/mise/dotfile-preflight` is the read-only guard for mise `[dotfiles]` adoption; it follows symlink chains and reports ownership before bootstrap mutates files. Dotfile targets were reconciled against the classifier inventory in `mbm-qkmx`: SSH, espanso, and kanata paths fixed; fish and git file handling deferred to `mbm-8afn`. +- Existing `config/*`, `bin/*`, and selected `home/common/programs/*` files remain source material while the migration is staged. + +## Host overlays + +Both MacBook Pro hosts share one baseline and differ only where hardware or work context requires it. + +`megabookpro` and `workbookpro` share app lists, dotfiles, shell setup, Pi setup, Helium setup, and fnox secret shape. Host-specific logic lives in scripts that branch on `hostname -s`, such as llama.cpp launch parameters. Settings-sync is not migrated in v1. + +## Boundaries + +Mise bootstrap is not a full nix-darwin or Home Manager clone. + +Keep special handling for privileged/system behavior: Determinate Nix install or repair, 1Password GUI placement in `/Applications`, Okta Verify package installation, kanata TCC-safe binary path, complex nested macOS plist writes, and any service that needs LaunchDaemons rather than user LaunchAgents. + +## Secrets + +Fnox becomes the user-land secret resolver and keeps 1Password as the vault. + +The migration renders legacy files under `~/.config/fnox/secrets/` so Pi, shell startup, Apple notarization, and `.s3cfg` keep working while callers move from OpNix paths. The 1Password service account token must stay encrypted or local-only, never committed as plaintext. + +## Project environments + +Mise project configs gradually replace `devenv.nix` where project needs fit tool versions, env files, tasks, and lightweight service wrappers. + +The root `mise.toml` covers `.dotfiles`; staged examples in `mise/projects/` cover `rx` and `verify-doctor`. Nix/devenv remains available for projects that still need Nix services, overlays, or exact nixpkgs package composition. + +## Version gate + +The migration requires mise `2026.6.6` or newer because the current installed `2026.6.5` does not include bootstrap/dotfiles commands. + +Until mise is upgraded, `mise bootstrap --dry-run` reports unknown `bootstrap` and `dotfiles` fields and stops on the hard `min_version` guard. This is expected and prevents accidental partial application with an older CLI. diff --git a/lat.md/migration/mise-parity-checklist.md b/lat.md/migration/mise-parity-checklist.md new file mode 100644 index 000000000..dedc7cb07 --- /dev/null +++ b/lat.md/migration/mise-parity-checklist.md @@ -0,0 +1,323 @@ +# Mise migration parity checklist + +This checklist tracks parity between current Nix ownership and the staged mise bootstrap migration before destructive bootstrap is allowed. + +Use this document as the durable audit ledger for ticket `mbm-xqjv`. Each row must eventually have discovery, research, validation, and plan decisions checked off before Nix ownership is removed. + +## Status legend + +Migration status records whether the current mise path can safely replace the current Nix path. + +- `not started` — Nix behavior is known, but no target implementation exists. +- `partial` — target exists, but it misses behavior, safety, or validation. +- `safe` — target is repeatable, idempotent, and validated enough for staged use. +- `unsafe` — target exists but must not be applied destructively yet. +- `deferred` — intentionally out of v1, or explicitly retained under Nix/manual for now. + +## Audit workflow + +Each responsibility gets four checkmarks before cutover. + +- [ ] Discovered: current Nix/Home Manager owner and source files identified. +- [ ] Researched: upstream constraints, macOS behavior, and host differences understood. +- [ ] Validated: non-destructive command or manual check exists and has been run on target host. +- [ ] Planned: target owner chosen with workaround or explicit retain/manual reason. + +## Host requirements + +Two MacBook Pros are hard v1 targets; non-macOS hosts are future targets. + +| Item | Current owner/source files | Target owner | Hosts | Validation | Status | Not repeatable yet | Workaround or retain reason | +| ------------------------------------------------------------------------- | ------------------------------------------------------------ | -------------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: `megabookpro` | `flake.nix`, `hosts/megabookpro.nix`, `home/megabookpro.nix` | mise bootstrap plus scripts; current machine | `megabookpro` | `hostname -s`; `mise bootstrap --dry-run`; `scripts/mise/doctor` | partial | Dry-run blocked by dotfile overwrite safety; several privileged areas still Nix-owned | Use as first validation host after unsafe rows become safe. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: `workbookpro` | `flake.nix`, `hosts/workbookpro.nix`, `home/workbookpro.nix` | same baseline plus host branches in scripts | `workbookpro` | Confirm hostname, partial dotfiles state, 1Password, Okta, kanata, llama tuning | partial | Host has partial Nix dotfiles installation; bootstrap must repair or clearly report | One-command bootstrap must support partial install before this host is cut over. | +| [x] Discovered [x] Researched [x] Validated [x] Planned: future non-macOS | none in current flake beyond Darwin assumptions | future platform bootstrap | UGREEN NAS, Raspberry Pi/Home Assistant | Not v1; document platform gates only | deferred | No Linux/BSD owner model yet | Not v1 blocker; keep Mac-first. | + +## One-command bootstrap requirement + +README install flow must become safe, idempotent, and useful from Terminal.app on fresh or half-configured Macs. + +| Item | Current owner/source files | Target owner | Hosts | Validation | Status | Not repeatable yet | Workaround or retain reason | +| ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------- | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: pasteable command | `README.md`, `scripts/aarch64-darwin_bootstrap.sh`, `scripts/mise/*` | README `curl` command that fetches bootstrap script | shared Macs | Fresh VM/manual dry-run; command prints plan before changes | partial | README still points at Nix-first installer | Keep current README until mise bootstrap is safe. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: idempotence | `scripts/mise/bootstrap-final`, `scripts/mise/doctor` | `scripts/mise/bootstrap-final` plus doctor/repair checks | shared Macs | Run twice in dry-run/safe mode; compare no-op output | partial | Scripts call installers and Brew without full preflight/diff report | Gate destructive steps behind checks and clear prompts. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: partial setup repair | `scripts/mise/ensure-homebrew`, `scripts/mise/ensure-determinate-nix`, current Nix activation | bootstrap preflight/doctor | `workbookpro` especially | Detect existing Nix, Brew, dotfile symlinks, launchd agents, app receipts | partial | Broken symlinks and Nix-store-owned paths are not classified yet | Report unsafe paths; do not overwrite until backup/adopt policy exists. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: hostname setup | `hosts/common.nix`, `flake.nix` host outputs | bootstrap script prompt and `scutil` writes | shared Macs | `scutil --get ComputerName`; `HostName`; `LocalHostName`; `hostname -s` | not started | mise does not prompt/repair hostnames | Prompt `megabookpro`/`workbookpro`, then set all macOS hostname fields. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: Command Line Tools and EULAs | Nix bootstrap assumes tools enough to run; Xcode MAS in `mise/Brewfile` | bootstrap script | shared Macs | `xcode-select -p`; `xcodebuild -license check`; MAS/Xcode receipt | partial | CLT/Xcode EULA may be interactive; MAS auth can block | Automate CLT where possible; clearly report EULA/MAS manual gates. | + +## System and platform ownership + +These rows cover nix-darwin system responsibilities from `hosts/` and `modules/`. + +| Item | Current owner/source files | Target owner | Hosts | Validation | Status | Not repeatable yet | Workaround or retain reason | +| ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ----------- | -------------------------------------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: Determinate Nix retained | `hosts/common.nix`, `modules/system.nix`, `/etc/nix/nix.custom.conf` | Nix retained plus `scripts/mise/ensure-determinate-nix` | shared Macs | `nix --version`; `/nix/nix-installer`; `/etc/nix/nix.conf`; `/etc/nix/nix.custom.conf` | partial | `nix.custom.conf` generation not reproduced; determinate repair detects marker only | Retain Nix for flakes/devenv/external repos; script should repair or report custom conf drift. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: user, shell, locale, paths | `hosts/common.nix`, `home/common/default.nix`, `mise.toml` `[env]`, `[bootstrap.user]` | mise env plus bootstrap shell setup | shared Macs | `dscl . -read /Users/seth UserShell`; `echo $SHELL`; env snapshot | partial | fish shell path changes from Nix store to `/opt/homebrew/bin/fish`; global env not all mapped | Use Brew fish; render shell env in fish config; keep Nix path only for retained tools. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: fonts | `hosts/common.nix` `fonts.packages` | Brew casks/font installer script or Nix retained | shared Macs | `system_profiler SPFontsDataType`; app font picker spot checks | not started | No font install source in mise/Brewfile | Prefer Brew font casks or script; retain Nix until complete. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: power/firewall/DNS/Tailscale | `modules/system.nix`, `hosts/common.nix` | scripts/mise privileged system script or manual/Nix retained | shared Macs | `pmset -g`; `socketfilterfw`; `networksetup -getdnsservers Wi-Fi`; `tailscale status` | not started | Needs sudo/system domains; mise macOS defaults do not cover all | Keep Nix/manual until a privileged idempotent script exists. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: sudo/PAM Touch ID | `modules/system.nix`, `modules/darwin/kanata.nix` | privileged script or Nix retained | shared Macs | `/etc/pam.d/sudo_local`; `sudo -l`; sudoers lint with `visudo -cf` | not started | sudoers/PAM writes are privileged and risky | Retain Nix until script uses temp files, `visudo`, and exact diff. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: Homebrew/MAS app layer | `modules/brew.nix`, `home/common/packages.nix`, `mise/Brewfile` | Brew Bundle plus MAS | shared Macs | `brew bundle check --file mise/Brewfile`; `mas list` | partial | MAS auth and cask drift not fully handled | Brew/MAS target OK after doctor reports auth/manual gates. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: nix-darwin activation behaviors | `modules/system.nix`, `home/common/services.nix`, `lib/*` | scripts/mise plus retained Nix where needed | shared Macs | Review generated plists/files; compare before/after inventory | partial | Home Manager launchd workaround and app copy activation not ported | Recreate only necessary parts; do not clone entire HM activation. | + +## macOS defaults and user preferences + +Mise covers simple defaults; nested plists and sensitive app container prefs need scripts or manual handling. + +| Item | Current owner/source files | Target owner | Hosts | Validation | Status | Not repeatable yet | Workaround or retain reason | +| ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------- | -------------------------------------------- | ----------- | ---------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------- | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: Dock/Finder/keyboard/trackpad/simple defaults | `modules/system.nix`, `mise.toml` `[bootstrap.macos.*]` | mise bootstrap macOS defaults | shared Macs | `defaults read` selected domains; UI spot checks | partial | Dock persistent apps and some typed nix-darwin keys not mapped | Safe after field-by-field compare and apply test. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: complex defaults | `modules/system.nix`, `scripts/mise/apply-macos-complex-defaults` | script renderer under `scripts/mise` | shared Macs | `defaults read com.apple.symbolichotkeys`; `activateSettings -u` | partial | Symbolic hotkeys and text replacements are not fully rendered | Build deterministic plist renderer; avoid ad hoc nested shell writes. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: text replacements | `modules/system.nix` `NSUserDictionaryReplacementItems` | script renderer or manual import | shared Macs | `defaults read NSGlobalDomain NSUserDictionaryReplacementItems` | partial | Current script does not write replacements | Need non-hacky plist write or keep manual. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: sandboxed app prefs | comments in `modules/system.nix` | manual | shared Macs | App settings UI checks | deferred | macOS Sequoia blocks defaults writes to sandbox containers in activation context | Explicit manual setup; not v1 automation. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: Raycast prefs | `modules/system.nix`, `mise/Brewfile` | manual or Raycast export/import if available | shared Macs | Raycast settings UI; `defaults read com.raycast.macos` | not started | No migration script; some permissions are app-managed | Prefer manual/export; avoid fragile defaults if sandbox/TCC applies. | + +## Symlink ownership inventory + +Home Manager currently creates several different symlink shapes, so mise adoption must resolve the whole chain before deciding whether a path is safe. + +A visible `~/.config/foo -> /nix/store/...-home-manager-files/...` symlink is not enough evidence. Some entries continue through a Home Manager indirection symlink and end at live repo source under `~/.dotfiles/config`; others end at immutable Nix store files generated from Nix expressions or copied from the flake source. + +### Live out-of-store config links + +These paths look Nix-owned at first glance but resolve back to editable repo config. Mise can adopt these as dotfiles after it verifies the final target is the expected repo path. + +| Installed path | Final target | Current owner/source files | Migration note | +| --------------------------------------- | ------------------------------------------------ | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| `.ssh/config` | `~/.dotfiles/config/ssh/config` | `home/common/programs/ssh/default.nix`, `config/ssh/config` | Move to mise dotfile or keep explicit SSH target. | +| `~/.config/ghostty` | `~/.dotfiles/config/ghostty` | `home/common/programs/ghostty/default.nix`, `config/ghostty` | Safe dotfile candidate after app install. | +| `~/.config/hammerspoon` | `~/.dotfiles/config/hammerspoon` | `home/common/programs/hammerspoon/default.nix`, `config/hammerspoon` | Safe link candidate, but generated fragments still needed. | +| `~/.config/kanata/macbook.kbd` | `~/.dotfiles/config/kanata/macbook.kbd` | `home/common/programs/kanata/default.nix`, `modules/darwin/kanata.nix`, `config/kanata` | Config file link is safe; daemon/TCC/bar behavior is not. | +| `~/.config/kanata/macbook-disabled.kbd` | `~/.dotfiles/config/kanata/macbook-disabled.kbd` | `home/common/programs/kanata/default.nix`, `modules/darwin/kanata.nix`, `config/kanata` | Config file link is safe; active `kanata.kbd` symlink is mutable and managed by activation/Hammerspoon. | +| `~/.config/kitty` | `~/.dotfiles/config/kitty` | `home/common/programs/kitty/default.nix`, `config/kitty` | Safe dotfile candidate. | +| `~/.config/nvim` | `~/.dotfiles/config/nvim` | `home/common/programs/nvim/default.nix`, `config/nvim` | Safe config link; package/wrapper/runtime parity separate. | +| `~/.config/tmux` | `~/.dotfiles/config/tmux` | `home/common/programs/tmux/default.nix`, `config/tmux` | Safe config link; `~/.local/share/tmux/nix.conf` is generated. | +| `~/Library/Application Support/espanso` | `~/.dotfiles/config/espanso` | `home/common/programs/espanso/default.nix`, `config/espanso` | Important: current live path is not `~/.config/espanso`; mise must target app support path or prove espanso reads XDG. | + +Other out-of-store links: `~/bin -> ~/.dotfiles/bin`, `~/.local/bin/emate -> /Applications/MailMate.app/Contents/Resources/emate`, and `~/iclouddrive -> ~/Library/Mobile Documents/com~apple~CloudDocs`. + +### Store-backed repo file links + +These paths point at the flake source copied into `/nix/store`, not the live checkout. Editing the installed target is impossible; mise should link them directly to working-tree files if they should stay editable. + +- Git globals: `~/.gitconfig`, `~/.gitignore`, `~/.ignore` from `home/common/programs/git/`. +- Pi agent files: `~/.pi/agent/AGENTS.md`, `APPEND_SYSTEM.md`, `keybindings.json`, `models.json`, `mcp.json`, custom extensions, custom skills, and custom prompts from `home/common/programs/pi-coding-agent/`. +- Tool files: `~/.config/eza/theme.yml`, `~/.config/ripgrep/rc`, `~/.config/yazi/plugins/smart-enter.yazi`, bat theme source, and MailMate Neovim bundle files. + +### Generated Nix store files + +These paths are generated from Nix expressions and need a renderer, committed replacement file, or explicit manual/retained owner under mise. + +- Shell and session files: fish `config.fish`, `conf.d/*`, `functions/*`, `.bashrc`, `.bash_profile`, `.profile`. +- Generated app/tool configs: `~/.config/starship.toml`, `shade/config.json`, `shade-next/config.toml`, `karabiner/karabiner.json`, `process-compose/*.yaml`, `slk/config.toml`, `surfingkeys/config.js`, `sesame/config.jsonc`, 1Password SSH agent config, `allowed_signers`. +- Generated fragments: `~/.local/share/hammerspoon/nix_path.lua`, `~/.local/share/hammerspoon/fragments/shade-next.lua`, `~/.local/share/tmux/nix.conf`. +- Package-generated config: Home Manager bat/fd/direnv/fontconfig/fish plugin files, Neovim plugin pack, browser external-extension JSON files, Helium/Brave dictionaries, MailMate keybindings/layouts, and keep files. + +### Adoption rule + +Mise preflight must classify every target by following symlinks to the final path. + +`scripts/mise/dotfile-preflight` reads `mise.toml` `[dotfiles]`, expands each installed target, follows the complete symlink chain, and prints a dry-run table plus chains. It also checks a small inventory-only set for legacy repo-final paths that current `mise.toml` does not target yet. It never mutates filesystem state and exits nonzero only for unreadable paths, broken symlink chains, loops, special files, or other `unknown/conflict` results. + +Classes: `repo-final`, `store-flake-source`, `store-generated`, `external-final`, `real-directory`, `real-file`, `missing`, or `unknown/conflict`. Only `repo-final` and `missing` are safe for automatic link adoption without a backup decision; every other class needs migration-specific handling. + +### Current mise dotfile classification + +The first classifier run validates the known repo-final links and exposes target mismatches that should drive the next migration ticket. + +| Status | Class | Origin | Target | Finding | +| -------------- | -------------------- | ----------- | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | +| safe | `repo-final` | `mise.toml` | `~/.config/ghostty` | Resolves through Home Manager store links to `~/.dotfiles/config/ghostty`. | +| safe | `repo-final` | `mise.toml` | `~/.config/hammerspoon` | Resolves through Home Manager store links to `~/.dotfiles/config/hammerspoon`; generated fragments deferred (mbm-8afn). | +| safe | `repo-final` | `mise.toml` | `~/.config/kitty` | Resolves through Home Manager store links to `~/.dotfiles/config/kitty`. | +| safe | `repo-final` | `mise.toml` | `~/.config/nvim` | Resolves through Home Manager store links to `~/.dotfiles/config/nvim`. | +| safe | `repo-final` | `mise.toml` | `~/.config/tmux` | Resolves through Home Manager store links to `~/.dotfiles/config/tmux`; generated fragment deferred (mbm-8afn). | +| safe | `repo-final` | `mise.toml` | `~/bin` | Resolves through Home Manager store links to `~/.dotfiles/bin`. | +| safe | `repo-final` | `mise.toml` | `~/.ssh/config` | Fixed target from `~/.config/ssh`; resolves through HM store links to `~/.dotfiles/config/ssh/config`. | +| safe | `repo-final` | `mise.toml` | `~/Library/Application Support/espanso` | Fixed target from `~/.config/espanso`; resolves through HM store links to `~/.dotfiles/config/espanso`. | +| safe | `repo-final` | `mise.toml` | `~/.config/kanata/macbook*.kbd` | Split from parent directory target to individual file links; daemon/bar migration deferred (mbm-ju5m). | +| safe | `missing` | `mise.toml` | `~/.config/fnox/config.toml` | No installed target exists yet; link creation is safe after parent directory handling. | +| needs handling | `store-generated` | `mise.toml` | `~/.config/mise/config.toml` | Current file is Nix-generated; mise should replace it only through an explicit bootstrap handoff. | +| needs handling | `real-directory` | `mise.toml` | `~/.config/fish` | Directory contains generated/store-linked files and mutable state; per-file reconciliation deferred (mbm-8afn). | +| needs handling | `store-flake-source` | `mise.toml` | `~/.gitconfig`, `~/.gitignore`, `~/.ignore` | Current files are copied into the Nix store from repo sources; mise should link live working-tree sources. | +| needs handling | `real-directory` | `mise.toml` | `~/.local/bin` | `symlink-each` target collides with existing mutable directory; individual entries need reconciliation. | + +## Dotfiles and shell + +Dotfile cutover must avoid overwriting Nix-store links, repo-final links, or mutable user state without explicit adoption/backups. + +| Item | Current owner/source files | Target owner | Hosts | Validation | Status | Not repeatable yet | Workaround or retain reason | +| ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | ----------- | ------------------------------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| [x] Discovered [x] Researched [x] Validated [x] Planned: dotfile symlink safety | `home/common/default.nix`, `home/common/programs/*`, `mise.toml` `[dotfiles]`, `scripts/mise/dotfile-preflight` | mise dotfiles with adopt/backup policy | shared Macs | `scripts/mise/dotfile-preflight`; `mise bootstrap --dry-run` after remaining reconciliation | partial | SSH, espanso, and kanata targets reconciled; fish, git files, mise config, `~/.local/bin` still need explicit reconciliation | Use classifier output to update mise targets and adoption/backup policy before dry-run. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: fish shell | `home/common/programs/fish/default.nix`, `mise/dotfiles/fish`, `mise.toml` | mise dotfile plus Brew fish | shared Macs | `fish -lc 'status fish-path; functions jj; echo $PLUG_EDITOR'` | partial | Generated secret/env loading and HM snippets may be missing | Port only required snippets; validate interactive shell. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: bash/zsh shell init | `home/common/programs/bash`, `home/common/programs/opnix/default.nix` | mise dotfile or script-rendered snippets | shared Macs | `bash -lc 'env'`; zsh login check if used | partial | fnox shell loading parity missing | Render/source fnox env snippets for all supported shells. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: git/jj/ssh signing | `home/common/programs/git`, `home/common/programs/jj`, `home/common/programs/ssh`, `modules/darwin/_1password.nix`, `mise.toml` | mise dotfiles plus 1Password app/CLI | shared Macs | `git config --global --list`; `jj config list`; `ssh-add -l`; test signed commit dry-run | partial | 1Password op-ssh-sign path may differ under cask; CLI integration unverified | Validate `/Applications/1Password.app/Contents/MacOS/op-ssh-sign`; keep exact path. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: Neovim/pinvim config | `config/nvim`, `home/common/programs/nvim`, `home/common/programs/pi-coding-agent`, `mise/bin/pinvim` | mise dotfiles plus wrappers/scripts | shared Macs | `nvim --version`; `pinvim --help`; pinvim health/manual E2E | partial | Nix-provided wrapper/runtime paths not fully mirrored | Keep scripts thin; validate real Nvim + Pi session before cutover. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: tmux/kitty/ghostty/yazi/starship/ripgrep/fd/bat/eza/fzf/zoxide | `config/*`, `home/common/programs/*`, `mise.toml` tools/dotfiles | mise dotfiles plus Aqua/Brew tools | shared Macs | launch each CLI; open Ghostty; `tmux source-file`; config smoke tests | partial | Some generated fragments/env vars may be missing | Standard dotfile adoption plus smoke test enough. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: Surfingkeys/browser configs | `home/common/programs/surfingkeys`, browser modules | dotfiles/manual browser extension config | shared Macs | Browser extension settings check | not started | Browser extension state not owned by mise | Manual/export unless reliable browser profile sync is chosen. | + +## Packages, GUI apps, and custom packages + +Package rows map broad package ownership; individual program modules remain listed in the program backlog. + +| Item | Current owner/source files | Target owner | Hosts | Validation | Status | Not repeatable yet | Workaround or retain reason | +| --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------------------- | ----------- | ----------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: bootstrap-critical CLIs | `hosts/common.nix`, `home/common/packages.nix`, `mise.toml` `[bootstrap.packages]`, `[tools]` | Brew for system CLIs; Aqua/mise for dev CLIs | shared Macs | `command -v just git curl vim fish rg fd jq` | partial | Some Nix-only tools not moved or intentionally retained | Keep Nix for `devenv`, nix tools, exact Nix packages. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: dev languages/toolchains | `home/common/packages.nix`, `home/common/programs/rust`, `mise.toml` `[tools]` | mise tools, except Nix/devenv retained | shared Macs | `node --version`; `python --version`; `rustc --version`; `uv --version` | partial | Nix language servers and helper CLIs not all mapped | Move low-risk CLIs to Aqua; retain hard Nix packages until replacement chosen. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: GUI apps | `home/common/packages.nix`, `modules/brew.nix`, `mise/Brewfile` | Brew casks/MAS plus special scripts | shared Macs | `brew bundle check`; `/Applications` and `~/Applications` inventory | partial | App placement differs from HM copyApps; TCC/signing effects untested | Use casks where normal; special apps get scripts. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: custom Nix app overlay | `pkgs/default.nix`, `pkgs/*.nix`, `lib/mkApp.nix`, `lib/builders/*` | Brew/upstream downloads/scripts or Nix retained | shared Macs | Per-app codesign/spctl and launch checks | partial | Tidewave, Helium, Brave Nightly wrappers, Handy, slk, custom MCP package not fully replaced | Keep Nix or create focused scripts only where needed. | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: fonts and icon resources | `hosts/common.nix`, `config/kanata/icons` | Brew/font script plus repo assets | shared Macs | font list; kanata-bar icon load | not started | No Brew font layer | Add font casks or retain Nix. | + +## Services and launchd + +Launchd migration must handle unload/reload safely, not duplicate Home Manager's broken macOS 15 `bootout --wait` behavior. + +| Item | Current owner/source files | Target owner | Hosts | Validation | Status | Not repeatable yet | Workaround or retain reason | +| ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------- | ---------------------------- | --------------------------------------------------------------------- | ----------- | ----------------------------------------------------------------------- | ----------------------------------------------------------- | +| [x] Discovered [x] Researched [ ] Validated [x] Planned: user LaunchAgents general | `home/common/services.nix`, `home/common/programs/*`, `mise.toml` launchd agents | mise bootstrap launchd plus script preflight | shared Macs | `launchctl print gui/$UID/