style: align comments with the project conventions
Apply a codebase-wide comment audit against the comment conventions: split banned semicolons and multi-sentence lines into one sentence per line, cut cross-file and history narration, trim file-top headers to a single purpose line, and drop verbosity that did not earn its place. Prose docs (CLAUDE.md, install.md) get the same one-sentence-per-line and no-semicolon treatment.
This commit is contained in:
45
CLAUDE.md
45
CLAUDE.md
@@ -1,13 +1,15 @@
|
||||
# dotfiles-nixos
|
||||
|
||||
One flake that builds every machine the user owns.
|
||||
The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overlays) lives in `.claude/CONTEXT.md`; the current deliverable's spec is `.claude/spec/laptop-mvi.md`.
|
||||
The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overlays) lives in `.claude/CONTEXT.md`.
|
||||
The current deliverable's spec is `.claude/spec/laptop-mvi.md`.
|
||||
|
||||
## Conventions
|
||||
|
||||
- Comments posted to Gitea (pull requests, issues, reviews) go out under the operator's account, so sign every one to make clear the author is the agent, not the operator.
|
||||
End the comment with a `— Claude` sign-off.
|
||||
(A dedicated bot account may replace this later; until then, the sign-off is the only marker.)
|
||||
(A dedicated bot account may replace this later.
|
||||
Until then, the sign-off is the only marker.)
|
||||
- Commit messages follow Conventional Commits, specified in `docs/conventional-commits.md`.
|
||||
Scope is the module or host the change belongs to (`fish`, `nvim`, `neogaia`), omitted for repo-wide changes.
|
||||
Keep messages free of Gitea-specific references: this repository is mirrored to GitHub, where issue and pull-request numbers resolve to unrelated things.
|
||||
@@ -21,7 +23,8 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla
|
||||
Flakes and the chaotic substituter come from this flake's own `nix.settings`, so no `NIX_CONFIG` export or per-command `--extra-experimental-features` is needed, and building a toplevel with `boot.kernelPackages = linuxPackages_cachyos` fetches the kernel from `nyx-cache` rather than compiling it.
|
||||
Both were true only while the machine still ran CachyOS against a distro Nix daemon.
|
||||
- The substituters a `nix build` fetches from are the **daemon's** (`/etc/nix/nix.conf`), *not* the `nix.settings` of the config being built — those only govern the built system.
|
||||
The two coincide here because the dev host runs this flake; they diverge on any machine that does not.
|
||||
The two coincide here because the dev host runs this flake.
|
||||
They diverge on any machine that does not.
|
||||
- Git identity is declared in the flake by `modules/git.nix`, which writes `alexion <contact@alexion.dev>` — the identity all history uses — on any host enabling `modules.git`.
|
||||
Every new host has to enable it, so that a host reads as a full checklist of what it carries.
|
||||
It is deployed on `neogaia` and verified: a commit in a repository outside this checkout is authored `alexion <contact@alexion.dev>` with no override.
|
||||
@@ -32,12 +35,15 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla
|
||||
With both global files present it prints only `~/.gitconfig`, which reads as proof that `~/.config/git/config` is being ignored entirely.
|
||||
It is not: drop `--global` and both files appear, each key resolving to the last file that sets it.
|
||||
A `git config --global <key> <value>` write also lands in `~/.gitconfig`, the file that outranks the flake-managed one.
|
||||
- The primary build/verify seam for any Host is `nix flake check`, which builds `checks.x86_64-linux.<host>` (the system toplevel); cheap targeted checks use `nix eval .#nixosConfigurations.<host>.config...`.
|
||||
- The primary build/verify seam for any Host is `nix flake check`, which builds `checks.x86_64-linux.<host>` (the system toplevel).
|
||||
Cheap targeted checks use `nix eval .#nixosConfigurations.<host>.config...`.
|
||||
- A flake only sees **git-tracked** files, so a new file that has not been `git add`ed is invisible to evaluation even though it exists on disk.
|
||||
The failure names the path and reads as if the file were missing: `error: Path 'secrets/shared.yaml' does not exist in Git repository`.
|
||||
Staging is enough; the file need not be committed.
|
||||
Staging is enough.
|
||||
The file need not be committed.
|
||||
- chaotic-nyx must **not** follow our `nixpkgs`, and its packages are built against chaotic's own pinned nixpkgs (its overlay defaults to `onTopOf = "flake-nixpkgs"`, the cache-friendly path).
|
||||
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.
|
||||
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.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.
|
||||
@@ -54,7 +60,8 @@ 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/agents/claude-code/skills/<name>/` here, applied by a rebuild.
|
||||
Editing a skill in place fails.
|
||||
Its source is `modules/agents/claude-code/skills/<name>/` 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.<user>.home.file` is keyed by whatever path string the **defining module wrote**, absolute or relative, not by one canonical form.
|
||||
@@ -62,9 +69,14 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla
|
||||
The other form fails with "does not provide attribute", so overriding an entry (e.g. setting `.force = true` on it) requires matching the writer's exact key.
|
||||
List the real keys with `nix eval --json .#nixosConfigurations.<host>.config.home-manager.users.<user>.home.file --apply builtins.attrNames` rather than guessing one.
|
||||
A key's `.source` is the input file, whose store path differs from the deployed symlink's target (home-manager copies it to a `hm_`-prefixed path) even though the contents match.
|
||||
- nixpkgs `vimPlugins.nord-nvim` is `shaunsingh/nord.nvim` (no `require("nord").setup()`); the config wants `gbprod/nord.nvim`, which is packaged as `vimPlugins.gbprod-nord`.
|
||||
- nixpkgs `vimPlugins.nvim-treesitter` tracks the rewritten `main` branch: there is no `require("nvim-treesitter.configs").setup{ensure_installed,highlight,indent}`. Under nixvim, use `plugins.treesitter` with `highlight.enable`/`indent.enable` and `grammarPackages = with config.programs.nixvim.plugins.treesitter.package.builtGrammars; [ ... ]` — the module's own `package.builtGrammars`, **not** `pkgs.vimPlugins.nvim-treesitter.*` (whose query files can mismatch). The module targets the main branch and enables features via neovim-native APIs (`vim.treesitter.start()`, `require'nvim-treesitter'.indentexpr()`).
|
||||
- Neovim is configured via **nixvim** (flake input `nixvim`, consumed as `inputs.nixvim.homeModules.nixvim` added to `home-manager.sharedModules`, config under `home-manager.users.<user>.programs.nixvim`). `nixvim.inputs.nixpkgs.follows = "nixpkgs"` is set; nixvim then emits a benign eval warning that its pinned nixpkgs differs from the followed one — builds and runs fine, do not "fix" it by dropping the follows.
|
||||
- nixpkgs `vimPlugins.nord-nvim` is `shaunsingh/nord.nvim` (no `require("nord").setup()`).
|
||||
The config wants `gbprod/nord.nvim`, which is packaged as `vimPlugins.gbprod-nord`.
|
||||
- nixpkgs `vimPlugins.nvim-treesitter` tracks the rewritten `main` branch: there is no `require("nvim-treesitter.configs").setup{ensure_installed,highlight,indent}`.
|
||||
Under nixvim, use `plugins.treesitter` with `highlight.enable`/`indent.enable` and `grammarPackages = with config.programs.nixvim.plugins.treesitter.package.builtGrammars; [ ... ]` — the module's own `package.builtGrammars`, **not** `pkgs.vimPlugins.nvim-treesitter.*` (whose query files can mismatch).
|
||||
The module targets the main branch and enables features via neovim-native APIs (`vim.treesitter.start()`, `require'nvim-treesitter'.indentexpr()`).
|
||||
- Neovim is configured via **nixvim** (flake input `nixvim`, consumed as `inputs.nixvim.homeModules.nixvim` added to `home-manager.sharedModules`, config under `home-manager.users.<user>.programs.nixvim`).
|
||||
`nixvim.inputs.nixpkgs.follows = "nixpkgs"` is set.
|
||||
nixvim then emits a benign eval warning that its pinned nixpkgs differs from the followed one — builds and runs fine, do not "fix" it by dropping the follows.
|
||||
- To reference the nixvim-built package's own attrs (e.g. treesitter `builtGrammars`) inside our NixOS module, give `home-manager.users.<user>` the module-function form (`hm: { programs.nixvim = { ... hm.config.programs.nixvim... }; }`), since the outer `config` is the NixOS config, not the home-manager one.
|
||||
- The agent's Bash sandbox blocks `sudo` and swallows it into a bare exit 1 with **no stderr**, which looks identical to the command itself failing.
|
||||
Re-run with the sandbox disabled to see the real error (`sudo: a password is required`) before diagnosing anything else.
|
||||
@@ -72,7 +84,8 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla
|
||||
- Sudo's credential cache is keyed per user rather than per terminal (`timestamp_type=global`, 60-minute window, declared by the claude-code module), so an authentication made in one terminal counts for commands the agent runs.
|
||||
Warming it with `sudo -v` through the agent's own shell — including the `!` prefix — never works: that shell has no controlling terminal, and sudo reports `a terminal is required to read the password`.
|
||||
It has to be a separate terminal.
|
||||
A `PreToolUse` hook refuses privileged commands while the cache is cold, so a cold cache announces itself instead of stalling; a failure *without* that message is the sandbox, not the cache.
|
||||
A `PreToolUse` hook refuses privileged commands while the cache is cold, so a cold cache announces itself instead of stalling.
|
||||
A failure *without* that message is the sandbox, not the cache.
|
||||
- An `mkOption` of a list or attribute-set type is **not** mandatory the way a scalar one is.
|
||||
Those types carry an `emptyValue`, so an option declared with no `default` and never set evaluates to `[ ]` or `{ }` instead of failing with "option used but not defined".
|
||||
A declaration that is genuinely required cannot be expressed by omitting the default — it needs an assertion, or a default chosen so that the silent case is the safe one.
|
||||
@@ -80,7 +93,9 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla
|
||||
- `home-manager.users.<user>` cannot be assigned twice at the same level in one module: `home-manager.users.${user}.home.packages` alongside `home-manager.users.${user}.programs.x` fails with `error: dynamic attribute 'alexion' already defined`.
|
||||
The interpolated key makes it a dynamic attribute, which nix will not merge the way it merges static paths.
|
||||
Nest both under a single `home-manager.users.${user} = { ... }`.
|
||||
- **Verifying a nixvim change headless:** `programs.nixvim.build.package`'s wrapper has **no `-u`**, so running `$OUT/bin/nvim` loads the caller's `~/.config/nvim` (the dev host's real config), *not* the built config — silently. To exercise the built config, launch with `-u "$(nix build --no-link --print-out-paths .#…programs.nixvim.build.initFile)"` and a scratch `HOME`/`XDG_CONFIG_HOME`. `conceallevel` is window-local: set it with `opt_local`/`vim.wo`, never `vim.bo[buf]` (which errors).
|
||||
- **Verifying a nixvim change headless:** `programs.nixvim.build.package`'s wrapper has **no `-u`**, so running `$OUT/bin/nvim` loads the caller's `~/.config/nvim` (the dev host's real config), *not* the built config — silently.
|
||||
To exercise the built config, launch with `-u "$(nix build --no-link --print-out-paths .#…programs.nixvim.build.initFile)"` and a scratch `HOME`/`XDG_CONFIG_HOME`.
|
||||
`conceallevel` is window-local: set it with `opt_local`/`vim.wo`, never `vim.bo[buf]` (which errors).
|
||||
- Host GPUs: `neogaia` is Intel and `zeus` (the desktop) is **AMD**.
|
||||
`raichu`, a server with no desktop, is the only Nvidia machine.
|
||||
`laptop-mvi.md`'s out-of-scope line calls zeus Nvidia, but that is stale and the document is kept historical and unchanged, so do not infer any host's GPU from it.
|
||||
@@ -110,7 +125,9 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla
|
||||
Engines are referenced by their current id, which the module maps from the old display names, so the default is `default = "ddg"`, not `"DuckDuckGo"` (the latter only warns and migrates).
|
||||
Prove the result by decoding the built file: `mozlz4a -d <search.json.mozlz4>` shows the `_metaData.hidden` flags and `defaultEngineId`.
|
||||
- `home.sessionVariables` do **not** reach the Hyprland session, so anything the compositor reads from its environment (cursor theme, toolkit hints) has to be declared elsewhere.
|
||||
home-manager writes those variables to `hm-session-vars.sh`, which only a login shell sources; UWSM starts the graphical session without it, so the compositor's environment never gains them.
|
||||
home-manager writes those variables to `hm-session-vars.sh`, which only a login shell sources.
|
||||
UWSM starts the graphical session without it, so the compositor's environment never gains them.
|
||||
This is why a Stylix cursor (`XCURSOR_THEME`/`XCURSOR_SIZE` via `home.pointerCursor`) silently fails to apply and Hyprland draws its built-in cursor: the variables exist in `sessionVariables` but not in the running session (`tr '\0' '\n' < /proc/$(pgrep -x Hyprland)/environ` shows them absent).
|
||||
The fix is Hyprland's own `env = KEY,VALUE` directive in `settings`, read at compositor startup regardless of the shell profile; `modules/desktop/hyprland/hyprland.nix` sets the cursor that way, sourced from `config.stylix.cursor`.
|
||||
The fix is Hyprland's own `env = KEY,VALUE` directive in `settings`, read at compositor startup regardless of the shell profile.
|
||||
`modules/desktop/hyprland/hyprland.nix` sets the cursor that way, sourced from `config.stylix.cursor`.
|
||||
Bibata ships XCursor format only (no `hyprcursor/` dir), which Hyprland renders through its XCursor fallback, so both `XCURSOR_*` and `HYPRCURSOR_*` naming the same theme are safe.
|
||||
|
||||
Reference in New Issue
Block a user