These three settings.Login fields were set at default priority, so a
downstream system.nix that wrote (e.g.) `services.logind.settings.Login.HandlePowerKey = "poweroff"`
would collide with Nomarchy's value instead of overriding it. Same
mkDefault treatment as the other lid-switch settings in this block.
Found during Pillar 8 audit of core/system modules.
`lib/state-schema.nix` declared `system.features.makima = false` but
the field was never wired anywhere: no matching option in
core/system/options.nix, no consumer in core/system/state.nix, no
references in the wider tree. Schema-only ghost — removed.
Found during Pillar 8 audit of core/system modules.
The persistence block at core/system/impermanence.nix:75 read
`users.nomarchy = { directories = [...]; }` — the username was a
literal, not a reference. For any user not literally named "nomarchy"
the block was silently inert and ~/.ssh, ~/.gnupg, ~/.local/share/keyrings,
Documents, Downloads, Pictures, Videos, Projects were wiped on every boot.
Adds `nomarchy.system.impermanence.user` (str, default "nomarchy") and
uses it via `users.${cfg.user}`. The installer now writes the chosen
username alongside `enable` and `mainLuksName` so impermanence installs
with non-default usernames are correct out of the box.
docs/OPTIONS.md: fixes the wrong path on the impermanence row
(documented `impermanence.enable`, real option is
`nomarchy.system.impermanence.enable`) and adds entries for
`mainLuksName` and `user`.
Found during Pillar 8 audit of core/system modules.
Two unreferenced asset files removed; two larger concerns deferred to
roadmap rows because they need more thought than a focused audit
allows.
Deleted:
- `features/apps/alacritty/config/alacritty.toml` — the alacritty
module uses `programs.alacritty.settings` (Nix attrset) exclusively;
nothing references the on-disk file. The neighbouring (already-empty)
`themes/` directory goes with it.
- `themes/templates/mako.ini.tpl` — no script reads it.
Deferred to ROADMAP "Later":
- `features/apps/chromium/Default/Preferences` is deployed as a Home
Manager symlink into chromium's mutable profile directory. Either
silently replaced on first save or silently failing to write —
either way the static defaults don't survive. The actual chromium
theming work happens via managed policies in
core/system/browser.nix. Needs chromium-internals knowledge to
decide whether to remove or rework, so flagged rather than
unilaterally deleted.
- `themes/templates/*.tpl` (the remaining 10 templates) are also
apparently orphan — deployed via xdg.dataFile but unconsumed by any
script. Likely vestigial from a pre-stylix templating system.
Logged as a separate row to decide deletion vs documentation as
user-reference assets.
Found during Pillar 8 audit of features/apps.
Two clusters of documented-but-non-functional options surfaced during
the Pillar 8 audit, both setting toggles that have zero runtime effect.
1. `nomarchy.toggles.skipVsCodeTheme` was declared in
core/home/options.nix, defaulted from lib/state-schema.nix, and
surfaced as `NOMARCHY_TOGGLE_SKIP_VSCODE_THEME` env var in
features/scripts/default.nix — but `features/apps/vscode.nix` always
sets `workbench.colorTheme` unconditionally, and no script reads the
env var. Setting the toggle to true did nothing. Removed from
options, schema, state, env-var export, and OPTIONS.md.
2. `nomarchy.themeLoader.apps.{waybar,mako,kitty,alacritty}` were
declared in themes/engine/loader.nix but only `btop` is actually
wired (line 87 gates the per-theme btop.theme deploy). The other
four had no consumer. The actual theming pipeline for those apps is
elsewhere: waybar themes inline from `colorScheme` in waybar.nix;
kitty and alacritty are themed by stylix targets in
themes/engine/stylix.nix; mako has no theme integration at all.
Removed the four dead options + updated OPTIONS.md to list only
btop with a note about where the other apps' theming lives.
`features/default.nix` had a let-block that read
`~/.config/home-manager/user-packages.json` at eval time via
`builtins.pathExists` + `builtins.readFile`, parsed it as JSON, and
filtered to valid pkgs — then never appended the result to
`home.packages` or anywhere else. The `userPackages` variable was
completely orphan.
Two problems with the dead code: (1) it was an undocumented hidden
mechanism (no docs mentioned `user-packages.json`), (2) it made flake
evaluation impurely depend on a user's home directory for no payoff —
flake outputs would silently differ between machines depending on the
presence of that file, even though nothing in the build used it.
Removed the let-block entirely. The nomarchyLib import stays.
Found during Pillar 8 audit of features/apps.
`themes/engine/sddm.nix` defaulted `services.displayManager.autoLogin`
to `enable = true; user = "nomarchy";` (both mkDefault). The installer
flow overrode both with the real username at normal priority, so this
was invisible there — but a hand-migrated user (per docs/MIGRATION.md)
who imported `nomarchy.nixosModules.system` without setting
`autoLogin.user` would auto-login as a nonexistent "nomarchy" user and
SDDM would error. `docs/MIGRATION.md` even documented the override as a
post-import chore.
Flipped the default to `enable = lib.mkDefault false`. Installer
generates `enable = true` directly so its flow is unchanged. Migration
flow now gets the safe default — opt-in instead of opt-out — and the
docs row is updated to reflect the new shape.
The hardcoded "nomarchy" username fallback for `autoLogin.user` is the
same class of bug as the impermanence persistence block was. A future
roadmap row to consolidate "primary user" across impermanence,
autoLogin, and any future modules might be worthwhile, but it's
deferred — this commit is the immediate fix.
Found during Pillar 8 audit of first-boot UX.
`nomarchy-welcome` wrote a "starter" `~/.config/home-manager/home.nix`
for users without one. Two problems:
1. Wrong path. The installer-generated canonical home.nix lives at
`/etc/nixos/home.nix` and is imported via the flake (both
home-manager.users and the standalone homeConfigurations). Nothing
in the installer flow ever reads `~/.config/home-manager/home.nix`
— it's a dead file.
2. Broken content. The starter is missing `home.username`,
`home.homeDirectory`, `home.stateVersion`, and doesn't import
`nomarchy.nixosModules.home`. Even on a hand-migration path it
wouldn't evaluate as a standalone HM config.
So in the installer flow it's dead, and in the migration flow it's
broken. Removed Step 4 entirely. The git-init step (was Step 5) is
now Step 4. Hand-migrated users follow `docs/MIGRATION.md`, which has
the correct home.nix template.
Found during Pillar 8 audit of first-boot UX.
Two unused helpers and a missing comment in the lib/ surface, found
during the Pillar 8 sweep.
- `readState` in `lib/default.nix` was exported but has no external
callers — only `readHomeState` and `readSystemState` use it
internally. Removed from the export list; the function stays in the
let-block (still wraps the two public readers).
- `getWithDefault` in `lib/state-schema.nix` was a complete dead
function: declared as a path-walking fallback helper but never called
anywhere in the tree. core/{system,home}/state.nix use inline
`togglesState.<key> or schema.<scope>.<key>` instead. Removed.
- Added a header comment to `lib/state-schema.nix` explaining the
schema's boundary — it lists every state.json field consumed by a
Nix option, but state.json may also hold runtime-only fields
(`welcome_done` from `nomarchy-welcome`) that are intentionally
off-schema because no Nix option reads them. Future readers will
otherwise think welcome_done is an orphan.
Logged a Later-column roadmap row for consolidating `flake.nix`'s
palette/themeNames re-imports with `nomarchyLib` so the theme list has
one source of truth instead of two.
Two declared-but-non-functional option subsystems in core/home were
documented in OPTIONS.md and actively misleading users.
1. `nomarchy.behavior.hyprland.{bindings,input,windowRules,autostart}`
were declared in core/home/behavior.nix with a `behaviorConfigs`
mapping let-binding — both completely unread elsewhere in the tree.
The actual hypr/*.conf files are deployed by
features/desktop/hyprland/default.nix with `lib.mkDefault`,
unconditionally. Setting `behavior.hyprland.bindings = false` had
zero effect. OPTIONS.md's "Disable Nomarchy's default Hyprland
keybindings" example was a lie. Removed the four dead options,
deleted behavior.nix entirely, dropped the import from
core/home/default.nix, and rewrote the OPTIONS.md example to use
`xdg.configFile."hypr/bindings.conf".source = ./mine` (which
actually works against the existing `lib.mkDefault` priority).
2. `nomarchy.overrides.{enable,paths}` advertised a file-based override
loader that doesn't exist. The module created
`~/.config/nomarchy/overrides/{hypr,waybar,apps}` directories and
wrote a README claiming "place files here to override upstream
defaults" — but `getOverrideOrDefault` was never called and `paths`
was never populated. Rewrote core/home/overrides.nix to keep just
the option declarations (so configs that already set these still
evaluate) and marked them clearly as reserved/no-op in OPTIONS.md.
Removed the misleading README write and dir-creation. Logged a
Next-column roadmap row for implementing the loader properly.
While here:
- Clarified `nomarchy.configOverrides` (the *working* bulk-redirect
mechanism) vs `nomarchy.overrides.*` (the reserved one) in OPTIONS.md
— they're different things and the "See Overrides below" link was
pointing at the broken subsystem.
- Fixed OPTIONS.md `nomarchy.iconsTheme` / `nomarchy.isLightMode`
default text — both are derived from the active theme in
core/home/state.nix, not the static literals the docs claimed.
- Updated docs/AGENT.md §2 and docs/STRUCTURE.md to reflect the
behavior.nix removal and the overrides.nix reservation.
Found during Pillar 8 audit of core/home modules.
Laptop, Desktop, Accessibility, and Gaming presets all shipped on
2026-04-26 but were still tagged (Next). Reorders the pillar so the
two genuinely open items (dGPU auto-detect, Surface support) lead.
Pillar 3 audited script existence; Pillar 8 audits feature behavior.
Adds a per-component sweep methodology (10 components, one PR each on
wave/qa-<component>) and lists it on the Now board so the next session
can pick it up without re-deriving scope.
Cleanup pass on Pillar 4: removes two "(Now)" entries (software-profile
multi-select, form-factor → laptop preset) already in the Shipped log,
and promotes the two remaining open items ("What's installed?" summary,
non-LUKS branch) to the Now board.
- Update lib/state-schema.nix to default both home and system themes to 'summer-night'.
- Fix 'nomarchy-theme-list' and 'nomarchy-theme-set-templates' to resolve themes and templates from '~/.local/share/nomarchy' instead of the obsolete '$NOMARCHY_PATH' (fixing failures on Live ISO).
- Update 'nomarchy-welcome' to properly convert Title Case theme display names back to kebab-case identifiers and add input validation to prevent crashes.
- Fix installer impermanence symlink by using a relative path ('../persist/etc/nixos'), ensuring it resolves during 'nixos-install' both inside and outside the chroot.
- Deploy '~/.XCompose' symlink via Home Manager and add 'nomarchy-restart-xcompose' to the menu.
- Relocate 'Nomarchy.ttf' to 'core/branding/' and move user-level scripts ('pkg-add', 'pkg-remove', 'env-update', 'preflight-migration') to 'features/scripts/utils/' to align with the distro architecture.
- Remove obsolete '$NOMARCHY_PATH' exports and redundant 'bashrc' template.
- Export theme templates via 'xdg.dataFile' for script accessibility.
Audited every entry in `installer/hardware-db.sh` against
`inputs.nixos-hardware.nixosModules` and found **21 of 43 entries (49%)
referenced modules that don't exist** in the upstream attribute set —
those installs would fail at eval time with "attribute not found"
errors on real hardware. Specifically:
- Framework 13 per-gen: nixos-hardware uses `framework-11th-gen-intel`,
not `framework-13-11th-gen-intel`. Fixed all four generations.
- Framework 13 AMD AI 300: `framework-amd-ai-300-series` (no "13-").
- Framework Intel Core Ultra: added `framework-intel-core-ultra-series1`.
- Framework 16 AMD AI 300: added `framework-16-amd-ai-300-series`.
- Framework generic fallback now uses the `framework` umbrella module.
- ThinkPad X1 Carbon: modules are `lenovo-thinkpad-x1-Nth-gen`,
not `-x1-carbon-genN`. Fixed gens 6/7/9/10/11; added X1 Nano.
- ThinkPad P14s: requires arch+gen suffix; switched to the AMD gen3/4/5
modules (the prior `lenovo-thinkpad-p14s` had no attribute).
- Surface Pro 6/7/8/10: all share `microsoft-surface-pro-intel`. Pro 9
keeps its dedicated module. Pro 3 fixed to `-pro-3`. Surface Book
2/3 and Intel-based Surface Laptop 3/4/5: no nixos-hardware module
— rows dropped; generic chassis+cpu+gpu detection still emits
sensible `common-pc-laptop`.
- ASUS ROG Strix G513 → `asus-rog-strix-g513im` (correct attr name).
- ASUS ROG Zephyrus GA403 didn't exist — dropped. Added `ga402x`,
`gu603h`, `g533zw`.
- ASUS Zenbook generic `asus-zenbook-ux` was non-existent — dropped
(too vague; available modules are per-model like `asus-zenbook-ux481`).
- Dell Latitude 5400 / 7480: no modules — replaced with the existing
`dell-latitude-7420`, `7430`, `7490`.
Added:
- ROG Ally / Ally X support (`asus-ally-rc71l` for `RC71L`,
`RC72LA`, and the "ROG Ally" product string). nixos-hardware
currently ships one module for both revisions.
Documented (in a footer comment) the devices nixos-hardware doesn't
cover so they're known-unsupported rather than accidentally missing:
- Valve Steam Deck → Jovian-NixOS as a separate flake input.
- Snapdragon X laptops → aarch64 only; Nomarchy installer is x86_64.
- Raspberry Pi → same as above.
Bug discovered along the way: the DB's pipe-separated row format
collides with bash regex alternation. A row like
`Microsoft|Surface Pro (10|8|7|6)|_|module` parses as 7 fields, with
"7" extracted as the module name. Surface Pro variants are now one
row per version.
CI gate added (`.forgejo/workflows/check.yml`): a new step extracts
every 4th-pipe-field from `HARDWARE_DB` and `comm -23`s it against
`inputs.nixos-hardware.nixosModules`. Any future entry pointing at a
non-existent module fails CI with a clear error. Closes the regression
class entirely.
Verified locally: bash -n + shellcheck --severity=error pass on
hardware-db.sh; the CI step's exact commands pass against the new DB.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The script hardcoded `xdg-open https://learn.omacom.io/2/the-nomarchy-manual`
— an upstream Omarchy page. Users hitting "Help → Manual" in nomarchy-menu
were sent to an unrelated site, and there's no nomarchy.org canonical
docs URL to point at instead.
Now opens `$HOME/.local/share/nomarchy/README.md`, which lives on every
installed system (per SKILL.md's "Out of Scope" note about
`~/.local/share/nomarchy/`) and links every doc in `docs/`. Falls back
to a notify-send "run nomarchy-update?" message if the source tree
isn't synced.
Pillar 6 entry in docs/ROADMAP.md updated to (Shipped).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two stale-doc cleanups in one commit. Both surfaced during the
post-Phase-B audit pass.
1. `docs/STRUCTURE.md` "Root Directory" listed three files that don't
exist anywhere in the tree:
- `GEMINI.md` (replaced long ago by `docs/AGENT.md`)
- root-level `STRUCTURE.md` (this file actually lives in `docs/`)
- `TODO.md` (long since replaced by `docs/ROADMAP.md`)
Replaced with the actual root layout (flake.nix, flake.lock,
README.md, .forgejo/, .githooks/) plus a `docs/` sub-tree that
names every doc in the directory — the missing pieces the deleted
bullets were trying to point at, now correctly located.
2. `docs/ROADMAP.md` Pillar 6 had three "Next" bullets that already
shipped on 2026-04-26 (the welcome wizard, TROUBLESHOOTING.md, and
the docs-index goal — README.md now links every doc in `docs/`).
Moved all three to `(Shipped)`.
Also rewrote the `nomarchy-manual` bullet — "orphaned reference
today" was stale (the script is called from nomarchy-menu and
nomarchy-theme-install per docs/SCRIPTS.md). The real remaining
issue is its hardcoded `xdg-open https://learn.omacom.io/...` —
an Omarchy URL that opens an unrelated upstream page when a user
triggers the menu's Help entry. The bullet now names that
specifically.
No code touched; doc-only.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Closes the last source-of-truth split after the state-defaults
centralization batches. The installer's heredoc was the only remaining
place that hardcoded the state.json literal — adding a default to the
schema previously required a parallel edit here, and silent drift was
exactly the bug class we kept fixing.
Before:
cat > /mnt/etc/nixos/state.json <<JSON_EOF
{
"theme": "nord",
"timezone": "${_state_tz}",
"dns": "DHCP",
...
}
JSON_EOF
After:
nix eval --impure --raw --expr "
let
flake = builtins.getFlake \"$NOMARCHY_REPO\";
lib = flake.inputs.nixpkgs.lib;
schema = import \"$NOMARCHY_REPO/lib/state-schema.nix\"
{ inherit lib; };
state = schema.system // { timezone = \"$_state_tz\"; };
in builtins.toJSON state
" | nrun jq '.' > /mnt/etc/nixos/state.json
Uses the flake's own pinned `inputs.nixpkgs` (matching what the rest of
Nomarchy resolves against), so the schema evaluates with the same `lib`
the consumer modules see. `nrun jq` pretty-prints for human inspection.
Behavioural notes:
- Output is identical to the old heredoc modulo alphabetical key
ordering — `builtins.toJSON` sorts keys, the heredoc was in
declaration order. Toggle scripts read/write via `jq` so it's
invisible to them.
- Dry-run path unchanged. `execute_dry_run` already bind-mounts a fake
/mnt for the generator; the generator's absolute paths still resolve.
- New schema fields show up automatically on the next install; no
parallel edit needed.
- `bash -n` + `shellcheck --severity=error` clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two related fixes that together close the "minimal wiring" gap behind
`nomarchy.system.features.hybridGPU`.
1. Complete the NVIDIA driver stack inside hardware.nix's hybridGPU
mkIf block.
Before: `hybridGPU = true` enabled supergfxd and... that was it.
supergfxd manages mode switching by black/unblacklisting the nvidia
kernel module, but without the rest of the NVIDIA stack actually
loaded the dGPU has no driver to drive. Hyprland/Wayland silently
stayed on the iGPU regardless of mode.
After: hybridGPU=true also wires
services.xserver.videoDrivers = ["nvidia"] (loads the driver
under Wayland too)
hardware.graphics.{enable,enable32Bit}
hardware.nvidia.modesetting.enable (required for
Wayland)
hardware.nvidia.powerManagement.enable
hardware.nvidia.package = config.boot.kernelPackages
.nvidiaPackages.stable
boot.kernelParams += "nvidia-drm.modeset=1"
All wired with lib.mkDefault so a downstream system.nix can pin a
beta driver, flip to the open kernel module, or set
`hardware.nvidia.prime.{offload.enable, intelBusId, nvidiaBusId}`
for render-offload. The bus IDs are per-machine (find via
`lspci -D`) so they stay user-supplied; docs/OPTIONS.md has the
full recipe.
2. Add lib.mkDefault to every state.json-derived assignment in
core/system/state.nix and core/home/state.nix.
Same priority bug on both sides: assignments like
`features.hybridGPU = systemState.features.hybridGPU or false`
landed at default priority. A downstream system.nix saying
`nomarchy.system.features.hybridGPU = true` would then conflict
with the state-derived value at the same priority, and Nix would
refuse the merge with "conflicting definition values" — the
user's override couldn't take effect.
Verified by an explicit eval: extending the default nixosConfig
with `nomarchy.system.features.hybridGPU = true` now resolves
cleanly and the full driver stack engages.
Side-effect: core/system/state.nix now reads from
lib/state-schema.nix like the home side does, completing the
schema-centralization started two batches ago.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Four resume-flow papercuts in `installer/install.sh` that hurt the
"interrupted install" path the most.
1. `--resume` with no state file is no longer silent.
The most common operator confusion: reboot the live ISO, forget
/tmp/ is tmpfs, re-run with --resume, watch the installer start
over from scratch without saying anything. Now: loud error, tmpfs
explanation, exit 1.
2. Validate the saved TARGET_DRIVE still exists on resume.
Live ISO USB sticks get unplugged between sessions, dev hosts
sometimes have non-deterministic /dev/sdX numbering. Without the
guard the install proceeds and fails with cryptic disko / mount
errors deep in execute_installation. Now we fail at load_state
with the actual reason and a clean recovery path.
3. Resume now shows what's being resumed.
`save_state` stamps an ISO-8601 timestamp; `load_state` prints
"Resumed from <path> (saved Xm ago)" plus a "Target: /dev/X → user
@ host" summary line. Lets the user Ctrl-C before any destructive
prompt fires if they're resuming onto the wrong machine.
4. `--help` documents the tmpfs limitation.
Saved state lives in /tmp/ which is tmpfs on the live ISO; --resume
only works within the same boot. The man-page now says so instead
of letting users discover it the hard way.
`format_age` is the one new helper — pretty-prints "Xs/Xm/Xh Ym/Xd"
relative to now, falls back to the raw timestamp if `date -d` can't
parse the input. shellcheck --severity=error passes.
Out of scope (potential future work):
- Persistent state across reboots (would need a writable USB / external
drive — chicken/egg with the installer setting up the only persistent
storage in the first place).
- `--show-state` flag to inspect a saved file without running.
- State-file schema versioning.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Kills a recurring bug class: state defaults previously lived in three
parallel places that drifted apart over time.
- lib/state-schema.nix (the canonical schema, referenced
nowhere except a description string)
- core/system/options.nix (default = "..." clauses on options)
- core/home/options.nix (same, on home options)
- core/home/state.nix (`or "..."` fallbacks for state.json reads)
When `state.json` is missing a key, three files have to agree on the
fallback. They keep silently drifting:
- The OOTB QA audit shipped fixes for this pattern.
- Earlier this session, `chore: switch default theme summer-night → nord`
fixed core/system/options.nix and core/home/state.nix — but missed
core/home/options.nix, which still defaulted nomarchy.theme to
"summer-night". Every consumer of the home option
(features/default.nix, vscode.nix, waybar, hyprland, theme engine)
resolved to the wrong theme when state.json was blank.
This change:
- Imports lib/state-schema.nix into all three consumers and replaces
every hardcoded default with `schema.<scope>.<key>`.
- Fixes the lingering nomarchy.theme = "summer-night" home-side bug as
a side-effect.
- Touches roughly 25 literals across the three files.
Verified `nix flake check --no-build` passes and every centralized value
evaluates to the exact literal it previously had. Off-schema option-only
defaults (isLightMode, formFactor, cursor.*, iconsTheme, keyring.enable,
etc.) are left hardcoded — they have no state.json counterpart, so
there's no source-of-truth split to resolve.
Out of scope (follow-up):
- Have installer/install.sh generate /mnt/etc/nixos/state.json from
the schema instead of hardcoded JSON — would close the last
split-brain surface (the installer can still drift from schema).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pillar 7 first step. `.forgejo/workflows/check.yml` runs on every push
to main and every PR. Three sequential checks in one job:
1. `nix flake check --no-build`
Catches eval regressions: broken option references, missing imports,
stale module argument shapes. The same command AGENT.md tells humans
to run by hand before declaring a change done.
2. `bash -n` + `shellcheck --severity=error` over every `nomarchy-*`
bash script.
Mirrors what `.githooks/pre-commit` does locally, but across the
whole tree on every push — so a branch that bypasses the hook (via
`--no-verify` or a fresh clone without `core.hooksPath` set) still
gets gated. Severity is capped at error to match the hook; the long
tail of style/info warnings can be cleaned up incrementally.
3. `docs/SCRIPTS.md` drift check.
Regenerates the audit doc to a temp file and `diff`s against the
committed version. Fails loudly with the fix command if a script
add/remove/rename didn't include the regeneration step.
Dry-run results on the current tree:
- `nix flake check --no-build`: pass (only pre-existing warnings).
- shellcheck across 159 scripts at severity=error: pass.
- SCRIPTS.md drift: clean.
Activation:
Forgejo Actions isn't enabled on the repo yet, so the workflow lands
dormant. To activate: enable Actions on the repo in Forgejo's settings
and register a `forgejo-runner` on any Docker-capable Linux host. The
workflow uses `ubuntu-latest` and installs Nix itself via
`DeterminateSystems/nix-installer-action`, so no special runner image
is needed.
Deferred to a follow-up batch (needs binary cache infra):
- Building ISOs in CI (`nomarchy-installer`, `nomarchy-live`, default).
- Release pipeline (`vYY.MM.x` tags publishing ISOs as artifacts).
- `nixosTest` per palette with golden-image screenshot diffs.
`docs/STRUCTURE.md` now documents `.forgejo/` and `.githooks/` so future
agents and contributors can find both.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pillar 3 Phase B, batch 4 (final). Triages the last 13 `unused?` rows:
five deletes and eight SKILL.md surfacings.
Deleted (no callers anywhere, work duplicated inline or marginal value):
- `nomarchy-restart-hyprctl` and `nomarchy-restart-mako`: stale comments
claimed "used by theme switching" but no Nomarchy script calls them.
Theme-set and refresh paths call `hyprctl reload` / `makoctl reload`
directly (see nomarchy-refresh-hyprland).
- `nomarchy-restart-tmux`: 3-line pgrep+source-file wrapper. Users can
`tmux source-file ~/.config/tmux/tmux.conf` themselves.
- `nomarchy-battery-present`: the battery monitor reads
`/sys/class/power_supply/BAT*` inline; the helper never got wired in.
- `nomarchy-sudo-keepalive`: intended to be `source`d from longer-running
scripts (nomarchy-update, etc.) but nothing sources it. Resurrect from
git history if a future caller actually needs it.
Surfaced in SKILL.md (now tagged `kept` by the audit):
- Themes: `nomarchy-theme-{remove,refresh,bg-install}`
- System: `nomarchy-sudo-{passwordless-toggle,reset}`,
`nomarchy-restart-trackpad` (intel_quicki2c THC reload — a real laptop
bug fix worth documenting)
- New Virtualization section: `nomarchy-windows-vm {install,launch,stop,status}`
- Enriched Troubleshooting's generic `nomarchy-refresh-<app>` example with
literal `nomarchy-refresh-fastfetch` so the audit catches it.
Verified `nix flake check --no-build` still passes and zero callers
reference the deleted scripts.
**Phase B is now complete.** Final audit state: 164 → 159 scripts, all
tagged `kept`, `unused?` = 0, missing references = 0. The audit table is
now a clean reference of what Nomarchy ships, not a triage backlog.
Logged in `docs/ROADMAP.md` Shipped.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pillar 3 Phase B, batch 3 — Batch A of the unused? clusters.
Deleted two dead webapp URI handlers:
- `nomarchy-webapp-handler-hey`
- `nomarchy-webapp-handler-zoom`
Neither was registered as a MimeType handler anywhere — a grep across
`*.desktop` files in `core/`, `features/`, `themes/`, `installer/`, and
`hosts/` returned zero matches. Without a `.desktop` registration the
system never routes `mailto:`/`zoom:`/`zoomus:` URIs to them, so the
handlers were unreachable code.
Kept the six remaining install/remove pairs (they're real CLI tools, just
unwired into any menu) and surfaced them in `SKILL.md` "Common Tasks" so
AI assistants can discover them on user request and the audit tags them
`kept`:
- Custom App Launchers: webapp-{install,remove,remove-all},
tui-{install,remove,remove-all}
- Voice dictation: voxtype-{install,remove,status}
Menu-wiring these (e.g. a "Setup → Apps" submenu in nomarchy-menu) is a
separate Pillar 6 onboarding job, not scoped here.
Regenerated `docs/SCRIPTS.md` — script count 166 → 164, `unused?` 21 → 13.
Logged in `docs/ROADMAP.md` Shipped.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Pillar 3 Phase B, batch 2. Five `unused?` scripts that either duplicate
NixOS-native facilities or reference infrastructure Nomarchy doesn't
ship. All five had no callers anywhere in the tree.
- `nomarchy-rollback`: ran `nixos-rebuild rollback` after listing
`snapper` snapshots. NixOS already exposes the previous generation in
the boot menu and `nixos-rebuild --rollback`; Nomarchy uses
impermanence, not snapper.
- `nomarchy-snapshot`: wrapped `snapper create/restore`. Same reason —
snapper isn't part of Nomarchy. The script's "nomarchy-update can use
this" comment never came true; nomarchy-update has no reference to it.
- `nomarchy-migrate-state`: one-time migration from old
`~/.config/home-manager/state.json` and `/etc/nixos/state.json` to the
unified `~/.config/nomarchy/state.json`. The installer now seeds the
unified file directly; no current install needs the migration.
- `nomarchy-config-direct-boot`: added an EFI boot entry for a Nomarchy
UKI. We don't build a UKI (no references anywhere in `core/` or
`hosts/`), so the script targeted nonexistent infrastructure.
- `nomarchy-npx-install`: generated npx wrappers in `~/.local/bin/`. An
Arch idiom — on NixOS the path is `nix-shell -p nodejs` or a
declarative `home.packages` entry.
Kept `nomarchy-build-iso` and `nomarchy-build-live-iso` (the user-flagged
useful build wrappers) and surfaced them in README §2 in place of the
raw `nix build` command, which both removes the audit's `unused?` flag
on them and shortens the docs.
Regenerated docs/SCRIPTS.md (171 → 166 scripts; 28 `unused?` → 21).
Logged in docs/ROADMAP.md Shipped.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Both the system option (`core/system/options.nix:theme`) and the home-side
state evaluator (`core/home/state.nix`) defaulted to "summer-night". The
installer-written state.json now seeds "nord" (see preceding installer
commit), and `lib/state-schema.nix` already defaults to "nord". Align the
hardcoded fallbacks here so a missing or blank state file lands on the
same theme everywhere instead of a now-inconsistent split.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The installer generates `nixosConfigurations.<hostname>` (see
installer/install.sh: `nixosConfigurations.$HOSTNAME`), but the system
update script was rebuilding `.#default` and using `--impure`. The
`#default` literal worked only on dev hosts that happened to be named
"default" and silently broke every toggle script on real installs.
Now resolves `$(hostname)` at runtime and aborts with a clear error if
empty. Dropped `--impure` — the flake doesn't need it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Several installer reliability fixes that were left uncommitted:
- Impermanence + multi-disk LUKS: disko-config.nix names the main LUKS
mapping `crypted` for single-disk and `crypted_main` once extraDrives is
non-empty. The impermanence rollback hook used to hardcode `crypted`,
which made every multi-disk install fail to mount root in initrd. Added
a `nomarchy.system.impermanence.mainLuksName` option and wired the
installer to write the correct value into the generated system.nix
based on the drive count.
- Password no longer cleartext in /etc/nixos: installer now hashes the
user password with `mkpasswd -m sha-512` and emits
`initialHashedPassword` instead of `initialPassword`. Added mkpasswd to
the live ISO. Cleartext is unset immediately after hashing.
USER_PASSWORD_HASH is deliberately not persisted in --resume state —
configure_user re-prompts on resume.
- Revision pinning that actually works on the live ISO: `inputs.self`
strips .git in the Nix store copy, so `git rev-parse HEAD` would silently
return empty on a real install and the generated flake would track main.
Live ISO now writes `/etc/nomarchy-rev` from `inputs.self.rev` at build
time; install.sh reads it first, falls back to git, and aborts with a
loud confirmation prompt if both are empty (instead of silently
installing an unpinned system).
- Generated `/mnt/etc/nixos/state.json`: toggle scripts (nomarchy-tz-select,
nomarchy-setup-{fido2,fingerprint}, nomarchy-toggle-hybrid-gpu,
nomarchy-wifi-powersave) `jq` this file in place and fail hard if it
doesn't exist. Fresh installs now ship a schema-conformant file matching
lib/state-schema.nix.
- Unmount /mnt before exiting `finish()` regardless of reboot choice. Clean
unmount avoids dirty BTRFS on reboot; on "no", leaving /mnt mounted
blocked a second installer run on the same live ISO.
- Removed obsolete `installer/disko-btrfs-luks.nix` (superseded by
`disko-config.nix` per commit 3aadc36) and dropped its dangling
`docs/STRUCTURE.md` reference.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The audit's "missing references" table held 15 rows — 2 real doc bugs and
13 grep false-positives — making Pillar 3 Phase B triage noisier than it
needed to be.
- Wrote themes/engine/scripts/nomarchy-theme-next so SKILL.md's documented
"cycle to next theme" command actually resolves.
- Scrubbed three stale `nomarchy-dev-*` references from SKILL.md (skill
frontmatter, body, and Out-of-Scope list) — they hallucinated a workflow
that doesn't exist and broke AI-assisted use of the skill.
- Added a line-context filter to both nomarchy-docs-scripts generators
that drops `nomarchy-*` tokens appearing in Nix pname/derivation idents,
/tmp/ and /etc/sudoers.d/ paths, nixosConfigurations.* / packages.*
flake outputs, mktemp -t prefixes, systemd unit vars, ./result/bin/run-
binaries, and docker container references.
- Added a small token-level denylist for five residual non-script
identifiers (nomarchy-plymouth, nomarchy-sddm-theme, nomarchy-live,
nomarchy-rev, nomarchy-windows) that survive line filtering because
they appear as bare Nix list refs, comment backticks, or compose-heredoc
identifiers.
Regenerated docs/SCRIPTS.md; the "Missing references" section is now
empty. Logged in docs/ROADMAP.md Shipped.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Scrubbed remaining upstream references and solidified Nomarchy's identity:
- Replaced 'Omarchy' and 'Spirit of Omarchy' with brand-independent terms in README.md and scripts.
- Updated nomarchy-welcome banner to 'The Professional NixOS Desktop'.
- Set nomarchy-version codename to 'Sovereign'.
- Verified core/system/branding.nix for OS-release and bootloader labels.
- Verified SDDM and Plymouth metadata for correct branding.
- Updated ROADMAP.md board.
Fixes identified during the thorough distro review:
- Restore automatic wallpaper switching by removing image filters from deployed themes.
- Fix broken 'Style' menu entries by creating missing about.txt and screensaver.txt branding files.
- Clean up conflicting keybindings by removing deprecated tiling.conf and updating doc generator.
- Remove legacy Nord theme hack from nomarchy-theme-set.
- Fix JSON parse error in summer-day waybar theme.
- Move temporary LUKS keyfile to /tmp/ so Disko omits it from runtime config
- Explicitly add x-systemd.requires and x-systemd.device-timeout=0 to BTRFS mount options
- Ensures all LUKS devices are decrypted before BTRFS attempts to mount
- installer: set recursive ownership of /etc/nixos to main user post-install
- themes: fix NOMARCHY_PATH and discovery logic for Lua theme menu
- scripts: update CLI wrappers (font, theme, wallpaper) to use Walker menus
- core: remove obsolete NOMARCHY_PATH and cleanup dead code
- features: add pkgs.lua for Walker and remove obsolete switcher.nix
- docs: update ROADMAP.md, SCRIPTS.md and STRUCTURE.md
The post-install standalone HM activation kept failing in new ways
(daemon access, git ownership, missing PATH on first boot). Wire HM as
a NixOS module in the generated flake instead, so first-boot dotfiles
are activated by `nixos-install` itself with proper system context. The
standalone `homeConfigurations.<user>` is kept alongside for fast
iteration via `nomarchy-env-update`. Also:
- Drop the chroot HM activation block from the installer entirely.
- Move `nomarchy-env-update` from `features/scripts/utils/` to
`core/system/scripts/` so it ships in `nomarchy-system-scripts` and
exists on a freshly-installed system regardless of HM state.
- Set system-wide git `safe.directory` for /etc/nixos and the
impermanence-relocated /persist/etc/nixos so the user-mode HM run
doesn't trip on the root-owned flake repo.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
HM activation inside `nixos-enter` failed with `big.lock: Permission
denied` because the chroot has no systemd and therefore no nix-daemon —
the user-level `nix run` fell back to single-user mode and couldn't
write /nix/var/nix/db. Launch nix-daemon manually for the activation
window and force NIX_REMOTE=daemon. Also mark /etc/nixos (and the
impermanence path) as a git safe.directory so HM doesn't trip over
git's dubious-ownership check on the root-owned repo. Make
nomarchy-env-update self-bootstrap via `nix run home-manager` when
home-manager isn't on PATH so the recovery hint actually works on a
freshly-installed system.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Pass --yes-wipe-all-disks to disko so the silent gum-spin path no
longer hangs forever waiting on a hidden "yes" confirmation prompt
(added in disko 1.13's destroy,format,mount mode).
- Stop threading an externally-built pkgs into the user flake's
nixosSystem; configure nixpkgs through the module system instead so
core/system/default.nix's nixpkgs.config.allowUnfree stops conflicting
with the assertion "system configures nixpkgs with an externally
created instance".
- Enable boot.loader.systemd-boot in the generated system.nix so the
installed system has an actual bootloader (disko already lays out a
1 GiB ESP at /boot).
- Bump nix.settings.download-buffer-size to 512 MiB to silence the
"download buffer is full" warning on large NAR fetches.
- Activate home-manager via `runuser -l` instead of `runuser -u … --
env HOME=…`. The latter only switches uid and leaves \$USER=root, so
HM's activation script saw root, warned, and wrote dotfiles into
/root/ — meaning the user's first login had no Hyprland config.
- Revert default Hyprland monitor line back to highres (live ISO and
user default) — preferred falls back to EDID's 1024x768 in QEMU and
on several laptop panels, which is the bug highres was put there to
defeat.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- nrun git git init -q passed 'git' as a subcommand to git itself,
failing the post-disko repo init. Drop the duplicated arg.
- disko's luks module reads passwordFile at the top level; placing it
under settings.* meant it was silently ignored and disko fell back
to askPassword=true, prompting the user again on luksOpen. Move the
option to the right scope.
- configure_impermanence now uses local rc, nrun gum confirm, and an
explicit case (0/1/130) with a final return 0 so a No answer no
longer aborts the installer.
- run_disko_with_retry hides disko's chatty output behind a gum spin
by default and surfaces the captured log on failure. Set
NOMARCHY_VERBOSE_DISKO=1 to stream output live.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
colors.toml and icons.theme were copy-pasted from summer-night (a dark
theme). Repalette to Everforest Light, mark as light via light.mode,
switch icons to Yaru-blue, fix the broken Waybar import, and add the
btop/neovim/vscode/rofi/walker app configs the sibling themes ship.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Fix critical bash dynamic scoping bug in install.sh (Impermanence/Form Factor).
- Polished Live ISO with auto-login and passwordless sudo.
- Repurposed nomarchy-toggle-suspend to directly execute systemctl suspend.
- Updated nomarchy-launch-wifi to use nmtui in alacritty.
- Optimized nomarchy-welcome to avoid redundant rebuilds via --no-update flag.
- Enabled nomarchy-welcome in Hyprland autostart.
- Wrapped Live ISO-modifying steps in welcome wizard to prevent failures.
- Removed obsolete hardware auto-detection from nomarchy-on-boot.
- Hardened script doc generator against false-positive wildcard tokens.
- Regenerated docs/SCRIPTS.md and updated docs/ROADMAP.md.
Updated the default Hyprland monitor configuration to 'monitor=,preferred,auto,1'. This ensures the best resolution is selected automatically while maintaining a 1x scale by default.
Set boot.kernelPackages to pkgs.linuxPackages_latest by default. This ensures Nomarchy users benefit from the latest hardware support and security features in the mainline kernel.
- Refactor generated flake.nix to use the Appliance Model.
- Downstream flake now only defines the 'nomarchy' input.
- Dependencies (nixpkgs, home-manager) are inherited from nomarchy.inputs to ensure maximum stability and version alignment with upstream.
- Fix disko-config.nix signature by adding '...' to handle unexpected CLI arguments.
- Update disko mode to 'destroy,format,mount' for the modern API and to avoid deprecation warnings.
- Fix infinite loops in 'configure_impermanence' and 'confirm_form_factor' caused by misinterpreting 'No' (rc=1) as an abort.
The nomarchy-* prefix is a name convention, not a language guarantee:
nomarchy-haptic-touchpad is Python. Without a shebang filter, the
pre-commit hook would run `bash -n` on it and abort every commit
that touched the Python helper. Filter to scripts whose shebang
matches `bash` before linting; everything else passes through.
Found via the set -e sweep (1e94818) — the survey caught
nomarchy-haptic-touchpad as a "broken" bash script when it was
just non-bash.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sweep across the three script directories: features/scripts/utils,
core/system/scripts, themes/engine/scripts. 142 of 169 bash scripts
gained `set -e`; 27 already had it; the one Python helper
(nomarchy-haptic-touchpad) was skipped via shebang detection.
Why: bash's default behavior is to continue past a failed command,
which means a script that does "do A; do B; do C" leaves the system
in a half-applied state when B fails - and the user gets no signal.
Several recent fix commits (theme partial-apply, waybar reload race,
installer prewipe silent failures) all trace back to this. set -e
turns silent corruption into a loud abort the user can act on.
The 11 scripts with explicit `|| true` markers stay safe under set -e
because || true coerces the exit to zero; the markers continue to
mean "I deliberately tolerate this failure here."
Deliberate exception: nomarchy-menu runs WITHOUT set -e. It is an
interactive UX loop where action branches do `cmd; back_to <self>`
so a failed action would abort the script under set -e and the menu
would disappear without feedback. Soft-failure - menu re-displays,
user picks again - is the right semantic. Documented inline.
Validation: bash -n on every modified script (zero failures). The
new pre-commit hook (27f5663) was just updated to filter by shebang
so it doesn't try to bash-syntax-check the Python helper - that
filter was uncovered by this sweep.
Risk: set -e can surface latent bugs in scripts that previously
relied on silent continuation. If anything breaks, it's a real bug
that was already broken and is now visible. Easy per-script revert
if any UX glitches show up.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds two-tier linting before the existing docs/SCRIPTS.md regenerate
step:
- bash -n on every changed nomarchy-* script. Catches syntax errors
that would otherwise be discovered at runtime by an unlucky user.
Always fatal.
- shellcheck --severity=error when shellcheck is on PATH. Catches
unquoted-var, use-before-define, missing-shebang, and other
bug-shaped patterns. Only error-level issues block - the long
tail of pre-existing warnings stays as a known cleanup task,
not a commit blocker. Hook silently skips this step when
shellcheck isn't installed (so contributors without it can still
commit).
Catches the class of bug that's bit us repeatedly: a script ships,
the runtime path that exercises the broken line is rare, and the bug
sits latent until a user trips it. Cheaper to catch at commit time.
Caveat: 156 nomarchy-* scripts already have shellcheck warnings
(severity warning/info/style); we deliberately ship around them via
the --severity=error gate. A future per-script audit can dial the
severity up as scripts get cleaned up.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>