Improve agent guidance for validation and style

This commit is contained in:
Fabio Scotto di Santolo
2026-03-23 17:47:43 +01:00
parent 40c81ea003
commit 60c995ca74

118
AGENTS.md
View File

@@ -8,81 +8,107 @@ Project type: Ansible-based infrastructure plus managed dotfiles.
- Inventory: `ansible/inventory/hosts.yml` - Inventory: `ansible/inventory/hosts.yml`
- Group vars: `ansible/inventory/group_vars/*.yml` - Group vars: `ansible/inventory/group_vars/*.yml`
- Host vars: `ansible/inventory/host_vars/*.yml` - Host vars: `ansible/inventory/host_vars/*.yml`
- Roles: `ansible/roles/*/tasks/main.yml` - Active roles: `dotfiles_common`, `packages_void`, `services_runit`, `profile_desktop_i3`
- Dotfiles source of truth: `dotfiles/`
- Scripts: `scripts/`
- Sensitive local material/examples: `secrets/`
- Active orchestration: `all -> dotfiles_common`; `void -> packages_void, services_runit, profile_desktop_i3`
- Roles present but not currently wired into `ansible/site.yml`: `base`, `dotfiles`, `packages_ubuntu`, `services_systemd`, `profile_workstation_gnome`, `profile_server` - Roles present but not currently wired into `ansible/site.yml`: `base`, `dotfiles`, `packages_ubuntu`, `services_systemd`, `profile_workstation_gnome`, `profile_server`
- Dotfiles source of truth: `dotfiles/`
- Utility scripts: `scripts/`
- Sensitive local material/examples: `secrets/`
## Local instruction files ## Local instruction files
Checked in this repository when this file was written: Checked in this repository when this file was written:
- `.cursorrules`: not present - `.cursorrules`: not present
- `.cursor/rules/`: not present - `.cursor/rules/`: not present
- `.github/copilot-instructions.md`: not present - `.github/copilot-instructions.md`: not present
If any of these appear later, treat them as higher-priority local instructions. If any of these files appear later, treat them as higher-priority local instructions.
## Working assumptions ## Working assumptions
- Favor idempotent, host-safe changes over cleverness. - Favor idempotent, host-safe changes over cleverness.
- Preserve the intended layering: `all -> OS -> profile -> host`. - Preserve the intended layering: `all -> OS -> profile -> host`.
- Validate on one host before broad rollout. - Validate on one host before broad rollout.
- Prefer minimal, targeted edits over cleanup refactors.
- Treat `secrets/` as sensitive and avoid exposing secret values.
- The repo contains both first-party dotfiles and vendored third-party plugin code. - The repo contains both first-party dotfiles and vendored third-party plugin code.
## Current orchestration model
`ansible/site.yml` currently applies:
- `all -> dotfiles_common`
- `void -> packages_void, services_runit, profile_desktop_i3`
Inventory also defines Ubuntu workstation and server hosts, but those paths are not yet orchestrated by the main playbook.
## Build, lint, and test commands ## Build, lint, and test commands
There is no compile/build step. Validation is Ansible syntax checks, inventory inspection, dry-runs, and linting. There is no compile/build step. Validation is based on Ansible syntax checks, inventory inspection, dry-runs, and linting.
Install base tooling if needed: Install base tooling if needed:
```bash ```bash
python3 -m pip install ansible python3 -m pip install ansible
ansible-galaxy collection install community.general
``` ```
Core commands: Core commands:
```bash ```bash
ansible-playbook ansible/site.yml ansible-playbook ansible/site.yml
ansible-playbook ansible/site.yml --check --diff ansible-playbook ansible/site.yml --check --diff
ansible-playbook ansible/site.yml --syntax-check
ansible-playbook ansible/site.yml --limit ikaros ansible-playbook ansible/site.yml --limit ikaros
ansible-playbook ansible/site.yml --limit nymph ansible-playbook ansible/site.yml --limit nymph
ansible-playbook ansible/site.yml --syntax-check
ansible-inventory --graph ansible-inventory --graph
ansible-inventory --host ikaros ansible-inventory --host ikaros
``` ```
Linting if available locally:
Linting and static checks if available locally:
```bash ```bash
ansible-lint ansible/site.yml ansible-lint ansible/site.yml
ansible-lint ansible/roles ansible-lint ansible/roles
yamllint ansible/ yamllint ansible/
sh -n scripts/bootstrap_mail.sh
shellcheck scripts/bootstrap_mail.sh
``` ```
## Single-test equivalents ## Single-test equivalents
There is no Molecule, pytest, or dedicated unit-test suite. Use these as the closest equivalent to a single targeted test. There is no Molecule, pytest, or dedicated unit-test suite. Use the narrowest validation that matches the change.
Fastest confidence check: Fastest confidence check:
```bash ```bash
ansible-playbook ansible/site.yml --syntax-check ansible-playbook ansible/site.yml --syntax-check
``` ```
Best default host-scoped safety check:
Best default host-scoped dry-run:
```bash ```bash
ansible-playbook ansible/site.yml --limit ikaros --check --diff ansible-playbook ansible/site.yml --limit ikaros --check --diff
``` ```
Start from one task when validating a narrow change:
Validate one host's resolved inventory only:
```bash
ansible-inventory --host ikaros
```
Validate a narrow Ansible change starting from one task:
```bash ```bash
ansible-playbook ansible/site.yml --limit ikaros --start-at-task "Copy common dotfiles" --check --diff ansible-playbook ansible/site.yml --limit ikaros --start-at-task "Copy common dotfiles" --check --diff
ansible-playbook ansible/site.yml --limit ikaros --start-at-task "Install Void nonfree repository if needed" --check --diff ansible-playbook ansible/site.yml --limit ikaros --start-at-task "Install Void nonfree repository if needed" --check --diff
ansible-playbook ansible/site.yml --limit ikaros --start-at-task "Copy desktop dotfiles" --check --diff ansible-playbook ansible/site.yml --limit ikaros --start-at-task "Copy desktop dotfiles" --check --diff
``` ```
Inventory-only resolution for one machine:
Validate the standalone mail bootstrap script only:
```bash ```bash
ansible-inventory --host ikaros sh -n scripts/bootstrap_mail.sh
shellcheck scripts/bootstrap_mail.sh
``` ```
Notes:
- Keep task names stable and unique enough for `--start-at-task`. Testing notes:
- Prefer `--check --diff` before package, service, PAM, or session changes. - Prefer `--limit` aggressively to avoid accidental multi-host rollout.
- Use `--limit` aggressively to avoid accidental multi-host rollout. - Prefer `--check --diff` before package, service, PAM, bootloader, or session changes.
- Keep task names stable and unique enough to support `--start-at-task`.
- If you change only vars, inventory inspection may be the quickest useful check.
## Code style guidelines ## Code style guidelines
### General principles ### General principles
- Prefer minimal, scoped changes that match the existing layout. - Keep orchestration in playbooks and implementation details in roles.
- Preserve idempotency and avoid hidden mutable state.
- Prefer declarative modules over imperative commands. - Prefer declarative modules over imperative commands.
- Keep orchestration in playbooks and implementation in roles.
- Avoid unrelated refactors while solving a targeted task. - Avoid unrelated refactors while solving a targeted task.
- Preserve idempotency and make state transitions explicit.
- Keep changes readable for humans reviewing YAML and shell.
### Ansible modules and task structure ### Ansible modules and task structure
- Use FQCN module names such as `ansible.builtin.copy` and `community.general.xbps`. - Use FQCN module names such as `ansible.builtin.copy` and `community.general.xbps`.
@@ -90,46 +116,55 @@ Notes:
- Use `command` only when no good module exists. - Use `command` only when no good module exists.
- Use `shell` only when shell features are genuinely required. - Use `shell` only when shell features are genuinely required.
- When using `command` or `shell`, set `changed_when` and `failed_when` when defaults are misleading. - When using `command` or `shell`, set `changed_when` and `failed_when` when defaults are misleading.
- Keep task names imperative, outcome-based, and unique enough to support `--start-at-task`. - Keep task names imperative, outcome-based, and unique enough for `--start-at-task`.
- Use `block` when a sequence of tasks shares one condition or operational purpose.
- Prefer `loop` with clear `loop_control.label` for user-facing collections.
### YAML formatting ### YAML formatting
- Start YAML files with `---`. - Start YAML files with `---`.
- Use 2-space indentation; do not use tabs. - Use 2-space indentation and never tabs.
- Keep lists and maps in stable, readable order. - Keep keys, lists, and maps in stable, readable order.
- Quote file modes as strings: `"0644"`, `"0755"`, `"0600"`, `"0700"`. - Quote file modes as strings: `"0644"`, `"0755"`, `"0600"`, `"0700"`.
- Prefer multiline Jinja when a one-line expression becomes hard to read. - Prefer multiline Jinja when a one-line expression becomes hard to read.
- Avoid formatting-only churn in untouched sections. - Avoid formatting-only churn in untouched sections.
### Variables, types, and templating ### Variables, types, and templating
- Use `snake_case` consistently. - Use `snake_case` consistently for variables and facts.
- Follow existing naming families like `common_packages`, `profile_packages`, and `host_packages`. - Follow existing naming families such as `common_packages`, `profile_packages`, `host_packages`, and `host_dotfiles`.
- Keep booleans as booleans, not quoted strings. - Keep booleans as booleans, not quoted strings.
- Keep structured data as YAML collections, not comma-separated strings. - Keep structured data as YAML collections, not comma-separated strings.
- Guard optional values with `default([])`, `default({})`, or `default('')` as appropriate. - Guard optional collections and maps with `default([])` and `default({})`.
- Guard optional strings with `default('')`.
- Build user paths from `{{ user_home }}` instead of hardcoding home directories. - Build user paths from `{{ user_home }}` instead of hardcoding home directories.
- Prefer explicit derived facts with `set_fact` only when reuse improves clarity.
### Naming conventions ### Naming conventions
- Role names stay lowercase with underscores. - Role names stay lowercase with underscores.
- Inventory groups and matching var files should stay semantically aligned. - Inventory groups and matching var files should stay semantically aligned.
- New variables should fit existing patterns instead of inventing one-off names. - Host-specific overrides belong in `host_vars`, not shared group files.
- New variables should fit existing naming patterns instead of introducing one-off aliases.
### Error handling and safety ### Error handling and safety
- Guard OS-specific or profile-specific behavior with `when` clauses. - Guard OS-specific or profile-specific behavior with `when` clauses.
- Prefer explicit loops and inputs over assumptions. - Prefer explicit inputs over assumptions about host state.
- Do not suppress failures without a clear operational reason. - Do not suppress failures without a clear operational reason.
- Use `failed_when: false` only for intentional state probes. - Use `failed_when: false` only for intentional probes or best-effort detection.
- Use `no_log: true` for secrets, tokens, passwords, and sensitive command results.
- Keep tasks non-interactive and automation-safe. - Keep tasks non-interactive and automation-safe.
- Avoid destructive operations in user homes unless the request clearly requires them. - Avoid destructive operations in user homes unless the request clearly requires them.
- Fail early with `ansible.builtin.fail` when prerequisites such as architecture or required metadata are missing.
### Dotfiles and script conventions ### Shell and script conventions
- Prefer POSIX `sh` for simple login/session scripts. - Prefer POSIX `sh` for simple login/session/bootstrap scripts.
- Use Bash only when Bash features are required; then use `#!/usr/bin/env bash`. - Use Bash only when Bash features are actually required; then use `#!/usr/bin/env bash`.
- Quote variable expansions unless intentional word splitting is required. - Quote variable expansions unless intentional word splitting is required.
- Keep helper functions small and focused.
- Exit with clear errors when required commands or files are missing.
- Preserve executable bits for deployed scripts where appropriate. - Preserve executable bits for deployed scripts where appropriate.
- Keep session-start changes conservative; verify startup paths and environment assumptions.
### Imports and external code ### Imports and external code
- There are no Python import conventions to optimize for here; most changes are YAML and shell. - There are no Python import conventions to optimize for here; most changes are YAML, Jinja, and shell.
- If you add Python later, follow standard-library, third-party, local import grouping and keep modules small.
- Treat `dotfiles/common/.tmux/plugins/` as vendored upstream code unless the task explicitly targets it. - Treat `dotfiles/common/.tmux/plugins/` as vendored upstream code unless the task explicitly targets it.
- Prefer changing first-party wrapper/config files before patching vendored plugin internals. - Prefer changing first-party wrapper/config files before patching vendored plugin internals.
@@ -138,31 +173,36 @@ Notes:
- `ansible/inventory/group_vars/*.yml`: shared defaults by layer. - `ansible/inventory/group_vars/*.yml`: shared defaults by layer.
- `ansible/inventory/host_vars/*.yml`: host-specific overrides only. - `ansible/inventory/host_vars/*.yml`: host-specific overrides only.
- `ansible/roles/*/tasks/main.yml`: role implementation details. - `ansible/roles/*/tasks/main.yml`: role implementation details.
- `ansible/templates/**/*.j2`: keep secrets and host-specific values parameterized.
- `dotfiles/`: user-facing config and session scripts deployed by roles. - `dotfiles/`: user-facing config and session scripts deployed by roles.
- `secrets/vault.yml`: treat as sensitive; do not expose values in tracked plain text. - `scripts/`: standalone local utilities; keep them safe to run manually.
## Validation expectations before finishing ## Validation expectations before finishing
Run the narrowest useful checks for the area you changed. Run the narrowest useful checks for the area you changed.
Default minimum: Default minimum:
```bash ```bash
ansible-playbook ansible/site.yml --syntax-check ansible-playbook ansible/site.yml --syntax-check
``` ```
Preferred for role or vars changes:
Preferred for role, template, or vars changes:
```bash ```bash
ansible-playbook ansible/site.yml --limit <host> --check --diff ansible-playbook ansible/site.yml --limit <host> --check --diff
``` ```
Useful supporting checks: Useful supporting checks:
```bash ```bash
ansible-inventory --graph ansible-inventory --graph
ansible-inventory --host <host> ansible-inventory --host <host>
ansible-lint ansible/site.yml ansible-lint ansible/site.yml
yamllint ansible/ yamllint ansible/
sh -n scripts/bootstrap_mail.sh
``` ```
## Agent workflow expectations ## Agent workflow expectations
- Read the relevant inventory, vars, role tasks, and deployed dotfiles before editing. - Read the relevant inventory, vars, role tasks, templates, and deployed dotfiles before editing.
- Do not change unrelated files to "clean things up". - Do not change unrelated files to "clean things up".
- Keep README and AGENTS aligned when workflows materially change. - Keep `README.md` and `AGENTS.md` aligned when workflows materially change.
- If you add a new operational area, also add the validation command agents should run. - If you add a new operational area, also add the validation command agents should run.
- Prefer host-limited validation before broad apply. - Prefer host-limited validation before broad apply.
- Call out any verification you could not run. - Call out any verification you could not run.