Commit Graph

174 Commits

Author SHA1 Message Date
Bernardo Magri
5ddb15ffef ci: add Forgejo Actions workflow (eval + lint)
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>
2026-05-18 17:34:05 +01:00
Bernardo Magri
46738c3663 chore(audit): final Phase B batch — close out unused? cluster
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>
2026-05-18 17:19:57 +01:00
Bernardo Magri
9be4363f4b chore(audit): triage webapp/tui/voxtype install-remove pairs
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>
2026-05-18 17:13:39 +01:00
Bernardo Magri
f93eb7435f chore(audit): delete NixOS-irrelevant Omarchy port scripts
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>
2026-05-18 17:08:09 +01:00
Bernardo Magri
6b2c678669 chore: switch default theme from summer-night to nord
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>
2026-05-18 17:01:29 +01:00
Bernardo Magri
ac4d66e54d fix: nomarchy-sys-update targets actual hostname, not '#default'
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>
2026-05-18 17:01:22 +01:00
Bernardo Magri
098cd42ac8 fix(installer): harden multi-disk LUKS, password handling, revision pinning
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>
2026-05-18 17:01:15 +01:00
Bernardo Magri
158ae308cc fix(audit): close all missing-references rows in script audit
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>
2026-05-18 16:58:54 +01:00
Bernardo Magri
c1895eefd4 feat: implement Pillar 8: Distro Branding
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.
2026-05-04 22:08:27 +01:00
Bernardo Magri
74e2dc34e3 chore: complete OOTB QA audit
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.
2026-05-04 21:44:31 +01:00
Bernardo Magri
3510a51492 fix(installer): resolve multi-disk LUKS/BTRFS boot hang
- 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
2026-05-03 09:13:02 +01:00
Bernardo Magri
7064108ce7 fix(distro): fix /etc/nixos ownership, theme discovery, and CLI wrappers
- 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
2026-05-03 08:59:13 +01:00
Bernardo Magri
bef7be01b8 fix(installer): wire HM as a NixOS module, move env-update to system layer
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>
2026-05-02 12:24:12 +01:00
Bernardo Magri
d4f50afc62 fix(installer): start nix-daemon and trust flake repo for HM activation
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>
2026-05-02 11:56:22 +01:00
Bernardo Magri
2f18d4efcf fix(installer): unblock disko, bootloader, HM activation, hyprland res
- 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>
2026-05-02 11:07:02 +01:00
Bernardo Magri
329dc009b6 fix(installer): repair git init, LUKS reprompt, impermanence-no exit, quiet disko
- 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>
2026-05-02 09:48:37 +01:00
Bernardo Magri
0af1395df2 fix(themes): complete summer-day with Everforest Light palette
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>
2026-05-02 09:48:21 +01:00
Bernardo Magri
f2b99e0f75 adding summer-day theme 2026-05-02 09:16:58 +01:00
Bernardo Magri
6411395d9f fix(qa): comprehensive out-of-the-box audit and repair
- 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.
2026-05-01 20:03:04 +01:00
Bernardo Magri
39b1a9c1b3 style(hyprland): set default monitor to 'preferred' resolution
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.
2026-05-01 17:00:57 +01:00
Bernardo Magri
c5544e56c8 feat(system): default to latest stable kernel
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.
2026-05-01 16:54:01 +01:00
Bernardo Magri
0306dff092 feat(installer): implement single-input flake architecture
- 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.
2026-05-01 16:51:53 +01:00
Bernardo Magri
3b977f181d fix(installer): resolve disko evaluation crash and infinite loops
- 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.
2026-05-01 16:43:05 +01:00
Bernardo Magri
61cd993e54 fix(githooks): skip bash linting on non-bash nomarchy-* scripts
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>
2026-04-30 20:50:32 +01:00
Bernardo Magri
1e9481849b chore: add 'set -e' to every nomarchy-* bash script that lacks it
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>
2026-04-30 20:50:13 +01:00
Bernardo Magri
27f5663cdf chore(githooks): lint changed nomarchy-* scripts on commit
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>
2026-04-30 20:43:37 +01:00
Bernardo Magri
28cc41abdd fix(restart-app): wait for SIGTERM to take effect before respawning
Previous behavior: `pkill -x $1` (no wait) followed by an immediate
background `setsid uwsm-app`. The new instance attached its wayland
surface while the old one's surface was still mapped. Layer-shell
apps got the same visible ghosting that waybar showed on theme switch
before the SIGUSR2 fix (386da51), and non-layer apps got brief double
instances.

