docs(agent): guardrail — never mkDefault a list/attrset option that merges
Some checks failed
Check / eval-and-lint (push) Failing after 7m7s

The "mkDefault everywhere user might override" rule is what caused the
home.packages bug. Clarify: mkDefault is for scalars; on list/attrset
options that other modules contribute to, a mkDefault def is dropped
entirely (filterOverrides) rather than merged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Bernardo Magri
2026-05-31 14:00:29 +01:00
parent f34f59c2aa
commit 00b8214cb1

View File

@@ -77,7 +77,7 @@ These are inherited from the established Nomarchy conventions. Violating them is
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.
4. **`lib.mkDefault` on *scalars* the user might override — never on lists/attrsets that merge.** If a downstream `system.nix` would reasonably want to change a scalar Nomarchy sets (a string, bool, int), set it with `lib.mkDefault`. If it must not be overridden, use `lib.mkForce` and explain why. **But do not wrap a list or attrset option (`home.packages`, `environment.systemPackages`, `boot.kernelParams`, …) in `lib.mkDefault`** when other modules also contribute to it: the module system's `filterOverrides` keeps only the highest-priority definitions, so a `mkDefault` list is *silently discarded the moment any module sets that option at normal priority* — it doesn't merge, it vanishes. This dropped the entire curated `home.packages` (firefox, mako, hyprlock, …) and broke notifications + the lock screen distro-wide (`1117dcf`, `f34f59c`). A plain list/attrset is the correct default — it merges, and downstream can still add to it (to *remove* a default, downstream uses `mkForce`/filtering, which is rare).
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.