2026-08-04 Obsidian’s DMG Layout Change Blocks hm switch

What happened

Routine update -c (flake update → check → hm switch) died in the build phase. Third instance of the shape logged in 2026-07-24 langfuse wrapt Pin Blocks hm switch After Flake Update and 2026-07-13 Darwin Nixpkgs Regressions Blocking hm switch: a nixpkgs bump lands a package whose Darwin build nixpkgs CI never exercised.

nixpkgs moved from a5cbcfe (2026-08-01) to 104240a (2026-08-03), which carried obsidian 1.12.7 → 1.13.4. The build died in unpackPhase with chmod: cannot access 'Obsidian.app': No such file or directory, cascading through home-manager-applications, -fonts, -path, and -generation.

Cause: Obsidian changed its macOS DMG layout at 1.13.4. The app now sits inside a Obsidian 1.13.4-universal/ volume folder rather than at the image root, so the derivation’s sourceRoot = "Obsidian.app" no longer resolves. Confirmed directly by listing the fetched DMG with 7zz l -ba, which showed a single Obsidian 1.13.4-universal/Obsidian.app root.

The fix

Added a Darwin-gated entry to the existing overlay list in nix/flake.nix, alongside the pythonPackagesExtensions fixes: drop sourceRoot so stdenv autodetects the single directory the DMG unpacks to, and cp -R Obsidian.app out of it in installPhase. Verified hm switch applies clean, CFBundleShortVersionString on the deployed bundle reads 1.13.4, and just check passes. Committed as 0c6eedc.

Also widened the leading comment on the overlays list, which had claimed the list was Python-package-only and was no longer true.

What was striking

The searching, not the patching. Before writing anything I checked GitHub for an existing fix and found NixOS/nixpkgs#548462, merged to master at 2026-08-03T14:37Z. The lock had landed on a rev from earlier the same day, so it missed the fix by hours. That turned an authoring problem into a transcription one: the overlay is upstream’s diff verbatim, which means it is guaranteed to become a clean no-op deletion rather than a divergence I have to reconcile later.

The version-independence is the part worth keeping. My first working attempt hardcoded sourceRoot = "Obsidian ${version}-universal/Obsidian.app", which builds but re-breaks on any future layout tweak. Upstream’s shape (delete sourceRoot entirely, let stdenv find the single root) is correct for both layouts and needs no maintenance. The reusable rule: on a fresh post-flake-update build break, search merged-but-unreleased nixpkgs PRs before authoring an overlay. A channel rev is a snapshot, not the state of master, and a same-day fix is common enough to check first.

Not captured to project memory: the overlay carries its own drop condition in a comment (nixpkgs-unstable advances past dea0d9ee), and the general lesson is the search habit rather than an Obsidian-specific fact.