Fix:
- Quote $1 (was unquoted, breaks if app name has whitespace - rare
  but cost-free to fix while we're here).
- After SIGTERM, poll pgrep for up to ~1.5s in 100ms ticks.
- If anything is still alive after the poll window, SIGKILL it -
  prevents a misbehaving process from holding the surface forever.
- Only spawn the new instance after the old one is confirmed gone.

Affects every caller that hits the non-systemd-managed restart path
(menu's update-process actions, voxtype install/remove, font-change
follow-ups, etc.).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:09:15 +01:00
Bernardo Magri
5fc9f5ee34 fix(theme): surface partial-apply failures instead of swallowing them
nomarchy-theme-set chains six optional "tell each app the theme changed"
steps. Each used `command -v X && X || true`, which collapsed two very
different outcomes into the same silent path:

  - X isn't installed -> skip (correct, expected, fine)
  - X exists but returned non-zero -> skip (wrong - user just got a
    half-applied theme with zero feedback about which app didn't refresh)

Replaced the inline guards with a small helper that distinguishes
absent from failed and accumulates real failures into a list. At the
end of the run, if anything failed, we notify-send a single message
naming the apps that didn't refresh ("Did not refresh: Waybar, btop")
and echo the same to stderr. The theme apply itself still completes -
we don't abort the chain on one failure - so the user gets the partial
benefit AND the diagnostic.

Same pattern as the waybar SIGUSR2 fix (386da51): make the hot path
loud about real problems while staying quiet about expected
no-installed states.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:08:39 +01:00
Bernardo Magri
5c5b377bd6 fix(menu): quick-state actions return to their submenu instead of exiting
After the cancel-branch sweep, the remaining UX inconsistency was in
action branches: changing one setting kicked the user out of the menu,
forcing a relaunch to change the next. Brought 16 actions across 5
submenus into the same return-to-self pattern theme/background got.

Classification rule applied:

- Quick-state actions (toggle, set, restart-service - finishes in
  milliseconds, no window opens) -> back_to <self>, so the user can
  chain "toggle nightlight, then toggle gaps, then restart waybar"
  without rerunning nomarchy-menu each time.
- Window-opening actions (editor, floating terminal, audio/wifi/bt
  launcher, browser, hyprpicker overlay, screenshot, screenrecord,
  share dialogs, lock/shutdown/logout) stay as one-shot exits -
  re-popping the menu over the new window would be visual noise.

Submenus changed:

- show_toggle_menu (8 toggles): screensaver, nightlight, idle, top
  bar, workspace layout, window gaps, 1-window ratio, display scaling.
- show_setup_power_menu: powerprofilesctl set returns; cancel still
  goes up to show_setup_menu (different destinations on each branch,
  so the if/else stays).
- show_font_menu: nomarchy-font-set returns; cancel still goes up.
- show_setup_system_menu: the suspend toggle (quick) returns;
  hibernate enable/disable (terminal) still exit.
- show_update_process_menu (5 service restarts): hypridle, hyprsunset,
  swayosd, walker, waybar.

For dynamically-rendered menus (show_setup_system_menu rebuilds its
options each invocation based on current state) this also gives free
visual feedback - the toggle's label flips between "Enable Suspend"
and "Disable Suspend" when the menu re-renders.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:01:48 +01:00
Bernardo Magri
4b99fa3846 fix(menu): route every cancel branch through back_to for direct-keybinding consistency
Audit of all show_*_menu functions after the theme/background fix found
11 more cancel branches that called their parent directly instead of
back_to. None are reachable from current keybindings (today's direct
invocations target submenus that already use back_to), so the bug is
latent — but any future `nomarchy-menu <area>` keybinding into one of
these would bounce the user into the parent on Esc instead of exiting
cleanly, the exact bug that prompted the previous commit's fix to
show_theme_menu / show_background_menu.

Mechanical sweep:

  *) show_main_menu   ;;  ->  *) back_to show_main_menu   ;;   (5 sites)
  *) show_setup_menu  ;;  ->  *) back_to show_setup_menu  ;;   (3 sites)
  *) show_update_menu ;;  ->  *) back_to show_update_menu ;;   (3 sites)

