fix(install): bake dconf, seed main, name ISO, display modes (#123–#126)
Some checks failed
Check / eval (push) Has been cancelled

Batch of existing-bug fixes from Acer/XPS install findings:

- #123: HM pre-activate creates /run/user/$UID and prefers
  dbus-run-session so dconf no longer aborts the desktop bake
- #124: main-built ISOs seed ?ref=main; patch-template rewrites
  inputs.nomarchy.url from NOMARCHY_FLAKE_URL (was set but unused)
- #125: image.baseName → nomarchy-live-….iso (not nixos-live-…)
- #126: Display mode list sorted by pixel area; toast shows hyprctl
  size; TESTING notes QEMU fixed-window scaling illusion

Verified: V0 flake check, installer-safety, baseName eval, patch-template
flakeUrl rewrite, mode-sort fixture.
This commit is contained in:
2026-07-15 09:58:53 +01:00
parent 25e498a5ef
commit 748d4af414
13 changed files with 118 additions and 178 deletions

View File

@@ -110,7 +110,7 @@ Boot the full desktop from a USB stick or VM without installing anything:
```sh
nix build .#nixosConfigurations.nomarchy-live.config.system.build.isoImage
# → result/iso/*.iso — dd to a stick, or boot it in QEMU:
# → result/iso/nomarchy-live-….iso — dd to a stick, or boot it in QEMU:
tools/test-live-iso.sh
```

View File

@@ -31,150 +31,6 @@ post-boot sessions). Preserve separation: the installer bake failure and the
flake pin are different root causes even when they show up on the same machine.
(Terminal / Ghostty-on-Acer → shipped as Kitty-only, #95.)
### 125. Live/install ISO artifact is still named `nixos-…`
**Observed:** a built live image lands as e.g.
`result/iso/nixos-live-26.05.20260710.8f0500b-x86_64-linux.iso` — users (and
USB writers) still see **NixOS**, not Nomarchy.
**Already branded elsewhere:** `isoImage.volumeID = "NOMARCHY_LIVE"`,
`isoImage.edition = "live"`, splash/GRUB theme, `system.nixos.distroName` /
`distroId = "nomarchy"` (os-release). The **filename** comes from
`image.baseName` / `image.fileName` (nixpkgs ISO module; formerly
`isoImage.isoBaseName` / `isoName`), which still default to the NixOS
template prefix.
**Fix direction:** set `image.baseName` (or the current nixpkgs equivalent)
to something like `nomarchy-live` in `hosts/live.nix` (and any install ISO
target if split later). Confirm boot menu strings / isolinux labels if they
still say NixOS after the rename. Guard: a cheap check or docs note that
`result/iso/nomarchy-*.iso` is the expected path (TESTING / README build
snippet).
**Pass:** `nix build …isoImage` produces a `nomarchy-…` ISO filename (not
`nixos-…`); volume label and user-facing boot branding stay Nomarchy.
### 126. Display resolution picker: choosing higher modes makes the image smaller (live VM)
**Observed (Bernardo, live ISO in a VM):** System Display resolution
picker — the **higher** the listed mode he picked, the **lower** the
on-screen resolution *appeared* (inverted / wrong mapping). Live session,
not necessarily install.
**Code path:** `modules/home/rofi.nix` `display)` — builds a mode list from
Hyprland tokens + `hyprctl monitors -j``.availableModes[]`, then
`sort -rV -u`, applies with
`hyprctl keyword monitor "$name,$mode,$pos,$scale"` and persists
`settings.monitors.$name`.
**Likely causes to separate:**
1. **Menu/sort bug** — labels or `sort -rV` on `WxH@R` strings dont match
what Hyprland applies (wrong mode string applied).
2. **QEMU/viewer scaling** — guest mode really increases while the host
window is fixed, so content *looks* smaller (not a Nomarchy bug; document).
3. **Scale factor interaction** — keeping current `.scale` while changing
mode on virtio-gpu yields surprising logical size.
**Fix direction:** reproduce in QEMU with `tools/test-live-iso.sh` (or
manual), log `hyprctl monitors -j` before/after each pick, assert applied
`width`/`height` match the chosen token. Fix sort/format if wrong; if pure
viewer scaling, tell the user and optionally show current mode from
`hyprctl` after apply. Prefer a permanent guard on mode-string formatting
if the bug is in our list/apply path.
**Pass:** picking `1920x1080@…` yields a monitor at 1920×1080 (hyprctl);
picking a lower mode yields lower; menu order is highest-first and honest.
If the only inversion is QEMU window chrome, document it in TESTING.md and
close with that finding — do not “fix” real hardware that was fine.
### 123. Installer HM pre-activate aborts on dconf / missing XDG_RUNTIME_DIR
**Seen on both machines** (Dell XPS 9350 and Acer M5-481T), same log shape at
the end of `nomarchy-install` after "✓ System installed":
```
— Baking the desktop —
Building the desktop generation...
▲ Desktop pre-activation failed (see /var/log/nomarchy-hm-preactivate.log
on the installed system); after first login run:
home-manager switch --flake ~/.nomarchy -b bak
Activating dconfSettings
dconf-CRITICAL **: Unable to create directory '/run/user/1001/dconf':
Permission denied. dconf will not work properly.
error: Could not connect: No such file or directory
```
Install still reports success and offers reboot — so first boot lands on a
**partial** Home Manager activation. That matches the post-boot pain Bernardo
had to fix by hand on both boxes: **GTK theme looking wrong** (Stylix/dconf
color-scheme never landed) and **missing nm-applet tray icon**
(`services.network-manager-applet` is on in `modules/home/default.nix`, but
only after a complete HM generation/activation). Do **not** file those as
separate product bugs until this bake succeeds; re-check them as acceptance
criteria of the fix.
**Root cause (code path):** `pkgs/nomarchy-install/nomarchy-install.sh`
"Baking the desktop" runs `$out/activate` via `runuser` inside `nixos-enter`.
There is no user session: `/run/user/$UID` is missing or unwritable, so the
dconf activation step dies and the scripted activate aborts mid-way. #83
already surfaces the recovery one-liner; the *bake itself* is what is broken.
**Fix direction:** before `runuser … activate`, create a private runtime dir
the target user owns (`mkdir -p /run/user/$UID && chown`, export
`XDG_RUNTIME_DIR`), and/or give activate a session bus if dconf still needs
one. Prefer making pre-activate succeed over papering over dconf. Keep the
existing failure hint for genuine failures.
**Pass:** a `tools/test-install.sh` (or equivalent) run ends with
"Desktop pre-activated — first boot is fully themed", **not** the dconf
CRITICAL; `/var/log/nomarchy-hm-preactivate.log` is absent or clean; first
graphical login has Stylix GTK theming + nm-applet in the tray **without** a
manual `home-manager switch`. V3: same on at least one of the two boxes that
regressed (HARDWARE-QUEUE).
### 124. Install seeds `?ref=v1` while writing main-era config (and v1 lags hard)
**Seen on Acer after install** (and inherent in every current install):
- ISO was built from **main**.
- Generated `~/.nomarchy/flake.nix` still has
`inputs.nomarchy.url = "…?ref=v1"` (`templates/downstream/flake.nix` +
`flake.nix` `flakeUrl` / `originalNode.ref = "v1"`).
- Installer **also** writes live `nomarchy.hardware.*` lines into
`system.nix` (`patch-template.py` — e.g. `nomarchy.hardware.intel.enable`).
- `v1` is far behind main (hundreds of commits; currently lacks the modern
`nomarchy.hardware` surface). After a pull / lock re-resolve to the forge,
rebuild dies with:
```
error: The option 'nomarchy.hardware' does not exist.
… intel = { enable = true; };
Did you mean 'nomarchy.services' or 'nomarchy.system'?
```
So a main-built ISO writes config that **only main understands**, then points
the machine at a release branch that does not have those options. Offline
compose-lock papers over first boot (path-locked to the ISO store), but the
first intentional update re-resolves to ancient v1 and the machine breaks.
**Product tension:** the standing branch model is "downstreams pin `v1`"
(ROADMAP / CONVENTIONS) — correct once `v1` is a real release pointer. While
`v1` lags development this hard, **installs from a main ISO must not leave
users on that lagging ref**.
**Fix direction (pick one and document):** (a) ISO build injects the ref the
image was built from (`main` or `v1`) into the template + `flakeUrl`; (b) pin
`original` to the exact ISO `rev` and only advance on explicit
`nomarchy-pull`; (c) stop writing options that do not exist on the pinned
ref (worse — loses hardware enablement). Prefer (a) or (b).
**Pass:** a machine installed from a main-built ISO has a flake whose nomarchy
input evaluates the same option surface the installer wrote (no
`nomarchy.hardware does not exist` on `nomarchy-rebuild` / first pull policy
as documented); a release ISO built from `v1` still pins `v1`. Guard: install
safety / template check that written options ⊆ pinned module surface, or that
the seeded ref matches the ISO source ref.
### 94. Live ISO/install: no default browser observed
**Progress 2026-07-13:** installed path VERIFIED at V1 — the exact HM

View File

@@ -427,24 +427,18 @@ the **T14s** (webcam case).
template installs). Firefox is deliberately absent; its absence is
correct, not a miss.
- [ ] **#123 install bake: first boot fully themed without manual HM switch**
after a fix for the dconf / XDG_RUNTIME_DIR abort lands, re-install
(or re-run bake) on **either** the Acer M5-481T or the Dell XPS 9350.
**Pass:** install ends with "Desktop pre-activated", no
`dconf-CRITICAL` / `Permission denied` in
re-install from a main ISO (this commit+) on **either** the Acer
M5-481T or the Dell XPS 9350. **Pass:** install ends with "Desktop
pre-activated", no `dconf-CRITICAL` in
`/var/log/nomarchy-hm-preactivate.log`, first graphical login has
coherent Stylix GTK theming **and** nm-applet in the Waybar tray
**without** running `home-manager switch` by hand. **Fail** = still
need the recovery one-liner, or GTK/nm-applet still wrong until a
manual switch (then the bake is still incomplete).
Stylix GTK + nm-applet **without** hand `home-manager switch`.
- [ ] **#95 Kitty-only terminal on Acer M5-481T** — after rebuild/install
from main: SUPER+Return opens **themed Kitty**; System Doctor opens
a floating classed Kitty window. No Ghostty on PATH required.
- [ ] **#124 flake pin after main-ISO install** — on a machine installed
from a **main-built** ISO (post-fix): `~/.nomarchy/flake.nix`
nomarchy input must not re-resolve to a lagging `v1` that lacks
options the installer wrote. **Pass:** `nomarchy-rebuild` (and a
deliberate pull policy as documented by the fix) does **not** die
with `The option 'nomarchy.hardware' does not exist`.
- [ ] **#124 flake pin after main-ISO install** — installed machines
`~/.nomarchy/flake.nix` has `?ref=main` (not lagging v1). **Pass:**
`nomarchy-rebuild` does not die with
`The option 'nomarchy.hardware' does not exist`.
## AMD dev box only
- [ ] **#118 smartd still runs where drives DO have SMART** (this commit) — the

