Commit Graph

67 Commits

Author SHA1 Message Date
Bernardo Magri
0486e037df docs(roadmap): close out Pillar 8 / Component 5 + log hyprsunset finding
Pillar 8 Component 5 (Desktop stack) closeout entry covers the five inline
fixes from this sweep (hyprland apps.conf wiring, NNOMARCHY typo pair,
broken waybar palette overrides, dead bindings files, mako post-fix verify)
plus the doc reconciles (SCRIPTS.md, KEYBINDINGS.md). One new Later row
captures the nightlight/hyprsunset toggle-vs-systemd inconsistency — Nix
always enables the systemd unit with a rebuild-time temperature while the
toggle script pkills/exec's hyprsunset directly and hardcodes 4000K
instead of reading nightlightTemperature.

Runtime verification (boot live ISO; eyeball waybar across panel
positions × form factors × all 22 palettes, walker launcher modes, the
screensaver at idle) remains on the user before declaring Component 5
fully closed.
2026-05-21 20:19:31 +01:00
Bernardo Magri
641ab0cfb0 chore(hyprland): drop two dead bindings files in default/hypr/
bindings.conf was explicitly labeled "Deprecated bindings file. New
installations include everything directly." and was sourced by nothing.
plain-bindings.conf referenced \$terminal/\$browser/\$fileManager/\$music/
\$messenger/\$passwordManager — Hyprland variables that aren't defined
anywhere in the loaded config tree — and was likewise sourced by nothing.
Both files have been superseded by features/desktop/hyprland/config/
bindings.conf (the user-overrideable, mkDefault-deployed one at
~/.config/hypr/bindings.conf) and the entries already inside
default/hypr/bindings/{utilities,media,clipboard,tiling-v2}.conf.

Regenerated docs/SCRIPTS.md; the diff drops the stale plain-bindings.conf
callers and incidentally corrects four scripts whose Origin column was
out of date after they moved from core/system/scripts/ to
features/scripts/utils/ (nomarchy-env-update, nomarchy-pkg-add,
nomarchy-pkg-remove, nomarchy-preflight-migration).
2026-05-21 20:03:09 +01:00
Bernardo Magri
72f7e7b93d docs(roadmap): log Hyprland keymap + waybar-toggle inconsistencies
Two behavioral wrinkles found during the Pillar 8 desktop-stack sweep
that need a design decision before they can be fixed. Logged as Later
rows so the audit doesn't lose them.

1. The Hyprland Wayland keymap is hardcoded to `us` in
   `core/home/config/nomarchy/default/hypr/input.conf:3`, ignoring the
   installer-chosen layout for native Wayland apps. Fix needs either a
   templated input.conf driven by a new home option, or session-level
   `XKB_DEFAULT_LAYOUT` propagation. Either path touches the installer
   heredoc and the home modules, so not a same-PR fix.

2. `nomarchy.toggles.waybar` is exported only as an env var consumed
   by the runtime toggle script. The Nix module always sets
   `programs.waybar.enable = lib.mkDefault true`, so the toggle is
   session-only — waybar comes back on every rebuild/reboot.
   Inconsistent with `toggles.idle` which correctly gates
   `services.hypridle.enable`. Needs a behavioral call (persistent
   gate vs intentional runtime-only with a clearer name).
2026-05-19 20:24:27 +01:00
Bernardo Magri
af8fa321ff docs(roadmap): log uwsm-in-virtualization module placement (Later)
core/system/virtualization.nix wires `programs.uwsm` + the Hyprland
session config at the top of the file — loaded unconditionally on every
install, with no actual relationship to libvirt/docker. Cosmetic
mislocation, not a behavior bug; logged as a Later row so it can be
fixed in a dedicated session module without growing this audit PR.

Found during Pillar 8 audit of core/system modules.
2026-05-19 19:13:47 +01:00
Bernardo Magri
99a6c7d547 fix(impermanence): user must match created account, not hardcoded "nomarchy"
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.
2026-05-19 19:13:23 +01:00
Bernardo Magri
85ef8745d7 chore: delete orphan config assets + log chromium/templates concerns
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.
2026-05-19 19:04:56 +01:00
Bernardo Magri
b82954a7b5 fix(options): drop dead skipVsCodeTheme + 4 themeLoader.apps toggles
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.
2026-05-19 19:04:25 +01:00
2529ca114f Merge branch 'main' into wave/qa-core-home 2026-05-19 18:49:15 +01:00
94927952db Merge pull request 'chore(lib): drop dead helpers, document schema boundary' (#2) from wave/qa-lib-schema into main
Reviewed-on: #2
2026-05-19 18:48:30 +01:00
Bernardo Magri
95101fda3f fix(sddm): default autoLogin off, not on with hardcoded "nomarchy"
`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.
2026-05-19 18:46:41 +01:00
Bernardo Magri
27d1506b54 chore(lib): drop dead helpers, document schema boundary
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.
2026-05-19 18:28:54 +01:00
Bernardo Magri
90f07ae75c fix(home): remove dead behavior options, reserve overrides API
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.
2026-05-19 18:08:58 +01:00
Bernardo Magri
bfd95cb40b docs(roadmap): mark Pillar 5 preset rows as Shipped
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.
2026-05-19 17:28:16 +01:00
Bernardo Magri
9283403d8f docs(roadmap): add Pillar 8 QA audit + clean stale Pillar 4 rows
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.
2026-05-19 17:26:22 +01:00
Bernardo Magri
ec6046793e fix(installer): hardware-db references real modules + add ROG Ally + CI lint
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>
2026-05-18 18:31:48 +01:00
Bernardo Magri
2b6d452509 fix: nomarchy-manual opens local README, not an Omarchy URL
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>
2026-05-18 18:24:19 +01:00
Bernardo Magri
ac846f4b03 docs: STRUCTURE.md root listing + Pillar 6 reality-check
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>
2026-05-18 18:21:36 +01:00
Bernardo Magri
03968e5d0d fix(installer): generate state.json from lib/state-schema.nix
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>
2026-05-18 18:18:25 +01:00
Bernardo Magri
d264371b46 fix: complete hybridGPU wiring + make state-derived options overridable
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>
2026-05-18 18:12:09 +01:00
Bernardo Magri
9c672953bc fix(installer): pre-flight resume polish (4 gaps)
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>
2026-05-18 18:00:02 +01:00
Bernardo Magri
7fa909ddf4 fix: centralize state defaults via lib/state-schema.nix
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>
2026-05-18 17:52:47 +01:00
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
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
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
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
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
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
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