Behavior under nested navigation (BACK_TO_EXIT=false) is unchanged:
back_to falls through to calling the parent function by name. Only
direct-invocation cancel paths gain the correct exit-0 behavior.

Action branches and go_to_menu's dispatch table intentionally still use
direct calls — those are forward navigation, not cancel.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 19:53:41 +01:00
Bernardo Magri
a741b0936c fix(menu): theme and background submenus return to parent instead of exiting
The menu navigation contract: a submenu invoked directly via keybinding
(BACK_TO_EXIT=true, set by go_to_menu when nomarchy-menu is launched
with a target argument) should `exit 0` after the user's action; a
submenu invoked from a parent menu (BACK_TO_EXIT=false) should call
`back_to <parent>` to return where the user came from. back_to() honors
both modes.

Three submenus violated the contract:

- show_theme_menu and show_background_menu shell out to walker's
  Elephant plugin and don't call back_to. After picking a theme or
  wallpaper from Main -> Style -> Theme, the script exits silently
  instead of returning to Style; the user has to relaunch the menu
  from scratch to change anything else.

- show_hardware_menu's cancel branch called show_trigger_menu directly
  instead of back_to show_trigger_menu, which would have bounced a
  direct-keybinding caller into Trigger instead of exiting cleanly.

Adds the missing back_to call to the two walker-backed submenus
(parented to show_style_menu) and converts the hardware cancel branch
to back_to. The 16 other show_*_menu functions already conform.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 19:50:36 +01:00
Bernardo Magri
f318585dc4 fix(installer): harden disk selection and partitioning phase
The disk phase was the dominant source of incomplete installs. Six
concrete failure modes addressed in one pass:

1. Live-ISO USB excluded from the disk picker. select_disk previously
   filtered loop|ram|zram|sr but not the device the installer booted
   from; picking it would format the boot media mid-install. New
   detect_live_iso_devices walks /, /iso, /run/initramfs/live,
   /nix/.ro-store, /nix/store and resolves each backing device to its
   parent disk via lsblk -no PKNAME. Override with
   NOMARCHY_INSTALL_ALLOW_ISO_TARGET=1 for the developer case.

2. 10 GiB minimum-capacity preflight. Disko fails late and obscurely
   on undersized media; surface it while the picker is still open.

3. prewipe_target_drive rewritten:
   - Enumerates every active dm-crypt mapping via dmsetup ls and
     closes those whose backing device is on the target drive. The
     old version only knew about the hardcoded names "crypted" /
     "crypted_main" so an aborted multi-disk run or a non-Nomarchy
     install would leave a holder open and silently break the wipe.
   - Drops `|| true` from wipefs / sgdisk / dd. After the LUKS and
     swap teardown above, a real failure means something is still
     holding the device — surface that instead of papering over it.
   - udevadm settle bounded to 30s so a flapping USB can't hang.
   - Post-wipe sanity check: refuse to hand the disk to disko if
     anything is still mounted off it.

4. run_disko_with_retry wraps the disko call. On failure, shows the
   last 30 lines of output via gum style and offers Retry /
   View full log / Abort. set -e is suspended for the disko call so
   the exit code can be inspected. The previous bare `disko --mode
   disko` aborted the whole installer with output scrolled past.

