# Conventions — how Nomarchy code is written The standing rules an agent must follow while coding. GOALS.md says what we're building; this says how. Details/rationale live in docs/ROADMAP.md's decision records and the README. ## Repo layout (the rule of thumb) `modules/` is the distro (reusable, no machine specifics) · `hosts/` is a machine · `themes/` is data · `pkgs/` is code · `tools/` is maintainer-only · `agent/` is loop state. If a new file doesn't obviously belong to one of those, it probably shouldn't exist. ## Nix style - **No formatter.** Files use deliberate aligned hand-formatting — match the surrounding style exactly; never reflow a file you're only touching a line of. - Distro defaults use `lib.mkDefault` so a plain downstream assignment wins; bind/exec lists concatenate. Behaviour options overridable, appearance flows from the state JSON. - Options live in the existing surfaces: `nomarchy.system.*` / `nomarchy.hardware.*` / `nomarchy.services.*` (NixOS, `system.nix`), everything else `nomarchy.*` (HM, `home.nix`). Update the README tables when the surface changes. ## Feature design - **In-flake state:** any user-settable config gets a menu writer that lands it in `theme-state.json` (`settings.*`), git-tracked. No `~/.local/state`, no side files. Instant-effect where possible (`--no-switch` + flip the service; the service reads the *live* working tree at start — the night-light `ExecCondition` pattern). Rebuild-baked values graduate via `mkDefault` reads of the settings key. - **Toggle vs package:** a `nomarchy.*` toggle only when there's real config behind it (units, groups, udev, firewall). A bare package goes in the downstream template's `home.packages` — opt-out is deleting the line. - **Opt-in features** ship a commented example in `templates/downstream/home.nix` or `system.nix`. That template is the single source of truth for machine files: `nomarchy-install` copies it and patches install-time values only (never a thinner second catalog). - **Menu:** new entries go in the right submenu (Tools › / System ›; root stays six entries), end lists with the shared `↩ Back`, self-gate on the feature's availability, and add the direct `SUPER+CTRL+` bind in `keybinds.nix` (single source — it feeds both Hyprland and the SUPER+? cheatsheet). - **Waybar:** new indicators self-gate (hidden when irrelevant), use named `writeShellScriptBin`s on PATH (so static configs can exec them by bare name), and are added to **both** the generated `waybar.nix` config **and** every `waybar.jsonc` whole-swap — summer-day, summer-night, executive-slate and boreal (the parity rule). - **Theming:** every new visual surface consumes the palette from the state JSON. There is no second renderer to keep in sync — add the key to the JSON, consume it in the module. ## Testing - docs/TESTING.md is canonical; LOOP.md's ladder (V0–V3) sets the required tier. Cheap first: `nix flake check --no-build`, `bash -n`, `py_compile`. - Prefer a permanent `checks.*` runNixOSTest over a one-off manual poke; reusable recipes (headless Hyprland with software GL, QMP screenshots, udev-event fakes) are indexed in MEMORY.md and demonstrated by the existing checks (`distro-id`, `hardware-toggles`, `battery-charge-limit`). - The honesty rule: report exactly what you verified and at which tier. ## Git - `main` is development (direct commits, pushed); `v1` is the release pointer — **human-only, fast-forward-only, never touched by agents**. - Commit style: `feat|fix|test|docs|chore(scope): summary`, body with what/why + verification tier + what remains. Bookkeeping (`agent/` updates) rides in the same commit as the change. - `flake.lock` moves only when the task *is* a lock bump, and only within the pinned release branches.