View File

@@ -19,6 +19,20 @@ Template:
---
## 2026-07-15 — Fix batch #123#126 (install bake, flake ref, ISO name, display)
- **Task:** Fix-only NOW batch: #123 dconf pre-activate, #124 main flake
seed, #125 ISO filename, #126 display mode list.
- **Did:** (123) pre-activate creates `/run/user/$UID` + prefers
`dbus-run-session -- activate`. (124) seed `?ref=main`; patch-template
rewrites `inputs.nomarchy.url` from NOMARCHY_FLAKE_URL (was unused);
template default main. (125) `image.baseName` → nomarchy-live-….iso.
(126) sort modes by pixel area; toast shows hyprctl size; TESTING QEMU note.
- **Verified:** V0 flake check; installer-safety; `image.baseName` eval
`nomarchy-live-…`; patch-template flakeUrl rewrite; mode sort unit fixture.
- **Pending:** V3 reinstall on Acer/XPS (#123/#124); optional full
`test-install.sh` V2 when time allows.
- **Next suggestion:** #112 disk-picker fd0, or V2 test-install for #123.
## 2026-07-15 — File #125 ISO name + #126 display resolution invert (no code)
- **Task:** Bernardo: (1) installer ISO still named nixos; (2) live VM
Display picker — higher modes made the screen look lower-res.

View File

@@ -137,6 +137,19 @@ iteration would otherwise rediscover.
without a hierarchy pass.
## Gotchas (cost a debugging session once)
- **Installer HM pre-activate needs XDG_RUNTIME_DIR (+ session bus) (#123):**
`runuser … activate` inside `nixos-enter` has no user session. Without
`mkdir -p /run/user/$UID` owned by the install user and
`XDG_RUNTIME_DIR` (prefer `dbus-run-session -- activate`), dconf dies
with Permission denied and the bake aborts mid-way — first boot looks
unthemed / missing nm-applet even though the system installed.
- **ISO filename is `image.baseName` (#125):** volumeID alone does not rename
`result/iso/…`; force `image.baseName` to `nomarchy-…` or the artifact
stays `nixos-live-….iso`.
- **Install flake ref must match the ISO branch (#124):** while `v1` lags
`main`, seed `?ref=main` (and compose-lock original) from main-built ISOs;
`NOMARCHY_FLAKE_URL` must actually rewrite `inputs.nomarchy.url` (was set
but unused until #124).
- Gum `filter` returns unmatched typed text by default; catalog-only pickers
require `--strict` plus an independent exact-membership validation boundary.
- Waybar `layer: top` renders above **even real-fullscreen windows** — the

View File

@@ -404,6 +404,13 @@ Design/decision records and a running log of shipped work (items marked
decision rather than a drive-by. Proved to fail: dropping snapshot makes it
name the missing entry. **V3 pending** — that the apps *launch* needs real
hardware (HARDWARE-QUEUE, Acer M5-481T).
- ✓ **Install fix batch (#123#126):** (123) HM pre-activate creates
`/run/user/$UID` + `dbus-run-session` so dconf no longer aborts the bake;
(124) main-built ISOs seed `?ref=main` and installer actually rewrites
`inputs.nomarchy.url` from `NOMARCHY_FLAKE_URL` (was set but unused);
(125) `image.baseName` → `nomarchy-live-….iso`; (126) Display mode list
sorted by pixel area + toast reports hyprctl size; TESTING notes QEMU
fixed-window scaling. V3: reinstall on Acer/XPS for #123.
- ✓ **Kitty is the only terminal (#95 complete):** Ghosttys OpenGL 4.3 floor
broke SUPER+Return (and classed doctor/calendar windows) on Ivy Bridge
HD 4000 (Acer M5-481T). Maintaining two terminals was pure complexity.

View File

@@ -122,6 +122,11 @@ Hyprland's headless backend otherwise aborts in a VM-only zero-output state.
script's qemu flags; on real hardware check `journalctl -b -u greetd`.
- **Tiny resolution**: the live config forces `monitor = ,highres,auto,1`;
if QEMU still picks 1024×768, resize the window — Hyprland follows.
- **Display menu + QEMU**: System Display mode list is sorted by **pixel
area** (highest first). After a pick, the toast shows hyprctls reported
size. In a **fixed-size** QEMU/viewer window, a higher guest mode still
*looks* smaller (more pixels in the same window) — that is viewer scaling,
not an inverted apply. Trust hyprctl / the toast numbers.
- **First theme switch is the slowest**: it evaluates the flake on a RAM
disk. Subsequent switches reuse the eval cache.

View File

@@ -51,16 +51,17 @@
(nixpkgs.lib.concatStringsSep "\n"
(builtins.attrNames nixos-hardware.nixosModules));
nixpkgsPath = nixpkgs.outPath;
# Branch model: `main` is the development default; `v1` is the
# release pointer — the stable line for the NixOS 26.05 rewrite,
# advanced to main once a batch is tested (rolling, no tags). A
# future major bump would become `v2`. Installed machines track
# `?ref=v1` on `nix flake update`, not the forge's default branch.
flakeUrl = "git+${gitUrl}?ref=v1";
# Branch model: `main` is development; `v1` is the release pointer
# (advanced to main after human QA). Installs from a *main-built* ISO
# must seed `?ref=main` — seeding v1 while it lags hundreds of
# commits left machines with installer-written `nomarchy.hardware.*`
# that v1 does not have (#124). When cutting a release ISO from the
# v1 pointer, change both of these to "v1".
flakeUrl = "git+${gitUrl}?ref=main";
# Future updates re-resolve from the forge (original); the install
# itself resolves from the ISO store (locked = path) — fully
# offline, even from a dirty-tree ISO.
originalNode = { type = "git"; url = gitUrl; ref = "v1"; };
originalNode = { type = "git"; url = gitUrl; ref = "main"; };
lockedNode = {
type = "path";
path = self.outPath;

View File

@@ -2,7 +2,7 @@
# without touching the disk. The live session bundles nomarchy-install
# (gum + disko + mkFlake) for a real install; this target also lets you
# test the distro end-to-end on real hardware before committing to disk.
{ lib, pkgs, username, nomarchySrc, ... }:
{ lib, pkgs, config, username, nomarchySrc, ... }:
let
# ISO boot branding: the Nomarchy monogram recolored to the palette accent,
@@ -84,6 +84,11 @@ in
isoImage.edition = lib.mkForce "live";
isoImage.splashImage = isoSplash; # isolinux / BIOS
isoImage.grubTheme = nomarchyGrubTheme; # GRUB / UEFI
# Filename: nixpkgs defaults image.baseName to "nixos-<edition>-…". Force
# nomarchy so result/iso/ is nomarchy-live-….iso, not nixos-live-….iso (#125).
image.baseName = lib.mkForce (
"nomarchy-${config.isoImage.edition}-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}"
);
# The minimal-CD profile slims the image for a CONSOLE installer; this
# ISO is the desktop, so re-enable what it strips. Above all

View File

@@ -643,26 +643,38 @@ ${themeRows}
fi
[ -n "$name" ] || exit 0
# Pick a mode: a few Hyprland resolution tokens, then the output's
# advertised modes (rounded refresh rate, deduped, highest first). The
# prompt shows the current mode.
# Pick a mode: Hyprland tokens, then advertised modes sorted by
# pixel area (highest first). Do NOT use sort -rV on "WxH@R"
# version sort is not geometric and mis-orders common modes (#126).
cur=$(printf '%s' "$mons" | jq -r --arg n "$name" \
'.[]|select(.name==$n)|"\(.width)x\(.height)@\(.refreshRate|round)"')
mode=$( {
printf 'preferred\nhighres\nhighrr\n'
printf '%s' "$mons" | jq -r --arg n "$name" '.[]|select(.name==$n)|.availableModes[]' \
| sed 's/Hz$//' | awk -F@ '{ printf "%s@%d\n", $1, $2 + 0.5 }' | sort -rV -u
| sed 's/Hz$//' \
| awk -F'[@x]' '
NF >= 2 {
w=$1+0; h=$2+0; r=($3==""?0:$3+0);
printf "%d\t%dx%d@%d\n", w*h, w, h, r
}' \
| sort -t$'\t' -k1,1nr -k2,2 \
| cut -f2- \
| awk 'NF && !seen[$0]++'
printf '%s\n' "$BACK"
} | rofi_menu -p "$name (now $cur)" ) || exit 0
[ "$mode" = "$BACK" ] && exec "$0" "$modeBack"
[ -n "$mode" ] || exit 0
# Apply live (keep the output's current position + scale), then persist.
# Notify with hyprctl-reported size so a QEMU fixed window that
# *looks* smaller at higher modes is not mistaken for an invert (#126).
pos=$(printf '%s' "$mons" | jq -r --arg n "$name" '.[]|select(.name==$n)|"\(.x)x\(.y)"')
scale=$(printf '%s' "$mons" | jq -r --arg n "$name" '.[]|select(.name==$n)|.scale')
if hyprctl keyword monitor "$name,$mode,$pos,$scale" >/dev/null 2>&1; then
nomarchy-theme-sync --quiet set "settings.monitors.$name" "$mode" --no-switch
notify-send "Display" "$name $mode"
got=$(hyprctl monitors -j | jq -r --arg n "$name" \
'.[]|select(.name==$n)|"\(.width)x\(.height)@\(.refreshRate|round)"')
notify-send "Display" "$name $mode (now ${got:-?})"
else
notify-send "Display" "Could not set $name to $mode."
fi ;;

View File

@@ -621,7 +621,8 @@ python3 "$SHARE/patch-template.py" "$FLAKE_DIR" <<PYJSON
"npu": $(if [[ -n "$NPU_VENDOR" ]]; then printf '%s' "$NPU_VENDOR" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip("\n")))'; else echo null; fi)
},
"resumeOffset": $resume_json,
"rootUuid": $root_uuid_json
"rootUuid": $root_uuid_json,
"flakeUrl": $(printf '%s' "${NOMARCHY_FLAKE_URL:?NOMARCHY_FLAKE_URL unset}" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().rstrip("\n")))')
}
PYJSON
@@ -771,10 +772,28 @@ nix-daemon &
daemon_pid=\$!
trap 'kill \$daemon_pid 2>/dev/null || true' EXIT
sleep 2
# Pre-activate has no graphical session: without XDG_RUNTIME_DIR, HM's
# dconfSettings step dies ("Unable to create directory /run/user/UID/dconf:
# Permission denied") and the rest of activation never runs — first boot
# then lands half-themed (#123). Create a private runtime dir the target
# user owns, and wrap activate in a session bus (dconf needs one).
uid=$USER_UID
install -d -o "$USERNAME" -g users -m 700 "/run/user/\$uid"
# BACKUP_EXT: collisions can't abort the activation (a stray
# autogenerated config gets moved aside instead).
runuser -u "$USERNAME" -- bash -lc \
"USER=$USERNAME HOME=/home/$USERNAME NIX_REMOTE=daemon HOME_MANAGER_BACKUP_EXT=bak \$out/activate"
if command -v dbus-run-session >/dev/null 2>&1; then
runuser -u "$USERNAME" -- env \
USER="$USERNAME" HOME="/home/$USERNAME" \
XDG_RUNTIME_DIR="/run/user/\$uid" \
NIX_REMOTE=daemon HOME_MANAGER_BACKUP_EXT=bak \
dbus-run-session -- "\$out/activate"
else
runuser -u "$USERNAME" -- env \
USER="$USERNAME" HOME="/home/$USERNAME" \
XDG_RUNTIME_DIR="/run/user/\$uid" \
NIX_REMOTE=daemon HOME_MANAGER_BACKUP_EXT=bak \
"\$out/activate"
fi
EOF
# NOMARCHY_TEST_FORCE_HM_FAIL=1 — unattended harness only (#54 V2): take
# the failure arm so the durable recovery hint is exercised without a

View File

@@ -57,6 +57,17 @@ def patch_flake(text: str, v: dict) -> str:
f'username = "{nix_str(v["username"])}"; # <- your login name',
"flake username",
)
# ISO build bakes the ref it was built from (main vs v1). Must match the
# option surface the installer writes into system.nix (#124).
if v.get("flakeUrl"):
text, n = re.subn(
r'inputs\.nomarchy\.url = "[^"]*";',
f'inputs.nomarchy.url = "{nix_str(v["flakeUrl"])}";',
text,
count=1,
)
if n != 1:
sys.exit("patch-template: could not patch inputs.nomarchy.url")
profiles = v.get("hardwareProfiles") or []
if profiles:
items = " ".join(f'"{nix_str(p)}"' for p in profiles)

View File

@@ -4,9 +4,12 @@
# The only input. nixpkgs, home-manager etc. come pinned through it —
# tested together upstream. This file is written once (by you or the
# installer) and never hand-edited afterwards; your machine lives in
# system.nix and home.nix. v1 is the release branch.
# Installer copies this template and patches username + hardwareProfile.
inputs.nomarchy.url = "git+https://git.bemagri.xyz/bernardo/nomarchy.git?ref=v1";
# system.nix and home.nix.
# Development tip is `main`. Release consumers pin `?ref=v1` once that
# pointer is current (installer overwrites this URL from the ISO build).
# Installer copies this template and patches username + hardwareProfile
# + the nomarchy input URL.
inputs.nomarchy.url = "git+https://git.bemagri.xyz/bernardo/nomarchy.git?ref=main";
outputs = { nomarchy, ... }:
nomarchy.lib.mkFlake {