2026-07-25 athena 125-Commit Catch-Up and the Intel node-gyp Wall

Follow-on from 2026-07-25 Remote Obsidian MCP Access Session Rollover: while on athena anyway, brought the box current.

What I set out to do

Pull the dotfiles repo on athena and run hm switch. Expected routine.

What I actually did

athena was 125 commits behind, sitting at 2026-07-07. Clean fast-forward, but two things had to be cleared first. nvim/lazy-lock.json had local drift (lazy.nvim had self-updated its own pin) against many upstream “chore: update nvim plugins” commits, so the pull would have refused. Stashed rather than discarded. And ntfy/templates/signoz.yml looked like an untracked file but is a symlink into the nix store: home-manager output landing inside the repo tree, because the repo is ~/.config.

Then the switch failed, and lied about it.

Failure 1, the exit code. I ran hm switch 2>&1 | tee log; echo "EXIT=${PIPESTATUS[0]}". athena’s shell is zsh, which has no PIPESTATUS (it uses $pipestatus, 1-indexed). The expansion was empty, the trailing echo became the last command, and the whole thing returned 0 while the build had actually died. Only grepping the log for ^error showed the truth.

Failure 2, the actual break. bitwarden-cli bundles the native lmdb module, which node-gyp compiles from source. That needs full Xcode, and athena has Command Line Tools only, so /usr/bin/xcodebuild is a stub that errors. It cascaded through home-manager-pathactivation-script → the generation. Nothing was applied.

Fixed by gating bitwarden-cli behind desktop, kept inline at its list position because packages.nix documents that gating must preserve ordering or every desktop host rehashes home-manager-path. A/B eval confirmed the aarch64 drvPath was byte-identical, and the package-list eval showed 0 bitwarden matches on x86_64-darwin against 1 on aarch64. Second switch landed: generation 10 → 11, bw correctly absent.

Also committed the previous session’s 2026-07-25 Lazy-Loading AGENTS.md and User-Only Skill Invocation work as three atomic commits, and fixed the root flake, which excluded x86_64-darwin and left athena with no direnv shell at all.

What was striking

  • The comment explaining an exclusion was wrong, and it was wrong in a way that hid the real constraint. The root flake blamed arrow-cpp via LiteLLM for excluding Intel. But that governs the desktop HM profile in nix/flake.nix; the root flake ships linters and formatters and has neither. The actual blocker was unstable dropping the platform. Believing the comment would have produced a fix that aborted. Same shape as the service-token reversal earlier today: a confidently-stated rationale on one side said nothing about the real constraint.
  • Two flakes, two pins. nix/flake.nix already solved x86_64-darwin with a nixpkgs-26.05-darwin input. The root flake never received it. Solving a problem once in a repo does not mean it is solved everywhere.
  • “Builds on Intel with 0 errors” was true until it wasn’t. That note has held since 2026-07-04 Home Manager athena Server Profile. The real rule is narrower: anything with a node-gyp native module fails, and it only ever shows on athena, because aarch64 gets a cache hit and never compiles. Expect the next one.
  • A trailing echo silently becomes your exit status. Worth remembering for any wrapped remote command, not just this one. set -o pipefail works in both shells; shell-specific array indexing does not.
  • The stale-comment and the lying-exit-code failures share a root: I trusted a summary (a comment, a status code) over the artifact it described.

direnv resolved, and a near-miss from brew upgrade

Pulled to bebad2a2 and direnv works on athena: loads the flake, uses the cached dev shell, resolves prek/stylua/ruff/shellcheck/just to store paths.

It looked broken first. nix develop --command true failed with [[ ! -v "${varName}" ]]: conditional binary operator expected, because -v needs bash 4.2+ and athena’s only bash is /bin/bash 3.2.57. An atlas control passed cleanly, which pointed at the platform rather than the flake. But direnv carries its own bash (bash_path = a nix bash-interactive-5.3p9), so it never touches the system one. The failure was an artifact of my SSH invocation, not the thing I was trying to test. Testing the proxy instead of the actual goal nearly produced a wrong conclusion.

Separately, a brew upgrade on the host bumped node to 26.5.0 while the MCP bridge had been running 21 days on the old one. Working, but only until something restarted it: a reboot would have been the first time mcpvault ever ran on node 26, and a failure there would have looked exactly like the outage this morning. Restarted it deliberately, under observation: new tree came up on Cellar/node/26.5.0, initialize returned mcpvault 1.28.1 in 4.5ms, edge still gives the healthy 401 Access challenge. cloudflared needed nothing (already 2026.7.3, which brew confirms is current, and its running process still maps a live binary rather than a deleted one).

Also gitignored the HM-generated /ntfy/ output, anchored like the existing /glance/ and /claude-ops/ entries so it cannot swallow a future source dir.

Top tomorrow

  1. Absent-data alert on the MCP bridge (carried over, still the highest-value item and the only thing that would have caught this morning’s rollover).
  2. athena’s generation 11 is stale — done. Switched to generation 12 on 47a7c3e0, then garbage collected: expired generations 3-11, 824 store paths deleted, 3.8 GiB freed (store 15G → 11G). GC ran clean, confirming the athena/atlas split holds: no .app bundles in athena’s store, so none of the App Management permission aborts that block GC on atlas. git status on athena is now completely clean for the first time, since the /ntfy/ ignore removed the last permanently-untracked file.
  3. athena’s Obsidian MCP setup is still hand-rolled outside Nix, so it drifts silently from the repo.retracted, this was wrong. Checked the history when challenged on it. The Homebrew setup is a documented decision from the 2026-07-03 runbook (“Toolchain — DECIDED: Homebrew-only, not the Nix dotfiles flake”), on the reasoning that nixpkgs 26.05 is the last release supporting x86_64-darwin (ends 2026) while Homebrew covers the hardware to ~2027. Sequencing forced it too: the MCP went up at 23:17 on 07-03, and athena’s HM profile did not exist until 2aff736 at 11:25 the next morning. It remains deliberate, since home-server.nix’s curated imports omit obsidian-remote-mcp.nix. The real loose end is far smaller: home.nix:63-67 still describes the retired NixOS box as owning the tunnel, which was logged as a follow-up that day and never done.

What was striking (second pass)

  • “Upgrade succeeded” and “the running process uses the upgrade” are different claims. brew cleanup deletes the old Cellar path out from under a live process. Checking the txt mapping in lsof distinguishes them; the version string alone does not.
  • A long-lived process silently pins a dependency version. The 21-day-old bridge was the only thing standing between node 26.5.0 and a surprise at next boot.