5. Sed-templated disko-golden.nix + disko-btrfs-multi.nix pair
   replaced by a single disko-config.nix Nix function of
   { mainDrive, extraDrives ? [] } called via --argstr / --arg.
   Templating Nix via shell-escaped string substitution caused at
   least one production bug (3aadc36 fixed embedded-newline
   escaping); function arguments are the right shape and eliminate
   the entire class of escaping concerns. Single-disk path is
   `extraDrives = []`; multi-disk gets BTRFS `-d single -m raid1`
   plus the additional /dev/mapper/* devices. Hosts that shipped
   /etc/disko-golden.nix now ship /etc/disko-config.nix.

6. EXIT trap added so the tmpfs LUKS key file (/dev/shm/nomarchy-
   luks.key) is removed even if the script aborts between key-write
   and the explicit unset. Replaced redundant `shred -u` on tmpfs
   with `rm -f` (already in RAM).

Verification: bash -n on install.sh, nix-instantiate parse + strict
eval on disko-config.nix in both single and multi shapes, full
nix flake check --no-build evaluating all three NixOS configurations
(default, nomarchy-installer, nomarchy-live) plus the installerVm.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 19:42:00 +01:00
Bernardo Magri
386da51178 fix(waybar): SIGUSR2 reload to avoid surface-recreate ghost on theme switch
Theme-switching ran systemctl --user restart waybar.service, which tears
down waybar's wayland layer-shell surface and creates a new one
back-to-back. Hyprland needs a frame to clear the destroyed surface; the
new instance attaches its surface immediately, so for a frame or two the
old waybar pixels remain visible behind/under the new bar - the
"artifacts and old colors on top of new" symptom most visible on the
fresh compositor of the live ISO.

Switch to SIGUSR2 reload, which makes waybar re-read config.jsonc and
CSS (including @import-ed files like ~/.config/nomarchy/current/theme/
waybar.css that theme-switch rewrites) without destroying the surface.
Full systemctl start is kept for the cold-start case.

Drive-by: replace the `systemctl list-unit-files` presence check with
`systemctl cat` - list-unit-files returns 0 even on no-match, so the
old check would always pick the systemctl branch and never fall through
to the pkill fallback on systems where waybar isn't a systemd unit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 19:24:25 +01:00
Bernardo Magri
d06ef86bb9 feat(gaming): add nomarchy.gaming.enable home-side window rule
Mirror of nomarchy.system.gaming.enable. When on, injects a Hyprland
windowrulev2 = fullscreen, class:^(steam_app_).*$ so games launched
through Steam grab the whole screen instead of opening windowed.

Gated via lib.mkIf so the rule is absent when the option is off
(AGENT.md guardrail: features must be option-gated). The rule is
appended to wayland.windowManager.hyprland.extraConfig (types.lines)
so it composes cleanly with the existing source-line entry point in
features/desktop/hyprland/default.nix.

Closes the "Gaming - Hyprland window rule" Next-column roadmap row.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 19:14:21 +01:00
Bernardo Magri
3aadc36bff fix(installer): implement robust step-based navigation and fix multi-line sed error
- Implement a step-based state machine in main loop to support 'Back' navigation via Esc.
- Refactor all prompts to use safe exit-code capture (rc -eq 130/1) and handle 'not submitted' output.
- Add input flushing after Esc events to prevent cascading backtrack signals.
- Add short-circuit checks to every wizard stage for reliable skip-forward behavior.
- Fix sed error when generating multi-disk configurations by escaping newlines in additional_disks.
- Add explicit 'Set a hostname' message to the hostname prompt.
- Convert unsafe short-circuit lists to safe if statements to prevent set -e crashes.
2026-04-26 22:17:00 +01:00
Bernardo Magri
55f0653e59 feat(desktop): default to highest monitor resolution
- Change default monitor rule from 'preferred' to 'highres' in monitors.conf.
- Explicitly force 'highres' in the live ISO (nomarchy-live) to avoid low-res fallbacks on some hardware.
- Update roadmap.
2026-04-26 20:03:46 +01:00
Bernardo Magri
dd48411013 feat(scripts): extend nomarchy-welcome into a guided wizard
- Added nomarchy.panelPosition option and state persistence.
- Updated Waybar to respect the panelPosition setting.
- Refactored nomarchy-welcome to use state.json instead of a flag file.
- Added prompts for theme, font, panel position, and starter home.nix generation.
- Updated documentation and roadmap.
2026-04-26 20:02:52 +01:00
Bernardo Magri
c66f0b19cd feat(installer): add multi-disk BTRFS support
- Allow selecting multiple drives in the TTY installer using gum choose --no-limit.
- Add installer/disko-btrfs-multi.nix template for BTRFS RAID/Single setups.
- Dynamically generate multi-disk disko configurations with LUKS-on-every-disk.
- Default to BTRFS 'single' data and 'raid1' metadata for maximum capacity across mismatched drives (e.g., 20GB + 120GB SSDs).
- Update roadmap and structure documentation to reflect the new capabilities.
2026-04-26 19:44:34 +01:00
Bernardo Magri
6de8ecd093 feat(distro): rename ISO targets and fix UEFI boot in live test script
- Rename installerIso and installerIsoGraphical to nomarchy-installer and nomarchy-live.
- Update host configurations with proper Nomarchy branding and volume IDs.
- Fix nomarchy-test-live-iso QEMU launch by using -drive if=pflash for UEFI firmware.
- Add nomarchy-build-live-iso utility script.
- Scrub remaining Omarchy references in Plymouth, installer messages, and docs.
- Regenerate docs/SCRIPTS.md to reflect new and renamed utilities.
2026-04-26 15:29:04 +01:00
Bernardo Magri
21230a05eb feat(installer): review-then-edit loop with field-level re-prompt
Previously the review screen only offered Confirm/Abort, so a typo or
wrong-disk choice meant aborting the whole run and starting over (or
hand-editing /tmp/nomarchy-install.state.sh). On --resume the situation
was worse: every prompt re-runs (each short-circuits when its var is
set), the user lands on a review they can't change.

review_configuration() now offers Continue / Edit a field / Abort. Edit
opens a multi-select of every saved field; chosen fields clear and the
next loop iteration in main() re-prompts only those. The LUKS passphrase
short-circuits when already set, so editing other fields doesn't
re-prompt for it.

Net flow change:
- Fresh install: same prompts, then review with Edit option (typo fixes
  without restarting).
- --resume: state loads, every prompt skips (vars set), lands straight on
  review — exactly what the roadmap entry called for.

Verified via `bash -n`. Live VM dry-run not exercised in this session.
2026-04-26 09:21:40 +01:00
Bernardo Magri
4b2f16c2f0 docs: add TROUBLESHOOTING.md for the five common rebuild errors
Covers: option-already-declared (duplicate mkOption), attribute-missing
(forgot to import nomarchy.nixosModules.system), Stylix target conflict
(needs lib.mkForce, not bare bool), home-manager .hm-bak churn (left over
from backupFileExtension after first install), and impermanence path
missing (dir not in environment.persistence list).

Each entry has the literal error text, the cause, and a copy-paste fix.
Linked from README.md and docs/MIGRATION.md so users hit it before
guessing.
2026-04-26 09:16:40 +01:00
Bernardo Magri
21ee9c6035 feat(system): add gaming preset module
Opt-in `nomarchy.system.gaming.enable` (default false). Wires
`programs.steam` (with `remotePlay` and `localNetworkGameTransfers`
firewall holes opened via `mkDefault`), `programs.gamemode` (the
launching user must be in the `gamemode` group), and
`services.flatpak`.

Two pieces of the original roadmap entry split into separate
Next-column rows so the system-side preset ships now:

  1. Hyprland fullscreen-on-Steam-launch window rule (home-side).
  2. Declarative flathub remote (nixpkgs has no API for this; needs
     either an overlay or a one-shot systemd unit).

The flatpak service is enabled but the user must add flathub
manually after first boot — documented in OPTIONS.md.
2026-04-26 09:10:52 +01:00
Bernardo Magri
8266dc7ee2 feat(system): add accessibility preset module
Opt-in `nomarchy.system.accessibility.enable` (default false —
accessibility is a personal preference, not hardware-derived). Wires
`services.gnome.at-spi2-core`, installs `pkgs.orca`, and sets
`XCURSOR_SIZE` to a configurable `accessibility.cursorSize` (default
32, up from NixOS's 24).

The original roadmap entry bundled Hyprland-side bits (slower
key-repeat, Orca launch keybinding, high-contrast palette). Those
require touching home-manager / theme files and a new palette
directory; split into a separate Next-column row so the system-side
preset ships now and the desktop integration follows independently.
2026-04-26 09:06:02 +01:00
Bernardo Magri
16ed8f1df1 docs(agent): require docs to ship with the change that triggers them
Adds an 8th guardrail and replaces §5.4 with an explicit "if you change
X, update Y" mapping covering options, scripts, keybindings, structure,
installer, themes, roadmap, conventions, and flake-level changes.

Each row names the doc to touch. The closing line forces a one-pass
check before declaring a change done — eliminates "docs catch-up" PRs
and keeps the distro and its docs from drifting apart.
2026-04-26 08:53:58 +01:00
Bernardo Magri
e9c9342965 feat(system): add desktop preset module
Mirror of the laptop preset for the desktop form factor. New
`nomarchy.system.desktop.enable` defaults to `formFactor == "desktop"`,
so the installer's existing formFactor write auto-flips it on without
installer changes (same pattern as laptop).

The module pins `powerManagement.cpuFreqGovernor` to `"performance"`
(via mkDefault) and enables `services.zfs.{autoScrub,trim}` so a
future ZFS pool gets sensible maintenance for free. The ZFS knobs are
no-ops until the user adds zfs to `boot.supportedFilesystems`.

Battery widget filtering is already driven by `formFactor` itself in
`features/desktop/waybar/default.nix`, so the preset doesn't repeat
it. Closes the "Desktop preset module" Next item.
2026-04-26 08:51:28 +01:00
Bernardo Magri
5b014cfa29 chore(audit): refine docs-scripts detector and lock in via pre-commit
Two detector bugs fixed:

1. grep_includes missed *.lua, *.ini, *.desktop, *.json — so callers in
   elephant providers (lua), mako on-button-* hooks (ini), and any future
   MimeType-registered URL handlers (.desktop) were invisible. Adding them
   reclassifies nomarchy-notification-dismiss and nomarchy-theme-bg-set
   from `unused?` to `kept` (true callers in mako/core.ini and the
   elephant background_selector lua).

2. The all_refs regex `nomarchy-[a-z0-9][a-z0-9-]+` greedily captured
   trailing dashes, producing junk missing-tokens like `nomarchy-pkg-`,
   `nomarchy-cmd-`, `nomarchy-restart-`, etc. from glob references like
   `for c in nomarchy-pkg-*`. Tightened to require an alphanumeric end
   character. Also restricted to grep_includes so the binary tmpfile
   path `nomarchy-menu-rows` no longer leaks in.

New .githooks/pre-commit re-runs the generator and stages docs/SCRIPTS.md
whenever a nomarchy-* script changes. Enable per clone with
`git config core.hooksPath .githooks` (now mentioned in docs/AGENT.md).

Net audit shift after regen: unused? scripts 31→29, missing tokens 30→28,
no false-positive prefix tokens remain.
2026-04-26 08:44:13 +01:00
Bernardo Magri
034da701a3 feat(system): add laptop power preset module
New `nomarchy.system.laptop.{enable,thermald}` options. `enable`
defaults to `formFactor == "laptop"`, so the installer's existing
formFactor write auto-flips the preset on without installer changes.

The module wires TLP (governors + 75/80 charge thresholds),
force-disables power-profiles-daemon (mutually exclusive with TLP),
enables upower and thermald (x86_64), adds the brightnessctl udev
rule so the existing brightness scripts work without root, and sets
a logind lid-switch policy that resolves to suspend-then-hibernate
when `hibernation.enable` is on, plain suspend otherwise.

Closes the "Form-factor → laptop preset auto-enable" Now item and
the "Laptop preset module" Next item from docs/ROADMAP.md in one
change.
2026-04-26 08:31:19 +01:00
Bernardo Magri
7086a6f29c feat(installer): add software-profile multi-select
- Add select_profiles step with gum choose --no-limit
- Implement state persistence and review for selected profiles
- Map profiles to home.packages and system-level toggles (Docker, Steam)
- Update generate_flake_config to emit profile-specific Nix snippets
- Fix duplicate environment.systemPackages in virtualization.nix
- Update ROADMAP.md
2026-04-25 22:44:24 +01:00
Bernardo Magri
1545e63c7d docs: update roadmap and scripts audit status after phase B 2026-04-25 22:40:33 +01:00
Bernardo Magri
f965f0be2c feat(audit): address batch 4 and finalize script audit
- Implement nomarchy-skill, nomarchy-manual, nomarchy-backup, nomarchy-install
- Implement nomarchy-install-docker-dbs (stub)
- Port nomarchy-docs-keybindings and nomarchy-docs-scripts to packaged scripts
- Add installerVm to flake.nix nixosConfigurations, packages, and apps
- Update nomarchy-test-installer to use nix run .#installerVm
- Add docker support to virtualization.nix and options.nix
- Add glow to script dependencies
- Finalize docs/SCRIPTS.md update
2026-04-25 22:39:11 +01:00