From 6f6f0178b1b60d3e3c69c0e78b383efe415804fb Mon Sep 17 00:00:00 2001 From: alexion Date: Thu, 23 Jul 2026 21:44:21 -0400 Subject: [PATCH 1/2] fix: apply the namespace edits dropped from the task 0031 merge The task 0031 commit moved the module files but a mis-staged `git add` left the option-path rewrites out, so main declared the old namespaces (`modules.claude-code`, `modules.gitea-axi`, `modules.pi`, `modules.desktop.hyprlock`/`hypridle`) while CONTEXT.md and ADR 0004 already documented the new ones. Rewrite the paths in the moved and in-place modules, the neogaia host, and the two live CLAUDE.md gotchas so the code matches the recorded convention. --- CLAUDE.md | 4 ++-- hosts/neogaia/default.nix | 6 +++--- modules/agents/claude-code/claude-code.nix | 4 ++-- modules/agents/pi.nix | 4 ++-- modules/agents/tools/gitea-axi.nix | 4 ++-- modules/desktop/desktop.nix | 4 ++-- modules/desktop/hyprland/hypridle.nix | 4 ++-- modules/desktop/hyprland/hyprlock.nix | 4 ++-- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index d3a8b71..52c016d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -40,7 +40,7 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla That is what lets the `nyx-cache.chaotic.cx` binary cache hit instead of compiling the CachyOS kernel from source; the tradeoff is that chaotic packages do not see our `unstable`/`stable` overlays. - The remote is self-hosted Gitea (`git.alexion.dev`), and the forge CLI is `gitea-axi` rather than `tea`. `gitea-axi` resolves the repository from the `origin` remote and discovers credentials from a `tea` login whose host matches the remote, so both are implicit inside a checkout. - It is installed on `neogaia` by `modules.gitea-axi`, and verified: `gitea-axi` run from this checkout renders the `alexion/dotfiles` dashboard authenticated, so the claude-code `SessionStart` hook that runs it now resolves to a real binary rather than a missing one. + It is installed on `neogaia` by `modules.agents.tools.gitea-axi`, and verified: `gitea-axi` run from this checkout renders the `alexion/dotfiles` dashboard authenticated, so the claude-code `SessionStart` hook that runs it now resolves to a real binary rather than a missing one. The package wraps the binary so `git` and `tea` are reachable without being on `PATH`, while still preferring the operator's own where present. Credentials: `~/.config/tea/config.yml` holds a token-bearing login named `alexion`, which `gitea-axi` uses and which also opens pull requests directly with `nix run nixpkgs#tea -- pr create --login alexion --repo alexion/dotfiles --base main --head ...`. The `--repo` flag is required on that path, since `tea` resolves `origin` only for a login whose SSH host matches. @@ -54,7 +54,7 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla On this machine the two are provably distinct: the daemon presents `SHA256:2ysuBX0+Z6GbdCTujz5JHX6rqnJzIyWhYNrxdhhGwEM`, while pushes to `git.alexion.dev` authenticate with `SHA256:nEhHwtHDnLlsuFxyfp+cETgHUZ8xDMxaPVmYM5vuCkA`. Renaming host keys after user keys, or vice versa, is therefore always wrong. - `~/.claude/skills` is generated by home-manager with `recursive = true`, so the directories are real and writable but every leaf file is a read-only symlink into the store. - Editing a skill in place fails; its source is `modules/claude-code/skills//` here, applied by a rebuild. + Editing a skill in place fails; its source is `modules/agents/claude-code/skills//` here, applied by a rebuild. Creating a new file under `~/.claude/skills/` succeeds silently and is the trap — it stays outside the repo and reaches no other machine. Copying out of that tree needs `cp -rL` plus `chmod -R u+w`: a plain `cp -r` copies the symlinks, putting store paths into the destination, and dereferenced files keep the store's read-only mode. - `home-manager.users..home.file` is keyed by **absolute** path, not by a path relative to the home directory. diff --git a/hosts/neogaia/default.nix b/hosts/neogaia/default.nix index a24268f..f132ad6 100644 --- a/hosts/neogaia/default.nix +++ b/hosts/neogaia/default.nix @@ -48,9 +48,9 @@ modules.git.enable = true; modules.tmux.enable = true; modules.nvim.enable = true; - modules.claude-code.enable = true; - modules.gitea-axi.enable = true; - modules.pi.enable = true; + modules.agents.claude-code.enable = true; + modules.agents.tools.gitea-axi.enable = true; + modules.agents.pi.enable = true; modules.desktop.enable = true; diff --git a/modules/agents/claude-code/claude-code.nix b/modules/agents/claude-code/claude-code.nix index b6221b2..a550b7c 100644 --- a/modules/agents/claude-code/claude-code.nix +++ b/modules/agents/claude-code/claude-code.nix @@ -8,11 +8,11 @@ # the package and manages ~/.claude. Login credentials are left unmanaged so they # survive rebuilds. let - cfg = config.modules.claude-code; + cfg = config.modules.agents.claude-code; user = config.user.name; in { - options.modules.claude-code.enable = lib.mkEnableOption '' + options.modules.agents.claude-code.enable = lib.mkEnableOption '' Claude Code, Anthropic's CLI, configured via home-manager. Enabling this also widens sudo's credential cache, keying it per user rather diff --git a/modules/agents/pi.nix b/modules/agents/pi.nix index c9554c1..e323321 100644 --- a/modules/agents/pi.nix +++ b/modules/agents/pi.nix @@ -7,11 +7,11 @@ # home-manager, which ships the package and manages ~/.pi/agent. # The login credential is left unmanaged, so it survives rebuilds. let - cfg = config.modules.pi; + cfg = config.modules.agents.pi; user = config.user.name; in { - options.modules.pi.enable = lib.mkEnableOption '' + options.modules.agents.pi.enable = lib.mkEnableOption '' Pi, a terminal coding agent, configured via home-manager''; config = lib.mkIf cfg.enable { diff --git a/modules/agents/tools/gitea-axi.nix b/modules/agents/tools/gitea-axi.nix index 36bf996..db28a12 100644 --- a/modules/agents/tools/gitea-axi.nix +++ b/modules/agents/tools/gitea-axi.nix @@ -8,11 +8,11 @@ # That module also declares the Claude Code context when that harness is # enabled on the host; enabling this alone installs the CLI and nothing else. let - cfg = config.modules.gitea-axi; + cfg = config.modules.agents.tools.gitea-axi; user = config.user.name; in { - options.modules.gitea-axi.enable = + options.modules.agents.tools.gitea-axi.enable = lib.mkEnableOption "gitea-axi, an agent-ergonomic CLI for Gitea issues and pull requests"; config = lib.mkIf cfg.enable { diff --git a/modules/desktop/desktop.nix b/modules/desktop/desktop.nix index 4e201e7..e1cc899 100644 --- a/modules/desktop/desktop.nix +++ b/modules/desktop/desktop.nix @@ -11,8 +11,8 @@ in config = lib.mkIf cfg.enable { modules.desktop.clipboard.enable = lib.mkDefault true; modules.desktop.hyprland.enable = lib.mkDefault true; - modules.desktop.hyprlock.enable = lib.mkDefault true; - modules.desktop.hypridle.enable = lib.mkDefault true; + modules.desktop.hyprland.hyprlock.enable = lib.mkDefault true; + modules.desktop.hyprland.hypridle.enable = lib.mkDefault true; modules.desktop.login.enable = lib.mkDefault true; modules.desktop.mako.enable = lib.mkDefault true; modules.desktop.portals.enable = lib.mkDefault true; diff --git a/modules/desktop/hyprland/hypridle.nix b/modules/desktop/hyprland/hypridle.nix index 914bfa6..4fdc76a 100644 --- a/modules/desktop/hyprland/hypridle.nix +++ b/modules/desktop/hyprland/hypridle.nix @@ -7,7 +7,7 @@ # Idle management: hypridle locks on idle, powers the displays off, and locks # before every suspend, so an unattended session always lands at hyprlock. let - cfg = config.modules.desktop.hypridle; + cfg = config.modules.desktop.hyprland.hypridle; user = config.user.name; hyprctl = "${config.programs.hyprland.package}/bin/hyprctl"; @@ -18,7 +18,7 @@ let lockCmd = "${pkgs.procps}/bin/pidof hyprlock || ${hyprlock}"; in { - options.modules.desktop.hypridle = { + options.modules.desktop.hyprland.hypridle = { enable = lib.mkEnableOption "hypridle idle management"; lockTimeout = lib.mkOption { diff --git a/modules/desktop/hyprland/hyprlock.nix b/modules/desktop/hyprland/hyprlock.nix index a76830c..03ba4e9 100644 --- a/modules/desktop/hyprland/hyprlock.nix +++ b/modules/desktop/hyprland/hyprlock.nix @@ -6,7 +6,7 @@ }: # The lock screen: hyprlock, a session-lock client whose surface the compositor owns, so it survives a crash of the locker rather than exposing the session. let - cfg = config.modules.desktop.hyprlock; + cfg = config.modules.desktop.hyprland.hyprlock; user = config.user.name; # The hyprlock this module installs, so the keybind and the idle daemon lock @@ -14,7 +14,7 @@ let hyprlock = "${config.home-manager.users.${user}.programs.hyprlock.package}/bin/hyprlock"; in { - options.modules.desktop.hyprlock.enable = lib.mkEnableOption "the hyprlock lock screen"; + options.modules.desktop.hyprland.hyprlock.enable = lib.mkEnableOption "the hyprlock lock screen"; config = lib.mkIf cfg.enable { home-manager.users.${user} = { -- 2.47.3 From 039802b9e219efd910ac8368645f6c3933bfaaea Mon Sep 17 00:00:00 2001 From: alexion Date: Thu, 23 Jul 2026 21:47:22 -0400 Subject: [PATCH 2/2] docs: record the git-add and dirty-tree gotchas behind the task 0031 miss --- CLAUDE.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 52c016d..2487f22 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -97,3 +97,9 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla A flat `windowrule =` entry is a comma-separated list of `field value` tokens, each of which **must** carry a value: matchers take a `match:` prefix and effects are bare, so floating one app is `windowrule = float 1, match:class ^(com\.gabm\.satty)$`. The old form fails at load with `invalid field float: missing a value`, because the effect token has no value. `windowrulev2` is removed and errors as deprecated. +- A multi-path `git add a b c` aborts entirely and stages **nothing** when any one pathspec matches no file, so a stale path in the list silently drops every other file from the commit. + This bit here: a path already removed by `git rm` was passed to a later `git add`, which failed with `fatal: pathspec ... did not match any files` and staged none of the real edits beside it, landing a commit that moved files but kept the old option paths. + Stage in separate `git add` calls, or `git status` the result before committing rather than trusting the add. +- A `nix build` or `nix flake check` on a **dirty** tree evaluates the working-copy content of tracked files, not what is committed, and only warns `Git tree ... is dirty`. + A green check on a dirty tree therefore proves nothing about the commit. + To verify a commit, build once on a clean tree (nothing uncommitted), where the absence of the dirty warning confirms the build reflects `HEAD`. -- 2.47.3