feat(flake): #134 — unstable.<pkg> in the downstream, one input, no hand pins
All checks were successful
Check / eval (push) Successful in 4m16s

Nomarchy now carries a nixpkgs-unstable input and exposes it through
overlays.default as an `unstable` attrset: in the template's
`home.packages = with pkgs; [ … ]` a user writes `unstable.lmstudio`
and is done. Downstream stays one locked input; nomarchy-pull moves it.
lib.nix needed zero changes — mkFlake's pkgs already applies the
overlay. The import is plain and non-recursive, so configs that never
reference unstable. pay nothing beyond the input fetch; allowUnfree
mirrors the base set so the motivating lmstudio resolves.

Home packages are the supported surface (documented convention). Costs
stated in the README, not hidden: you own the pinned+unstable
combination; a second toolchain lands in your closure (~690 of ~4k
paths shared, measured on lmstudio); it moves when the lock moves.
Lock bumps now move two channels — VERIFICATION §5 and CONVENTIONS
updated accordingly. flake.lock gained exactly one node
(nixos-unstable @ 753cc8a); nothing else moved.

Verification: V1 — nix flake check --no-build green; new eval-only
checks.unstable-seam green (26.05pre vs 26.11pre lib.version, no
builds); template HM activation built via mkFlake with unstable.hello
enabled and its closure carries the unstable store path (pg2zfr… vs
pinned nm7p8w…). Implementation by a Sonnet subagent; design and
review on Fable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 15:16:29 +01:00
parent 2f5c1c8b99
commit 07400ab4f9
9 changed files with 108 additions and 28 deletions

View File

