Files
Nomarchy/docs/AGENT.md
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

15 KiB
Raw Blame History

Agent Instructions for Nomarchy

You're an AI coding agent picking up the Nomarchy project. This document gives you everything you need to be useful from the first turn: what Nomarchy is, how it's organized, what the rules are, and how to actually pick up the next piece of work.

If anything below conflicts with what the user just said, the user wins. If anything below conflicts with the existing code, read the code — these notes can rot.


1. What Nomarchy is

Nomarchy is a NixOS-based distribution that ships the Omarchy Hyprland desktop on a strictly declarative, flake-based foundation. It targets power users who want the polish of Omarchy without giving up reproducibility.

Concretely:

  • A flake at the repo root exposes nixosModules.system (foundational OS modules) and nixosModules.home (apps + desktop), plus three nixosConfigurations (installerIso, installerIsoGraphical, default) and standalone homeConfigurations.
  • Downstream users get the distro by importing nomarchy.nixosModules.system and nomarchy.nixosModules.home from their own /etc/nixos/flake.nix. The Nomarchy installer generates that flake for them; an existing-NixOS user can hand-write it (see docs/MIGRATION.md).
  • A bash/gum TUI installer lives in installer/install.sh (~1100 lines). It auto-detects hardware via installer/hardware-db.sh (DMI + lspci + BAT* sysfs), prompts for the rest, and generates flake.nix, system.nix, home.nix, and hardware-selection.nix into /mnt/etc/nixos/.
  • The desktop is Hyprland + waybar + walker + a curated theming engine (themes/) with 22 palettes wired through Stylix.

Read in this order to come up to speed:

  1. README.md — public face.
  2. docs/STRUCTURE.md — directory layout and module logic.
  3. docs/OPTIONS.md — every nomarchy.* option a downstream flake can set.
  4. docs/ROADMAP.md — what's planned. This is your work queue.
  5. docs/SCRIPTS.md — the script & menu audit table (Pillar 3 of the roadmap).
  6. docs/MIGRATION.md — how an existing NixOS install becomes Nomarchy.
  7. docs/creating-themes.md — when palette work comes up.

2. How the repo is organized

core/         Foundational OS + user defaults. Don't put apps here.
  system/       NixOS modules, all imported via core/default.nix.
    options.nix       nomarchy.system.* options live here.
    hardware.nix      nomarchy.hardware.* options + module wiring.
    network.nix       NetworkManager, DNS, networkmanagerapplet.
    impermanence.nix  Erase-Your-Darlings root wipe.
    scripts/          Low-level system scripts (battery, brightness, hardware).
  home/         Home Manager modules.
    options.nix       Most home-side nomarchy.* options.
    behavior.nix      nomarchy.behavior.* (deploy-default-config toggles).
    overrides.nix     File-based overrides from ~/.config/nomarchy/overrides/.
    config/           Plain dotfiles symlinked into ~/.config.

features/     Apps and desktop components. Add new apps here.
  apps/         One subdir per app (alacritty, btop, kitty, vscode.nix, …).
  desktop/      hyprland, waybar, idle.nix, nightlight.nix, …
  scripts/      User-PATH scripts, battery-monitor user service.
    utils/        ~68 nomarchy-* user scripts.
    default.nix   Packages them as nomarchy-system-scripts derivation.

themes/       Theme engine + 22 palettes.
  engine/       Loader, Stylix glue, switcher, scripts (font, theme, wallpaper).
  palettes/     One subdir per palette (summer-night, tokyo-night, …).

hosts/        ISO host configs (installerIso, installerIsoGraphical/live-iso).
installer/    The bash/gum TUI + disko configs + hardware-db.sh.
lib/          Shared Nix helpers (state schema, color resolution, paths).
docs/         All long-form documentation. README.md stays at repo root.
bin/          Convenience wrappers for testing (nomarchy-test-installer, …).

When you add a new feature:

  • A new app → features/apps/<name>/default.nix (+ optional config/), import it from features/default.nix.
  • A new system service → core/system/<name>.nix, import from core/default.nix.
  • A new toggle → add to core/system/options.nix or core/home/options.nix, wire it into the relevant module, document it in docs/OPTIONS.md.

3. Guardrails (non-negotiable unless the user overrides)

