Add context for agents

Change-Id: If338782e9bba9a13bf4e7d55c1ddb36130683866
Signed-off-by: Will Szumski <will@stackhpc.com>
This commit is contained in:
Will Szumski committed 2026-06-01 15:50:42 +01:00
1 parent 461c60de89
commit d7396e066c
8 files changed
+746

No files matched your search

+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
oldstate=$(set +o)
set -eu
set -o pipefail
# Resolve the Kayobe checkout root and set the default agent workdir beneath it.
PARENT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
AGENTS_ROOT="$(dirname "${PARENT}")"
REPO_ROOT="$(dirname "${AGENTS_ROOT}")"
export KAYOBE_AGENT_WORKDIR="${KAYOBE_AGENT_WORKDIR:-${REPO_ROOT}/.agents/workdir}"
eval "$oldstate"
+151
View File
@@ -0,0 +1,151 @@
---
name: add-cli-command
description: "Add a new Kayobe CLI command end-to-end. Use when: adding a CLI subcommand, creating a new kayobe command, wiring up a playbook to the CLI. Touches commands.py, setup.cfg, playbook, and unit test."
---
# Add a Kayobe CLI Command
## When to Use
- Adding a new `kayobe <noun> <verb>` command
- Wiring an existing or new Ansible playbook into the CLI
## Procedure
### 1. Define the command class in `kayobe/cli/commands.py`
Choose the right mixin combination:
| If the command runs... | Inherit from |
|---|---|
| Only Kayobe playbooks | `KayobeAnsibleMixin, VaultMixin, Command` |
| Kayobe + Kolla Ansible | `KollaAnsibleMixin, KayobeAnsibleMixin, VaultMixin, Command` |
Pattern for Kayobe-only commands:
```python
class MyNewCommand(KayobeAnsibleMixin, VaultMixin, Command):
"""Short description of the command."""
def get_parser(self, prog_name):
parser = super(MyNewCommand, self).get_parser(prog_name)
# Add command-specific arguments here if needed
return parser
def take_action(self, parsed_args):
self.app.LOG.debug("Running my new command")
playbooks = _build_playbook_list("my-new-playbook")
self.run_kayobe_playbooks(parsed_args, playbooks,
limit="target-group")
```
Pattern for commands that also invoke Kolla Ansible (inherit `KollaAnsibleMixin`):
```python
class MyNewKollaCommand(KollaAnsibleMixin, KayobeAnsibleMixin, VaultMixin, Command):
"""Short description of the command."""
def get_parser(self, prog_name):
parser = super(MyNewKollaCommand, self).get_parser(prog_name)
return parser
def take_action(self, parsed_args):
self.app.LOG.debug("Running my new kolla command")
self.handle_kolla_tags_limits_deprecation(parsed_args)
playbooks = _build_playbook_list("my-new-playbook")
self.run_kayobe_playbooks(parsed_args, playbooks)
# Run kolla-ansible steps as needed, e.g.:
# self.run_kolla_ansible_overcloud(parsed_args, "deploy")
```
`handle_kolla_tags_limits_deprecation(parsed_args)` **must** be called first in
every `KollaAnsibleMixin` command. It enforces warnings and mutual-exclusion
checks for `--kolla-limit`, `--kolla-tags`, and `--kolla-skip-tags`.
Key helpers:
- `_build_playbook_list("name1", "name2")` builds absolute paths to `ansible/<name>.yml`
- `_get_playbook_path("name")` returns a single playbook path
- Use `ignore_limit=True` for plays that must always run against all hosts (e.g. localhost config generation)
- Use `extra_vars={}` for runtime overrides
- Use `tags=` and `check=False` when needed
### 2. Register entry points in `setup.cfg`
Add **two** entries:
```ini
# Under [entry_points] > kayobe.cli=
my_new_command = kayobe.cli.commands:MyNewCommand
# Under kayobe.cli.my_new_command =
kayobe.cli.my_new_command =
hooks = kayobe.cli.commands:HookDispatcher
```
The entry point name uses underscores and maps to the CLI as `kayobe my new command`.
### 3. Create the Ansible playbook
Create `ansible/my-new-playbook.yml` using kebab-case:
```yaml
---
- name: Description of what this play does
hosts: target-group
tags:
- my-new-playbook
roles:
- role: my-role
```
Keep the playbook minimal. Put logic in a role under `ansible/roles/`.
### 4. Add a unit test in `kayobe/tests/unit/cli/test_commands.py`
```python
@mock.patch.object(commands.KayobeAnsibleMixin,
"run_kayobe_playbooks")
def test_my_new_command(self, mock_run):
command = commands.MyNewCommand(TestApp(), [])
parser = command.get_parser("test")
parsed_args = parser.parse_args([])
result = command.run(parsed_args)
self.assertEqual(0, result)
expected_calls = [
mock.call(
mock.ANY,
[utils.get_data_files_path("ansible", "my-new-playbook.yml")],
limit="target-group",
),
]
self.assertListEqual(expected_calls, mock_run.call_args_list)
```
### 5. Add a release note
```bash
tox -e venv -- reno new add-my-new-command
```
Use the `features` section.
### 6. Update documentation
Add or update the relevant section in `doc/source/`.
### 7. Validate
```bash
tox -e py3 # unit tests pass
tox -e pep8 # style checks pass
```
## Checklist
- [ ] Command class in `kayobe/cli/commands.py` with correct mixins
- [ ] Two `setup.cfg` entry points (command + hook dispatcher)
- [ ] Ansible playbook in `ansible/` (kebab-case)
- [ ] Unit test in `kayobe/tests/unit/cli/test_commands.py`
- [ ] Release note via reno
- [ ] Documentation updated
- [ ] `tox -e py3` and `tox -e pep8` pass
+134
View File
@@ -0,0 +1,134 @@
---
name: add-config-variable
description: "Add a new Kayobe configuration variable with proper inventory defaults, operator examples, and docs. Use when: adding a config knob, introducing a new setting, creating a new component config file. Touches group_vars, etc/kayobe, and doc/source."
---
# Add a Kayobe Configuration Variable
## When to Use
- Adding a new user-configurable setting
- Introducing a variable for a new or existing component
- Renaming or restructuring existing configuration
## Procedure
### 1. Add the inventory default
Add the variable to the appropriate file under `ansible/inventory/group_vars/`.
- Global defaults go in `ansible/inventory/group_vars/all/<component>` (extensionless file).
- Group-specific defaults go in `ansible/inventory/group_vars/<group>/<component>`.
**For scalar variables:**
```yaml
# Description of what this variable controls. Default is <value>.
my_component_setting: "default_value"
```
**For list variables, use the three-tier pattern:**
```yaml
# Combined list of items (do not override directly).
my_component_items: >
{{ my_component_items_default +
my_component_items_extra }}
# List of default items.
my_component_items_default:
- item1
- item2
# List of extra items added by the operator.
my_component_items_extra: []
```
This lets operators extend defaults via `_extra` without replacing the whole list.
### 2. Add the commented example in `etc/kayobe/`
**Only do this for variables that operators are expected to override directly.**
Skip this step for internal defaults — those should remain inventory-only.
Add a commented-out version in the appropriate `etc/kayobe/` file:
```yaml
# Description of what this variable controls. Default is <value>.
#my_component_setting:
```
For three-tier lists, document all three variables:
```yaml
# Combined list of items.
#my_component_items:
# List of default items.
#my_component_items_default:
# List of extra items added by the operator.
#my_component_items_extra:
```
Rules:
- Keep the same comment text as the inventory default (adjusted for operator audience).
- Preserve the `###` section dividers and comment style used in the rest of the file.
- The file name in `etc/kayobe/` should follow the file alignment reference.
### 3. Update documentation
Add or update the variable description in the existing authoritative doc page
for the component. Search `doc/source/` to find the right file — do not assume
a filename from the component name. For example:
- Kolla-ansible settings → `doc/source/configuration/reference/kolla-ansible.rst`
- Kolla settings → `kolla.rst`
- OpenStack/kayobe settings → `doc/source/configuration/reference/kayobe.rst`
- Host-group settings (firewall, sysctl, etc.) → `doc/source/configuration/reference/hosts.rst`
- IPA → `doc/source/configuration/reference/ironic-python-agent.rst`
When in doubt, `grep -r 'nearby_variable_name' doc/source/` to locate the right page.
### 4. Add a release note
```bash
tox -e venv -- reno new add-my-component-setting
```
Use the `features` section for new variables, `upgrade` if existing behavior changes.
### 5. Validate
```bash
tox -e pep8 # yamllint checks etc/kayobe/
tox -e ansible-lint # lint the inventory and playbooks
```
## File Alignment Reference
| Inventory defaults | Operator examples | Docs |
|---|---|---|
| `ansible/inventory/group_vars/all/<component>` | `etc/kayobe/<component>.yml` | Find by searching `doc/source/` |
| `ansible/inventory/group_vars/<group>/<component>` | `etc/kayobe/<group>.yml` | Same docs file |
Note: for group-scoped variables the `etc/kayobe/` file is named after the
**group** (e.g. `compute`, `controllers`), not the component file inside
that group. For example `ansible/inventory/group_vars/compute/firewall`
corresponds to `etc/kayobe/compute.yml`.
## Naming Conventions
- Prefix variables with the component name: `kolla_`, `compute_`, `seed_`, `dns_`, etc.
- Use `snake_case` for all variable names.
- Use `_default` and `_extra` suffixes for the three-tier list pattern.
- Use `_enabled` (bool) for feature toggles.
## Checklist
- [ ] Variable added in `ansible/inventory/group_vars/` with comment and default value
- [ ] Commented example added in `etc/kayobe/<component>.yml`
- [ ] Documentation updated in `doc/source/`
- [ ] Release note via reno
- [ ] `tox -e pep8` passes (yamllint)
- [ ] `tox -e ansible-lint` passes (if touching playbooks or roles)
@@ -0,0 +1,177 @@
---
name: sync-kolla-feature-flags
description: "Synchronise Kayobe Kolla feature flags with a target kolla-ansible tree. Use when: updating for a new OpenStack/Kolla release, refreshing kolla_feature_flags, regenerating etc/kayobe kolla_enable_* examples, and checking removed services in overcloud image regex maps."
---
# Sync Kolla Feature Flags
## When to Use
- Preparing Kayobe for a new Kolla Ansible release
- Refreshing the canonical `kolla_feature_flags` list
- Regenerating the `kolla_enable_*` commented flags in `etc/kayobe/kolla.yml`
- Verifying removed services are not left behind in inventory defaults
Last validated for `2026.1`.
## Procedure
### 1. Generate the canonical flag list from Kolla Ansible
Prompt for the target `<release>` (for example `2026.1`), then ensure the
kolla-ansible checkout exists under `${KAYOBE_AGENT_WORKDIR}` in
`kolla-ansible/`.
Before cloning or reusing the checkout, source `.agents/scripts/agent-environment.sh` to
set `KAYOBE_AGENT_WORKDIR`.
If `kolla-ansible/` does not exist, clone with:
```bash
source .agents/scripts/agent-environment.sh
git clone https://github.com/openstack/kolla-ansible -b stable/<release> "${KAYOBE_AGENT_WORKDIR}/kolla-ansible"
```
If `kolla-ansible/` already exists, checkout the correct branch:
```bash
source .agents/scripts/agent-environment.sh
git -C "${KAYOBE_AGENT_WORKDIR}/kolla-ansible" status --porcelain
```
If this command returns any output, stop and ask the user to clean, commit, or
stash changes before continuing. Abort all sync checks until the checkout is
clean.
Then run:
```bash
source .agents/scripts/agent-environment.sh
git -C "${KAYOBE_AGENT_WORKDIR}/kolla-ansible" fetch origin
git -C "${KAYOBE_AGENT_WORKDIR}/kolla-ansible" checkout stable/<release>
git -C "${KAYOBE_AGENT_WORKDIR}/kolla-ansible" pull --ff-only origin stable/<release>
```
Then run:
```bash
source .agents/scripts/agent-environment.sh
tools/kolla-feature-flags.sh "${KAYOBE_AGENT_WORKDIR}/kolla-ansible"
```
The script output is the canonical sorted list for
`ansible/roles/kolla-ansible/vars/main.yml`.
### 2. Update `kolla_feature_flags`
Replace the `kolla_feature_flags` list in
`ansible/roles/kolla-ansible/vars/main.yml` with the script output from step 1.
### 3. Regenerate `kolla_enable_*` examples
Run:
```bash
tools/feature-flags.py
```
Copy the script output and replace the `kolla_enable_*` commented flag block in
`etc/kayobe/kolla.yml`.
### 4. Apply repository guardrails learned from prior syncs
After syncing, verify that removed services are consistently absent from all
Kayobe-owned locations. Run `git grep <service>` to find any stragglers.
#### `ansible/inventory/group_vars/all/kolla`
- No removed-service entries in `overcloud_container_image_regex_map`.
- No removed-service `kolla_enable_*` defaults.
- No stale dependencies on a removed service (e.g. `kolla_enable_placement`
previously depended on `kolla_enable_zun`; remove such references).
#### `etc/kayobe/kolla.yml`
- No commented `#kolla_enable_<service>:` or `#kolla_enable_horizon_<service>:`
entries for removed services.
#### `ansible/roles/kolla-openstack/defaults/main.yml`
- No removed-service include globs in
`kolla_openstack_custom_config_include_globs_default`.
- No removed-service template override rules in
`kolla_openstack_custom_config_rules_default`.
- No `kolla_enable_<service>` default variable block for removed services.
#### `doc/source/configuration/reference/kolla-ansible.rst`
- No removed-service rows in the custom config path table.
#### `ansible/roles/kolla-ansible/defaults/main.yml`
- No commented `#kolla_enable_<service>:` entries for removed services.
#### `ansible/roles/kolla-ansible/templates/kolla/globals.yml`
- No commented or active removed-service-specific globals
(e.g. `#docker_configure_for_zun`).
#### `ansible/roles/kolla-ansible/templates/overcloud-components.j2`
- No `[<service>:children]` group entries for removed services.
- New top-level services (not sub-exporters/sub-services of an existing
component) need a `[<service>:children]` entry here. Check the kolla-ansible
source inventory for guidance on whether a new flag warrants a component
group.
#### `ansible/roles/kolla-ansible/templates/overcloud-services.j2`
- No `[<service>-*:children]` group entries or `<service>` child group
references for removed services.
- New services need appropriate `[<service>-*:children]` entries here. Each
container defined by kolla-ansible for the service typically needs one.
Sub-exporters/sub-services of an existing component (e.g.
`prometheus-openstack-network-exporter`, `prometheus-valkey-exporter`) go
here only, not in `overcloud-components.j2`.
#### `ansible/container-engine.yml`
- No playbook vars that reference removed-service feature flags
(e.g. `docker_configure_for_zun: "{{ kolla_enable_zun | bool }}"`).
#### `ansible/roles/kolla-ansible/tests/test-extras.yml`
- No `kolla_enable_<service>: True` entries in the "enable everything" block.
- No `#enable_<service>: True` entries in the commented expected-variables
block.
Known removed-service examples include: `influxdb`, `kuryr`, `telegraf`, `zun`.
### 5. Optional template sanity check
A prior scan found no removable removed-service-specific files under:
- `ansible/roles/kolla-openstack/templates/kolla/config`
Use this as a reference point when deciding whether template deletions are
needed during future syncs.
## Checklist
- [ ] Prompted for target `<release>`
- [ ] Ensured `kolla-ansible/` exists in repo root on `stable/<release>`
- [ ] Confirmed `kolla-ansible/` has no uncommitted changes before checkout
- [ ] Aborted all sync checks if `kolla-ansible/` was dirty
- [ ] Ran `tools/kolla-feature-flags.sh <path to kolla-ansible source>`
- [ ] Updated `ansible/roles/kolla-ansible/vars/main.yml`
- [ ] Ran `tools/feature-flags.py`
- [ ] Updated `etc/kayobe/kolla.yml` `kolla_enable_*` block
- [ ] Confirmed removed services absent from `ansible/inventory/group_vars/all/kolla`
- [ ] Confirmed removed services absent from `ansible/roles/kolla-ansible/defaults/main.yml`
- [ ] Confirmed removed services absent from `ansible/roles/kolla-ansible/templates/kolla/globals.yml`
- [ ] Confirmed removed services absent from `ansible/roles/kolla-ansible/templates/overcloud-components.j2`
- [ ] Confirmed removed services absent from `ansible/roles/kolla-ansible/templates/overcloud-services.j2`
- [ ] Confirmed removed services absent from `ansible/container-engine.yml`
- [ ] Confirmed removed services absent from `ansible/roles/kolla-ansible/tests/test-extras.yml`
- [ ] Confirmed removed services absent from `ansible/roles/kolla-openstack/defaults/main.yml`
- [ ] Confirmed removed services absent from `doc/source/configuration/reference/kolla-ansible.rst`
@@ -0,0 +1,138 @@
---
name: sync-kolla-inventory
description: "Synchronise Kayobe overcloud inventory templates with Kolla Ansible multinode inventory. Use when: updating for a new OpenStack/Kolla release, copying inventory group changes from kolla-ansible, and validating top-level/components/services split in Kayobe templates."
---
# Sync Kolla Inventory
## When to Use
- Preparing Kayobe for a new Kolla Ansible release
- Syncing inventory changes from kolla-ansible multinode inventory
- Verifying new/removed inventory groups are reflected in the correct Kayobe
template
Last validated for `2026.1`.
## Source and Targets
- Source inventory in kolla-ansible:
- `ansible/inventory/multinode`
- Kayobe targets:
- `ansible/roles/kolla-ansible/templates/overcloud-top-level.j2`
- `ansible/roles/kolla-ansible/templates/overcloud-components.j2`
- `ansible/roles/kolla-ansible/templates/overcloud-services.j2`
Kayobe splits multinode inventory into three parts.
## Boundary Rules
### Top level
Target: `ansible/roles/kolla-ansible/templates/overcloud-top-level.j2`
- This file is heavily templated and does not typically need changes.
- Look for changes in multinode before the `[baremetal]` group.
- If there are changes in that region, port them carefully while preserving
existing Kayobe templating.
### Components
Target: `ansible/roles/kolla-ansible/templates/overcloud-components.j2`
- Includes multinode groups from `[baremetal]` down to this marker:
```text
# Additional control implemented here. These groups allow you to control which
# services run on which hosts at a per-service level.
```
- This file maps components to top-level groups.
### Services
Target: `ansible/roles/kolla-ansible/templates/overcloud-services.j2`
- Includes multinode groups from the marker above to end of file.
- This file maps service containers to components.
- Keep Kayobe-specific small changes in this section unless intentionally
removed.
## Procedure
### 1. Get the source inventory snapshot
Prompt for the target `<release>` (for example `2026.1`), then ensure the
kolla-ansible checkout exists under `${KAYOBE_AGENT_WORKDIR}` in
`kolla-ansible/`.
Before cloning or reusing the checkout, source `.agents/scripts/agent-environment.sh` to set
`KAYOBE_AGENT_WORKDIR`.
If `kolla-ansible/` does not exist, clone with:
```bash
source .agents/scripts/agent-environment.sh
git clone https://github.com/openstack/kolla-ansible -b stable/<release> "${KAYOBE_AGENT_WORKDIR}/kolla-ansible"
```
If `kolla-ansible/` already exists, checkout the correct branch:
```bash
source .agents/scripts/agent-environment.sh
git -C "${KAYOBE_AGENT_WORKDIR}/kolla-ansible" status --porcelain
```
If this command returns any output, stop and ask the user to clean, commit, or
stash changes before continuing. Abort all sync checks until the checkout is
clean.
Then run:
```bash
source .agents/scripts/agent-environment.sh
git -C "${KAYOBE_AGENT_WORKDIR}/kolla-ansible" fetch origin
git -C "${KAYOBE_AGENT_WORKDIR}/kolla-ansible" checkout stable/<release>
git -C "${KAYOBE_AGENT_WORKDIR}/kolla-ansible" pull --ff-only origin stable/<release>
```
### 2. Compare multinode against Kayobe split templates
Review group additions/removals/renames in the source and map each change by
section boundary:
- Before `[baremetal]` -> `overcloud-top-level.j2`
- `[baremetal]` to marker -> `overcloud-components.j2`
- Marker to end -> `overcloud-services.j2`
### 3. Apply updates to the correct Kayobe template
Port each relevant change into the appropriate file while preserving existing
Kayobe templating and comments.
### 4. Validate new services are placed correctly
When new services appear in multinode:
- Add top-level component groups to `overcloud-components.j2` when appropriate.
- Add per-service container groups to `overcloud-services.j2`.
- Sub-services/exporters of an existing component generally belong in
`overcloud-services.j2` only.
### 5. Re-check Kayobe-specific behavior
After sync, confirm intentional Kayobe differences are still present and valid,
particularly in `overcloud-services.j2`.
## Checklist
- [ ] Prompted for target `<release>`
- [ ] Ensured `kolla-ansible/` exists in repo root on `stable/<release>`
- [ ] Confirmed `kolla-ansible/` has no uncommitted changes before checkout
- [ ] Aborted all sync checks if `kolla-ansible/` was dirty
- [ ] Compared kolla-ansible `ansible/inventory/multinode` with Kayobe templates
- [ ] Reviewed pre-`[baremetal]` changes for `overcloud-top-level.j2`
- [ ] Synced component groups into `overcloud-components.j2`
- [ ] Synced service groups into `overcloud-services.j2`
- [ ] Verified new services are in the correct file(s)
- [ ] Preserved intentional Kayobe-specific service-section changes
View File
Whitespace-only changes.
+1
View File
@@ -65,6 +65,7 @@ ansible/collections/
# Virtualenvs
ansible/kolla-venv/
venvs
.agents/workdir/
# Vagrant
.vagrant
+129
View File
@@ -0,0 +1,129 @@
# AGENTS.md
Guidance for coding agents working in this repository.
## Scope
These instructions apply to the whole repository.
## Repository Overview
Kayobe is an OpenStack project for deploying containerised OpenStack to bare metal. The repository combines:
- Python application code in `kayobe/`
- Ansible playbooks, plugins, and inventory defaults in `ansible/`
- Ansible roles used by Kayobe in `ansible/roles/`
- Top-level CI-oriented roles in `roles/`, used by Zuul jobs
- Example operator configuration in `etc/kayobe/`
- Documentation in `doc/source/`
When changing behavior, prefer to preserve the existing split between code, default inventory values, and operator-facing example configuration.
## Core Conventions
### Python
- Follow PEP 8 style for Python, along with the OpenStack Style Commandments referenced in `HACKING.rst`.
- Use the existing Apache 2.0 file header for new Python files.
- Keep imports grouped as standard library, third-party, then local imports.
- Use module-level logging with `LOG = logging.getLogger(__name__)`.
- Raise project exceptions from `kayobe.exception.KayobeException` or a specific subclass.
- Follow existing Cliff command patterns in `kayobe/cli/commands.py`:
- mixins provide shared parser and execution behavior
- command classes implement `get_parser()` and `take_action()` or `run()` consistently with nearby code
- CLI naming is driven by `setup.cfg` entry points
### Ansible
- Keep top-level playbooks in `ansible/` minimal. Put reusable logic in roles.
- Use kebab-case names for playbooks in `ansible/`.
- Preserve the current YAML style and comment formatting in inventory defaults and example config.
- Custom Ansible plugins live in `ansible/action_plugins/`, `ansible/filter_plugins/`, `ansible/lookup_plugins/`, and `ansible/test_plugins/`.
- For **filter and lookup plugins**, implementations live in `kayobe/plugins/` with thin re-export wrappers in the `ansible/*_plugins/` directories. Put new filter/lookup logic in `kayobe/plugins/` and add a wrapper under `ansible/*_plugins/` if following this pattern.
- For **action plugins**, implementations are typically full standalone modules directly in `ansible/action_plugins/` with no counterpart in `kayobe/plugins/`. Follow the nearest existing plugin of the same kind.
- Roles use standard Ansible layout such as `defaults/main.yml`, `tasks/main.yml`, and `vars/main.yml`.
- Put roles used by Kayobe playbooks under `ansible/roles/`.
- Treat the top-level `roles/` directory as Zuul/CI-focused content rather than the main home for Kayobe runtime roles.
- For list-type variables, follow the three-tier composition pattern where applicable: `<var>_default` + `<var>_extra` combined into `<var>`. This lets operators extend defaults without replacing them.
### Inventory Defaults And Example Config
- Inventory defaults in `ansible/inventory/group_vars/` are commonly stored in extensionless files, for example `ansible/inventory/group_vars/all/kolla`.
- Operator-facing example configuration lives in `etc/kayobe/*.yml` and typically contains commented-out variables with explanatory text.
- If you add or rename a variable in `ansible/inventory/group_vars/<group>/<file>` or `ansible/inventory/group_vars/all/<file>`, also add or update the corresponding commented example in `etc/kayobe/<file>.yml` when that file represents the same component.
- The component file name should usually stay aligned across these locations, for example inventory defaults in `.../kolla` and example configuration in `etc/kayobe/kolla.yml`.
- Keep user-overridable values documented in `etc/kayobe/` rather than only discoverable from inventory defaults.
### Testing
- Unit tests live in `kayobe/tests/unit/`, mirroring the module structure.
- Use `unittest.TestCase` with `self.maxDiff = None`.
- Mock external calls with `@mock.patch.object(..., autospec=True)`.
- CLI command tests instantiate the command class with a test app, parse arguments, call `run()`, and assert the expected playbook calls via `mock_run.call_args_list`.
- Molecule tests for roles live in `ansible/roles/<role>/molecule/` and use testinfra for verification.
- Shared molecule helpers are in `kayobe/tests/molecule/utils.py`.
### Documentation
- Update documentation when behavior, configuration, or workflows change.
- For any non-trivial change, add or update the relevant documentation in `doc/source/`.
- Prefer `doc/source/` for contributor or user-facing changes, and keep wording consistent with the existing docs.
### Commit Messages
- Use the 50/72 rule: subject line ≤ 50 characters, body lines wrapped at 72 characters.
- Write the subject in the imperative mood (e.g. "Fix broken conditional" not "Fixed broken conditional").
- Separate the subject from the body with a blank line.
- Use the body to explain *what* and *why*, not *how*.
- A `Change-Id` trailer is added automatically by a git hook. Always preserve it when amending a commit.
- Reference bugs with a `Closes-Bug: #<id>` trailer (Launchpad) or `Related-Bug: #<id>` if the commit does not fully fix the bug.
### Release Notes
- Kayobe uses `reno` for release notes, stored in `releasenotes/notes/`.
- Default to adding a release note for code, behavior, configuration, and other user-visible changes.
- Docs-only, CI-only, and `TrivialFix` changes generally should not add a release note.
- Create new release notes with `tox -e venv -- reno new <summary-line-with-dashes>`.
- Use an appropriate section such as `features`, `fixes`, `upgrade`, or `deprecations`, following the templates in `releasenotes/templates/`.
## Validation
Use the narrowest relevant validation for the change you make.
Follow the validation workflow defined in `tox.ini`, and treat issues reported by the tools run via `tox` as authoritative for the checks they perform.
- Python tests: `tox -e py3`
- Python style, lint, and static checks: `tox -e pep8`
- Ansible checks: `tox -e ansible-lint`
- Ansible syntax checks: `tox -e ansible-syntax`
- Role/integration checks: `tox -e molecule`
If a change touches Ansible inventory defaults or example config, at minimum run the relevant lint or syntax validation when feasible.
For Python changes, run `tox -e pep8` as a mandatory local style check when the environment permits running repository validation.
## Working Rules For Agents
- Do not modify any files not tracked by git unless explicitly instructed to or when creating a new file.
- When skills need temporary files or external checkouts, always source `source .agents/scripts/agent-environment.sh` before running shell commands.
- `KAYOBE_AGENT_WORKDIR` is set by `.agents/scripts/agent-environment.sh` and defaults to `.agents/workdir/` under the Kayobe checkout root.
- Override it by setting `KAYOBE_AGENT_WORKDIR` explicitly.
- Read nearby files before making structural changes; patterns in this repo are deliberate and often repeated.
- If repository tooling does not fully determine style or structure, follow nearby files and existing project patterns.
- Prefer focused changes over refactors.
- Do not remove comments from `etc/kayobe/*.yml` unless they are being replaced with better operator guidance.
- Do not put secrets or site-specific values into repository defaults.
- Respect existing user changes in the worktree and avoid reverting unrelated files.
- When adding new configuration knobs, check all three places as applicable:
- inventory defaults in `ansible/inventory/group_vars/`
- operator example config in `etc/kayobe/`
- documentation in `doc/source/`
## Useful References
- `README.rst`
- `HACKING.rst`
- `CONTRIBUTING.rst`
- `setup.cfg`
- `tox.ini`
- `kayobe/cli/commands.py`
- `ansible/inventory/group_vars/all/`
- `etc/kayobe/`