# DankMaterialShell (niri) Profile Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add a `my.profiles.dank` NixOS profile that runs DankMaterialShell (DMS) on the niri Wayland compositor as a login session parallel to KDE on mibook, backed by a new opt-in `my.profiles.home-manager` module. **Architecture:** Three flake inputs (`home-manager`, `niri` = niri-flake, `dank` = DankMaterialShell) are added to `flake.nix`. A `my.profiles.home-manager` module unconditionally imports the home-manager NixOS module and, when enabled, sets global HM settings. A `my.profiles.dank` module enables `my.profiles.home-manager` as a dependency, turns on niri (system session) + the DMS nixosModule (daemons/packages), and configures `home-manager.users.finn` with the DMS + niri-flake HM modules for keybinds/spawn. mibook enables `dank`. **Tech Stack:** Nix flakes, flake-parts, NixOS modules, home-manager (as NixOS module), niri-flake, DankMaterialShell flake, quickshell (from `pkgs.unstable`). ## Global Constraints - Do **not** modify `machines/configuration.nix`. `inputs` and `self` are already threaded into every module via `_module.args`; consume them as module arguments. - **Top-level `imports` must reference flake modules via `self.inputs.*`, not `inputs.*`.** `self` is a specialArg (available during `imports` resolution); `inputs` comes from `_module.args` (config-derived) and using it in an `imports` list causes infinite recursion. Inside the `config` body (including nested `home-manager.users..imports`), either `inputs` or `self.inputs` is fine. - Base channel is pinned `nixpkgs/nixos-26.05`; `pkgs.unstable` overlay is available everywhere. quickshell must come from `pkgs.unstable.quickshell` (needs ≥ 0.3.0). - home-manager scoped to user `finn` only; `home.stateVersion = "26.05"`. - Follow the existing profile pattern exactly: `let cfg = config.my.profiles.; in { options.my.profiles..enable = lib.mkEnableOption "..."; config = lib.mkIf cfg.enable { ... }; }`. - Format every new/edited `.nix` file with `nixfmt-rfc-style` before committing. - Commit messages end with the repo's trailer lines (Co-Authored-By + Claude-Session) as seen in recent history. - KDE (`my.profiles.kde-desktop`) stays enabled on mibook; do not remove it. --- ### Task 1: Add flake inputs **Files:** - Modify: `flake.nix:4-15` (the `inputs = { ... }` block) **Interfaces:** - Produces: flake inputs `inputs.home-manager`, `inputs.niri`, `inputs.dank`, reachable from any NixOS module (via the existing `_module.args.inputs = self.inputs`). Later tasks consume `inputs.home-manager.nixosModules.home-manager`, `inputs.niri.homeModules.niri`, `inputs.dank.nixosModules.dank-material-shell`, `inputs.dank.homeModules.dank-material-shell`, `inputs.dank.homeModules.niri`. - [ ] **Step 1: Add the three inputs** In `flake.nix`, inside the `inputs = { ... }` block, after the `nixos-generators` entry and before the closing `};`, add: ```nix home-manager = { url = "github:nix-community/home-manager"; inputs.nixpkgs.follows = "nixpkgs"; }; niri.url = "github:sodiboo/niri-flake"; dank.url = "github:AvengeMedia/DankMaterialShell"; ``` - [ ] **Step 2: Resolve the lock file** Run: `nix flake lock` Expected: completes without error; `flake.lock` gains `home-manager`, `niri`, `niri-flake`-transitive, and `dank` (DankMaterialShell) nodes. If it reports a bad URL, re-check the `github:owner/repo` strings. - [ ] **Step 3: Verify inputs expose the expected outputs** Run: `nix eval --raw '.#nixosConfigurations.mibook.pkgs.system'` first to confirm the flake still evaluates (expected: `x86_64-linux`). Then run: `nix flake show 'github:AvengeMedia/DankMaterialShell' --allow-import-from-derivation 2>/dev/null | grep -E 'dank-material-shell|nixosModules|homeModules'` Expected: output lists `nixosModules.dank-material-shell` and `homeModules.dank-material-shell` and `homeModules.niri`. If the niri output name differs (e.g. `dankMaterialShell.niri`), note the actual name — Task 3 Step 1 must use whatever this shows. - [ ] **Step 4: Format and commit** ```bash nixfmt-rfc-style flake.nix git add flake.nix flake.lock git commit -m "chore: add home-manager, niri-flake, and DankMaterialShell flake inputs Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_018Sv2QAunLcLo3tS1YsfLgN" ``` --- ### Task 2: `my.profiles.home-manager` module **Files:** - Create: `modules/environments/home-manager/default.nix` - Modify: `modules/environments/default.nix` (add `./home-manager` to `imports`) **Interfaces:** - Consumes: `inputs.home-manager.nixosModules.home-manager` (Task 1). - Produces: option `my.profiles.home-manager.enable`. When enabled, sets `home-manager.useGlobalPkgs = true`, `home-manager.useUserPackages = true`, `home-manager.extraSpecialArgs = { inherit inputs self; }`. Also makes the `home-manager.users.` option available (from the unconditional import) so Task 3 can populate `home-manager.users.finn`. - [ ] **Step 1: Create the module** Create `modules/environments/home-manager/default.nix`: ```nix { config, lib, inputs, self, ... }: let cfg = config.my.profiles.home-manager; in { imports = [ self.inputs.home-manager.nixosModules.home-manager ]; options.my.profiles.home-manager.enable = lib.mkEnableOption "home-manager integration"; config = lib.mkIf cfg.enable { home-manager.useGlobalPkgs = true; home-manager.useUserPackages = true; home-manager.extraSpecialArgs = { inherit inputs self; }; }; } ``` - [ ] **Step 2: Register the module** In `modules/environments/default.nix`, add `./home-manager` to the `imports` list (place it after `./hyprland`): ```nix ./hyprland ./home-manager ``` - [ ] **Step 3: Verify it evaluates disabled (no behavior change)** Run: `nix build '.#nixosConfigurations.mibook.config.system.build.toplevel' --dry-run` Expected: evaluates and shows a build plan with no errors. Because `my.profiles.home-manager.enable` defaults to false, importing the HM module must not change the build outcome. - [ ] **Step 4: Verify the option exists and defaults false** Run: `nix eval '.#nixosConfigurations.mibook.config.my.profiles.home-manager.enable'` Expected: `false` - [ ] **Step 5: Format and commit** ```bash nixfmt-rfc-style modules/environments/home-manager/default.nix modules/environments/default.nix git add modules/environments/home-manager/default.nix modules/environments/default.nix git commit -m "feat(home-manager): add opt-in my.profiles.home-manager module Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_018Sv2QAunLcLo3tS1YsfLgN" ``` --- ### Task 3: `my.profiles.dank` module **Files:** - Create: `modules/environments/dank/default.nix` - Modify: `modules/environments/default.nix` (add `./dank` to `imports`) **Interfaces:** - Consumes: `my.profiles.home-manager.enable` (Task 2); `inputs.dank.nixosModules.dank-material-shell`, `inputs.dank.homeModules.dank-material-shell`, `inputs.dank.homeModules.niri`, `inputs.niri.homeModules.niri` (Task 1); `pkgs.unstable.quickshell`. - Produces: option `my.profiles.dank.enable`. - [ ] **Step 1: Create the module** Create `modules/environments/dank/default.nix`. Use the exact `homeModules`/`nixosModules` output names confirmed in Task 1 Step 3 — the block below assumes `dank-material-shell` and `niri`: ```nix { config, lib, pkgs, inputs, self, ... }: let cfg = config.my.profiles.dank; in { imports = [ self.inputs.dank.nixosModules.dank-material-shell ]; options.my.profiles.dank.enable = lib.mkEnableOption "DankMaterialShell on niri"; config = lib.mkIf cfg.enable { # Dependency: declarative user config comes from home-manager. my.profiles.home-manager.enable = true; # System side: niri Wayland session + DMS daemons/packages. programs.niri.enable = true; programs.dank-material-shell = { enable = true; systemd.enable = true; quickshell.package = pkgs.unstable.quickshell; }; # Home side: DMS shell + niri keybinds/spawn for finn. home-manager.users.finn = { imports = [ inputs.niri.homeModules.niri inputs.dank.homeModules.dank-material-shell inputs.dank.homeModules.niri ]; programs.dank-material-shell = { enable = true; systemd.enable = true; quickshell.package = pkgs.unstable.quickshell; niri.enableKeybinds = true; niri.enableSpawn = true; }; home.stateVersion = "26.05"; }; }; } ``` - [ ] **Step 2: Register the module** In `modules/environments/default.nix`, add `./dank` to the `imports` list (after `./home-manager`): ```nix ./home-manager ./dank ``` - [ ] **Step 3: Verify it evaluates disabled** Run: `nix eval '.#nixosConfigurations.mibook.config.my.profiles.dank.enable'` Expected: `false` Run: `nix build '.#nixosConfigurations.mibook.config.system.build.toplevel' --dry-run` Expected: evaluates without error (dank disabled → no behavior change yet). - [ ] **Step 4: Format and commit** ```bash nixfmt-rfc-style modules/environments/dank/default.nix modules/environments/default.nix git add modules/environments/dank/default.nix modules/environments/default.nix git commit -m "feat(dank): add DankMaterialShell (niri) profile Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_018Sv2QAunLcLo3tS1YsfLgN" ``` --- ### Task 4: Enable on mibook and build the full toplevel **Files:** - Modify: `machines/mibook/environments.nix:7-17` (the `my.profiles = { ... }` block) **Interfaces:** - Consumes: `my.profiles.dank.enable` (Task 3). - [ ] **Step 1: Enable the profile** In `machines/mibook/environments.nix`, inside the `my.profiles = { ... }` block, add: ```nix dank.enable = true; ``` - [ ] **Step 2: Build the full toplevel (the real test)** Run: `nix build '.#nixosConfigurations.mibook.config.system.build.toplevel'` Expected: builds successfully. This exercises the DMS package, quickshell from unstable, niri, and the home-manager generation for finn. If it fails on a **quickshell/Qt6 version mismatch** (see spec risks): drop `quickshell.package = pkgs.unstable.quickshell;` from **both** the system and HM `programs.dank-material-shell` blocks in `modules/environments/dank/default.nix` and rebuild, letting DMS's module pick its own default quickshell. If it fails on an **unknown HM option** (e.g. `niri.enableKeybinds`), run `nix eval '.#nixosConfigurations.mibook.config.home-manager.users.finn.programs.dank-material-shell' --apply builtins.attrNames 2>&1 | head` to list the real option names and adjust. Re-run the build until it passes. - [ ] **Step 3: Run flake check** Run: `nix flake check` Expected: passes (all nixosConfigurations evaluate). If `nix flake check` is slow or pulls DMS's own checks, `nix build '.#nixosConfigurations.mibook.config.system.build.toplevel'` from Step 2 passing is the authoritative gate. - [ ] **Step 4: Format and commit** ```bash nixfmt-rfc-style machines/mibook/environments.nix git add machines/mibook/environments.nix modules/environments/dank/default.nix git commit -m "feat(mibook): enable DankMaterialShell (niri) session Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_018Sv2QAunLcLo3tS1YsfLgN" ``` - [ ] **Step 5: Manual acceptance (human, on hardware)** Run: `sudo nixos-rebuild test --flake '.#mibook'` Then log out, pick the **niri** session at SDDM, log in as finn. Expected: DMS bar/shell appears; `Mod+Space` opens the launcher; `Mod+V` opens the clipboard; the KDE session is still selectable and works. Note this step cannot be automated — it requires the physical machine. --- ## Notes for the implementer - **Order matters:** Task 1 must land first (inputs must resolve before any module can import them). Tasks 2→3→4 are strictly sequential. - **Output-name drift is the top risk.** The DMS flake has renamed outputs over time (there are deprecated aliases like `dankMaterialShell.niri`). Task 1 Step 3 pins down the real names against the locked revision; use those, not the names in this plan, if they differ. - **No unit-test framework here.** Nix evaluation + a successful `toplevel` build *is* the test. Do not fabricate a test harness. - Do not touch `machines/configuration.nix`, `machines/jupiter/*`, or the KDE profile.