These are inherited from the established Nomarchy conventions. Violating them is a bug.

  1. Declarative-first. No imperative state in core/. Mutable state goes in ~/.config/nomarchy/state.json or in NixOS / home-manager options.
  2. Downstream-flake friendly. Every behavior toggle is a nomarchy.* option documented in docs/OPTIONS.md. Adding a feature without a corresponding option is a bug.
  3. Opt-in by default. New features default off (or default to existing behavior). The installer can flip defaults for the user being installed, but the option must read sensibly when set by hand.
  4. lib.mkDefault everywhere user might override. If a downstream system.nix would reasonably want to change something Nomarchy sets, set it with lib.mkDefault. If it must not be overridden, use lib.mkForce and explain why in a comment.
  5. Reuse before invent. ~155 nomarchy-* scripts already exist across core/system/scripts/, features/scripts/utils/, themes/engine/scripts/. Grep before writing a new one.
  6. No comments that narrate. Don't write comments explaining what the code does. Only write a comment when the why is non-obvious — a hidden constraint, a subtle invariant, a workaround.
  7. No backwards-compat shims. If you remove a thing, remove it everywhere. No re-exports, no // removed markers.
  8. Docs ride with the change. Every change ships in the same commit as its doc updates — no follow-up "docs catch-up" PRs. The mapping is in §5.4 below; if a change touches something in that table and the matching doc didn't move with it, the change is incomplete.

4. How to find work to do

The roadmap (docs/ROADMAP.md) is the source of truth. It has three columns and seven pillars.

Default rule: prefer items in the Now column. Pick whichever matches the user's current ask, or the smallest one if no ask is in flight.

If the user asked for something not in the roadmap:

  • Do the work.
  • After it ships, propose a one-line addition to docs/ROADMAP.md so future you doesn't re-discover it.

If the user asks "what's next?", reply with the Now column items in 23 sentences and let them pick.

The script & menu audit (Pillar 3)

This is the largest single open work item. Phase A (inventory) hasn't run yet; docs/SCRIPTS.md is just scaffolding. The user explicitly flagged this pillar as important.

Phase A shipped. The inventory lives at docs/SCRIPTS.md and is regenerated by:

./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md

It tags every row with one of kept / unused? / missing. Phase B is the per-batch porting/removal: pick ~10 rows tagged missing or unused?, decide one of port-from-omarchy / delete-dead / stub-with-notify, and ship as one PR per batch on a wave/audit-<batch> branch. Each PR description should reference the rows it closes; reviewers spot-check that every caller of a removed/renamed script is updated.


5. Workflow per change

Steps you should follow for any non-trivial change:

  1. Understand the current state first.

    • git status and git log -5 so you know what's just landed and what's in flight.
    • Read the relevant files. Don't infer.
  2. Plan if it's non-trivial. Tasks with three or more steps benefit from a TaskCreate list. Tasks that touch the architecture benefit from plan mode.

  3. Reuse existing options and scripts. Grep core/system/options.nix, core/home/options.nix, and the three script directories before adding anything.

  4. Touch the docs in the same change. Use this table — if your change matches a row, the doc edit ships in the same commit. No exceptions, no follow-ups.

    If you change… Update in the same commit
    A nomarchy.* option (added, removed, renamed, default flipped, type changed, description changed) docs/OPTIONS.md (matching row) and any other row that mentions it (e.g. formFactor is referenced from laptop.enable's row)
    A nomarchy-* script (added, removed, renamed) docs/SCRIPTS.md — the pre-commit hook regenerates it; verify it's staged. If core.hooksPath isn't set, run ./bin/utils/nomarchy-docs-scripts --out docs/SCRIPTS.md manually.
    A keybinding (bindd = / bindeld =) under core/home/config/.../hypr/ docs/KEYBINDINGS.md — regenerate via ./bin/utils/nomarchy-docs-keybindings --out docs/KEYBINDINGS.md; spot-check the README highlights still hold
    A directory was added/removed/renamed under core/, features/, themes/, hosts/, installer/, lib/, bin/, or docs/ docs/STRUCTURE.md (tree + module logic prose) and §2 of this file (docs/AGENT.md) if the change is structural enough to affect new-agent onboarding
    A feature module's behavior changed in a way an existing user would notice (a default flipped, a service started/stopped, a key remapped) README.md if it's user-visible at the marketing level; otherwise just docs/OPTIONS.md
    An installer prompt added/removed/reordered, or generated-file shape changed docs/MIGRATION.md if it affects hand-written flake.nix setups; installer/install.sh heredoc comments if a generated file's shape changed
    A theme palette added/removed, or the theming engine's contract changed docs/creating-themes.md and the palette count in README.md
    A roadmap item shipped Move the entry to the Shipped section at the bottom of docs/ROADMAP.md. Don't delete — that's our changelog.
    A roadmap item discovered (new scope) Add a one-line row to Now, Next, or Later in docs/ROADMAP.md. Don't tell the user verbally and forget.
    A workflow / convention / guardrail you'd want a future agent to follow docs/AGENT.md (this file).
    A new flake input, output, or major architectural decision docs/STRUCTURE.md and docs/AGENT.md §1.

    Before declaring done, do one explicit pass: did the change cross any row above? If yes, is the matching doc edit staged? If you can't answer "yes" to both, you're not done.

  5. Verify the change evaluates (cheap, do this before declaring done):

    nix --extra-experimental-features 'nix-command flakes' flake check --no-build
    bash -n installer/install.sh   # if you touched it
    

    First clone? Enable the repo's pre-commit hook so docs/SCRIPTS.md regenerates whenever you add/modify/remove a nomarchy-* script:

    git config core.hooksPath .githooks
    

    For installer changes, also do a dry-run end-to-end:

    sudo /etc/install.sh --dry-run    # in the live ISO or VM
    

    For waybar / Hyprland visual changes, the only reliable check is booting the live ISO with nomarchy-test-live-iso. If you can't boot it, say so rather than claiming success.

  6. Commit narrowly. One concept per commit. The commit subject is <type>: <imperative summary> (feat:, fix:, docs:, chore:). The body explains the why.

  7. Push only when the user asks. Local commits are free; pushing publishes.


6. Patterns worth knowing

Adding a new option

# core/system/options.nix  (or core/home/options.nix for home-side)
mything.enable = lib.mkEnableOption ''
  Concise description that reads well in `nix eval`. Mention any
  pre-conditions (groups, kernel modules, hardware needed).
'';

Then wire it inside a config = lib.mkIf cfg.mything.enable { ... } block and document it in docs/OPTIONS.md.

Filtering modules conditionally

We do this for waybar (drop battery on desktop) in features/desktop/waybar/default.nix:25-39. Pattern:

let
  rawSettings = builtins.fromJSON (builtins.readFile configFile);
  laptopOnly = [ "battery" "custom/battery" ];
  filterModules = mods:
    if config.nomarchy.formFactor == "laptop" then mods
    else builtins.filter (m: !(builtins.elem m laptopOnly)) mods;
  settings = rawSettings // {
    modules-right  = filterModules (rawSettings.modules-right  or []);
    modules-center = filterModules (rawSettings.modules-center or []);
    modules-left   = filterModules (rawSettings.modules-left   or []);
  };
