2026-07-20 lib.uv Refactor and athena 26.05 Pin

What I set out to do

Started as a discussion, not a task. A long thread on where the Nix - Home Manager module set sits relative to AOP / feature-oriented programming and the dendritic pattern, which surfaced two concrete pieces of work: the uvLib helper was distributed the wrong way, and athena (x86_64-darwin) had been frozen by a nixpkgs change. Both landed on main.

What I actually did

The framing thread (feeds the atomic notes)

Worked through AOP vs FOP against the actual tree, grounded in cloned copies of mightyiam/infra, vic/den, Misterio77, EmergentMind, srid rather than recall. Findings worth keeping:

  • Squatting programs.*/services.* for repo-declared options is the dominant practice, not a smell. Nobody uses a my.*/custom.* prefix; cross-cutting concepts get bare top-level nouns (hostSpec, colorscheme). There is no official position in the nixpkgs/HM manuals. So local.* is unusual only in having a prefix at all, and programs.signoz squatting is fine. A duplicate option declaration is a loud eval error, not a silent hazard (verified).
  • Dendritic lists boolean enable options as an anti-pattern (“importing a module should enable the feature it provides”). That is only coherent with curated imports; it is incompatible with auto-discovery by construction. Our desktop listFilesRecursive forces the flags. So the capability model is the tax auto-discovery charges, not an independent design choice.
  • import-tree (vic) is a packaged listFilesRecursive; the load-bearing part of dendritic is the named bucket, not the glob. We import everything into one bucket (annotation-heavy); dendritic imports into many (composition-heavy). Duals: a dead flag is silent for us, a missed guard reaches the build for them.
  • Wrote The Nix Module System Has No Dependency Resolution: import is the only edge, imports+dedup is manual dependency declaration not resolution, and the only native expression of “A requires B” is an assertions that fails, never one that resolves. This is exactly why the uvLib/claude.nix coupling could not be auto-satisfied.

lib.uv refactor (commit 7b40430)

uvLib was injected via _module.args.uvLib: python.nix set the real builder under mkIf local.python.enable, base.nix defaulted it to null, consumers guarded on hasUv = uvLib != null. That is a hidden transitive dependency — a module’s value depended on whether python.nix was imported, an edge written nowhere. claude.nix dereferenced it unconditionally, safe only because the one host with python.enable=false also happened not to import claude.nix.

Moved the builder to nix/lib/uv.nix, exposed as lib.uv.mkProject by extending the module-arg lib at the flake boundary in mkHome. Verified the mechanism against HM source: homeManagerConfiguration takes lib ? pkgs.lib, and modules/default.nix re-extends it with lib.hm via stdlib-extended.nix; .extend composes, so modules see both lib.uv and lib.hm. This is the EmergentMind/Misterio lib.extend convention, applied to a helper that closes over flake inputs (partial-applied at the boundary, like Misterio’s import ./lib/colors.nix { lib = base; }).

The deeper fix was decoupling the builder from the capability flag: building a local tool is a pure function of flake inputs, orthogonal to whether the host provisions the Python stack. local.python.enable had conflated them. Consequence: shell.nix/claude.nix build their venvs unconditionally (self-contained, no uv CLI at runtime), the hasUv gate is gone, and generateCheckUpstreamIssuesCompletion — a uv run tool that does need the CLI — now gates on local.python.enable specifically. The claude.nix bug is fixed at the root: no null to deref.

athena 26.05 pin (commit 20d7075)

nixpkgs-unstable dropped x86_64-darwin (Intel Mac sunset), so the shared unstable nixpkgs aborts instantiating that system. athena was frozen. Pinned x86_64-darwin’s nixpkgs to the maintained nixpkgs-26.05-darwin branch (last release supporting the platform, tip current today), selected per-system in perSystem’s _module.args.pkgs and the base16 helper.

Two decisions I reversed on evidence:

  • Not a matched HM pin. First added home-manager release-26.05 too; eval failed on programs.fzf.changeDirWidget does not exist — our modules use HM-master-only options. So HM stays on master, only nixpkgs moves. That is the minimal delta from athena’s prior working env.
  • Not a frozen unstable SHA. Tempting for minimal skew, but 26.05-darwin is maintained and is exactly what nixpkgs’ deprecation warning recommends.

The HM-master-vs-26.05 skew surfaced one assertion: HM master defaults enableNushellIntegration on and asserts fzf >= 0.73; 26.05 ships 0.72. We never use nushell, so it is now explicitly off (harmless everywhere).

What was striking

  • The whole uvLib mess traces to one line: home.nix’s listFilesRecursive. Auto-discovery → must-have-flags → ambient injection → the null dance → the bug. The refactor didn’t add a feature; it undid a coupling that auto-discovery had forced.
  • Three times this session a “last available version” or “which lib” question had a non-obvious right answer that only reading source (HM eval-config.nix, the fzf assertion, the nixpkgs branch dates) settled. 26.05-darwin tip being dated today is what killed the frozen-SHA option.
  • atlas drvPath stayed zvdqhbqmm3k9 across every step of both changes, and athena 878f8pvaz36 across the merge. The list-ordering discipline (reference_nix_module_nesting_reorders_lists) held.

Still open

  • athena needs a real hm switch to fully validate: eval is clean, but x86_64-darwin cannot build on the aarch64 host, so build-time skew in unstable-following inputs (stylix, the uv2nix/pyproject trio) built against 26.05 pkgs can only surface there. Expected harmless warning on switch: home.enableNixpkgsReleaseCheck flagging the intentional HM/nixpkgs mismatch; silencing it on the server profile is a one-line follow-up.
  • The two flake.nix commits both restructured mkHome, so the branch merge conflicted; resolved by re-applying the refactor surgically onto main rather than fighting diff3 markers.