@@ -519,6 +519,17 @@ reload # exec zsh (reload the shell)
line to slim the machine, uncomment an extra (a browser, email, full TeX line to slim the machine, uncomment an extra (a browser, email, full TeX
Live), or add your own. No `nomarchy.apps.*` toggles — a package list is Live), or add your own. No `nomarchy.apps.*` toggles — a package list is
already its own opt-out, so the distro doesn't impose these or wrap them. already its own opt-out, so the distro doesn't impose these or wrap them.
- **A newer individual app (unstable channel, #134):** Nomarchy carries a
second, newer nixpkgs channel and exposes it as `unstable` through its
overlay, so any package can be taken from it by prefixing `unstable.` in
`home.packages` — e.g. `unstable.lmstudio` — with no second flake input of
your own to manage. Three costs, stated plainly: (a) **you own this
combination** — nothing upstream tests a pinned Nomarchy plus an unstable
app together; (b) an unstable package brings its own second toolchain into
your closure (measured: `unstable.lmstudio` shares only ~690 of ~4k store
paths with the pinned set); (c) it moves whenever the flake lock moves
(`nomarchy-pull`), not on its own schedule. Home packages only — that's
the supported surface, not `system.nix` or the module set.
## Roadmap & known issues ## Roadmap & known issues

View File

@@ -89,29 +89,6 @@ rofi#2317, and take the fix when a release carries it. If it is still open at
the next lock bump, that is fine — nothing here rots meanwhile. the next lock bump, that is fine — nothing here rots meanwhile.
*Last checked 2026-07-17: open, no maintainer response, no linked PR.* *Last checked 2026-07-17: open, no maintainer response, no linked PR.*
### 134. `unstable.<pkg>` in the downstream — decided, build it
**Decided 2026-07-17 (Bernardo): Nomarchy carries the channel.** Add a
`nixpkgs-unstable` input to the distro flake and expose it through
`overlays.default` as an `unstable` attrset, so
`home.packages = [ unstable.lmstudio ];` just works — downstream stays one
locked input and `nomarchy-pull` still moves it. Scope is **home packages
only**; no `unstable.*` surface in system.nix, no `stable.` synonym for now.
The input rides in the eval unconditionally (an unused input still locks +
fetches — accepted). Original diagnosis stands: `mkFlake` has no overlay
seam and `homeConfigurations` uses mkFlake's own `pkgs`, so nothing short of
the distro carrying the channel keeps the user out of `fetchTarball` pinning.
Costs accepted with the ruling, to be stated in docs, not hidden: lock bumps
now move two channels (VERIFICATION §5 full-checklist applies to both); a
user who reaches for `unstable.` pays a second stdenv/glibc in their closure
(measured: lmstudio shares only ~690 of ~3.74k paths); and nothing upstream
tests pinned-Nomarchy + unstable-app together — the README needs an explicit
"you own this combination" paragraph. Ship with a commented example in
`templates/downstream/home.nix` (opt-in convention) and a README table note.
Pass = flake check green + a V1 build of the template HM closure with one
`unstable.` package enabled. Related: #133.
## LATER ## LATER
- **Wallpapers artifact split** (ROADMAP § Faster switches — decided, - **Wallpapers artifact split** (ROADMAP § Faster switches — decided,
@@ -312,7 +289,7 @@ decide. **Resolved** entries stay for history; agents treat them as closed.
is promoted, a public binary cache (cachix) is the likely first step. is promoted, a public binary cache (cachix) is the likely first step.
- **#134 unstable packages** — build it: Nomarchy carries the - **#134 unstable packages** — build it: Nomarchy carries the
`nixpkgs-unstable` input, `unstable.*` via the overlay, home-scope only. `nixpkgs-unstable` input, `unstable.*` via the overlay, home-scope only.
Moved to NEXT with the full decided shape. Shipped the same day (ROADMAP § `unstable.<pkg>` in the downstream).
- **#114 greeter layouts** — document only: a VT has one keymap by design, - **#114 greeter layouts** — document only: a VT has one keymap by design,
so tuigreet cannot honour per-device layouts. Shipped as the README so tuigreet cannot honour per-device layouts. Shipped as the README
"Greeter keyboard layout" note + a RECOVERY.md pointer; entry deleted. "Greeter keyboard layout" note + a RECOVERY.md pointer; entry deleted.

View File

@@ -68,5 +68,7 @@ those, it probably shouldn't exist.
- Commit style: `feat|fix|test|docs|chore(scope): summary`, body with - Commit style: `feat|fix|test|docs|chore(scope): summary`, body with
what/why + verification tier + what remains. Bookkeeping (`agent/` what/why + verification tier + what remains. Bookkeeping (`agent/`
updates) rides in the same commit as the change. updates) rides in the same commit as the change.
- `flake.lock` moves only when the task *is* a lock bump, and only within - `flake.lock` moves only when the task *is* a lock bump. The release
the pinned release branches. inputs stay within their pinned release branches; the `nixpkgs-unstable`
input (#134, feeds `unstable.*`) tracks `nixos-unstable` and bumps
together with the rest — never alone, never `nix flake update`.

View File

@@ -19,6 +19,25 @@ Template:
--- ---
## 2026-07-17 — #134: `unstable.<pkg>` seam shipped
- **Task:** BACKLOG #134 (NEXT, decided shape from this morning's triage).
- **Did:** `nixpkgs-unstable` input + `unstable` attrset in
`overlays.default` (plain lazy import, allowUnfree mirrored);
`lib.nix` untouched by design — mkFlake's pkgs already applies the
overlay. Template opt-in comment (`unstable.lmstudio`), README costs
paragraph, `checks.unstable-seam` (eval-only), VERIFICATION §5 +
CONVENTIONS two-channel lock rules. Lock gained exactly one node
(nixos-unstable @ 753cc8a, 2026-07-15). Sonnet implemented; design +
review here.
- **Verified:** V1 — flake check green; unstable-seam check green
(26.05pre vs 26.11pre lib.version); template HM activation built with
`unstable.hello` and its closure carries the unstable store path
(pg2zfr… vs pinned nm7p8w…).
- **Pending:** nothing; Bernardo's real-world case is him adding
`unstable.lmstudio` to ~/.nomarchy/home.nix whenever he likes.
- **Next suggestion:** queue is NEXT-empty save watches (#143/#146/#149);
NOW holds only the install-findings stub awaiting Bernardo's call.
## 2026-07-17 — #127 CLOSED: the brick was hypridle eating its own resume path ## 2026-07-17 — #127 CLOSED: the brick was hypridle eating its own resume path
- **Task:** BACKLOG #127 (NOW). Two Sonnet forensics passes over the machine's - **Task:** BACKLOG #127 (NOW). Two Sonnet forensics passes over the machine's
journal; interpretation, verdict and close-out here. Bernardo ruled: close + journal; interpretation, verdict and close-out here. Bernardo ruled: close +

View File

@@ -137,7 +137,10 @@ Maintenance follows the same ladder:
- **Flake lock bumps**: treat as maximum-blast-radius changes. Build, - **Flake lock bumps**: treat as maximum-blast-radius changes. Build,
boot the VM, run the full regression checklist, and do a visual boot the VM, run the full regression checklist, and do a visual
spot-check of the session (themes can shift with upstream package spot-check of the session (themes can shift with upstream package
changes). Never merge a lock bump on "it evaluates". changes). Never merge a lock bump on "it evaluates". Since #134 the
lock carries **two channels** (the release pin and the
`nixos-unstable` pin feeding `unstable.*`) — a bump moves both, and
this checklist applies to both.
- **Theme imports / new themes**: import via `tools/import-palettes.py`, - **Theme imports / new themes**: import via `tools/import-palettes.py`,
then the full §3 visual protocol; additionally verify the theme-switch then the full §3 visual protocol; additionally verify the theme-switch
round trip (into the new theme and back out). round trip (into the new theme and back out).

View File

@@ -493,6 +493,23 @@ Design/decision records and a running log of shipped work (items marked
V2 by render, both heads + the password dialog, via the new V2 by render, both heads + the password dialog, via the new
`tools/plymouth-preview.sh`. V3 (a real docked boot/shutdown) is queued: the `tools/plymouth-preview.sh`. V3 (a real docked boot/shutdown) is queued: the
fake heads exist from the start, so the rig cannot resize a canvas. fake heads exist from the start, so the rig cannot resize a canvas.
- ✓ **`unstable.<pkg>` in the downstream (#134, 2026-07-17):** Nomarchy now
carries a `nixpkgs-unstable` input and exposes it through
`overlays.default` as an `unstable` attrset — in the template's
`home.packages = with pkgs; [ … ]` a user writes `unstable.lmstudio` and
is done: one downstream input, still locked, still moved by
`nomarchy-pull`. `lib.nix` needed zero changes (mkFlake's pkgs already
applies the overlay). A plain non-recursive `import` keeps it lazy —
configs that never write `unstable.` pay nothing beyond the input fetch;
`allowUnfree` mirrors the base set so the motivating lmstudio resolves.
Home packages are the supported surface (documented convention — the
overlay is technically visible system-side, deliberately undocumented).
Costs stated in the README, not hidden: you own the combination, second
toolchain in the closure (~690 of ~4k paths shared), moves with the lock.
Lock bumps now move two channels — VERIFICATION §5 + CONVENTIONS updated.
V1: template HM activation built with `unstable.hello`, closure carries
the unstable store path; `checks.unstable-seam` (eval-only) guards the
seam permanently; lock diff added exactly the one node.
- ✓ **The idle brick's CAUSE found (#127 closed, 2026-07-17):** the incident - ✓ **The idle brick's CAUSE found (#127 closed, 2026-07-17):** the incident
was misdated — it did not happen "before 12:51:34 Jul 15" (that boot was was misdated — it did not happen "before 12:51:34 Jul 15" (that boot was
healthy all day and ended in a routine clean reboot); it ended the healthy all day and ended in a routine clean reboot); it ended the

17
flake.lock generated
View File

@@ -196,6 +196,22 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-unstable": {
"locked": {
"lastModified": 1784120854,
"narHash": "sha256-KesHgItiZPgGX740axSiQLcIQ8D24MDqNpkKYWIek8k=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "753cc8a3a87467296ddd1fa93f0cc3e81120ee46",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nur": { "nur": {
"inputs": { "inputs": {
"flake-parts": [ "flake-parts": [
@@ -226,6 +242,7 @@
"home-manager": "home-manager", "home-manager": "home-manager",
"nixos-hardware": "nixos-hardware", "nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"nixpkgs-unstable": "nixpkgs-unstable",
"stylix": "stylix" "stylix": "stylix"
} }
}, },

View File

@@ -4,6 +4,11 @@
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05";
# Second, newer channel — exists solely to feed the `unstable.*` overlay
# seam (#134), so downstream can pick individual newer apps without a
# second flake input of their own.
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
home-manager = { home-manager = {
url = "github:nix-community/home-manager/release-26.05"; url = "github:nix-community/home-manager/release-26.05";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
@@ -27,7 +32,7 @@
}; };
}; };
outputs = { self, nixpkgs, home-manager, stylix, nixos-hardware, ... }: outputs = { self, nixpkgs, nixpkgs-unstable, home-manager, stylix, nixos-hardware, ... }:
let let
system = "x86_64-linux"; system = "x86_64-linux";
username = "nomarchy"; # reference host only; downstream picks its own username = "nomarchy"; # reference host only; downstream picks its own
@@ -79,6 +84,17 @@
# the `nomarchy.stateFile` option — reading it stays pure. # the `nomarchy.stateFile` option — reading it stays pure.
overlays.default = final: prev: { overlays.default = final: prev: {
# A second, newer channel for individual apps (#134) — e.g. lmstudio,
# whose fast-moving upstream a pinned nixpkgs lags. Plain `import`,
# deliberately NOT final-recursive: it evaluates only when a config
# actually references `unstable.something`, so checkouts that never
# write `unstable.` pay nothing for it. allowUnfree mirrors the base
# set (lib.nix) so unfree apps — lmstudio is the motivating case —
# resolve here too.
unstable = import nixpkgs-unstable {
inherit (prev.stdenv.hostPlatform) system;
config.allowUnfree = true;
};
nomarchy-state-sync = final.callPackage ./pkgs/nomarchy-state-sync { nomarchy-state-sync = final.callPackage ./pkgs/nomarchy-state-sync {
# Presets baked into the package, so `nomarchy-state-sync list` # Presets baked into the package, so `nomarchy-state-sync list`
# works on machines that don't check out this repo. # works on machines that don't check out this repo.
@@ -2268,6 +2284,22 @@
) )
''; '';
}; };
# #134: proves the `unstable.*` overlay seam exists and is really a
# second channel, without building anything from it — evaluation-only,
# so this stays cheap even though nixpkgs-unstable is a whole extra
# input. `pkgs` here already carries overlays.default (line ~35).
unstable-seam =
let
expect = cond: msg: nixpkgs.lib.assertMsg cond "unstable-seam: ${msg}";
ok =
expect (pkgs.unstable ? hello)
"pkgs.unstable has no `hello` the overlay attr is missing or broken"
&& expect (pkgs.unstable.lib.version != pkgs.lib.version)
("pkgs.unstable.lib.version == pkgs.lib.version this is the same "
+ "channel as the pinned nixpkgs, not a second one");
in
assert ok; pkgs.runCommand "nomarchy-unstable-seam" { } "touch $out";
}; };
# ─── Reference host ──────────────────────────────────────────── # ─── Reference host ────────────────────────────────────────────

View File

@@ -201,6 +201,8 @@
# Local AI (Ollama is a service toggle in system.nix) # Local AI (Ollama is a service toggle in system.nix)
# lmstudio # (unfree; large closure) # lmstudio # (unfree; large closure)
# unstable.lmstudio # newer lmstudio from the unstable channel —
# # you own this combination (see README)
# alpaca # GTK Ollama UI # alpaca # GTK Ollama UI
]; ];
} }