in { ... }

Form-factor (laptop vs desktop)

nomarchy.system.formFactor and nomarchy.formFactor are the two halves of the same flag (system + home). Default "laptop". The installer auto-detects via compgen -G "/sys/class/power_supply/BAT*" and writes the explicit value into both generated files. Use this option to gate any laptop-only UI / service.

State (state.json)

Theme, font, wallpaper, and a few feature toggles live in ~/.config/nomarchy/state.json so they can change without a rebuild. Schema is in lib/state-schema.nix. The Home Manager evaluator reads it via lib/default.nix. Don't add new state without justifying it — most "state" should be a NixOS option instead.

Scripts derivation

User-PATH scripts ship via nomarchy-system-scripts (core/system/scripts-derivation.nix) plus the per-category dependencies declared in features/scripts/default.nix:categoryDeps. When you add a script:

  1. Drop the file in features/scripts/utils/ or core/system/scripts/.
  2. chmod +x it (it'll be wrapped with the right deps automatically).
  3. Reference the right categoryDeps group in features/scripts/default.nix if it needs new tools.
  4. Test that which <script> resolves after a rebuild.

Heredocs in installer/install.sh

The system-config generator uses unquoted heredocs (<< EOF) so $VAR expands. The home-config generator now also uses unquoted heredocs — any literal $ or backtick in the body must be escaped (see installer/install.sh:1043-1131). If you forget and the install fails with a "unbound variable" or unexpected command output, that's why.


7. Things to never do without explicit user OK

  • git push --force, git reset --hard, git clean -f, rm -rf outside obvious sandbox dirs.
  • Touch flake.lock unless the user asked to update inputs.
  • Bump nixpkgs major version (e.g. 25.1126.05) — that's a release decision.
  • Add a new flake input. They're load-bearing across all three host configurations.
  • Skip pre-commit hooks (--no-verify).
  • Ship a feature without a nomarchy.* option to gate it.
  • Ship a removal without updating callers (grep first).
  • Mock the database / external services in tests — use the real thing.

8. Where to put your own notes

  • Long-lived docsdocs/.
  • Working notes / per-session plans~/.claude/plans/. Don't commit them.
  • Memory about the user, this project, or process preferences → your harness's memory directory at /home/bernardo/.claude/projects/-home-bernardo-Projects-nomarchy/memory/. Each entry gets a frontmatter file plus a one-line index entry in MEMORY.md.

9. The shape of a good handoff

Before you stop, leave the next agent (or future you) something to land on:

  • The roadmap is up to date — items you shipped are in Shipped.
  • git status is clean (or has one obvious WIP commit on a wave/... branch).
  • If you discovered new scope, you logged it as a roadmap row, not a verbal heads-up.
  • Any plan file you wrote in ~/.claude/plans/ is named after the task, not the date.
  • The user's last message has been answered concisely.

That's it. The roadmap tells you what; this file tells you how. Go pick a Now item.