Context
We evaluated migrating to Nix/home-manager and decided against it — Stow + Homebrew fits the day-to-day workflow better. But the one genuine capability Nix had that we lack is pinned, reproducible package versions: Homebrew removed Brewfile.lock.json, formulae are rolling, and brew pin only blocks upgrades (it can't install or reproduce a specific version elsewhere). So today two machines that both "install from the repo" can end up on different tool versions.
Goal
Investigate how to get reproducible package versions across machines/environments without building our own lock system. We want the benefit (version parity) without owning bespoke lockfile tooling that we then have to maintain.
Approaches to evaluate (with trade-offs)
- Version-annotated snapshot —
brew list --versions / brew bundle dump captured to a checked-in file as a record (not enforced). Cheapest; documents but doesn't enforce.
- Runtime version managers with declared versions —
mise (or asdf) with a committed .tool-versions for node/ruby/rust/etc. Strong, portable, already declarative; overlaps with our FNM/rbenv/rustup setup — assess consolidation.
- Homebrew mechanisms — pinned tap revisions,
brew extract to a personal tap, or brew bundle against a frozen tap. Assess maintenance cost.
- Third-party declarative-but-not-Nix — devbox / flox (Nix under the hood, friendlier surface). Note where these reintroduce the Nix cost we rejected.
- Nix packages-only — last resort, packages layer only, dotfiles stay on Stow. Only if nothing lighter delivers parity.
Deliverable
A short recommendation: which layer(s) are worth pinning, the chosen mechanism, and the maintenance cost — explicitly ruling in/out a custom lock. Feeds directly into the portable-install work (see the Docker/SSH portability issue), since reproducing exact versions is what makes remote installs trustworthy.
Context
We evaluated migrating to Nix/home-manager and decided against it — Stow + Homebrew fits the day-to-day workflow better. But the one genuine capability Nix had that we lack is pinned, reproducible package versions: Homebrew removed
Brewfile.lock.json, formulae are rolling, andbrew pinonly blocks upgrades (it can't install or reproduce a specific version elsewhere). So today two machines that both "install from the repo" can end up on different tool versions.Goal
Investigate how to get reproducible package versions across machines/environments without building our own lock system. We want the benefit (version parity) without owning bespoke lockfile tooling that we then have to maintain.
Approaches to evaluate (with trade-offs)
brew list --versions/brew bundle dumpcaptured to a checked-in file as a record (not enforced). Cheapest; documents but doesn't enforce.mise(or asdf) with a committed.tool-versionsfor node/ruby/rust/etc. Strong, portable, already declarative; overlaps with our FNM/rbenv/rustup setup — assess consolidation.brew extractto a personal tap, orbrew bundleagainst a frozen tap. Assess maintenance cost.Deliverable
A short recommendation: which layer(s) are worth pinning, the chosen mechanism, and the maintenance cost — explicitly ruling in/out a custom lock. Feeds directly into the portable-install work (see the Docker/SSH portability issue), since reproducing exact versions is what makes remote installs trustworthy.