docs: add technical writing guide
This commit is contained in:
208
AGENTS.md
208
AGENTS.md
@@ -1,110 +1,118 @@
|
|||||||
# dotfiles-nixos
|
# dotfiles-nixos
|
||||||
|
|
||||||
One flake that builds every machine the user owns.
|
One flake builds every machine I own.
|
||||||
The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overlays) lives in `~/Documents/ai-artifacts/projects/dotfiles/003-dotfiles-context.md`.
|
|
||||||
|
|
||||||
## Conventions
|
## Repository 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.
|
- Follow `docs/technical-writing.md` for comments, user-facing descriptions, commit prose, pull-request prose, and Markdown documentation.
|
||||||
End the comment with a `— Claude` sign-off.
|
- Comments posted to the forge under the operator's account must end with `— Pi`.
|
||||||
(A dedicated bot account may replace this later.
|
The sign-off identifies the agent as the author instead of the operator.
|
||||||
Until then, the sign-off is the only marker.)
|
- Commit messages follow Conventional Commits, defined in `docs/conventional-commits.md`.
|
||||||
- Commit messages follow Conventional Commits, specified in `docs/conventional-commits.md`.
|
Use a scope for the changed module or host, such as `fish`, `nvim`, or `neogaia`.
|
||||||
Scope is the module or host the change belongs to (`fish`, `nvim`, `neogaia`), omitted for repo-wide changes.
|
- Keep commit messages about the Git history, not the forge workflow.
|
||||||
Keep messages free of Gitea-specific references: this repository is mirrored to GitHub, where issue and pull-request numbers resolve to unrelated things.
|
Do not reference issue or pull-request numbers unless the commit itself requires a stable external reference.
|
||||||
- When a graphical application is added, give it a `window-rewrite` icon mapping in `modules/desktop/waybar.nix`.
|
- Use `gitea-axi` for Gitea issues, pull requests, labels, reviews, comments, and milestones.
|
||||||
Without one its windows fall back to the generic default glyph on the workspace indicator instead of showing a recognisable per-application icon.
|
It resolves this repository from the `origin` remote inside the checkout.
|
||||||
Match on the window class, which `hyprctl clients -j | jq -r '.[].class' | sort -u` lists for the running session.
|
|
||||||
|
## Dotfiles Conventions
|
||||||
|
|
||||||
|
### Desktop
|
||||||
|
|
||||||
|
- When adding a graphical application, add a `window-rewrite` icon mapping in `modules/desktop/waybar.nix`.
|
||||||
|
The workspace indicator uses this mapping to show an application icon instead of the generic default glyph.
|
||||||
|
Match the mapping on the window class.
|
||||||
|
Run `hyprctl clients -j | jq -r '.[].class' | sort -u` to list classes in the running session.
|
||||||
|
- Set Hyprland session environment variables in `modules/desktop/hyprland/hyprland.nix` with `env = KEY,VALUE`.
|
||||||
|
`home.sessionVariables` do **not** reach the Hyprland session because UWSM does not source `hm-session-vars.sh`.
|
||||||
|
Source cursor values from `config.stylix.cursor`.
|
||||||
|
Set `XCURSOR_*` and `HYPRCURSOR_*` to the same theme for Bibata.
|
||||||
|
- Configure the Steam desktop client as an XWayland application.
|
||||||
|
SDL Wayland selectors do not make the visible client native Wayland.
|
||||||
|
Keep fractional scaling sharp with Hyprland's `xwayland.force_zero_scaling` and Steam's `STEAM_FORCE_DESKTOPUI_SCALING`.
|
||||||
|
|
||||||
|
### Firefox
|
||||||
|
|
||||||
|
- Manage Firefox search engines through `programs.firefox.search` with `force = true`.
|
||||||
|
Hide built-in engines with `<engine>.metaData.hidden = true` because omission does not remove them.
|
||||||
|
Use current engine ids, such as `default = "ddg"` instead of `"DuckDuckGo"`.
|
||||||
|
Decode the built `search.json.mozlz4` with `mozlz4a -d <search.json.mozlz4>` to check the result.
|
||||||
|
- Avoid Home Manager Firefox extension settings unless the whole profile can use the legacy extension-storage backend.
|
||||||
|
Any non-empty `profiles.<name>.extensions.settings.<id>.settings` sets `extensions.webextensions.ExtensionStorageIDB.enabled = false` for the whole profile.
|
||||||
|
|
||||||
|
### Neovim
|
||||||
|
|
||||||
|
- Nixvim's flake input follows the root nixpkgs source, but its Home Manager module does not reuse the host's `pkgs` instance.
|
||||||
|
Keep `programs.nixvim.nixpkgs.useGlobalPackages = true`.
|
||||||
|
That setting makes Nixvim use the shared package set without warning that its source default changed.
|
||||||
|
|
||||||
|
### AI Tooling
|
||||||
|
|
||||||
|
- Use produced literals for Herdr key names that include shifted punctuation.
|
||||||
|
For example, use `prefix+<` instead of `prefix+shift+comma`.
|
||||||
|
- Treat `~/.pi/agent/skills` as generated output from Home Manager.
|
||||||
|
Do not edit skills in that directory.
|
||||||
|
A new file created there stays outside the repo.
|
||||||
|
Shared global skills come from the `skills` flake through `modules/agents/skills.nix`.
|
||||||
|
Update the `skills` flake input, then rebuild.
|
||||||
|
- Keep generated `.agents/skills/.gitignore` files from ignoring symlinked skills.
|
||||||
|
Pi skill discovery honors `.gitignore`, `.ignore`, and `.fdignore` inside scanned skill directories.
|
||||||
|
An ignored symlinked skill does not load even when `.agents/skills/<name>/SKILL.md` exists.
|
||||||
|
- Write directly to flake-managed Pi extension, prompt, and skill directories only for throwaway development or local experiments.
|
||||||
|
Home Manager activation can overwrite or hide unmanaged files in those directories.
|
||||||
|
Promote finished work into the dotfiles module before treating it as deployed.
|
||||||
|
|
||||||
## Gotchas
|
## Gotchas
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
|
||||||
- Guest modules are host-agnostic wiring, while host files own deployment-specific service configuration such as served volumes and host paths.
|
- Guest modules are host-agnostic wiring, while host files own deployment-specific service configuration such as served volumes and host paths.
|
||||||
For example, `guests/copyparty.nix` should expose options and translate them into guest mounts/service settings, while `hosts/pikachu/default.nix` chooses `/inbox`, `/media`, and their backing paths.
|
For example, `guests/copyparty.nix` should expose options and translate them into guest mounts/service settings, while `hosts/pikachu/default.nix` chooses `/inbox`, `/media`, and their backing paths.
|
||||||
- `sops.secrets.<name>.owner` must name a user known on the decrypting host.
|
- `self.lib.registry.collect { hosts = self.nixosConfigurations; }` reads only top-level host `config.registry` declarations.
|
||||||
For guest-only service users, do not set the owner to the guest uid unless the host also declares that user.
|
Registry declarations inside guest interiors live under `config.containers.<guest>.config.registry`.
|
||||||
Use a host-visible owner plus a shared group mode when an identity-mapped guest service can read through its group.
|
The collected registry does not include those guest-interior declarations.
|
||||||
- Actual Budget's NixOS service runs `actual-server` with `WorkingDirectory = services.actual.settings.dataDir`, and the `actual-server` CLI wrapper sets `ACTUAL_CONFIG_PATH=./config.json` when that current directory contains `config.json`, overriding the NixOS module's generated config path before the server loads configuration.
|
Publish guest values from host-side code, or extend the collector to recurse into guests.
|
||||||
When migrating old community-script state to the normal upstream `/data` shape, move or rewrite a copied `/data/config.json` so it cannot shadow the declarative module settings.
|
See http://git.alexion.dev/alexion/dotfiles/issues/65.
|
||||||
- Subagent completion delivery is non-blocking through immediate spawn, milestone notifications, retained terminal entries, and `subagent_list` or `subagent_result` retrieval.
|
- A NixOS module function argument default such as `registry ? config.registry` does not safely handle an unprovided module argument.
|
||||||
`subagent_wait` intentionally blocks the parent tool call until its condition or timeout, so do not use it merely to keep background work alive during an interactive workflow.
|
The module system still queries `_module.args.registry`.
|
||||||
- Nixvim's flake input following the root nixpkgs source does not make its Home Manager module reuse the host's `pkgs` instance.
|
Evaluation fails when that argument is absent.
|
||||||
Keep `programs.nixvim.nixpkgs.useGlobalPackages = true` so Nixvim uses the shared package set without warning that its source default was affected.
|
For an optional special-argument fallback, bind module arguments through `@args`.
|
||||||
- This host has no `python` or `python3` command on its ordinary `PATH`.
|
Test `args ? <name>` inside the module body.
|
||||||
For ad hoc Python, use Nix explicitly, such as `nix shell nixpkgs#python3 -c python3 <script>`.
|
|
||||||
- ADR bodies are immutable records of decisions as they were made, while frontmatter is mutable.
|
### Build Inputs
|
||||||
When a decision changes or its premise proves wrong, preserve the original body, update its status, and add a new ADR that supersedes it.
|
|
||||||
Filename migrations preserve references in immutable bodies through frontmatter aliases rather than rewriting those bodies.
|
- `chaotic-nyx` must **not** follow this flake's `nixpkgs`.
|
||||||
- This repo pins no Nix formatter, and its committed `.nix` files are not clean under current `nixfmt-rfc-style`.
|
Chaotic builds its packages against its own pinned `nixpkgs`.
|
||||||
Running `nixfmt` across a file reflows untouched code (for example `lib.nix`'s `deriveMac` list and multi-line assertion messages) and injects churn unrelated to the change.
|
Its overlay defaults to `onTopOf = "flake-nixpkgs"`, the cache-friendly path.
|
||||||
Format only the lines being written or changed, matching the surrounding style by hand.
|
That setup lets `nyx-cache.chaotic.cx` serve the CachyOS kernel instead of building it from source.
|
||||||
- This repo is developed on `neogaia`, which now runs the NixOS it builds.
|
Chaotic packages do not see this flake's `unstable` or `stable` overlays.
|
||||||
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.
|
### Deployment
|
||||||
- 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.
|
|
||||||
Verify it that way rather than from this checkout, whose `.git/config` carries the same identity and would mask a broken module.
|
|
||||||
`~/.gitconfig` (a second global file that outranks the flake-managed `~/.config/git/config`) currently holds only a `tea` credential helper and no `user.*`, so it does not shadow the identity, but it is undeclared and will not survive a reimage.
|
|
||||||
- The primary build/verify seam is `nix flake check`, which builds project-level checks plus `checks.x86_64-linux.<host>` system toplevels.
|
|
||||||
Cheap targeted host checks use `nix eval .#nixosConfigurations.<host>.config...`.
|
|
||||||
- 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.
|
|
||||||
- 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.
|
|
||||||
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 <branch> ...`.
|
|
||||||
The `--repo` flag is required on that path, since `tea` resolves `origin` only for a login whose SSH host matches.
|
|
||||||
The same token reads PR discussion, which `tea` itself does poorly: `tea pr <n> --comments` prints only the body, and `-f comments` returns no comments field at all.
|
|
||||||
Use the API instead, taking the token from `.logins[] | select(.name=="alexion") | .token`.
|
|
||||||
Review comments are **not** at `/issues/<n>/comments` — that endpoint holds only top-level discussion and is usually empty.
|
|
||||||
Inline comments need two calls: `/pulls/<n>/reviews` for the review ids, then `/pulls/<n>/reviews/<id>/comments` for the bodies, whose `path` and `diff_hunk` fields say what each one is attached to.
|
|
||||||
A review row with an empty `body` is the normal shape when the operator left only inline comments.
|
|
||||||
- `~/.claude/skills` and `~/.pi/agent/skills` are home-manager-generated (`recursive = true`), so editing a skill in place fails and a new file created there silently escapes the repo.
|
|
||||||
Shared global skills come from the `skills` flake through `modules/agents/skills.nix`, applied by a rebuild.
|
|
||||||
Claude-specific legacy skills, when kept, live under `modules/agents/claude-code/skills/<name>/`.
|
|
||||||
- Pi skill discovery honors `.gitignore`, `.ignore`, and `.fdignore` inside scanned skill directories.
|
|
||||||
A generated `.agents/skills/.gitignore` entry that ignores a symlinked skill also prevents Pi from loading that skill, even when `.agents/skills/<name>/SKILL.md` exists and the symlink target is valid.
|
|
||||||
- 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`.
|
|
||||||
- `nixos-generate-config --show-hardware-config` needs root on this machine even just to print: unprivileged it dies at `Failed to retrieve subvolume info for /`, because the root filesystem is btrfs.
|
|
||||||
- This repo's claude-code module sets sudo's credential cache to per-user (`timestamp_type=global`, 60-minute window), so an authentication made in one real terminal counts for the agent's commands.
|
|
||||||
A `PreToolUse` hook refuses privileged commands while the cache is cold, so a cold cache announces itself instead of stalling.
|
|
||||||
A privileged-command failure *without* that message is the sandbox, not the cache.
|
|
||||||
- Host GPUs: `neogaia` is Intel, `zeus` (the desktop) is **AMD**, and `raichu` (a headless server) is the only Nvidia machine.
|
|
||||||
The corrected fact also lives in artifact `006-dotfiles-hyprland-compositor-adr.md`.
|
|
||||||
- This repo's `programs.firefox` `search` (with `force = true`) writes `search.json.mozlz4`.
|
|
||||||
Omission alone does not prune a built-in engine, since Firefox reconciles its app-provided engines back in, so remove one by listing it with `<engine>.metaData.hidden = true`.
|
|
||||||
Engines are referenced by their current id, so the default is `default = "ddg"`, not `"DuckDuckGo"`.
|
|
||||||
Decode the built file with `mozlz4a -d <search.json.mozlz4>` to check the result.
|
|
||||||
- Any non-empty Home Manager Firefox `profiles.<name>.extensions.settings.<id>.settings` causes Home Manager to set `extensions.webextensions.ExtensionStorageIDB.enabled = false` globally for that profile.
|
|
||||||
This repo's Stylix Firefox `colorTheme` settings trigger it, so every extension in the profile uses the legacy extension-storage backend regardless of how it is installed.
|
|
||||||
- `home.sessionVariables` do **not** reach the Hyprland session, since UWSM does not source `hm-session-vars.sh`.
|
|
||||||
The cursor is therefore set through Hyprland's own `env = KEY,VALUE` in `modules/desktop/hyprland/hyprland.nix`, sourced from `config.stylix.cursor`.
|
|
||||||
Bibata ships XCursor format only (no `hyprcursor/` dir), rendered through Hyprland's XCursor fallback, so `XCURSOR_*` and `HYPRCURSOR_*` naming the same theme are both safe.
|
|
||||||
- `neogaia` is a wifi laptop with a btrfs root and no ZFS pools, so it cannot honestly carry `modules.network`, `modules.zfs`, or a networked/pool-mounted guest.
|
|
||||||
Enabling networkd takes over its DNS, its CachyOS `zfs-kernel` build is marked broken, and it has no bridge or pool to attach to.
|
|
||||||
Verify these against it ad hoc through `nixosConfigurations.neogaia.extendModules` (forcing a ZFS-capable `boot.kernelPackages` for the zfs case) plus `nix eval` of the derived values, never by committing the enablement.
|
|
||||||
Committed networked and pool-mounted guests belong on a wired storage host such as `pikachu`, not on `neogaia`.
|
|
||||||
- Herdr key names for shifted punctuation are not interchangeable with the physical base key plus `shift`.
|
|
||||||
The tab rename binding must use the produced literal, such as `prefix+<`, rather than `prefix+shift+comma`.
|
|
||||||
- Flake-managed Pi extension, prompt, and skill directories may still be written directly for throwaway development or local experiments.
|
|
||||||
The risk is that a later Home Manager activation can overwrite or hide those unmanaged files, so finished work must be promoted into the dotfiles module before it counts as deployed.
|
|
||||||
- Pi's tool discovery checks `~/.pi/agent/bin` before `PATH`, and downloaded generic Linux binaries there can be unusable on NixOS with the stub-ld error.
|
|
||||||
This flake patches Pi to validate local tool binaries before selecting them, so it falls back to usable `fd`/`rg` from `PATH` instead.
|
|
||||||
Stale unpatched launchers are the remaining failure mode for broken `@` autocomplete.
|
|
||||||
- Nix flake evaluation ignores untracked files in this checkout.
|
|
||||||
Keep a new auto-loaded module staged or committed until it is removed, otherwise `nix flake check` and `nixos-rebuild --flake` evaluate without it and report its options as missing.
|
|
||||||
- The current Steam desktop client is an XWayland application.
|
|
||||||
Its CEF windows do not support Ozone and Steam composites them into an SDL surface with X11 extensions, so SDL Wayland selectors do not make the visible client native Wayland.
|
|
||||||
Keep fractional scaling sharp with Hyprland's `xwayland.force_zero_scaling` and Steam's own `STEAM_FORCE_DESKTOPUI_SCALING` instead.
|
|
||||||
- AdGuard's LAN wildcard rewrite for `*.alexion.dev` points names at the reverse-proxy guest, including non-HTTP ports.
|
|
||||||
Keep `git.alexion.dev:2022` working by proxying that TCP stream through the reverse-proxy guest to Gitea.
|
|
||||||
- Remote deploy helpers that run target-side `sudo` over SSH need a TTY.
|
- Remote deploy helpers that run target-side `sudo` over SSH need a TTY.
|
||||||
Use `ssh -t` unless the target command is guaranteed not to prompt for sudo credentials.
|
Use `ssh -t` unless the target command cannot prompt for sudo credentials.
|
||||||
- `self.lib.registry.collect { hosts = self.nixosConfigurations; }` reads top-level host `config.registry` declarations only.
|
|
||||||
Registry declarations inside guest interiors live under `config.containers.<guest>.config.registry` and do not enter the collected fleet registry unless host-side code also publishes them or the collector is explicitly extended to recurse into guests.
|
### Host Environment
|
||||||
- A NixOS module function argument default such as `registry ? config.registry` is not a safe fallback for an unprovided module argument.
|
|
||||||
The module system still queries `_module.args.registry` and fails when it is absent.
|
- This host has no `python` or `python3` command on its ordinary `PATH`.
|
||||||
When an optional special-argument fallback is intentionally needed, bind the module arguments through `@args` and test `args ? <name>` inside the module body.
|
For one-off Python commands, use Nix explicitly, such as `nix shell nixpkgs#python3 -c python3 <script>`.
|
||||||
|
- `nixos-generate-config --show-hardware-config` needs root on this machine, even when it only prints.
|
||||||
|
Without root, it fails with `Failed to retrieve subvolume info for /`.
|
||||||
|
The root filesystem uses btrfs.
|
||||||
|
- Nix flake evaluation ignores untracked files in this checkout.
|
||||||
|
Stage or commit new auto-loaded modules before evaluating the flake.
|
||||||
|
Otherwise, `nix flake check` and `nixos-rebuild --flake` cannot see those modules.
|
||||||
|
They can then report missing options that exist only in the working tree.
|
||||||
|
|
||||||
|
### Secrets
|
||||||
|
|
||||||
|
- `sops.secrets.<name>.owner` must name a user known on the decrypting host.
|
||||||
|
Do not set the owner to a guest-only service user unless the host also declares that user.
|
||||||
|
Use a host-visible owner plus a shared group mode when the guest service can read the decrypted secret file through its group.
|
||||||
|
|
||||||
|
### Service Migrations
|
||||||
|
|
||||||
|
- Actual Budget's NixOS service runs `actual-server` with `WorkingDirectory = services.actual.settings.dataDir`.
|
||||||
|
The `actual-server` CLI wrapper sets `ACTUAL_CONFIG_PATH=./config.json` when the working directory contains `config.json`.
|
||||||
|
That environment variable overrides the config path generated by the NixOS module.
|
||||||
|
During migration from old community-script state, move or rewrite a copied `/data/config.json`.
|
||||||
|
Otherwise, the copied file shadows the declarative module settings.
|
||||||
|
|||||||
55
docs/dictionary.md
Normal file
55
docs/dictionary.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# Technical Writing and Domain Dictionary
|
||||||
|
|
||||||
|
This dictionary defines project-specific controlled vocabulary, prohibited terms, and domain allowlists.
|
||||||
|
It applies to comments, user-facing descriptions, commit prose, pull-request prose, and Markdown documentation in this repository.
|
||||||
|
|
||||||
|
ASD-STE100 Simplified Technical English partly informs these rules.
|
||||||
|
This repository uses those ideas as a local style discipline.
|
||||||
|
It does not claim formal ASD-STE100 compliance.
|
||||||
|
|
||||||
|
## Strictly Banned Fluff and Latin Terms
|
||||||
|
|
||||||
|
Do not use these Latin abbreviations, conversational filler words, and subjective qualifiers.
|
||||||
|
Remove them or use the approved replacements.
|
||||||
|
|
||||||
|
| Disapproved Term | Approved Replacement | Notes |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `i.e.` | `that is` | Latin abbreviation. Unclear to non-native speakers. |
|
||||||
|
| `e.g.` | `for example` | Latin abbreviation. |
|
||||||
|
| `etc.` | `and so forth`, `and others` | Latin abbreviation. Vague and non-exhaustive. |
|
||||||
|
| `vs.` / `versus` | `compared to`, `against` | Latin abbreviation. |
|
||||||
|
| `via` | `by using`, `through`, `with` | Latin term. Prefer direct prepositions. |
|
||||||
|
| `ad hoc` | `one-off`, `for this case` | Latin term. Prefer direct wording. |
|
||||||
|
| `simply` | omit | Subjective filler. Code and concepts are not inherently simple. |
|
||||||
|
| `just` | omit | Minimizing filler word. |
|
||||||
|
| `obviously` | omit | Subjective qualifier. |
|
||||||
|
| `clearly` | omit | Subjective qualifier. |
|
||||||
|
| `easily` | omit | Subjective qualifier. |
|
||||||
|
| `properly` | `correctly`, or specify exact behavior | Vague and subjective. State the exact condition. |
|
||||||
|
| `as necessary` | specify exact condition | Ambiguous. Define the condition explicitly. |
|
||||||
|
| `very` / `quite` | omit | Empty intensifiers. |
|
||||||
|
| `basically` | omit | Conversational filler. |
|
||||||
|
| `actually` | omit | Conversational filler. |
|
||||||
|
| `really` | omit | Empty intensifier. |
|
||||||
|
| `furthermore` / `moreover` | `also`, `and` | Overly formal transitional filler. |
|
||||||
|
|
||||||
|
## Technical and Domain Allowed Language
|
||||||
|
|
||||||
|
Use only approved project-specific terms as controlled vocabulary in documentation and comments.
|
||||||
|
Add a term here when repeated project prose needs it.
|
||||||
|
Use the simplest accurate word when a term is not listed.
|
||||||
|
|
||||||
|
| Approved Domain Term | Category | Description and Permitted Context |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| agent | Agent tooling | An AI coding assistant acting in the operator's environment. |
|
||||||
|
| operator | Agent tooling | The human driving the development process. Use when distinguishing the human from the agent acting on their behalf. |
|
||||||
|
| guest | dotfiles | A systemd-nspawn container managed by this flake. Use for configured containers under `guests/`. |
|
||||||
|
| host | dotfiles | A machine with a NixOS configuration built by this flake. |
|
||||||
|
| module | dotfiles | In this repository, a self-contained configuration under `modules/`, usually scoped to one program, service, or subsystem. Use `Nix module` for the language-level module-system concept. |
|
||||||
|
| registry | dotfiles | A typed configuration namespace whose entries can be collected across hosts and reused by other configuration. |
|
||||||
|
| Conventional Commits | Git | A commit-message format defined in `docs/conventional-commits.md`. |
|
||||||
|
| forge | Git | A web service that hosts Git repositories, pull requests, issues, and code review comments. Use when the exact provider does not matter. |
|
||||||
|
| flake | Nix | A Nix project with pinned inputs and declared outputs. Use for the repository-level build unit defined by `flake.nix`. |
|
||||||
|
| Nix module | Nix | A Nix language unit that declares or sets options through the Nix module system. |
|
||||||
|
| overlay | Nix | A function that modifies a Nix package set by adding or replacing packages. |
|
||||||
|
| workspace | Window management | A named or numbered group of windows in a graphical session. Some desktop environments call this a virtual desktop. |
|
||||||
109
docs/technical-writing.md
Normal file
109
docs/technical-writing.md
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
# Technical Writing
|
||||||
|
|
||||||
|
This guide defines writing rules for comments, user-facing descriptions, commit prose, pull-request prose, and Markdown documentation.
|
||||||
|
These rules apply throughout this repository.
|
||||||
|
|
||||||
|
ASD-STE100 Simplified Technical English partly informs these rules.
|
||||||
|
This repository uses those ideas as a local style discipline.
|
||||||
|
It does not claim formal ASD-STE100 compliance.
|
||||||
|
|
||||||
|
Read the local dictionary with this guide.
|
||||||
|
In this guide, “the dictionary” means `docs/dictionary.md`.
|
||||||
|
The dictionary defines banned terms and the strict domain allowlist.
|
||||||
|
|
||||||
|
## Structural Conciseness and Grammar Rules
|
||||||
|
|
||||||
|
- **Clear and concise style:** Use direct, concise writing.
|
||||||
|
Use simple present tense for factual descriptions.
|
||||||
|
Do not use uncertainty words such as `could`, `might`, and `should` unless uncertainty is the subject.
|
||||||
|
Use each domain word with the meaning assigned in the dictionary.
|
||||||
|
Avoid: `The tool should usually handle the input correctly.`
|
||||||
|
Use: `The tool validates the input.`
|
||||||
|
- **Avoid jargon and inflated language:** Use common technical words.
|
||||||
|
Avoid unnecessary jargon in three categories:
|
||||||
|
1. Linguistic and grammatical meta-jargon.
|
||||||
|
Avoid: `modal auxiliaries`
|
||||||
|
Use: `uncertain words`
|
||||||
|
2. Inflated corporate phrasing.
|
||||||
|
Avoid: `utilize`
|
||||||
|
Use: `use`
|
||||||
|
Avoid: `commence`
|
||||||
|
Use: `start`
|
||||||
|
3. Unapproved domain inventions.
|
||||||
|
Do not use project-specific terms unless the dictionary defines them.
|
||||||
|
General technical words do not need dictionary entries.
|
||||||
|
Add a common word to the dictionary when it has a project-specific meaning.
|
||||||
|
If a term is vague or overloaded, choose one precise term.
|
||||||
|
List avoided alternatives in the dictionary.
|
||||||
|
Choose the simplest word that accurately communicates the meaning.
|
||||||
|
- **Sentence length limits:** Code comments, user-facing descriptions, and procedural instructions have a 20-word limit.
|
||||||
|
Descriptive Markdown paragraphs have a 25-word limit.
|
||||||
|
Do not put multiple sentences on one physical line.
|
||||||
|
Wrap a sentence across multiple physical lines when readability requires it.
|
||||||
|
Split long sentences into multiple concise sentences.
|
||||||
|
Avoid: `The command configures the service with the generated settings and restarts it after the file changes.`
|
||||||
|
Use: `The command configures the service with the generated settings.`
|
||||||
|
Use: `It restarts the service after the file changes.`
|
||||||
|
- **Active voice:** Always use active voice.
|
||||||
|
The subject must perform the action.
|
||||||
|
Do not use passive voice.
|
||||||
|
Avoid: `Stale entries are invalidated by the cache.`
|
||||||
|
Use: `The cache invalidates stale entries.`
|
||||||
|
- **Imperative mood:** Procedural instructions and task steps must start with an imperative verb.
|
||||||
|
Summary fragments for declarations use third-person singular.
|
||||||
|
Avoid: `The token should be validated before use.`
|
||||||
|
Use for a task step: `Validate the token before use.`
|
||||||
|
Use for a declaration summary: `Validates the token before use.`
|
||||||
|
- **Noun clusters:** Do not write more than three consecutive nouns without a preposition or hyphen.
|
||||||
|
Avoid: `session cache entry lookup`
|
||||||
|
Use: `lookup of session cache entries`
|
||||||
|
- **One idea per sentence:** Express only one instruction or core concept per sentence.
|
||||||
|
Do not combine multiple operations in one compound sentence.
|
||||||
|
Avoid: `Validate the input and restart the service when validation succeeds.`
|
||||||
|
Use: `Validate the input.`
|
||||||
|
Use: `Restart the service when validation succeeds.`
|
||||||
|
- **Code token exemption:** Text in Markdown backticks, URLs, and code blocks counts as one word.
|
||||||
|
Sentence-length limits and noun-cluster limits do not apply to these tokens.
|
||||||
|
Example: `curl --fail --silent https://example.com/health` counts as one word.
|
||||||
|
- **Cause and time precision:** Use `because` for causality.
|
||||||
|
Use `since` only for passage of time.
|
||||||
|
Use `as` only for simultaneous action.
|
||||||
|
Avoid: `Since the token is expired, validation fails.`
|
||||||
|
Use: `Because the token is expired, validation fails.`
|
||||||
|
|
||||||
|
## In-Code Documentation Conventions
|
||||||
|
|
||||||
|
- **Declaration coverage and exemptions:** Document public interfaces, configuration options, fields, and non-trivial public methods.
|
||||||
|
Omit comments on obvious declarations when the name and context are self-explanatory.
|
||||||
|
Avoid: `Enable this option.`
|
||||||
|
Use: `Enables automatic cleanup after a successful run.`
|
||||||
|
- **Declaration non-redundancy:** Comments must explain purpose, preconditions, invariants, or non-obvious consequences.
|
||||||
|
Do not repeat information that the name, type, path, or parameter list already conveys.
|
||||||
|
Avoid: `The timeout option sets the timeout.`
|
||||||
|
Use: `Controls how long the command waits before it reports failure.`
|
||||||
|
- **Multi-line declaration comments:** Start multi-line declaration comments with one summary sentence.
|
||||||
|
Put a blank line between the summary and detail paragraphs.
|
||||||
|
Use detail paragraphs for preconditions, invariants, examples, and non-obvious consequences.
|
||||||
|
Avoid:
|
||||||
|
|
||||||
|
```text
|
||||||
|
The caller must load the signing key before calling this method.
|
||||||
|
Validates the token and returns false for expired tokens.
|
||||||
|
```
|
||||||
|
|
||||||
|
Use:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Validates the token.
|
||||||
|
|
||||||
|
The caller must load the signing key before calling this method.
|
||||||
|
Expired tokens return false instead of an error.
|
||||||
|
```
|
||||||
|
- **Code symbol references:** Enclose identifiers, paths, commands, option names, and file names in Markdown backticks.
|
||||||
|
Avoid: Run npm test before editing package.json.
|
||||||
|
Use: Run `npm test` before editing `package.json`.
|
||||||
|
- **Boolean descriptions:** Start boolean descriptions with the behavior verb.
|
||||||
|
Use `True if` only when the value reports state instead of enabling behavior.
|
||||||
|
Avoid: `Flag that specifies whether retries are enabled.`
|
||||||
|
Use: `Enables retry behavior.`
|
||||||
|
Use for state: `True if the token is expired.`
|
||||||
@@ -7,6 +7,7 @@ diff --git a/packages/coding-agent/src/utils/tools-manager.ts b/packages/coding-
|
|||||||
const result = spawnSync(cmd, ["--version"], { stdio: "pipe" });
|
const result = spawnSync(cmd, ["--version"], { stdio: "pipe" });
|
||||||
- // Check for ENOENT error (command not found)
|
- // Check for ENOENT error (command not found)
|
||||||
- return result.error === undefined || result.error === null;
|
- return result.error === undefined || result.error === null;
|
||||||
|
+ // Downloaded generic Linux binaries can exist but fail on NixOS before they print a version.
|
||||||
+ return (result.error === undefined || result.error === null) && result.status === 0;
|
+ return (result.error === undefined || result.error === null) && result.status === 0;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -181,8 +181,8 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# gbprod/nord.nvim, set up in ./config.lua.
|
# `gbprod-nord` provides the `require("nord").setup()` API used in `./config.lua`.
|
||||||
# nixvim's colorschemes.nord is a different plugin.
|
# nixpkgs packages `shaunsingh/nord.nvim` as `nord-nvim`, which is a different plugin.
|
||||||
extraPlugins = [ pkgs.vimPlugins.gbprod-nord ];
|
extraPlugins = [ pkgs.vimPlugins.gbprod-nord ];
|
||||||
|
|
||||||
extraConfigLua = builtins.readFile ./config.lua;
|
extraConfigLua = builtins.readFile ./config.lua;
|
||||||
|
|||||||
Reference in New Issue
Block a user