12 KiB
AGENTS.md
Guidance for coding agents working in this repository. Project type: Ansible-driven infrastructure, workstation/server provisioning, and user dotfiles.
Repository Map
- Entry playbook:
ansible/site.yml - Inventory:
ansible/inventory/hosts.yml - Group vars:
ansible/inventory/group_vars/*.yml - Host vars:
ansible/inventory/host_vars/*.yml - Shared templates:
ansible/templates/**/*.j2 - Roles:
ansible/roles/* - Role assets:
ansible/roles/*/{tasks,templates,files,handlers}/ - Dotfiles:
dotfiles/ - Scripts:
scripts/ - Secrets:
secrets/
Topology And Orchestration
- Void desktops:
ikaros,nymph - Ubuntu workstation:
deadalus - Ubuntu server:
prometheus - Workstation topology now supports Linux host + Ubuntu dev and Windows 11 host + Ubuntu WSL dev as separate layers
- The WSL dev environment is intended to be managed by running Ansible locally from inside the distro, while the Windows host is managed remotely via PSRP and Windows package installs default to
winget_psrp - Most hosts use
ansible_connection: local - Current playbook layering:
all:!workstation_host_windows -> dotfiles_common,void -> packages_void + services_runit + profile_desktop_common + profile_desktop_i3 + profile_desktop_sway + profile_desktop_hyprland + profile_desktop_host,workstation_dev_ubuntu -> packages_ubuntu + services_systemd + profile_workstation_dev_common,workstation_host_linux -> profile_workstation_gnome,workstation_dev_wsl -> packages_ubuntu + services_systemd + profile_workstation_dev_common + profile_workstation_dev_wsl,workstation_host_windows -> profile_workstation_host_windows,ubuntu_server -> packages_ubuntu + services_systemd + profile_server - Present but currently unwired roles:
base,dotfiles
Local Instruction Files
Checked when this file was updated:
.cursorrules: not present.cursor/rules/: not present.github/copilot-instructions.md: not present If any of these files appear later, treat them as higher-priority repo-local instructions.
Working Principles
- Preserve the layering
all -> OS -> profile -> host - Prefer minimal, targeted edits over cleanup or refactors
- Preserve idempotency and reproducibility
- Validate on one limited host before broad rollout
- Treat
secrets/as sensitive; never print secret values - Avoid editing vendored code under
dotfiles/desktop/.tmux/plugins/unless explicitly asked - Keep
ansible/site.ymlsmall; orchestration belongs there, implementation belongs in roles - Read the relevant inventory, vars, role tasks, templates, files, handlers, and dotfiles before editing
Build, Lint, And Test Commands
There is no compile/build pipeline. Confidence comes from syntax checks, dry runs, linting, and targeted shell validation.
Install tooling if needed:
python3 -m pip install ansible ansible-lint yamllint shellcheck-py
ansible-galaxy collection install -r ansible/collections/requirements.yml
Vault handling:
secrets/vault.ymlis the shared encrypted vars filesecrets/vault.local.ymlis an optional machine-local encrypted override file and should stay untrackedsecrets/.vault_passis an optional local password file; if absent, Ansible falls back to an interactive prompt viascripts/vault_password_client.sh
Core validation from the repo root:
ansible-playbook ansible/site.yml --syntax-check
ansible-playbook ansible/site.yml --limit ikaros --check --diff
ansible-playbook ansible/site.yml --limit nymph --check --diff
ansible-playbook ansible/site.yml --limit deadalus --check --diff
ansible-playbook ansible/site.yml --limit deadalus-wsl --check --diff
ansible-playbook ansible/site.yml --limit prometheus --check --diff
ansible-lint ansible/site.yml
ansible-lint ansible/roles
yamllint ansible/
Useful execution commands:
ansible-playbook ansible/site.yml
ansible-playbook ansible/site.yml --limit ikaros
ansible-playbook ansible/site.yml --limit nymph
ansible-playbook ansible/site.yml --limit deadalus
ansible-playbook ansible/site.yml --limit deadalus-wsl
ansible-playbook ansible/site.yml --limit prometheus
scripts/bootstrap_mail.sh
pwsh -File scripts/bootstrap_windows_workstation.ps1
Single-Test Equivalents
Use the narrowest command matching the changed area.
- Playbook syntax only:
ansible-playbook ansible/site.yml --syntax-check - Single host dry run:
ansible-playbook ansible/site.yml --limit <host> --check --diff - Single host, selected tags:
ansible-playbook ansible/site.yml --limit <host> --tags <tag1>,<tag2> --check --diff - Single task restart point:
ansible-playbook ansible/site.yml --limit <host> --start-at-task "<task name>" --check --diff - Single role lint:
ansible-lint ansible/roles/<role> - Single YAML file lint:
yamllint ansible/path/to/file.yml - Waybar config validation:
python3 -m json.tool dotfiles/desktop/.config/waybar/config-sway.jsonc >/dev/nullandpython3 -m json.tool dotfiles/desktop/.config/waybar/config-hyprland.jsonc >/dev/null - Script syntax/lint:
sh -n scripts/bootstrap_mail.shandshellcheck scripts/bootstrap_mail.sh - Windows bootstrap script parse check:
pwsh -NoProfile -Command "[void][System.Management.Automation.Language.Parser]::ParseFile('scripts/bootstrap_windows_workstation.ps1', [ref]$null, [ref]$null)"
Code Style Guidelines
General
- Keep orchestration in playbooks and implementation in roles
- Prefer declarative modules over imperative shell commands
- Make state transitions explicit
- Avoid unrelated refactors in the same change
- Keep comments sparse and only for non-obvious behavior
YAML And Formatting
- Start YAML files with
--- - Use 2-space indentation; never use tabs
- Keep existing ordering stable when editing lists and maps
- Quote file modes as strings such as
"0644","0755","0600","0700" - Avoid formatting-only churn in untouched sections
Modules, Imports, And Task Structure
- Use FQCN module names such as
ansible.builtin.copy,ansible.builtin.template,ansible.windows.win_powershell, andcommunity.general.xbps - Prefer dedicated modules over
ansible.builtin.commandoransible.builtin.shell - Use
commandonly when no module fits; useshellonly when shell features are required - When using
commandorshell, setchanged_whenandfailed_whenwhen defaults are misleading - Prefer
ansible.builtin.import_tasksfor static task composition andinclude_tasksonly for dynamic or conditional includes - Keep task names imperative, descriptive, and stable enough for
--start-at-task - Tag tasks consistently with existing families such as
packages,services,dotfiles,gnome,wsl,vscode,nvidia, anddotfiles:* - Prefer
loopwithloop_control.labelfor multi-item tasks - Use handlers for service restarts when configuration changes should trigger them
Variables, Types, And Naming
- Use
snake_casefor vars, facts, registered values, and custom facts - Follow existing families such as
common_packages,profile_packages,host_packages,desktop_common_packages,enabled_services,host_enabled_services,windows_winget_packages, andwindows_vscode_extensions - Keep booleans as booleans, not quoted strings
- Keep structured values as YAML lists/maps, not comma-separated strings
- Guard optional lists with
default([]), mappings withdefault({}), and strings withdefault('') - Build managed-user paths from
{{ user_home }}where applicable - Put host-specific overrides in
host_vars, not sharedgroup_vars
Templates, Dotfiles, And Shell
- Keep secrets parameterized through vars; never hardcode them in templates or dotfiles
- Prefer role
templates/for variable-driven config and rolefiles/for static payloads - Preserve destination paths and permissions unless the task requires a change
- Dotfiles should stay deployable on real machines; avoid repo-only hacks in
dotfiles/ - Keep
dotfiles/desktop/.config/waybar/config-*.jsonceffectively valid JSON - Prefer POSIX
shfor simple scripts; use Bash only when needed - Use
set -euin POSIX shell scripts unless there is a clear reason not to - Quote variable expansions unless intentional splitting is required
Error Handling, Safety, And Idempotency
- Fail early with
ansible.builtin.failwhen prerequisites are missing - Guard OS-specific, DE-specific, version-specific, and host-specific behavior with
when - Use
no_log: truefor passwords, tokens, private keys, and secret-bearing command results - Use
failed_when: falseonly for intentional probes - Keep tasks non-interactive unless explicitly user-driven
- Avoid destructive changes in user homes unless clearly required
- For firewall changes, allow required access before enabling the firewall
- When running commands as the desktop user, set
become_userand the requiredHOMEor session environment explicitly
Area-Specific Notes
profile_desktop_commonmanages shared Void desktop bootstrap,emptty, PAM hooks, dotfiles, mail bootstrap, and shared desktop toolingprofile_desktop_i3contains the X11/i3 session piecesprofile_desktop_swaycontains the wlroots/Sway session pieces and deploys shared Sway + Waybar dotfilesprofile_desktop_hyprlandcontains the optional Hyprland/Wayland session piecesprofile_desktop_hostcarries host-specific desktop overrides such as NVIDIA, PRIME wrappers, and host-only WirePlumber configprofile_workstation_dev_commoncarries the Ubuntu dev layer shared by native workstation and WSL Ubuntuprofile_workstation_gnomecarries Linux host-only GNOME setup, extensions, and UFWprofile_workstation_dev_wslcarries WSL-specific Ubuntu tweaks such assystemdand PSRP Python dependenciesprofile_workstation_host_windowsmanages the Windows 11 host via PSRP over HTTPS usingnegotiateby default, installs host applications viawingetwith a configurablewindows_package_backenddefaulting towinget_psrp, applies Windows shell tweaks, manages taskbar pins through a local Start layout policy withPinListPlacement="Replace", and sets Windows Terminal's default profile to Ubuntudeadalus-wslis modeled as a local inventory target intended to be run from inside the Ubuntu WSL distro- Windows taskbar pinning is driven by the ordered
windows_taskbar_pinslist inansible/inventory/group_vars/workstation_host_windows.yml; validate identifiers from a real Windows session before changing that list - Do not auto-restart
empttyduring playbook runs on active desktop hosts; prefer a manual restart from SSH or another TTY after the run dotfiles/desktop/.xinitrcaffects X11 login behaviordotfiles/desktop/.local/bin/start-sway-sessionandstart-hyprland-sessionare critical session bootstrap pathsnymphhas special NVIDIA/PRIME handling, host-specific WirePlumber camera config, host-specific Sway overrides, and deterministic workspace placement viakanshi
Validation Expectations Before Finishing
Default minimum:
ansible-playbook ansible/site.yml --syntax-check
Run a host-limited dry run whenever the change affects a real host profile, package set, service set, session, PAM stack, templates, or dotfiles.
For workstation changes, prefer:
ansible-playbook ansible/site.yml --limit deadalus --check --diff
ansible-playbook ansible/site.yml --limit deadalus-wsl --check --diff
If you touched scripts/bootstrap_mail.sh, also run:
sh -n scripts/bootstrap_mail.sh
shellcheck scripts/bootstrap_mail.sh
If you touched scripts/bootstrap_windows_workstation.ps1, also run:
pwsh -NoProfile -Command "[void][System.Management.Automation.Language.Parser]::ParseFile('scripts/bootstrap_windows_workstation.ps1', [ref]$null, [ref]$null)"
Agent Workflow Expectations
- Do not revert unrelated worktree changes made by the user
- Keep
README.mdandAGENTS.mdaligned when workflows materially change - If you add a new operational area, also add the validation command agents should run
- Prefer host-limited validation first:
ikarosornymphfor Void desktop work,deadalusfor Ubuntu workstation work, andprometheusfor server work - Call out checks you could not run and any follow-up verification needed