From 8c254f2eb1151b5b400953abb2c7efe1281bd549 Mon Sep 17 00:00:00 2001 From: Jelle Spreeuwenberg Date: Tue, 21 Apr 2026 16:03:55 +0200 Subject: [PATCH] docs: add repository agent guidance --- AGENTS.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..fa81857 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,73 @@ +# AGENTS.md + +## Purpose + +This repo uses the Dendritic Pattern with `flake-parts`. + +Design and change the configuration as a composition of **features**, not as a host-first tree. + +For deeper design rationale and pattern descriptions, refer to `.agents/dendritic-design-with-flake-parts.wiki`. + +## Core Terms + +- **Feature**: a flake-parts module under `modules/` that defines one coherent concern. +- **Aspect**: a reusable module published at `flake.modules..`. +- **Module class**: the configuration context of an aspect. This repo primarily uses `nixos` and `homeManager`. +- **Feature module**: the flake-parts module that defines aspects, flake outputs, options, or shared helpers. + +In this repo, `flake.nix` imports `./modules` recursively via `inputs.import-tree`. Any non-private `.nix` file under `modules/` is therefore treated as a feature module. + +## Design Principles + +- Work bottom-up. Define features first; assemble hosts from features. +- Keep semantic ownership local. A feature should contain the configuration for that concern across all relevant module classes. +- Name aspects semantically. The aspect name should usually match the file or directory name that defines it. +- Prefer small, composable aspects. Build larger configurations with `imports`. +- Import aspects unconditionally and only within the same module class. +- Put conditions inside module content with `lib.mkIf` or `lib.mkMerge`, never around `imports`. +- Avoid importing the same aspect multiple times along one import path. +- Keep private helper files next to the feature that uses them and prefix them with `_` so `import-tree` does not import them as feature modules. +- Put shared schemas and constructors in dedicated shared modules, not ad hoc host files. + +## Repo Structure + +- `modules/features/`: reusable features and most aspect definitions. +- `modules/hosts//default.nix`: host features that assemble NixOS aspects into `flake.modules.nixos.`. +- `modules/secrets/`: secret-related features shared by hosts. +- `modules/flake-parts.nix`: flake-parts entrypoint; defines systems, formatter, and `flake.nixosConfigurations`. +- `modules/lib.nix`: shared constructors in `config.meta.lib`, especially `mkHost`, `mkHostUser`, and `mkCaddyReverseProxy`. +- `modules/users.nix`: canonical user attrsets exposed through `meta.lib.users`. +- `modules/features/meta.nix`: shared metadata schema for `meta.host` and `meta.user`. + +## How Features Are Applied Here + +- Reusable NixOS concerns are published as `flake.modules.nixos.`. +- Reusable Home Manager concerns are published as `flake.modules.homeManager.`. +- Hosts are aspects too. `orion`, `polaris`, and `zenith` are `nixos` aspects assembled from smaller aspects. +- Host modules should use `config.meta.lib.mkHost` to define `meta.host`, base imports, hostname, and state version. +- Per-user Home Manager composition should use `config.meta.lib.mkHostUser` so `meta.host` and `meta.user` stay available to Home Manager features. +- Features may rely on the `meta` contract. Existing modules already read `config.meta.host`, `config.meta.user`, and `config.meta.lib`. + +## Preferred Aspect Patterns + +- **Simple Aspect**: use for one self-contained concern in one or more module classes. +- **Multi Context Aspect**: use when one concern must configure both `nixos` and `homeManager`. +- **Inheritance Aspect**: use by importing a parent aspect and extending it. +- **Conditional Aspect**: use `lib.mkMerge` plus `lib.mkIf` for conditional content. + +Use **Collector Aspect** only when composition through imports or shared library helpers is insufficient. + +## Change Rules + +- When adding a feature, add or extend aspects under `modules/features/` and let hosts opt into them explicitly. +- When adding a host, create `modules/hosts//default.nix` and keep host-local generated files private as `_hardware.nix`, `_disk.nix`, or similar. +- When a feature needs local data or helper code, keep it inside that feature directory and prefix non-feature files with `_` when they live under `modules/`. +- Do not place arbitrary non-feature `.nix` files under `modules/` unless they are intentionally private and excluded from recursive import. +- If a concern is shared across hosts, it belongs in a reusable feature, not inline in one host unless it is truly host-specific. + +## Practical Heuristics + +- If you are about to edit a host because of a reusable concern, that concern probably wants its own feature. +- If a Home Manager module needs host or user facts, prefer reading `config.meta.host` or `config.meta.user` instead of duplicating literals. +- If a concern spans system and user space, keep both aspects in one feature so the behavior stays coherent. +- If imports would need to be conditional, redesign the aspect boundary instead.