{"owner":"saltstack","repo":"salt","hasSkills":true,"totalSkillsCount":19,"totalTokensCount":22067,"categories":["copilot-instructions","subagent-persona","gemini-rule","claude-rule","anthropic-skill","plugin-manifest"],"hasMcp":true,"mcpConfig":{"mcpServers":{"salt":{"command":"npx","args":["-y","@modelcontextprotocol/server-salt"]}}},"found":["agents/COPILOT.md","agents/CURSOR.md","agents/README.md","agents/GEMINI.md","agents/CLAUDE.md","agents/docs/package-building.md",".github/copilot-instructions.md","agents/docs/architecture.md","agents/docs/development-setup.md","agents/docs/testing.md","agents/docs/git-and-ci.md","agents/docs/module-templates.md","agents/docs/troubleshooting.md","agents/mcp/README.md","agents/mcp/mcp-config.json","agents/reports/silent_drop_audit_3008_to_master.md","agents/mcp/salt_test/README.md","scripts/suse/yum/plugins/README.md","scripts/suse/zypper/plugins/commit/README.md"],"skills":{"agents/COPILOT.md":"# GitHub Copilot Instructions for Salt Development\n\n## Project Context\n\nThis is the Salt project - a Python-based configuration management and remote execution system using master-minion architecture with a dynamic loader system for plugins.\n\n## Coding Philosophy\n\n**CRITICAL**: Write code as a CPython core developer:\n- Deep understanding of Python internals, memory management, GC\n- Know performance implications of different implementations\n\n**Zen of Python**: Readability counts. Explicit > implicit. Simple > complex. Errors never pass silently.\n\n**TDD for Bug Fixes**: Write failing test → verify fail → fix bug → verify pass → run related tests\n\n---\n\n## Git Workflow\n\n**CRITICAL: NO attribution lines**\n- **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n- Use imperative mood: \"Fix bug\" not \"Fixed bug\"\n- Reference issues: `#NNNN`\n- Small, focused commits\n- Rebase before push\n\n---\n\n## Architecture\n\n**Salt components:**\n- **Master-Minion**: Central master controls distributed minions\n- **Event Bus**: Real-time communication\n- **Execution Modules** (`salt/modules/`): CLI commands (264+)\n- **State Modules** (`salt/states/`): Declarative configuration (126+)\n- **Utils** (`salt/utils/`): Shared utilities (170+)\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete details.**\n\n---\n\n## Code Patterns\n\n### Execution Module\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Return True to load, False or (False, reason) to skip\"\"\"\n    if not salt.utils.path.which(\"required_binary\"):\n        return False, \"required_binary not found\"\n    return True\n\ndef function_name(param1, param2=None):\n    \"\"\"\n    CLI Example::\n        salt '*' module.function_name value\n    \"\"\"\n    if not param1:\n        raise salt.exceptions.SaltInvocationError(\"param1 required\")\n    return __salt__[\"cmd.run\"](f\"command {param1}\")\n```\n\n### State Module Return\n```python\ndef managed(name, value=None):\n    ret = {\"name\": name, \"result\": False, \"changes\": {}, \"comment\": \"\"}\n\n    # Validate → Check current → If correct return → If test mode return None → Apply → Return\n    if not name:\n        ret[\"comment\"] = \"Name required\"\n        return ret\n\n    current = __salt__[\"module.get\"](name)\n    if current == value:\n        ret[\"result\"] = True\n        ret[\"comment\"] = \"Already correct\"\n        return ret\n\n    if __opts__[\"test\"]:\n        ret[\"result\"] = None\n        ret[\"changes\"] = {\"old\": current, \"new\": value}\n        ret[\"comment\"] = \"Would change\"\n        return ret\n\n    try:\n        __salt__[\"module.set\"](name, value)\n        ret[\"result\"] = True\n        ret[\"changes\"] = {\"old\": current, \"new\": value}\n        ret[\"comment\"] = \"Updated\"\n    except Exception as exc:\n        ret[\"comment\"] = str(exc)\n\n    return ret\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete templates.**\n\n---\n\n## Dunder Variables\n\nSalt injects these into module scope:\n\n- `__opts__`: Config - `__opts__[\"test\"]`, `__opts__.get(\"id\")`\n- `__grains__`: System data - `__grains__.get(\"os_family\")`\n- `__pillar__`: Secure data - `__pillar__.get(\"password\")`\n- `__context__`: Per-run cache for expensive operations\n- `__salt__`: Execution modules - `__salt__[\"pkg.install\"](\"nginx\")`\n- `__utils__`: Utilities - `__utils__[\"files.is_text\"](path)`\n- `__states__`: State modules (states only)\n\n**Context caching pattern:**\n```python\nif \"pkg_list\" not in __context__:\n    __context__[\"pkg_list\"] = expensive_fetch()\nreturn __context__[\"pkg_list\"]\n```\n\n---\n\n## Required Elements\n\n### Logging\n```python\nlog = logging.getLogger(__name__)\nlog.debug(\"Processing: %s\", filename)  # Good - lazy formatting\nlog.debug(f\"Processing: {filename}\")   # Bad - f-strings evaluated early\n```\n**Never log secrets.**\n\n### File Handling\n```python\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nApplies to modules, states, utils, and tests alike.\n\n### Docstrings\nEvery function requires description, parameters, and CLI example:\n```python\ndef my_function(name, value=None):\n    \"\"\"\n    Description of functionality\n\n    name\n        Description of name parameter\n\n    CLI Example::\n        salt '*' module.my_function foo value=bar\n    \"\"\"\n```\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Operation failures\n    SaltInvocationError,      # Invalid arguments\n    CommandNotFoundError,     # Missing binaries\n)\n\nif not required_param:\n    raise SaltInvocationError(\"required_param must be provided\")\n```\n\n### __virtual__ Function\nDeclare platform/dependency requirements:\n```python\n# Platform check\ndef __virtual__():\n    if salt.utils.platform.is_windows():\n        return False, \"Not available on Windows\"\n    return True\n\n# Virtual name (for interface modules)\n__virtualname__ = \"pkg\"\ndef __virtual__():\n    if __grains__.get(\"os_family\") == \"Debian\":\n        return __virtualname__\n    return False\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for all patterns.**\n\n---\n\n## Development Environment\n\n**Two environments required:**\n- **venv310**: Testing 3006.x/3007.x branches\n- **venv312**: Testing master branch + pre-commit\n\n```bash\n# venv310\npython3.10 -m venv venv310 && source venv310/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.10/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.10/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && deactivate\n\n# venv312\npython3.12 -m venv venv312 && source venv312/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.11/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.11/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && pre-commit install && deactivate\n```\n\n**Always use full paths:** `./venv312/bin/pytest` (master); `./venv310/bin/pytest` (3006.x/3007.x)\n\n**See [agents/docs/development-setup.md](agents/docs/development-setup.md) for complete setup.**\n\n---\n\n## Testing\n\n### Pytest Fixture\n```python\nimport pytest\nimport salt.modules.mymodule as mymodule\n\n@pytest.fixture\ndef configure_loader_modules():\n    return {\n        mymodule: {\n            \"__opts__\": {\"test\": False},\n            \"__grains__\": {\"os\": \"Linux\"},\n            \"__salt__\": {},\n        }\n    }\n\ndef test_function():\n    result = mymodule.my_function(\"test\")\n    assert result == \"expected\"\n```\n\n### Running Tests\n```bash\n# Nox\nnox -e test-3 -- tests/pytests/unit/test_loader.py\nnox -e test-3 -- --lf  # Last failed\n\n# Direct (faster)\n./venv312/bin/pytest tests/pytests/unit/test_foo.py -v\n```\n\n**See [agents/docs/testing.md](agents/docs/testing.md) for complete guide.**\n\n---\n\n## Common Patterns\n\n### Platform Detection\n```python\nsalt.utils.platform.is_windows()\nsalt.utils.platform.is_linux()\n__grains__.get(\"os_family\")  # \"Debian\", \"RedHat\", etc.\n```\n\n### Run Commands\n```python\noutput = __salt__[\"cmd.run\"](\"ls -la\")\nresult = __salt__[\"cmd.run_all\"](\"cmd\")  # {\"retcode\": 0, \"stdout\": \"...\", \"stderr\": \"...\"}\n```\n\n### File Operations\n```python\ncontent = __salt__[\"file.read\"](path)\n__salt__[\"file.write\"](path, content)\n__salt__[\"file.file_exists\"](path)\n```\n\n---\n\n## Checklists\n\n### Execution Module\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform-specific\n- [ ] Input validation (`SaltInvocationError`)\n- [ ] Error handling (`CommandExecutionError`)\n- [ ] Context caching for expensive ops\n- [ ] Unit tests\n\n### State Module\n- [ ] Returns: name, result (True/False/None), changes, comment\n- [ ] result=None for test mode\n- [ ] Check current state before modifying\n- [ ] Populate changes dict with old/new\n\n---\n\n## Documentation\n\n**Essential guides in `agents/docs/`:**\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - venv310/venv312 setup, dependencies\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete architecture, module types, loader\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates, all patterns\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test templates, mocking, running tests\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit guidelines, PR workflow, CI reproduction\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Common issues and solutions\n\n**Key example files:** `salt/modules/test.py`, `salt/modules/file.py`, `salt/states/pkg.py`\n\n---\n\n## Key Principles\n\n- **Idempotency**: Safe to run multiple times\n- **Testing**: Every function needs tests\n- **Documentation**: Docstrings with CLI examples\n- **Error Handling**: Use Salt exception classes\n- **Performance**: Cache in `__context__`\n- **Security**: Validate inputs, never log secrets\n","agents/CURSOR.md":"# Salt Development Rules for Cursor\n\nYou are working on Salt, a Python-based configuration management and remote execution system. Follow these rules when writing or modifying Salt code.\n\n## Critical Rules\n\n**CODING PHILOSOPHY:**\n- Write as a CPython core developer - know internals, memory management, GC, performance\n- Zen of Python: Readability counts. Explicit > implicit. Simple > complex\n- TDD for bugs: Write failing test → verify fail → fix → verify pass → run related tests\n\n**GIT WORKFLOW:**\n- **NEVER** add \"Generated with Claude Code\" or \"Co-Authored-By: Claude\" to commits\n- Use imperative mood: \"Fix bug\" not \"Fixed bug\"\n- Reference issues: `#NNNN`\n- Small, focused commits\n- Rebase before push\n\n**LINTING:**\n- Black: 88 char lines (`black .`)\n- isort: Profile 3, trailing commas (`isort .`)\n- Lint: `nox -e lint-salt`\n\n---\n\n## Architecture Quick Reference\n\n**Salt Components:**\n- Master-Minion architecture for distributed configuration management\n- Event-driven communication via event bus\n- Execution modules: CLI commands (`salt/modules/`) - 264+\n- State modules: Declarative config (`salt/states/`) - 126+\n- Utils: Shared functions (`salt/utils/`) - 170+\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete architecture.**\n\n---\n\n## Module Structure\n\n### Execution Module Skeleton\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Returns True/False/(False, reason)\"\"\"\n    if not salt.utils.path.which(\"required_cmd\"):\n        return False, \"required_cmd not found\"\n    return True\n\ndef my_function(name, param=None):\n    \"\"\"\n    CLI Example::\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    if not name:\n        raise salt.exceptions.SaltInvocationError(\"name required\")\n    return __salt__[\"cmd.run\"](f\"command {name}\")\n```\n\n### State Module Return Structure\n```python\nret = {\n    \"name\": name,        # Resource name\n    \"result\": False,     # True/False/None (test mode)\n    \"changes\": {},       # {\"old\": ..., \"new\": ...}\n    \"comment\": \"\"        # What happened\n}\n```\n\n**State flow:** Validate → Check current → If correct return → If test mode return None → Make changes → Return result\n\n**Complete templates: [agents/docs/module-templates.md](agents/docs/module-templates.md)**\n\n---\n\n## Dunder Dictionaries\n\n- `__opts__`: Config (`__opts__[\"test\"]`, `__opts__.get(\"id\")`)\n- `__grains__`: System info (`__grains__.get(\"os_family\")`)\n- `__pillar__`: Secure data (`__pillar__.get(\"password\")`)\n- `__context__`: Per-run cache - use for expensive operations\n- `__salt__`: Execution modules (`__salt__[\"pkg.install\"](\"nginx\")`)\n- `__utils__`: Utilities (`__utils__[\"files.is_text\"](path)`)\n- `__states__`: State modules (states only)\n\n**Context cache pattern:**\n```python\nif \"cache_key\" not in __context__:\n    __context__[\"cache_key\"] = expensive_operation()\nreturn __context__[\"cache_key\"]\n```\n\n---\n\n## Common Patterns\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Operation failed\n    SaltInvocationError,      # Bad arguments\n    CommandNotFoundError,     # Binary not found\n)\n```\n\n### Logging\n```python\nlog = logging.getLogger(__name__)\nlog.debug(\"Processing %s\", filename)  # Good - lazy formatting\nlog.debug(f\"Processing {filename}\")   # Bad - f-strings evaluated early\n```\n**Never log secrets.**\n\n### File Handling\n```python\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nApplies to modules, states, utils, and tests alike.\n\n### Platform Detection\n```python\nsalt.utils.platform.is_windows()\nsalt.utils.platform.is_linux()\n__grains__.get(\"os_family\")  # \"Debian\", \"RedHat\", etc.\n```\n\n### Decorators\n```python\nfrom salt.utils.decorators.path import which\nfrom salt.utils.decorators import depends, memoize\n\n@which(\"systemctl\")  # Require binary\n@depends(\"docker\")   # Require Python module\n@memoize            # Cache forever\n```\n\n---\n\n## Development Environment\n\n**Two environments required:**\n- **venv310**: Testing 3006.x/3007.x branches\n- **venv312**: Testing master branch + pre-commit\n\n```bash\n# venv310\npython3.10 -m venv venv310 && source venv310/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.10/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.10/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && deactivate\n\n# venv312\npython3.12 -m venv venv312 && source venv312/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.11/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.11/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && pre-commit install && deactivate\n```\n\n**Always use full paths:** `./venv312/bin/python`, `./venv312/bin/pytest` (master); `./venv310/...` for 3006.x/3007.x\n\n**Complete setup: [agents/docs/development-setup.md](agents/docs/development-setup.md)**\n\n---\n\n## Testing\n\n### Pytest Structure\n```python\nimport pytest\nimport salt.modules.mymodule as mymodule\n\n@pytest.fixture\ndef configure_loader_modules():\n    return {\n        mymodule: {\n            \"__opts__\": {\"test\": False},\n            \"__grains__\": {\"os\": \"Linux\"},\n            \"__salt__\": {},\n        }\n    }\n\ndef test_function():\n    result = mymodule.my_function(\"test\")\n    assert result == \"expected\"\n```\n\n### Running Tests\n```bash\n# Nox\nnox -e test-3 -- tests/pytests/unit/test_loader.py\nnox -e test-3 -- --lf  # Last failed\n\n# Direct (faster)\n./venv312/bin/pytest tests/pytests/unit/test_foo.py -v\n```\n\n**Complete guide: [agents/docs/testing.md](agents/docs/testing.md)**\n\n---\n\n## Code Checklist\n\n### Execution Module\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform-specific\n- [ ] Input validation (`SaltInvocationError`)\n- [ ] Error handling (`CommandExecutionError`)\n- [ ] Context caching for expensive ops\n- [ ] Unit tests\n\n### State Module\n- [ ] Returns: name, result (True/False/None), changes, comment\n- [ ] result=None for test mode\n- [ ] Check current state before modifying\n- [ ] Populate changes dict with old/new\n\n---\n\n## Quick Reference\n\n### Common Imports\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.args\nimport salt.utils.platform\nimport salt.utils.path\n\nlog = logging.getLogger(__name__)\n```\n\n### Run Commands\n```python\noutput = __salt__[\"cmd.run\"](\"ls -la\")\nresult = __salt__[\"cmd.run_all\"](\"cmd\")  # {\"retcode\": 0, \"stdout\": \"...\", \"stderr\": \"...\"}\n```\n\n### File Operations\n```python\ncontent = __salt__[\"file.read\"](path)\n__salt__[\"file.write\"](path, content)\nexists = __salt__[\"file.file_exists\"](path)\n```\n\n---\n\n## Documentation Links\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - venv setup, verification\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete architecture, all module types\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates, patterns\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test templates, mocking, container testing\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit rules, PR workflow, CI reproduction\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Common issues and solutions\n\n**Key example files:** `salt/modules/test.py`, `salt/modules/file.py`, `salt/states/pkg.py`\n\n---\n\n## Key Principles\n\n- **Idempotency**: Safe to run multiple times\n- **Testing**: Every function needs tests\n- **Documentation**: Docstrings with CLI examples\n- **Error Handling**: Use Salt exceptions\n- **Performance**: Cache in `__context__`\n- **Security**: Validate inputs, never log secrets\n","agents/README.md":"# AI Agent Instructions for Salt Development\n\nThis directory contains instruction files for various AI coding assistants to help them better understand and work with the Salt codebase.\n\n## Purpose\n\nThese files provide AI agents with:\n- Salt's architecture and design patterns\n- Coding conventions and best practices\n- Testing guidelines and requirements\n- Common pitfalls to avoid\n\n## Available Instructions\n\n- **CLAUDE.md** - Instructions for Anthropic's Claude (Claude Code, Claude.ai, etc.)\n- **GEMINI.md** - Instructions for Google's Gemini\n- **CURSOR.md** - Instructions for Cursor IDE\n- **COPILOT.md** - Instructions for GitHub Copilot\n\nEach instruction file provides a quick reference and links to detailed documentation (see below).\n\n## Detailed Documentation\n\nThe `docs/` directory contains comprehensive guides that are referenced by all agent instruction files. This provides a single source of truth for detailed information:\n\n- **[development-setup.md](docs/development-setup.md)** - Complete virtual environment setup\n  - venv310 and venv312 setup instructions\n  - Platform-specific dependencies\n  - Installation verification steps\n  - Common troubleshooting\n\n- **[architecture.md](docs/architecture.md)** - Salt architecture deep dive\n  - Master-minion architecture\n  - All module types (execution, state, grains, pillar, beacon, engine, etc.)\n  - Directory structure\n  - Key components (state compiler, transport layer, event bus, loader system)\n\n- **[module-templates.md](docs/module-templates.md)** - Complete code templates\n  - Full execution module template with all patterns\n  - Full state module template with flow diagram\n  - All `__virtual__()` patterns (5 variations)\n  - Error handling examples\n  - Common decorators and utilities\n\n- **[testing.md](docs/testing.md)** - Comprehensive testing guide\n  - Test directory structure\n  - Unit test templates (legacy and pytest styles)\n  - Mocking patterns with examples\n  - Running tests (Nox, venv, container)\n  - Container testing for CI reproduction\n  - Linting and formatting\n\n- **[git-and-ci.md](docs/git-and-ci.md)** - Git workflow and CI\n  - Commit guidelines (**NO AI attribution!**)\n  - PR workflow with gh CLI commands\n  - Branch strategy\n  - CI failure reproduction workflow\n  - Container setup and debugging\n\n- **[package-building.md](docs/package-building.md)** - Package Building Guide\n  - Building RPMs locally using CI containers\n  - Building DEBs locally\n  - Creating source tarballs and onedir artifacts\n\n- **[troubleshooting.md](docs/troubleshooting.md)** - Common issues and solutions\n  - Import order issues\n  - Module discovery problems\n  - ZeroMQ issues\n  - Python 3.11+ compatibility gotchas\n  - Container filesystem behavior\n  - Lazy loading behavior\n  - Common error messages and fixes\n\n## How to Use\n\n**Recommended approach:** Create a symlink from the root directory to the agent file you want to use.\n\n### Symlink (Recommended)\n\nCreating a symlink keeps your local setup in sync with updates to the canonical instructions:\n\n```bash\n# From the root of the Salt repository\nln -s agents/CLAUDE.md CLAUDE.md\n# or\nln -s agents/CURSOR.md .cursorrules\n# or\nln -s agents/GEMINI.md GEMINI.md\n# or\nln -s agents/COPILOT.md COPILOT.md\n```\n\n**Why symlink?**\n- Automatically receives updates when canonical files are updated\n- Links to `agents/docs/` work correctly\n- No maintenance required\n\n### Alternative: IDE-Specific Configuration\n\nSome tools may allow you to specify a custom path to instruction files in their settings. Consult your tool's documentation.\n\n**Note:** If you need to customize the instructions, it's better to propose changes to the canonical files in `agents/` so everyone benefits, rather than maintaining a personal copy.\n\n## MCP Servers\n\nThis directory also contains Model Context Protocol (MCP) servers that expose Salt development tools to AI agents:\n\n- **mcp/salt_test/** - Testing tools server\n  - Direct pytest execution (quick local testing)\n  - CI failure discovery (analyze PR failures)\n  - Container testing (reproduce CI failures)\n\nSee [mcp/README.md](mcp/README.md) for setup and usage instructions.\n\n### Quick Setup\n\n1. Install MCP SDK: `pip install mcp`\n2. Configure in `~/.config/claude-code/mcp_config.json` (see `mcp/mcp-config.json` for template)\n3. Set GitHub token for CI features: `export GITHUB_TOKEN=\"your_token\"`\n\nWith MCP configured, you can ask Claude:\n- \"What tests are failing in PR #68562?\"\n- \"Run the loader tests locally\"\n- \"Reproduce the failure from PR #68562 on debian-11\"\n\n## .gitignore\n\nThe root-level instruction files (CLAUDE.md, GEMINI.md, etc.) are intentionally ignored by git to prevent personal configurations from being committed. Only the canonical versions in this directory are tracked.\n\n## Contributing\n\nIf you discover better patterns, common issues, or ways to improve these instructions:\n\n1. Edit the appropriate file in this `agents/` directory\n2. Submit a pull request with your improvements\n3. Include a brief explanation of what the change helps agents understand better\n\n## File Naming Conventions\n\n- **CLAUDE.md** - For Claude-based tools\n- **GEMINI.md** - For Gemini-based tools\n- **CURSOR.md** - For Cursor IDE (may also be named `.cursorrules`)\n- **COPILOT.md** - For GitHub Copilot (may also be named `.github/copilot-instructions.md`)\n\nNote: Some tools may use different filenames. Check your tool's documentation for the correct filename and location.\n","agents/GEMINI.md":"# Salt Development Instructions for Gemini\n\nYou are assisting with development of Salt, a powerful infrastructure automation and configuration management system written in Python. This guide provides essential quick reference and links to detailed documentation.\n\n## Coding Philosophy\n\n**CRITICAL**: Write code as a CPython core developer:\n- Understand Python internals, memory management, GC\n- Know performance implications\n- Be aware of implementation details\n\n**Zen of Python**: Readability counts. Explicit > implicit. Simple > complex. Errors never pass silently.\n\n**TDD for Bug Fixes**:\n1. Write failing test first → 2. Verify it fails → 3. Fix bug (minimal changes) → 4. Verify test passes → 5. Run related tests\n\n---\n\n## Architecture Quick Reference\n\nSalt is a Python-based configuration management system:\n- **Master-Minion Architecture**: Central master controls distributed minions\n- **Event-Driven**: Real-time communication via event bus\n- **Plugin-Based**: Extensible through loader system\n\n**Key Module Types:**\n- **Execution Modules** (`salt/modules/`): CLI commands on minions (264+)\n- **State Modules** (`salt/states/`): Declarative configuration (126+)\n- **Utils** (`salt/utils/`): Shared utility functions (170+)\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete architecture.**\n\n---\n\n## Dunder Dictionaries\n\nSalt injects special variables into module scope:\n\n**Always Available:**\n- `__opts__` - Config options: `__opts__.get(\"id\")`, `__opts__[\"test\"]`\n- `__grains__` - System data: `__grains__.get(\"os_family\")`\n- `__pillar__` - Secure data: `__pillar__.get(\"password\")`\n- `__context__` - Per-run cache for expensive operations\n\n**Module-Specific:**\n- `__salt__` - Other execution modules: `__salt__[\"pkg.install\"](\"nginx\")`\n- `__utils__` - Utility functions: `__utils__[\"files.is_text\"](path)`\n- `__states__` - State modules (in state modules only)\n\n**Context Caching Pattern:**\n```python\nif \"pkg_list\" not in __context__:\n    __context__[\"pkg_list\"] = expensive_fetch()  # Once per run\nreturn __context__[\"pkg_list\"]\n```\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for loader system details.**\n\n---\n\n## Execution Module Structure\n\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Returns True to load, False or (False, reason) to skip\"\"\"\n    if not salt.utils.path.which(\"required_binary\"):\n        return False, \"required_binary not found\"\n    return True\n\ndef my_function(name, param=None):\n    \"\"\"\n    CLI Example:\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    if not name:\n        raise salt.exceptions.SaltInvocationError(\"name required\")\n    return __salt__[\"cmd.run\"](f\"command {name}\")\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete templates.**\n\n---\n\n## State Module Structure\n\n**Required Return:**\n```python\n{\n    \"name\": name,              # Resource identifier\n    \"result\": True/False/None, # Success/Failure/Test mode\n    \"changes\": {},             # {\"old\": ..., \"new\": ...}\n    \"comment\": \"\"              # What happened\n}\n```\n\n**State Flow:**\n1. Validate input → 2. Check current state → 3. If correct: return success → 4. If test mode: return `result=None` with proposed changes → 5. Make changes → 6. Return result\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete state templates.**\n\n---\n\n## Common Patterns\n\n### Logging\n```python\nlog = logging.getLogger(__name__)\n# Use lazy formatting (NOT f-strings)\nlog.debug(\"Processing: %s\", filename)  # Good\nlog.debug(f\"Processing: {filename}\")   # Bad\n```\n**Never log sensitive data.**\n\n### File Handling\n```python\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nApplies to modules, states, utils, and tests alike.\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,   # Operation failed\n    SaltInvocationError,     # Invalid arguments\n    CommandNotFoundError,    # Binary not found\n)\n```\n\n### Platform Detection\n```python\nimport salt.utils.platform\nif salt.utils.platform.is_windows():\n    # Windows-specific\nif __grains__.get(\"os_family\") == \"Debian\":\n    # Debian-based\n```\n\n---\n\n## Git Workflow\n\n### CRITICAL RULES\n\n1. **NO attribution lines** - **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n2. **Imperative mood** - \"Fix bug\", not \"Fixed bug\"\n3. **Reference issues** - Use `#NNNN`\n4. **Small commits** - One logical change per commit\n5. **Rebase before push** - Linear history\n\n**Good Commit:**\n```\nFix loader cache invalidation on module reload\n\nClear cache dict on reload to ensure fresh module imports.\n\nFixes #12345\n```\n\n**Bad (NEVER):**\n```\nFixed bugs\nCo-Authored-By: Claude <noreply@anthropic.com>\n```\n\n**See [agents/docs/git-and-ci.md](agents/docs/git-and-ci.md) for complete workflow.**\n\n---\n\n## Checklists\n\n### Execution Module\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform-specific\n- [ ] Input validation (`SaltInvocationError`)\n- [ ] Error handling (`CommandExecutionError`)\n- [ ] Context caching for expensive ops\n- [ ] Unit tests\n\n### State Module\n- [ ] Returns: name, result, changes, comment\n- [ ] `result=None` for test mode\n- [ ] Check current state first\n- [ ] Populate changes dict\n- [ ] Clear comments\n\n---\n\n## Detailed Documentation\n\n**Essential guides in `agents/docs/`:**\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - venv310/venv312 setup, dependencies, verification\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete architecture, module types, loader, event bus\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates, `__virtual__()` patterns, decorators\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test structure, templates, mocking, running tests\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit guidelines, PR workflow, CI reproduction\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Import issues, ZeroMQ, Python 3.11+, lazy loading\n\n---\n\n## Key Example Files\n\n- Simple: `salt/modules/test.py`\n- Complex: `salt/modules/file.py`\n- Package: `salt/modules/aptpkg.py`\n- Service: `salt/modules/systemd_service.py`\n- States: `salt/states/file.py`, `salt/states/pkg.py`\n\n---\n\n## Summary\n\n1. **Follow patterns** - Use existing modules as templates\n2. **Use dunders** - `__salt__`, `__opts__`, `__grains__`, `__context__`\n3. **Implement `__virtual__()`** - Declare dependencies\n4. **Handle errors** - Use Salt exception classes\n5. **Write tests** - Every function needs tests\n6. **Document** - Docstrings with CLI examples required\n7. **Think idempotent** - Safe to repeat\n8. **Cache wisely** - Use `__context__`\n\n**When in doubt, check existing modules and refer to detailed docs.**\n\n**Remember: NEVER add AI attribution lines to commits!**\n","agents/CLAUDE.md":"# Salt Development Instructions for Claude\n\nYou are assisting with development of Salt, a powerful infrastructure automation and configuration management system. This document provides essential quick reference information and links to detailed documentation.\n\n## Table of Contents\n\n1. [Coding Philosophy](#coding-philosophy)\n2. [Architecture Quick Reference](#architecture-quick-reference)\n3. [Dunder Dictionaries](#dunder-dictionaries)\n4. [Module Basics](#module-basics)\n5. [State Basics](#state-basics)\n6. [Git Workflow](#git-workflow)\n7. [Checklists](#checklists)\n8. [Detailed Documentation](#detailed-documentation)\n\n---\n\n## Coding Philosophy\n\n**CRITICAL**: Approach Salt development as a CPython core developer:\n- Know memory management and garbage collection implications\n- Understand performance trade-offs between implementations\n- Be aware of CPython implementation details and GIL impact\n\n**TDD for Bug Fixes**: Always write a failing test first, verify it fails, fix the bug, verify it passes, then run related tests.\n\n**Zen of Python**: Readability counts. Explicit is better than implicit. Simple is better than complex. Errors should never pass silently.\n\n---\n\n## Architecture Quick Reference\n\nSalt is a Python-based configuration management system using master-minion architecture with an event-driven plugin system.\n\n**Key Module Types:**\n- **Execution Modules** (`salt/modules/`) - CLI commands on minions (264+)\n- **State Modules** (`salt/states/`) - Declarative configuration (126+)\n- **Utils** (`salt/utils/`) - Shared utility functions (170+)\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete architecture details.**\n\n---\n\n## Dunder Dictionaries\n\nSalt injects special dictionaries into module scope via the loader:\n\n**Always Available:**\n- `__opts__` - Configuration options (test mode, minion ID, etc.)\n- `__grains__` - System information (OS, platform, custom data)\n- `__pillar__` - Secure data for this minion\n- `__context__` - Per-run cache (persists during Salt execution)\n\n**Module-Specific:**\n- `__salt__` - Access other execution modules\n- `__utils__` - Utility functions\n- `__states__` - Call other states (in state modules only)\n\n**Example: Context Caching**\n```python\nif \"cache_key\" not in __context__:\n    __context__[\"cache_key\"] = expensive_operation()  # Only once per run\nreturn __context__[\"cache_key\"]\n```\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for detailed loader system documentation.**\n\n---\n\n## Module Basics\n\n**Execution Module Structure:**\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Return True to load, False or (False, reason) to skip\"\"\"\n    if salt.utils.platform.is_windows():\n        return False, \"Not available on Windows\"\n    return True\n\ndef my_function(name, param=None):\n    \"\"\"\n    CLI Example::\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    if not name:\n        raise salt.exceptions.SaltInvocationError(\"name is required\")\n    return __salt__[\"cmd.run\"](f\"command {name}\")\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete templates and patterns.**\n\n---\n\n## State Basics\n\n**State Return Structure (REQUIRED):**\n```python\nret = {\n    \"name\": name,        # Name parameter\n    \"result\": False,     # True (success), False (fail), None (test mode)\n    \"changes\": {},       # Dict of changes: {\"old\": ..., \"new\": ...}\n    \"comment\": \"\"        # What happened\n}\n```\n\n**State Flow:**\n1. Validate input\n2. Check current state\n3. If already correct: return success\n4. If test mode (`__opts__[\"test\"]`): return `result=None` with proposed changes\n5. Make changes and return result\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete state templates.**\n\n---\n\n## Git Workflow\n\n### CRITICAL RULES\n\n1. **NO attribution lines** - **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n2. **Use imperative mood** - \"Fix bug\", not \"Fixed bug\" or \"Fixes bug\"\n3. **Reference issues** - Use `#NNNN` to reference GitHub issues\n4. **Small, focused commits** - One logical change per commit\n5. **Rebase before push** - Keep history linear and clean\n\n**Good Commit:**\n```\nFix loader cache invalidation on module reload\n\nWhen modules are reloaded, the loader cache was not properly\ninvalidated, causing stale references. Clear the cache dict\non reload to ensure fresh module imports.\n\nFixes #12345\n```\n\n**Bad Commit (NEVER DO THIS):**\n```\nFixed some bugs\n\nCo-Authored-By: Claude <noreply@anthropic.com>\n```\n\n**See [agents/docs/git-and-ci.md](agents/docs/git-and-ci.md) for complete git workflow and CI reproduction.**\n\n---\n\n## Checklists\n\n### Execution Module Checklist\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform/dependency specific\n- [ ] Input validation with `SaltInvocationError`\n- [ ] Error handling with `CommandExecutionError`\n- [ ] Context caching for expensive operations\n- [ ] `salt.utils.files.fopen()` for any file I/O, never bare `open()`\n- [ ] Unit tests\n\n### State Module Checklist\n- [ ] Returns dict with: name, result, changes, comment\n- [ ] `result=None` for test mode (`__opts__[\"test\"]`)\n- [ ] Check current state before changing\n- [ ] Populate changes dict with old/new values\n- [ ] Clear comment explaining outcome\n\n### Common Imports\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.args\nimport salt.utils.platform\nimport salt.utils.path\n\nlog = logging.getLogger(__name__)\n```\n\n### Logging\n```python\n# Use lazy formatting (NOT f-strings)\nlog.debug(\"Processing file: %s\", filename)  # Good\nlog.debug(f\"Processing file: {filename}\")   # Bad\n```\n\n**Never log sensitive data** (passwords, tokens, keys).\n\n### File Handling\n```python\n# Use salt.utils.files.fopen(), NOT the bare open() builtin\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nThis applies everywhere, including unit tests.\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Operation failed\n    SaltInvocationError,      # Invalid arguments\n    CommandNotFoundError,     # Binary not found\n)\n```\n\n---\n\n## Detailed Documentation\n\n**Essential guides in `agents/docs/`:**\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - Virtual environment setup (venv310 and venv312), platform-specific dependencies, installation verification\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete Salt architecture, all module types, loader system, event bus\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates for execution and state modules, `__virtual__()` patterns, decorators\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test structure, unit test templates, mocking patterns, running tests\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit guidelines, PR workflow, CI failure reproduction, container testing\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Import issues, module discovery, ZeroMQ, Python 3.11+ compatibility, lazy loading\n\n---\n\n## Key Example Files\n\n- Simple module: `salt/modules/test.py`\n- Complex module: `salt/modules/file.py`\n- Package manager: `salt/modules/aptpkg.py`\n- Service: `salt/modules/systemd_service.py`\n- State examples: `salt/states/file.py`, `salt/states/pkg.py`\n\n---\n\n## Summary\n\nWhen writing Salt code:\n\n1. **Follow the patterns** - Use existing modules as templates\n2. **Use dunders correctly** - `__salt__`, `__opts__`, `__grains__`, `__context__`\n3. **Implement `__virtual__()`** - Declare platform and dependency requirements\n4. **Handle errors properly** - Use Salt's exception classes\n5. **Write tests** - Every new function needs unit tests\n6. **Document thoroughly** - Docstrings with CLI examples are required\n7. **Think idempotent** - Operations should be safe to repeat\n8. **Cache wisely** - Use `__context__` for expensive operations\n\n**When in doubt, look at existing modules for patterns and refer to the detailed documentation.**\n\n**Remember: NEVER add AI attribution lines to commits!**\n","agents/docs/package-building.md":"# Package Building Guide\n\nThis guide describes how to build Salt packages (RPM, Deb, etc.) locally using the same methods as the CI/CD pipeline.\n\n## Overview\n\nSalt packages are built using the `tools` script (specifically `tools pkg`) running inside a CI container. This ensures the build environment matches the official release environment.\n\nThe general process is:\n1.  Enter the appropriate CI container\n2.  Setup the Python environment\n3.  Build the source tarball\n4.  Build the \"Onedir\" (a self-contained directory with Python and dependencies)\n5.  Build the final package (RPM/Deb) using the Onedir\n\n## Prerequisites\n\n-   Docker installed and running\n-   `python-tools-scripts` installed in your local environment (optional, but helpful for some commands)\n\n## Reference Workflows\n\nThe instructions in this guide are derived from the official GitHub Actions workflows. If you need to verify the current build process or versions, check these files:\n\n-   **`.github/workflows/ci.yml`**: The main entry point for CI builds. It defines the high-level orchestration.\n-   **`.github/workflows/build-salt-onedir.yml`**: Defines how the \"Onedir\" (relocatable Python environment) is built. Check this for `relenv` and `python` versions.\n-   **`.github/workflows/build-packages.yml`**: Defines how the final RPM/Deb/Windows/macOS packages are built using the Onedir.\n-   **`.github/actions/build-source-tarball/action.yml`**: The steps to create the initial source distribution.\n\n## Building RPMs (Linux)\n\n### 1. Enter the Build Container\n\nFrom the root of the Salt repository:\n\n```bash\ndocker run --rm -it \\\n  -v \"$(pwd):/salt\" \\\n  -w /salt \\\n  ghcr.io/saltstack/salt-ci-containers/testing:fedora-42 \\\n  bash\n```\n\n*Note: Use `fedora-42` for RPM builds. For Deb builds, use `debian-11` or similar.*\n\n### 2. Setup Python Environment (Inside Container)\n\nOnce inside the container:\n\n```bash\n# Detect python version (likely 3.10 or 3.14 depending on branch/container)\nPY_VER=$(python3 -c \"import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')\")\n\n# Create and activate venv\npython3 -m venv venv\nsource venv/bin/activate\n\n# Install build dependencies\npip install --upgrade pip setuptools\npip install -r requirements/static/ci/py${PY_VER}/tools.lock\npip install -e .\n```\n\n### 3. Build Source Tarball\n\nCreate the source distribution:\n\n```bash\npython3 -m tools pkg source-tarball\n```\n\nThis will create `dist/salt-<version>.tar.gz`.\n\n### 4. Build Salt Onedir\n\nThe \"Onedir\" is an artifact containing Salt and all its dependencies (including Python itself) in a relocatable directory.\n\n```bash\n# Get the version from the tarball\nSALT_VERSION=$(ls dist/salt-*.tar.gz | sed 's/dist\\/salt-//;s/.tar.gz//')\nmkdir -p artifacts\n\n# Build the Onedir\n# Note: CI uses specific pinned versions for relenv and python.\n# Check .github/workflows/build-salt-onedir.yml for current versions.\npython3 -m tools pkg build salt-onedir \"dist/salt-${SALT_VERSION}.tar.gz\" \\\n    --platform linux \\\n    --package-name artifacts/salt \\\n    --relenv-version 0.22.4\n\n# Cleanup and Archive\npython3 -m tools pkg pre-archive-cleanup artifacts/salt\ntar -cJf \"artifacts/salt-${SALT_VERSION}-onedir-linux-x86_64.tar.xz\" -C artifacts salt\n```\n\n### 5. Build RPM Package\n\nUse the Onedir artifact to build the final RPM.\n\n```bash\npython3 -m tools pkg build rpm \\\n    --relenv-version=0.22.4 \\\n    --python-version=3.10.19 \\\n    --onedir=\"salt-${SALT_VERSION}-onedir-linux-x86_64.tar.xz\"\n```\n\nThe RPMs will be generated in `~/rpmbuild/RPMS/x86_64/`.\n\n### 6. Retrieve Artifacts\n\nBefore exiting the container, copy the RPMs to the mounted volume:\n\n```bash\nmkdir -p artifacts/rpm\ncp -r ~/rpmbuild/RPMS/x86_64/*.rpm artifacts/rpm/\n```\n\n## Building DEBs (Linux)\n\nThe process is similar to RPMs but uses the `deb` command and a Debian container.\n\n1.  Use `ghcr.io/saltstack/salt-ci-containers/testing:debian-11` (or newer).\n2.  Follow steps 2-4 above (Source Tarball & Onedir).\n3.  Run `python3 -m tools pkg build deb ...` instead of `rpm`.\n\n## Building Windows/macOS Packages\n\nWindows and macOS packages are typically built on their respective host OSs in CI, not in Docker containers. Refer to `.github/workflows/build-packages.yml` for the specific steps and requirements (signing certificates, etc.).\n",".github/copilot-instructions.md":"../agents/COPILOT.md","agents/docs/architecture.md":"# Salt Architecture\n\n## What is Salt?\n\nSalt is a Python-based configuration management system using a master-minion architecture:\n- **Master**: Central server that manages configuration and sends commands\n- **Minion**: Agent running on managed systems that executes commands\n- **Event Bus**: Real-time communication system between components\n- **Loader System**: Dynamic plugin system for extending functionality\n\n## Module Types\n\nSalt uses a loader system to discover and load different types of modules:\n\n### Execution Modules (`salt/modules/`)\nFunctions executed on minions (e.g., `pkg.install`, `file.read`). There are 264+ execution modules providing the CLI commands available via Salt.\n\n**Examples:**\n- `salt/modules/pkg.py` - Package management (virtual module)\n- `salt/modules/file.py` - File operations\n- `salt/modules/cmd.py` - Command execution\n- `salt/modules/systemd_service.py` - systemd service management\n\n### State Modules (`salt/states/`)\nDeclarative configuration management (e.g., `pkg.installed`, `file.managed`). There are 126+ state modules providing idempotent configuration.\n\n**Examples:**\n- `salt/states/pkg.py` - Package installation states\n- `salt/states/file.py` - File management states\n- `salt/states/service.py` - Service management states\n\n### Runner Modules (`salt/runners/`)\nMaster-side operations (e.g., `salt-run jobs.list_jobs`). Runners execute on the Salt master.\n\n### Wheel Modules (`salt/wheel/`)\nMaster configuration management (key management, config). Used for managing the Salt master itself.\n\n### Grain Modules (`salt/grains/`)\nStatic system information collection. Grains provide facts about the minion system (OS, CPU, memory, etc.).\n\n### Pillar Modules (`salt/pillar/`)\nDynamic configuration data backends. Pillars provide secure, targeted data to minions.\n\n### Beacon Modules (`salt/beacons/`)\nSystem monitoring triggers (watch for events). Beacons monitor system events and fire events on the event bus.\n\n### Engine Modules (`salt/engines/`)\nLong-running processes on master or minion. Engines provide persistent functionality.\n\n### Returner Modules (`salt/returners/`)\nStore or send command output to external systems (databases, message queues, etc.).\n\n### Renderer Modules (`salt/renderers/`)\nTemplate engines (Jinja, Mako, YAML, JSON). Renderers process state files.\n\n### Matcher Modules (`salt/matchers/`)\nTarget matching (glob, pcre, grain, pillar, compound). Matchers determine which minions execute commands.\n\n### Utility Modules (`salt/utils/`)\nShared utility functions (170+ modules). Utilities provide common functionality across Salt.\n\n## Directory Structure\n\n```\nsalt/\n├── master.py, minion.py, state.py  # Core daemon implementations\n├── loader/                         # Plugin loader system (LazyLoader)\n├── modules/                        # 264+ execution modules\n├── states/                         # 126+ state modules\n├── runners/, wheel/                # Master operations and config\n├── beacons/, engines/              # Monitoring triggers, long-running processes\n├── grains/, pillar/                # System info, config data backends\n├── renderers/, returners/          # Template engines, return data storage\n├── matchers/, transport/           # Targeting systems, communication layer\n├── utils/                          # 170+ utility modules\n└── ext/                            # Vendored third-party code\n\ntests/\n├── pytests/\n│   ├── unit/                       # Fast tests, no daemons/network\n│   ├── functional/                 # End-to-end, no external deps\n│   ├── integration/                # With daemons/network\n│   ├── scenarios/                  # Scenario-based testing\n│   └── pkg/                        # Package install/upgrade tests\n└── support/                        # Test helpers and fixtures\n```\n\n## Key Architectural Components\n\n### State Compiler\n\n`salt/state.py` compiles YAML/Jinja templates into execution chunks. Uses **NetworkX** for dependency graph management with requisites:\n\n- `require`: Must run after\n- `watch`: Run if watched state changes\n- `onchanges`: Run only if watched state changes\n- `onfail`: Run if watched state fails\n- `listen`: Listen for notifications\n- `prereq`: Run before (with test mode check)\n\nAll state operations are idempotent and support test mode (`test=True`).\n\n### Transport Layer\n\n`salt/transport/` handles communication between master and minions:\n\n- **ZeroMQ** (default): High-performance messaging\n- **TCP**: Alternative transport\n- **WebSocket**: For browser-based access\n\n### Security\n\n- **PKI-based authentication** (`salt/crypt.py`)\n- **AES encryption** for all payloads\n- **Master key acceptance workflow** prevents unauthorized access\n- **External auth (eauth)** for Salt API access\n\n### Event Bus\n\n`salt/utils/event.py` provides ZeroMQ pub/sub with namespaced event tags for real-time communication between all components.\n\nEvents are namespaced with tags like:\n- `salt/job/<jid>/new` - New job started\n- `salt/job/<jid>/ret/<minion_id>` - Job return from minion\n- `salt/auth` - Authentication events\n- `salt/minion/<minion_id>/start` - Minion started\n\n### Loader System\n\nThe loader system (`salt/loader/`) dynamically discovers and loads modules at runtime. Key features:\n\n- **LazyLoader**: Loads modules on first access, not import\n- **Virtual names**: Modules can override their name via `__virtual__()`\n- **Dunder injection**: Injects `__salt__`, `__opts__`, `__grains__`, etc.\n- **Caching**: Modules are cached after first load\n\n## Async/Await Transition\n\nSalt is transitioning from **Tornado** coroutines to native **asyncio**:\n- Use native `async`/`await` syntax, not `@tornado.gen.coroutine`\n- Prefer asyncio patterns over Tornado patterns\n- Be aware of mixed async code during transition\n\n## Master-Minion Communication\n\n1. Minion authenticates with master via PKI\n2. Master accepts minion key\n3. Minion connects to master on port 4506 (request server)\n4. Master publishes commands on port 4505 (publish server)\n5. Minions subscribe to master's publish port\n6. Commands are encrypted with AES\n7. Results return via request server\n\n## File Server\n\nSalt includes a file server that serves files to minions:\n- `salt://` URLs reference files on the file server\n- Multiple backends: local, git, S3, HTTP, etc.\n- Files are cached on minions\n\nFor details on the Git backends and performance, see [GitFS Providers](gitfs-providers.md).\n\n## Targeting\n\nMinions can be targeted in multiple ways:\n- **Glob**: `*`, `web*.example.com`\n- **Grain**: `os:Ubuntu`, `kernel:Linux`\n- **Pillar**: `role:webserver`\n- **Compound**: `G@os:Ubuntu and webserver*`\n- **List**: `web1,web2,web3`\n- **PCRE**: Regular expressions\n\n## Performance Considerations\n\n### Lazy Loading\nModules only load on first access. Import-time side effects may not occur until first use of a module function.\n\n### Context Caching\nUse `__context__` to cache expensive operations within a Salt run (single execution).\n\n### Module Discovery\n- Module directories need `__init__.py`\n- Filename becomes namespace: `salt/modules/pkg.py` → `pkg.*`\n- Use `__virtualname__` for different name than filename\n- **Performance tip**: Filename should match virtualname when possible\n\n## Additional Resources\n\n### Documentation\n- Module development: `doc/topics/development/modules/developing.rst`\n- State writing guide: `doc/ref/states/writing.rst`\n- Contributing guidelines: `CONTRIBUTING.rst`\n\n### Example Modules\n- Simple execution module: `salt/modules/test.py`\n- Complex execution module: `salt/modules/file.py`\n- Package manager: `salt/modules/aptpkg.py`, `salt/modules/yumpkg.py`\n- Service manager: `salt/modules/systemd_service.py`\n- Simple state: `salt/states/pkg.py`\n- Complex state: `salt/states/file.py`\n","agents/docs/development-setup.md":"# Salt Development Environment Setup\n\nThis guide covers setting up your local development environment for Salt development.\n\n## Virtual Environment Requirements\n\nSalt development requires setting up **two** virtual environments:\n\n- **venv310 (Python 3.10)**: For running tests on 3006.x and 3007.x branches\n- **venv314 (Python 3.14)**: For running tests on master branch AND running pre-commit hooks\n\n## Prerequisites\n\n- Python 3.10 and Python 3.14 installed on your system\n- Git repository cloned\n- Internet connection for downloading dependencies\n\n## Setup venv310 (Python 3.10)\n\nThis environment is used for testing on 3006.x and 3007.x branches.\n\n```bash\n# Create virtual environment\npython3.10 -m venv venv310\n\n# Activate\nsource venv310/bin/activate\n\n# Upgrade pip\npip install --upgrade pip setuptools wheel\n\n# Install platform-specific dependencies (choose your OS)\n# Linux:\npip install -r requirements/static/pkg/py3.10/linux.lock\n\n# macOS:\npip install -r requirements/static/pkg/py3.10/darwin.lock\n\n# Windows:\npip install -r requirements/static/pkg/py3.10/windows.lock\n\n# Install pytest requirements\npip install -r requirements/pytest.txt\n\n# Install Salt in editable mode\npip install -e .\n\n# Install tools dependencies (for using tools/ commands)\npip install -r requirements/static/ci/py3.10/tools.lock\n\n# Install pre-commit and python-tools-scripts\npip install pre-commit python-tools-scripts\n\n# Deactivate\ndeactivate\n```\n\n## Setup venv314 (Python 3.14)\n\nThis environment is used for testing on master branch and running pre-commit hooks.\n\n```bash\n# Create virtual environment\npython3.14 -m venv venv314\n\n# Activate\nsource venv314/bin/activate\n\n# Upgrade pip\npip install --upgrade pip setuptools wheel\n\n# Install platform-specific dependencies (choose your OS)\n# Linux:\npip install -r requirements/static/pkg/py3.14/linux.lock\n\n# macOS:\npip install -r requirements/static/pkg/py3.14/darwin.lock\n\n# Windows:\npip install -r requirements/static/pkg/py3.14/windows.lock\n\n# Install pytest requirements\npip install -r requirements/pytest.txt\n\n# Install Salt in editable mode\npip install -e .\n\n# Install tools dependencies\npip install -r requirements/static/ci/py3.14/tools.lock\n\n# Install pre-commit and python-tools-scripts\npip install pre-commit python-tools-scripts\n\n# Install pre-commit hooks\npre-commit install\n\n# Deactivate\ndeactivate\n```\n\n## Environment Usage\n\n### venv310 is used for:\n- Running tests on 3006.x and 3007.x branches\n- Direct pytest execution on older branches\n- Tools commands (`python -m tools`)\n\n### venv314 is used for:\n- Running tests on master branch\n- Running pre-commit hooks\n- Code formatting and linting checks\n- Pre-commit validation before commits\n\n## Verify Installation\n\n```bash\n# Test Salt import (master / default dev)\n./venv314/bin/python -c \"import salt.version; print(salt.version.__version__)\"\n\n# Run a simple test\n./venv314/bin/pytest tests/pytests/unit/test_loader.py -v\n\n# Test tools (3006.x/3007.x workflow)\n./venv310/bin/python -m tools --help\n```\n\n## IMPORTANT: Using Virtual Environments\n\nWhen running tests or tools, always use the full path to the venv executable or activate the venv first. This ensures you're using the correct Python environment with all dependencies installed.\n\n**Good examples:**\n```bash\n./venv310/bin/pytest tests/pytests/unit/test_foo.py -v\n./venv314/bin/pre-commit run --files salt/loader/lazy.py\n./venv310/bin/python -m tools ts pytest run tests/pytests/unit/\n```\n\n**Alternatively, activate first:**\n```bash\nsource venv314/bin/activate\npytest tests/pytests/unit/test_foo.py -v\ndeactivate\n```\n\n## Troubleshooting\n\n### ModuleNotFoundError: No module named 'ptscripts'\n\nIf you see this error when running `python -m tools`, you need to install the tools dependencies:\n\n```bash\n./venv310/bin/pip install -r requirements/static/ci/py3.10/tools.lock\n./venv310/bin/pip install python-tools-scripts\n```\n\n### venv not found\n\nIf you get \"No such file or directory\" for venv paths, make sure you've created the virtual environment and are running commands from the repository root directory.\n\n### Permission errors\n\nOn some systems you may need to use `python3.10` and `python3.14` explicitly instead of just `python3`.\n\n### Pre-commit hooks not running\n\nMake sure you've run `pre-commit install` in the venv314 environment:\n\n```bash\nsource venv314/bin/activate\npre-commit install\ndeactivate\n```\n","agents/docs/testing.md":"# Salt Testing Guide\n\nSalt uses multiple test frameworks. All new code should include tests.\n\n## Test Directory Structure\n\n```\ntests/\n├── unit/               # Fast unit tests (no daemons, heavy mocking)\n│   ├── modules/        # Tests for salt/modules/\n│   ├── states/         # Tests for salt/states/\n│   └── utils/          # Tests for salt/utils/\n├── integration/        # Integration tests (with daemons)\n│   ├── modules/        # Tests for salt/modules/\n│   ├── states/         # Tests for salt/states/\n│   └── ...\n└── pytests/           # Pytest-based tests (newer style)\n    ├── unit/\n    ├── integration/\n    └── functional/\n```\n\n## Unit Test Templates\n\n### Legacy Style (TestCase)\n\n```python\n\"\"\"\nTests for salt.modules.mymodule\n\"\"\"\n\nimport pytest\n\nimport salt.modules.mymodule as mymodule\nfrom salt.exceptions import CommandExecutionError, SaltInvocationError\nfrom tests.support.mixins import LoaderModuleMockMixin\nfrom tests.support.mock import MagicMock, patch\nfrom tests.support.unit import TestCase\n\n\nclass MyModuleTestCase(TestCase, LoaderModuleMockMixin):\n    \"\"\"\n    Test cases for salt.modules.mymodule\n    \"\"\"\n\n    def setup_loader_modules(self):\n        \"\"\"\n        Setup loader modules - injects dunders\n        \"\"\"\n        return {\n            mymodule: {\n                \"__opts__\": {\"test\": False},\n                \"__grains__\": {\"os\": \"Linux\"},\n                \"__salt__\": {},\n            }\n        }\n\n    def test_simple_function(self):\n        \"\"\"\n        Test simple function\n        \"\"\"\n        result = mymodule.my_function(\"test\")\n        self.assertEqual(result, \"expected\")\n\n    def test_with_mock(self):\n        \"\"\"\n        Test with mocked subprocess call\n        \"\"\"\n        mock_cmd = MagicMock(return_value={\"retcode\": 0, \"stdout\": \"success\"})\n        with patch.dict(mymodule.__salt__, {\"cmd.run_all\": mock_cmd}):\n            result = mymodule.my_function(\"test\")\n            self.assertTrue(result)\n            mock_cmd.assert_called_once()\n\n    def test_error_handling(self):\n        \"\"\"\n        Test error conditions\n        \"\"\"\n        with self.assertRaises(SaltInvocationError):\n            mymodule.my_function(None)\n```\n\n### Pytest Style (Newer)\n\n```python\n\"\"\"\nTests for salt.modules.mymodule\n\"\"\"\n\nimport pytest\n\nimport salt.modules.mymodule as mymodule\nfrom salt.exceptions import SaltInvocationError\n\n\n@pytest.fixture\ndef configure_loader_modules():\n    \"\"\"\n    Setup module dunders\n    \"\"\"\n    return {\n        mymodule: {\n            \"__opts__\": {\"test\": False},\n            \"__grains__\": {\"os\": \"Linux\"},\n            \"__salt__\": {},\n        }\n    }\n\n\ndef test_simple_function():\n    \"\"\"\n    Test simple function\n    \"\"\"\n    result = mymodule.my_function(\"test\")\n    assert result == \"expected\"\n\n\ndef test_error_handling():\n    \"\"\"\n    Test error conditions\n    \"\"\"\n    with pytest.raises(SaltInvocationError):\n        mymodule.my_function(None)\n```\n\n## Mocking Patterns\n\n### Basic Mocking\n\n```python\nfrom tests.support.mock import MagicMock, patch, call\n\n# Mock a function call\nmock_func = MagicMock(return_value=\"result\")\nwith patch(\"salt.modules.mymodule.some_function\", mock_func):\n    result = mymodule.my_function()\n    mock_func.assert_called_once_with(\"expected_arg\")\n\n# Mock dunder dict\nwith patch.dict(mymodule.__salt__, {\"cmd.run\": MagicMock(return_value=\"output\")}):\n    result = mymodule.my_function()\n\n# Mock with side effects\nmock_func = MagicMock(side_effect=[\n    {\"retcode\": 1, \"stdout\": \"error\"},  # First call fails\n    {\"retcode\": 0, \"stdout\": \"success\"},  # Second call succeeds\n])\nwith patch.dict(mymodule.__salt__, {\"cmd.run_all\": mock_func}):\n    result = mymodule.my_function()\n```\n\n## Test Best Practices\n\n1. **One test per function behavior**: Test success cases, error cases, edge cases separately\n2. **Use descriptive test names**: `test_function_name_when_condition_then_expected_result`\n3. **Mock external dependencies**: File I/O, network calls, subprocess calls\n4. **Test error handling**: Verify exceptions are raised correctly\n5. **Test input validation**: Verify bad inputs are rejected\n6. **Keep tests fast**: Unit tests should run in milliseconds\n7. **Don't test implementation details**: Test the public API, not internal helpers\n\n## Running Tests\n\n### Using Nox (Recommended for CI Consistency)\n\n```bash\n# Run all tests\nnox -e test-3\n\n# Run specific test file\nnox -e test-3 -- tests/pytests/unit/test_loader.py\n\n# Run with pattern matching\nnox -e test-3 -- -k test_auth\n\n# Run specific test function\nnox -e test-3 -- tests/pytests/unit/test_loader.py::test_load_modules\n\n# Run last failed tests\nnox -e test-3 -- --lf\n\n# Run with specific Python version\nnox -e test-3.10\nnox -e test-3.14\n\n# Run with specific transport\nnox -e test-tcp-3\nnox -e test-zeromq-3\n\n# Run with coverage\nnox -e test-3 --coverage\nnox -e coverage-report\n```\n\n### Using Virtual Environment (Faster for Development)\n\nIf you have a local venv setup:\n\n```bash\n# Run tests directly with pytest (master branch: use venv312)\n./venv312/bin/pytest tests/pytests/unit/test_foo.py::test_bar -v\n\n# Run pre-commit checks on specific files\n./venv312/bin/pre-commit run --files salt/loader/lazy.py\n```\n\n## Container Testing (Reproduce CI Failures)\n\nTo reproduce CI failures locally using the same container environment:\n\n### 1. Download CI artifacts\n\n```bash\n./venv310/bin/python -m ptscripts ci download-artifacts --run-id <RUN-ID>\n```\n\n### 2. Create container\n\n```bash\n./venv310/bin/python -m ptscripts container create <IMAGE> --name <NAME>\ndocker start <NAME>\n```\n\n### 3. Setup container\n\n```bash\n# Decompress dependencies\ndocker exec <NAME> python3 -m nox -e decompress-dependencies -- linux x86_64\n\n# Create relenv toolchain symlink (Python 3.14+ only)\ndocker exec <NAME> bash -c \"mkdir -p /root/.local/relenv && ln -sf /root/.cache/relenv/toolchains /root/.local/relenv/toolchain\"\n```\n\n### 4. Run tests\n\n```bash\ndocker exec <NAME> python3 -m nox -e ci-test-onedir -- <TEST-PATH> --run-slow -x -v\n```\n\n### Example container images\n\n- `ghcr.io/saltstack/salt-ci-containers/testing:debian-11`\n- `ghcr.io/saltstack/salt-ci-containers/testing:ubuntu-22.04`\n- `ghcr.io/saltstack/salt-ci-containers/testing:rockylinux-9`\n\n## Linting and Formatting\n\n### Black (Code Formatting)\n\n```bash\n# Format all code\nblack .\n\n# Check without modifying\nblack --check .\n```\n\nBlack uses 88 character line length by default.\n\n### isort (Import Sorting)\n\n```bash\n# Sort all imports\nisort .\n\n# Check without modifying\nisort --check .\n```\n\nisort is configured with profile 3 and trailing commas in `pyproject.toml`.\n\n### Linting\n\n```bash\n# Lint both salt and tests\nnox -e lint\n\n# Lint Salt code only\nnox -e lint-salt\n\n# Lint tests only\nnox -e lint-tests\n```\n\n## Example Test Files\n\nFor reference, see:\n- Unit test example: `tests/unit/modules/test_systemd_service.py`\n- Complex mocking: `tests/unit/modules/test_cron.py`\n- Documentation: `doc/topics/tutorials/writing_tests.rst`\n","agents/docs/git-and-ci.md":"# Git Workflow and CI\n\n## Commit Guidelines\n\n### CRITICAL RULES\n\n1. **NO attribution lines** - **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n2. **Use imperative mood** - \"Fix bug\", not \"Fixed bug\" or \"Fixes bug\"\n3. **Reference issues** - Use `#NNNN` to reference GitHub issues\n4. **Small, focused commits** - One logical change per commit\n5. **Rebase before push** - Keep history linear and clean\n\n### Good Commit Message Example\n\n```\nFix loader cache invalidation on module reload\n\nWhen modules are reloaded, the loader cache was not properly\ninvalidated, causing stale references. Clear the cache dict\non reload to ensure fresh module imports.\n\nFixes #12345\n```\n\n### Bad Commit Message Example\n\n```\nFixed some bugs\n\nCo-Authored-By: Claude <noreply@anthropic.com>\n```\n\n**Why this is bad:**\n- Past tense instead of imperative mood\n- Vague description\n- Contains AI attribution (NEVER do this)\n\n## PR Workflow\n\n### Finding and Checking PRs\n\n```bash\n# Find PR by branch name\ngh pr list --repo saltstack/salt --head <BRANCH-NAME> --limit 5\n\n# Check PR status\ngh pr view <PR-NUMBER> --repo saltstack/salt\ngh pr checks <PR-NUMBER> --repo saltstack/salt\n\n# Watch CI run\ngh run watch <RUN-ID> --repo saltstack/salt\n```\n\n## Branch Strategy\n\n- **Main PR branch**: `master` (not `main`)\n- **Release branches**: `3006.x`, `3007.x`, etc.\n- **Merge-forward branches**: `merge/3007.x/master-YY-MM-DD`\n\n## CI Failure Reproduction\n\nWhen CI tests fail, you can reproduce the exact failure locally using containers.\n\n### Prerequisites\n\n- Docker installed and running\n- Virtual environment setup (see [development-setup.md](development-setup.md))\n- `ptscripts` installed (`pip install python-tools-scripts`)\n\n### Step 1: Download CI Artifacts\n\nFirst, get the CI artifacts from the failed run:\n\n```bash\n./venv310/bin/python -m ptscripts ci download-artifacts --run-id <RUN-ID>\n```\n\nThe run ID can be found in the GitHub Actions URL or via `gh run list`.\n\n### Step 2: Create Container\n\nCreate a container using the same image as CI:\n\n```bash\n./venv310/bin/python -m ptscripts container create <IMAGE> --name <NAME>\ndocker start <NAME>\n```\n\n**Example container images:**\n- `ghcr.io/saltstack/salt-ci-containers/testing:debian-11`\n- `ghcr.io/saltstack/salt-ci-containers/testing:ubuntu-22.04`\n- `ghcr.io/saltstack/salt-ci-containers/testing:ubuntu-24.04`\n- `ghcr.io/saltstack/salt-ci-containers/testing:rockylinux-8`\n- `ghcr.io/saltstack/salt-ci-containers/testing:rockylinux-9`\n- `ghcr.io/saltstack/salt-ci-containers/testing:fedora-40`\n- `ghcr.io/saltstack/salt-ci-containers/testing:amazonlinux-2`\n- `ghcr.io/saltstack/salt-ci-containers/testing:amazonlinux-2023`\n- `ghcr.io/saltstack/salt-ci-containers/testing:photonos-3`\n- `ghcr.io/saltstack/salt-ci-containers/testing:photonos-4`\n- `ghcr.io/saltstack/salt-ci-containers/testing:photonos-5`\n\n### Step 3: Setup Container\n\n```bash\n# Decompress dependencies\ndocker exec <NAME> python3 -m nox -e decompress-dependencies -- linux x86_64\n\n# Create relenv toolchain symlink (Python 3.14+ only)\ndocker exec <NAME> bash -c \"mkdir -p /root/.local/relenv && ln -sf /root/.cache/relenv/toolchains /root/.local/relenv/toolchain\"\n```\n\n### Step 4: Run Tests\n\n```bash\ndocker exec <NAME> python3 -m nox -e ci-test-onedir -- <TEST-PATH> --run-slow -x -v\n```\n\n**Examples:**\n```bash\n# Run specific test file\ndocker exec mycontainer python3 -m nox -e ci-test-onedir -- tests/pytests/unit/modules/test_pkg.py --run-slow -x -v\n\n# Run specific test function\ndocker exec mycontainer python3 -m nox -e ci-test-onedir -- tests/pytests/unit/modules/test_pkg.py::test_install --run-slow -x -v\n\n# Run with pattern matching\ndocker exec mycontainer python3 -m nox -e ci-test-onedir -- tests/pytests/unit/ -k test_systemd --run-slow -x -v\n```\n\n### Step 5: Debug in Container\n\nIf you need to debug interactively:\n\n```bash\n# Shell into container\ndocker exec -it <NAME> bash\n\n# Inside container, activate environment and run tests\ncd /salt\npython3 -m nox -e ci-test-onedir -- tests/pytests/unit/test_foo.py -x -v\n```\n\n### Step 6: Cleanup\n\n```bash\n# Stop and remove container\ndocker stop <NAME>\ndocker rm <NAME>\n\n# Clean artifacts\nrm -rf artifacts/ nox-*.zip nox.*.tar.*\n```\n\n## Container Filesystem Behavior\n\nWhen testing in containers, be aware:\n\n- Changes to `salt/` source are visible immediately (mounted volume)\n- Changes to Salt library may need artifact refresh:\n  ```bash\n  docker exec <NAME> cp /salt/salt/modules/foo.py \\\n      /salt/artifacts/salt/lib/python3.14/site-packages/salt/modules/\n  ```\n\n## Stale Artifacts\n\nAlways clean before downloading new CI artifacts:\n\n```bash\nrm -rf artifacts/ nox-*.zip nox.*.tar.*\n```\n\nOld artifacts can cause confusing test failures.\n\n## Common CI Issues\n\n### Tests Pass Locally But Fail in CI\n\nPossible causes:\n1. **Different Python version** - CI uses specific Python versions per branch\n2. **Platform differences** - CI runs on Linux, you might be on macOS/Windows\n3. **Missing dependencies** - CI containers have exact dependency versions\n4. **Timing issues** - CI environment may be slower/faster\n5. **Stale artifacts** - Clean and re-download\n\n### Container Setup Fails\n\nIf dependency decompression fails:\n1. Check you downloaded artifacts for correct run ID\n2. Ensure Docker has enough disk space\n3. Try removing old containers and images\n\n### Relenv Toolchain Issues\n\nFor Python 3.14+ containers, you must create the symlink:\n\n```bash\ndocker exec <NAME> bash -c \"mkdir -p /root/.local/relenv && ln -sf /root/.cache/relenv/toolchains /root/.local/relenv/toolchain\"\n```\n\nThis is a known path mismatch between relenv versions.\n\n## Using MCP Server for CI Workflows\n\nThe agents/mcp/salt_test MCP server provides tools for discovering CI failures:\n\n- `ci_pr_failures` - Get all failing tests from a PR\n- `ci_run_failures` - Get failures from specific CI run\n- `ci_failure_summary` - Get summary of recent failures\n\nSee [agents/mcp/salt_test/README.md](../mcp/salt_test/README.md) for details.\n\n## Best Practices\n\n1. **Always clean artifacts** before downloading new ones\n2. **Use exact CI container images** for reproduction\n3. **Note Python version** - use venv310 for 3006.x/3007.x, venv314 for master\n4. **Check run logs** in GitHub Actions for exact command that failed\n5. **Test locally first** before pushing to avoid CI churn\n6. **Keep commits small** for easier review and debugging\n","agents/docs/module-templates.md":"# Salt Module Templates\n\nThis document provides comprehensive templates for writing Salt modules.\n\n## Execution Module Template\n\nAll execution modules follow this basic structure:\n\n```python\n\"\"\"\nModule docstring explaining purpose\n\"\"\"\n\nimport logging\n\nimport salt.exceptions\nimport salt.utils.args\nimport salt.utils.data\nimport salt.utils.files\nimport salt.utils.path\nimport salt.utils.platform\n\n# Set up logging\nlog = logging.getLogger(__name__)\n\n# Module metadata\n__virtualname__ = \"mymodule\"  # Optional: override module name\n__proxyenabled__ = [\"*\"]  # Optional: enable for proxy minions\n__func_alias__ = {\n    \"list_\": \"list\"  # Optional: map function names to avoid Python keywords\n}\n\n\ndef __virtual__():\n    \"\"\"\n    Only load this module if requirements are met\n    Returns True, False, or (False, reason)\n    \"\"\"\n    if not salt.utils.path.which(\"mycommand\"):\n        return False, \"mycommand binary not found\"\n    return True\n\n\ndef my_function(name, param=None):\n    \"\"\"\n    Function description\n\n    name\n        Description of name parameter\n\n    param\n        Description of param parameter\n\n    CLI Example:\n\n    .. code-block:: bash\n\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    log.debug(\"Running my_function with name=%s\", name)\n\n    # Implementation\n    result = do_something(name, param)\n\n    return result\n```\n\n## State Module Template\n\nState modules provide idempotent, declarative configuration. They call execution modules to do the actual work.\n\n### State Return Structure\n\nEvery state function MUST return this exact structure:\n\n```python\nret = {\n    \"name\": name,        # The name parameter passed to the state\n    \"result\": result,    # True (success), False (failure), or None (test mode)\n    \"changes\": {},       # Dictionary of changes made\n    \"comment\": comment   # String or list describing what happened\n}\n```\n\n### Complete State Template\n\n```python\n\"\"\"\nState module docstring\n\"\"\"\n\nimport logging\n\nimport salt.exceptions\n\nlog = logging.getLogger(__name__)\n\n\ndef __virtual__():\n    \"\"\"\n    Only load if the corresponding execution module is available\n    \"\"\"\n    if \"mymod.do_thing\" in __salt__:\n        return True\n    return (False, \"mymod execution module not available\")\n\n\ndef managed(name, value=None, **kwargs):\n    \"\"\"\n    Ensure something is in the desired state\n\n    name\n        The resource identifier\n\n    value\n        The desired value\n\n    Example:\n\n    .. code-block:: yaml\n\n        /etc/myconfig:\n          mymod.managed:\n            - value: foo\n    \"\"\"\n    # Initialize return dict\n    ret = {\n        \"name\": name,\n        \"result\": False,\n        \"changes\": {},\n        \"comment\": \"\"\n    }\n\n    # 1. Validate input\n    if not name:\n        ret[\"comment\"] = \"Name is required\"\n        return ret\n\n    # 2. Check current state\n    current = __salt__[\"mymod.get_current\"](name)\n\n    # 3. Compare with desired state\n    if current == value:\n        ret[\"result\"] = True\n        ret[\"comment\"] = f\"{name} is already in the desired state\"\n        return ret\n\n    # 4. Handle test mode\n    if __opts__[\"test\"]:\n        ret[\"result\"] = None\n        ret[\"comment\"] = f\"{name} would be updated\"\n        ret[\"changes\"] = {\"old\": current, \"new\": value}\n        return ret\n\n    # 5. Make changes\n    try:\n        result = __salt__[\"mymod.set_value\"](name, value)\n    except Exception as exc:\n        ret[\"comment\"] = f\"Failed to update {name}: {exc}\"\n        return ret\n\n    # 6. Verify changes\n    new_value = __salt__[\"mymod.get_current\"](name)\n\n    # 7. Set result and changes\n    if new_value == value:\n        ret[\"result\"] = True\n        ret[\"changes\"] = {\"old\": current, \"new\": new_value}\n        ret[\"comment\"] = f\"{name} was updated\"\n    else:\n        ret[\"result\"] = False\n        ret[\"comment\"] = f\"Failed to update {name}\"\n\n    return ret\n```\n\n### State Flow Diagram\n\n```\n1. Validate Input → Invalid? Return with result=False\n                  ↓\n2. Check Current State\n                  ↓\n3. Already Correct? → Yes: Return with result=True, no changes\n                  ↓ No\n4. Test Mode? → Yes: Return with result=None, proposed changes\n                  ↓ No\n5. Make Changes\n                  ↓\n6. Verify Changes\n                  ↓\n7. Return Result (True/False) with changes dict\n```\n\n### State Result Values\n\n- **`result=True`**: State succeeded, system is in desired state\n- **`result=False`**: State failed, system is NOT in desired state\n- **`result=None`**: Test mode, changes would be made but weren't\n\n### Changes Dictionary\n\nThe `changes` dictionary should show what changed:\n\n```python\n# Simple change\nret[\"changes\"] = {\"old\": \"foo\", \"new\": \"bar\"}\n\n# Multiple changes\nret[\"changes\"] = {\n    \"user\": {\"old\": \"root\", \"new\": \"apache\"},\n    \"mode\": {\"old\": \"0644\", \"new\": \"0755\"},\n}\n\n# New resource created\nret[\"changes\"] = {\"created\": name}\n\n# Resource removed\nret[\"changes\"] = {\"removed\": name}\n```\n\n## The __virtual__ Function Patterns\n\nThe `__virtual__()` function determines whether a module should load on a particular system. It runs before any other module code and should be fast.\n\n### Return Values\n\n1. **`True`**: Load module with filename as name\n2. **`False`**: Don't load module\n3. **`(False, reason)`**: Don't load, with explanation\n4. **`\"string\"`**: Load module with this name (virtualname)\n5. **`(\"string\", reason)`**: Load with name, with explanation\n\n### Pattern 1: Simple Platform Check\n\n```python\ndef __virtual__():\n    \"\"\"\n    Only work on POSIX-like systems\n    \"\"\"\n    if salt.utils.platform.is_windows():\n        return (\n            False,\n            \"The file execution module cannot be loaded: only available on \"\n            \"non-Windows systems - use win_file instead.\",\n        )\n    return True\n```\n\n### Pattern 2: Virtual Name with OS Check\n\nUsed when multiple modules implement the same interface (e.g., `pkg` for different package managers).\n\n```python\n__virtualname__ = \"pkg\"\n\ndef __virtual__():\n    \"\"\"\n    Confirm this module is on a Debian-based system\n    \"\"\"\n    if __grains__.get(\"os_family\") == \"Debian\":\n        return __virtualname__\n    return False, \"The pkg module could not be loaded: unsupported OS family\"\n```\n\n### Pattern 3: Binary Availability Check\n\n```python\ndef __virtual__():\n    \"\"\"\n    Only load if systemctl is available\n    \"\"\"\n    if not salt.utils.path.which(\"systemctl\"):\n        return False, \"The systemd module could not be loaded: systemctl not found\"\n    return True\n```\n\n### Pattern 4: Complex Multi-Condition Check\n\n```python\n__virtualname__ = \"service\"\n\ndef __virtual__():\n    \"\"\"\n    Only work on systems that have been booted with systemd\n    \"\"\"\n    is_linux = __grains__.get(\"kernel\") == \"Linux\"\n    is_booted = salt.utils.systemd.booted(__context__)\n    is_offline = salt.utils.systemd.offline(__context__)\n\n    if is_linux and (is_booted or is_offline):\n        return __virtualname__\n\n    return (\n        False,\n        \"The systemd execution module failed to load: only available on Linux \"\n        \"systems which have been booted with systemd.\",\n    )\n```\n\n### Pattern 5: Check for Execution Module Availability\n\nUsed in state modules to verify required execution modules exist.\n\n```python\ndef __virtual__():\n    \"\"\"\n    Only make these states available if a pkg provider has been detected\n    \"\"\"\n    if \"pkg.install\" in __salt__:\n        return True\n    return (False, \"pkg module could not be loaded\")\n```\n\n### Best Practices for __virtual__\n\n- Keep `__virtual__()` fast - it runs on every module scan\n- Provide clear error messages explaining why the module didn't load\n- Use `salt.utils.platform` for platform detection\n- Check grains for OS-specific logic\n- Cache expensive checks in `__context__` if needed\n\n## Common Module Patterns\n\n### Decorators\n\nSalt provides decorators for common tasks:\n\n```python\nfrom salt.utils.decorators import depends, memoize\nfrom salt.utils.decorators.path import which\n\n# Ensure binary exists\n@which(\"systemctl\")\ndef restart_service(name):\n    \"\"\"Only available if systemctl is found\"\"\"\n    pass\n\n# Ensure Python module exists\n@depends(\"docker\")\ndef list_containers():\n    \"\"\"Only available if docker module is installed\"\"\"\n    pass\n\n# Memoize expensive operations\n@memoize\ndef get_system_info():\n    \"\"\"Cache result for lifetime of process\"\"\"\n    pass\n```\n\n### Function Aliases\n\nMap function names to avoid Python keywords:\n\n```python\n__func_alias__ = {\n    \"list_\": \"list\",      # my_module.list() → my_module.list_()\n    \"exec_\": \"exec\",      # my_module.exec() → my_module.exec_()\n}\n```\n\n### Cross-Calling Modules\n\n```python\ndef my_function():\n    \"\"\"Call other execution modules\"\"\"\n    # Get file contents\n    content = __salt__[\"file.read\"](\"/etc/hosts\")\n\n    # Run a command\n    output = __salt__[\"cmd.run\"](\"ls -la\")\n\n    # Install a package\n    result = __salt__[\"pkg.install\"](\"nginx\")\n\n    return result\n```\n\n### Argument Handling\n\n```python\nimport salt.utils.args\n\ndef my_function(*args, **kwargs):\n    \"\"\"Handle various argument formats\"\"\"\n    # Clean extra kwargs from state data\n    kwargs = salt.utils.args.clean_kwargs(**kwargs)\n\n    # Parse key=value arguments\n    kwargs.update(salt.utils.args.parse_input(args))\n\n    return kwargs\n```\n\n### Path Handling\n\n```python\nimport salt.utils.path\n\ndef my_function():\n    \"\"\"Handle paths safely\"\"\"\n    # Find binary in PATH\n    binary = salt.utils.path.which(\"nginx\")\n    if not binary:\n        return False\n\n    # Join paths correctly for OS\n    config_path = salt.utils.path.join(\"/etc\", \"nginx\", \"nginx.conf\")\n\n    return config_path\n```\n\n### File Handling\n\n**Always use `salt.utils.files.fopen()` instead of the bare `open()` builtin**\nwhen reading or writing files in Salt code (modules, states, utils, and\ntests alike). `saltpylint`'s `resource-leakage` check (`W8470`) fails CI on\nany bare `open()` call.\n\n```python\nimport salt.utils.files\n\ndef my_function(path):\n    \"\"\"Read/write files the Salt way\"\"\"\n    with salt.utils.files.fopen(path, \"r\") as fp_:\n        content = fp_.read()\n\n    with salt.utils.files.fopen(path, \"w\") as fp_:\n        fp_.write(content)\n\n    return content\n```\n\n```python\n# Bad - fails the resource-leakage lint check\nwith open(path, \"r\") as fp_:\n    content = fp_.read()\n```\n\n`fopen()` is a thin wrapper around `open()` that additionally normalizes\nline endings/encoding across platforms and ensures file handles are tracked\nand closed consistently. Use it for every `open()` call, including binary\nmode (`\"rb\"`/`\"wb\"`) and in unit tests -- the lint check applies there too.\n\n### Platform Detection\n\n```python\nimport salt.utils.platform\n\ndef my_function():\n    \"\"\"Handle platform differences\"\"\"\n    if salt.utils.platform.is_windows():\n        return \"C:\\\\Windows\"\n    elif salt.utils.platform.is_linux():\n        return \"/etc\"\n    elif salt.utils.platform.is_darwin():\n        return \"/Library\"\n\n    # Check specific OS family\n    if __grains__.get(\"os_family\") == \"Debian\":\n        return \"apt\"\n    elif __grains__.get(\"os_family\") == \"RedHat\":\n        return \"yum\"\n```\n\n### Context Caching Pattern\n\n```python\ndef _get_cached_value():\n    \"\"\"Safe context usage for standalone calls\"\"\"\n    try:\n        context = __context__\n    except NameError:\n        context = {}\n\n    if \"my_key\" not in context:\n        context[\"my_key\"] = expensive_operation()\n    return context.get(\"my_key\")\n```\n\n## Error Handling\n\nUse Salt's exception classes for consistent error handling:\n\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Command/operation failed\n    SaltInvocationError,      # Bad function arguments\n    MinionError,              # General minion error\n    CommandNotFoundError,     # Binary not found\n)\n\ndef my_function(path, required_param):\n    \"\"\"\n    Example with proper error handling\n    \"\"\"\n    # Validate required parameters\n    if not required_param:\n        raise SaltInvocationError(\"required_param is mandatory\")\n\n    # Validate parameter values\n    if not os.path.exists(path):\n        raise SaltInvocationError(f\"Path does not exist: {path}\")\n\n    # Check for required binary\n    binary = salt.utils.path.which(\"mytool\")\n    if not binary:\n        raise CommandNotFoundError(\"mytool binary not found in PATH\")\n\n    # Execute operation with error handling\n    try:\n        result = __salt__[\"cmd.run_all\"](f\"{binary} {path}\")\n    except Exception as exc:\n        raise CommandExecutionError(\n            f\"Failed to run mytool on {path}: {exc}\"\n        )\n\n    # Check command result\n    if result[\"retcode\"] != 0:\n        raise CommandExecutionError(\n            f\"mytool failed: {result['stderr']}\"\n        )\n\n    return result[\"stdout\"]\n```\n\n### When to Use Each Exception\n\n- **`SaltInvocationError`**: Invalid function arguments, missing required parameters\n- **`CommandExecutionError`**: Operation failed (file not found, command failed, etc.)\n- **`CommandNotFoundError`**: Required binary not in PATH\n- **`MinionError`**: General minion-side errors\n- **Standard exceptions**: Use Python's built-in exceptions when appropriate\n\n## Module Checklists\n\n### Execution Module Checklist\n\n- [ ] Proper imports (logging, salt.exceptions, salt.utils.*)\n- [ ] Logger configured: `log = logging.getLogger(__name__)`\n- [ ] `__virtual__()` function if platform/dependency specific\n- [ ] Docstrings on all functions with CLI examples\n- [ ] Input validation with proper exceptions\n- [ ] Error handling with CommandExecutionError/SaltInvocationError\n- [ ] Use `__context__` for caching expensive operations\n- [ ] Unit tests covering success and error cases\n\n### State Module Checklist\n\n- [ ] Returns correct dict structure (name, result, changes, comment)\n- [ ] Validates input parameters\n- [ ] Checks current state before making changes\n- [ ] Handles test mode (`__opts__[\"test\"]`)\n- [ ] Verifies changes after applying\n- [ ] Sets `result` correctly (True/False/None)\n- [ ] Populates `changes` dict with before/after\n- [ ] Provides clear `comment` explaining what happened\n\n## Reference Files\n\n- Simple example: `salt/modules/test.py`\n- Complex example: `salt/modules/file.py`\n- Platform-specific: `salt/modules/aptpkg.py`, `salt/modules/systemd_service.py`\n- State examples: `salt/states/file.py`, `salt/states/pkg.py`\n- Documentation: `doc/topics/development/modules/developing.rst`, `doc/ref/states/writing.rst`\n","agents/docs/troubleshooting.md":"# Subagent: troubleshooting\n\nPath: `agents/docs/troubleshooting.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/docs/troubleshooting.md)","agents/mcp/README.md":"# Subagent: readme\n\nPath: `agents/mcp/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/mcp/README.md)","agents/mcp/mcp-config.json":"# Mcp Skill\n\nPath: `agents/mcp/mcp-config.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/mcp/mcp-config.json)","agents/reports/silent_drop_audit_3008_to_master.md":"# Subagent: silent_drop_audit_3008_to_master\n\nPath: `agents/reports/silent_drop_audit_3008_to_master.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/reports/silent_drop_audit_3008_to_master.md)","agents/mcp/salt_test/README.md":"# Subagent: readme\n\nPath: `agents/mcp/salt_test/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/mcp/salt_test/README.md)","scripts/suse/yum/plugins/README.md":"# plugins Documentation\n\nPath: `scripts/suse/yum/plugins/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/scripts/suse/yum/plugins/README.md)","scripts/suse/zypper/plugins/commit/README.md":"# commit Documentation\n\nPath: `scripts/suse/zypper/plugins/commit/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/scripts/suse/zypper/plugins/commit/README.md)"},"files":{"agents/COPILOT.md":"# GitHub Copilot Instructions for Salt Development\n\n## Project Context\n\nThis is the Salt project - a Python-based configuration management and remote execution system using master-minion architecture with a dynamic loader system for plugins.\n\n## Coding Philosophy\n\n**CRITICAL**: Write code as a CPython core developer:\n- Deep understanding of Python internals, memory management, GC\n- Know performance implications of different implementations\n\n**Zen of Python**: Readability counts. Explicit > implicit. Simple > complex. Errors never pass silently.\n\n**TDD for Bug Fixes**: Write failing test → verify fail → fix bug → verify pass → run related tests\n\n---\n\n## Git Workflow\n\n**CRITICAL: NO attribution lines**\n- **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n- Use imperative mood: \"Fix bug\" not \"Fixed bug\"\n- Reference issues: `#NNNN`\n- Small, focused commits\n- Rebase before push\n\n---\n\n## Architecture\n\n**Salt components:**\n- **Master-Minion**: Central master controls distributed minions\n- **Event Bus**: Real-time communication\n- **Execution Modules** (`salt/modules/`): CLI commands (264+)\n- **State Modules** (`salt/states/`): Declarative configuration (126+)\n- **Utils** (`salt/utils/`): Shared utilities (170+)\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete details.**\n\n---\n\n## Code Patterns\n\n### Execution Module\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Return True to load, False or (False, reason) to skip\"\"\"\n    if not salt.utils.path.which(\"required_binary\"):\n        return False, \"required_binary not found\"\n    return True\n\ndef function_name(param1, param2=None):\n    \"\"\"\n    CLI Example::\n        salt '*' module.function_name value\n    \"\"\"\n    if not param1:\n        raise salt.exceptions.SaltInvocationError(\"param1 required\")\n    return __salt__[\"cmd.run\"](f\"command {param1}\")\n```\n\n### State Module Return\n```python\ndef managed(name, value=None):\n    ret = {\"name\": name, \"result\": False, \"changes\": {}, \"comment\": \"\"}\n\n    # Validate → Check current → If correct return → If test mode return None → Apply → Return\n    if not name:\n        ret[\"comment\"] = \"Name required\"\n        return ret\n\n    current = __salt__[\"module.get\"](name)\n    if current == value:\n        ret[\"result\"] = True\n        ret[\"comment\"] = \"Already correct\"\n        return ret\n\n    if __opts__[\"test\"]:\n        ret[\"result\"] = None\n        ret[\"changes\"] = {\"old\": current, \"new\": value}\n        ret[\"comment\"] = \"Would change\"\n        return ret\n\n    try:\n        __salt__[\"module.set\"](name, value)\n        ret[\"result\"] = True\n        ret[\"changes\"] = {\"old\": current, \"new\": value}\n        ret[\"comment\"] = \"Updated\"\n    except Exception as exc:\n        ret[\"comment\"] = str(exc)\n\n    return ret\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete templates.**\n\n---\n\n## Dunder Variables\n\nSalt injects these into module scope:\n\n- `__opts__`: Config - `__opts__[\"test\"]`, `__opts__.get(\"id\")`\n- `__grains__`: System data - `__grains__.get(\"os_family\")`\n- `__pillar__`: Secure data - `__pillar__.get(\"password\")`\n- `__context__`: Per-run cache for expensive operations\n- `__salt__`: Execution modules - `__salt__[\"pkg.install\"](\"nginx\")`\n- `__utils__`: Utilities - `__utils__[\"files.is_text\"](path)`\n- `__states__`: State modules (states only)\n\n**Context caching pattern:**\n```python\nif \"pkg_list\" not in __context__:\n    __context__[\"pkg_list\"] = expensive_fetch()\nreturn __context__[\"pkg_list\"]\n```\n\n---\n\n## Required Elements\n\n### Logging\n```python\nlog = logging.getLogger(__name__)\nlog.debug(\"Processing: %s\", filename)  # Good - lazy formatting\nlog.debug(f\"Processing: {filename}\")   # Bad - f-strings evaluated early\n```\n**Never log secrets.**\n\n### File Handling\n```python\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nApplies to modules, states, utils, and tests alike.\n\n### Docstrings\nEvery function requires description, parameters, and CLI example:\n```python\ndef my_function(name, value=None):\n    \"\"\"\n    Description of functionality\n\n    name\n        Description of name parameter\n\n    CLI Example::\n        salt '*' module.my_function foo value=bar\n    \"\"\"\n```\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Operation failures\n    SaltInvocationError,      # Invalid arguments\n    CommandNotFoundError,     # Missing binaries\n)\n\nif not required_param:\n    raise SaltInvocationError(\"required_param must be provided\")\n```\n\n### __virtual__ Function\nDeclare platform/dependency requirements:\n```python\n# Platform check\ndef __virtual__():\n    if salt.utils.platform.is_windows():\n        return False, \"Not available on Windows\"\n    return True\n\n# Virtual name (for interface modules)\n__virtualname__ = \"pkg\"\ndef __virtual__():\n    if __grains__.get(\"os_family\") == \"Debian\":\n        return __virtualname__\n    return False\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for all patterns.**\n\n---\n\n## Development Environment\n\n**Two environments required:**\n- **venv310**: Testing 3006.x/3007.x branches\n- **venv312**: Testing master branch + pre-commit\n\n```bash\n# venv310\npython3.10 -m venv venv310 && source venv310/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.10/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.10/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && deactivate\n\n# venv312\npython3.12 -m venv venv312 && source venv312/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.11/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.11/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && pre-commit install && deactivate\n```\n\n**Always use full paths:** `./venv312/bin/pytest` (master); `./venv310/bin/pytest` (3006.x/3007.x)\n\n**See [agents/docs/development-setup.md](agents/docs/development-setup.md) for complete setup.**\n\n---\n\n## Testing\n\n### Pytest Fixture\n```python\nimport pytest\nimport salt.modules.mymodule as mymodule\n\n@pytest.fixture\ndef configure_loader_modules():\n    return {\n        mymodule: {\n            \"__opts__\": {\"test\": False},\n            \"__grains__\": {\"os\": \"Linux\"},\n            \"__salt__\": {},\n        }\n    }\n\ndef test_function():\n    result = mymodule.my_function(\"test\")\n    assert result == \"expected\"\n```\n\n### Running Tests\n```bash\n# Nox\nnox -e test-3 -- tests/pytests/unit/test_loader.py\nnox -e test-3 -- --lf  # Last failed\n\n# Direct (faster)\n./venv312/bin/pytest tests/pytests/unit/test_foo.py -v\n```\n\n**See [agents/docs/testing.md](agents/docs/testing.md) for complete guide.**\n\n---\n\n## Common Patterns\n\n### Platform Detection\n```python\nsalt.utils.platform.is_windows()\nsalt.utils.platform.is_linux()\n__grains__.get(\"os_family\")  # \"Debian\", \"RedHat\", etc.\n```\n\n### Run Commands\n```python\noutput = __salt__[\"cmd.run\"](\"ls -la\")\nresult = __salt__[\"cmd.run_all\"](\"cmd\")  # {\"retcode\": 0, \"stdout\": \"...\", \"stderr\": \"...\"}\n```\n\n### File Operations\n```python\ncontent = __salt__[\"file.read\"](path)\n__salt__[\"file.write\"](path, content)\n__salt__[\"file.file_exists\"](path)\n```\n\n---\n\n## Checklists\n\n### Execution Module\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform-specific\n- [ ] Input validation (`SaltInvocationError`)\n- [ ] Error handling (`CommandExecutionError`)\n- [ ] Context caching for expensive ops\n- [ ] Unit tests\n\n### State Module\n- [ ] Returns: name, result (True/False/None), changes, comment\n- [ ] result=None for test mode\n- [ ] Check current state before modifying\n- [ ] Populate changes dict with old/new\n\n---\n\n## Documentation\n\n**Essential guides in `agents/docs/`:**\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - venv310/venv312 setup, dependencies\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete architecture, module types, loader\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates, all patterns\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test templates, mocking, running tests\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit guidelines, PR workflow, CI reproduction\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Common issues and solutions\n\n**Key example files:** `salt/modules/test.py`, `salt/modules/file.py`, `salt/states/pkg.py`\n\n---\n\n## Key Principles\n\n- **Idempotency**: Safe to run multiple times\n- **Testing**: Every function needs tests\n- **Documentation**: Docstrings with CLI examples\n- **Error Handling**: Use Salt exception classes\n- **Performance**: Cache in `__context__`\n- **Security**: Validate inputs, never log secrets\n","agents/CURSOR.md":"# Salt Development Rules for Cursor\n\nYou are working on Salt, a Python-based configuration management and remote execution system. Follow these rules when writing or modifying Salt code.\n\n## Critical Rules\n\n**CODING PHILOSOPHY:**\n- Write as a CPython core developer - know internals, memory management, GC, performance\n- Zen of Python: Readability counts. Explicit > implicit. Simple > complex\n- TDD for bugs: Write failing test → verify fail → fix → verify pass → run related tests\n\n**GIT WORKFLOW:**\n- **NEVER** add \"Generated with Claude Code\" or \"Co-Authored-By: Claude\" to commits\n- Use imperative mood: \"Fix bug\" not \"Fixed bug\"\n- Reference issues: `#NNNN`\n- Small, focused commits\n- Rebase before push\n\n**LINTING:**\n- Black: 88 char lines (`black .`)\n- isort: Profile 3, trailing commas (`isort .`)\n- Lint: `nox -e lint-salt`\n\n---\n\n## Architecture Quick Reference\n\n**Salt Components:**\n- Master-Minion architecture for distributed configuration management\n- Event-driven communication via event bus\n- Execution modules: CLI commands (`salt/modules/`) - 264+\n- State modules: Declarative config (`salt/states/`) - 126+\n- Utils: Shared functions (`salt/utils/`) - 170+\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete architecture.**\n\n---\n\n## Module Structure\n\n### Execution Module Skeleton\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Returns True/False/(False, reason)\"\"\"\n    if not salt.utils.path.which(\"required_cmd\"):\n        return False, \"required_cmd not found\"\n    return True\n\ndef my_function(name, param=None):\n    \"\"\"\n    CLI Example::\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    if not name:\n        raise salt.exceptions.SaltInvocationError(\"name required\")\n    return __salt__[\"cmd.run\"](f\"command {name}\")\n```\n\n### State Module Return Structure\n```python\nret = {\n    \"name\": name,        # Resource name\n    \"result\": False,     # True/False/None (test mode)\n    \"changes\": {},       # {\"old\": ..., \"new\": ...}\n    \"comment\": \"\"        # What happened\n}\n```\n\n**State flow:** Validate → Check current → If correct return → If test mode return None → Make changes → Return result\n\n**Complete templates: [agents/docs/module-templates.md](agents/docs/module-templates.md)**\n\n---\n\n## Dunder Dictionaries\n\n- `__opts__`: Config (`__opts__[\"test\"]`, `__opts__.get(\"id\")`)\n- `__grains__`: System info (`__grains__.get(\"os_family\")`)\n- `__pillar__`: Secure data (`__pillar__.get(\"password\")`)\n- `__context__`: Per-run cache - use for expensive operations\n- `__salt__`: Execution modules (`__salt__[\"pkg.install\"](\"nginx\")`)\n- `__utils__`: Utilities (`__utils__[\"files.is_text\"](path)`)\n- `__states__`: State modules (states only)\n\n**Context cache pattern:**\n```python\nif \"cache_key\" not in __context__:\n    __context__[\"cache_key\"] = expensive_operation()\nreturn __context__[\"cache_key\"]\n```\n\n---\n\n## Common Patterns\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Operation failed\n    SaltInvocationError,      # Bad arguments\n    CommandNotFoundError,     # Binary not found\n)\n```\n\n### Logging\n```python\nlog = logging.getLogger(__name__)\nlog.debug(\"Processing %s\", filename)  # Good - lazy formatting\nlog.debug(f\"Processing {filename}\")   # Bad - f-strings evaluated early\n```\n**Never log secrets.**\n\n### File Handling\n```python\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nApplies to modules, states, utils, and tests alike.\n\n### Platform Detection\n```python\nsalt.utils.platform.is_windows()\nsalt.utils.platform.is_linux()\n__grains__.get(\"os_family\")  # \"Debian\", \"RedHat\", etc.\n```\n\n### Decorators\n```python\nfrom salt.utils.decorators.path import which\nfrom salt.utils.decorators import depends, memoize\n\n@which(\"systemctl\")  # Require binary\n@depends(\"docker\")   # Require Python module\n@memoize            # Cache forever\n```\n\n---\n\n## Development Environment\n\n**Two environments required:**\n- **venv310**: Testing 3006.x/3007.x branches\n- **venv312**: Testing master branch + pre-commit\n\n```bash\n# venv310\npython3.10 -m venv venv310 && source venv310/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.10/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.10/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && deactivate\n\n# venv312\npython3.12 -m venv venv312 && source venv312/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.11/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.11/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && pre-commit install && deactivate\n```\n\n**Always use full paths:** `./venv312/bin/python`, `./venv312/bin/pytest` (master); `./venv310/...` for 3006.x/3007.x\n\n**Complete setup: [agents/docs/development-setup.md](agents/docs/development-setup.md)**\n\n---\n\n## Testing\n\n### Pytest Structure\n```python\nimport pytest\nimport salt.modules.mymodule as mymodule\n\n@pytest.fixture\ndef configure_loader_modules():\n    return {\n        mymodule: {\n            \"__opts__\": {\"test\": False},\n            \"__grains__\": {\"os\": \"Linux\"},\n            \"__salt__\": {},\n        }\n    }\n\ndef test_function():\n    result = mymodule.my_function(\"test\")\n    assert result == \"expected\"\n```\n\n### Running Tests\n```bash\n# Nox\nnox -e test-3 -- tests/pytests/unit/test_loader.py\nnox -e test-3 -- --lf  # Last failed\n\n# Direct (faster)\n./venv312/bin/pytest tests/pytests/unit/test_foo.py -v\n```\n\n**Complete guide: [agents/docs/testing.md](agents/docs/testing.md)**\n\n---\n\n## Code Checklist\n\n### Execution Module\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform-specific\n- [ ] Input validation (`SaltInvocationError`)\n- [ ] Error handling (`CommandExecutionError`)\n- [ ] Context caching for expensive ops\n- [ ] Unit tests\n\n### State Module\n- [ ] Returns: name, result (True/False/None), changes, comment\n- [ ] result=None for test mode\n- [ ] Check current state before modifying\n- [ ] Populate changes dict with old/new\n\n---\n\n## Quick Reference\n\n### Common Imports\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.args\nimport salt.utils.platform\nimport salt.utils.path\n\nlog = logging.getLogger(__name__)\n```\n\n### Run Commands\n```python\noutput = __salt__[\"cmd.run\"](\"ls -la\")\nresult = __salt__[\"cmd.run_all\"](\"cmd\")  # {\"retcode\": 0, \"stdout\": \"...\", \"stderr\": \"...\"}\n```\n\n### File Operations\n```python\ncontent = __salt__[\"file.read\"](path)\n__salt__[\"file.write\"](path, content)\nexists = __salt__[\"file.file_exists\"](path)\n```\n\n---\n\n## Documentation Links\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - venv setup, verification\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete architecture, all module types\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates, patterns\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test templates, mocking, container testing\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit rules, PR workflow, CI reproduction\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Common issues and solutions\n\n**Key example files:** `salt/modules/test.py`, `salt/modules/file.py`, `salt/states/pkg.py`\n\n---\n\n## Key Principles\n\n- **Idempotency**: Safe to run multiple times\n- **Testing**: Every function needs tests\n- **Documentation**: Docstrings with CLI examples\n- **Error Handling**: Use Salt exceptions\n- **Performance**: Cache in `__context__`\n- **Security**: Validate inputs, never log secrets\n","agents/README.md":"# AI Agent Instructions for Salt Development\n\nThis directory contains instruction files for various AI coding assistants to help them better understand and work with the Salt codebase.\n\n## Purpose\n\nThese files provide AI agents with:\n- Salt's architecture and design patterns\n- Coding conventions and best practices\n- Testing guidelines and requirements\n- Common pitfalls to avoid\n\n## Available Instructions\n\n- **CLAUDE.md** - Instructions for Anthropic's Claude (Claude Code, Claude.ai, etc.)\n- **GEMINI.md** - Instructions for Google's Gemini\n- **CURSOR.md** - Instructions for Cursor IDE\n- **COPILOT.md** - Instructions for GitHub Copilot\n\nEach instruction file provides a quick reference and links to detailed documentation (see below).\n\n## Detailed Documentation\n\nThe `docs/` directory contains comprehensive guides that are referenced by all agent instruction files. This provides a single source of truth for detailed information:\n\n- **[development-setup.md](docs/development-setup.md)** - Complete virtual environment setup\n  - venv310 and venv312 setup instructions\n  - Platform-specific dependencies\n  - Installation verification steps\n  - Common troubleshooting\n\n- **[architecture.md](docs/architecture.md)** - Salt architecture deep dive\n  - Master-minion architecture\n  - All module types (execution, state, grains, pillar, beacon, engine, etc.)\n  - Directory structure\n  - Key components (state compiler, transport layer, event bus, loader system)\n\n- **[module-templates.md](docs/module-templates.md)** - Complete code templates\n  - Full execution module template with all patterns\n  - Full state module template with flow diagram\n  - All `__virtual__()` patterns (5 variations)\n  - Error handling examples\n  - Common decorators and utilities\n\n- **[testing.md](docs/testing.md)** - Comprehensive testing guide\n  - Test directory structure\n  - Unit test templates (legacy and pytest styles)\n  - Mocking patterns with examples\n  - Running tests (Nox, venv, container)\n  - Container testing for CI reproduction\n  - Linting and formatting\n\n- **[git-and-ci.md](docs/git-and-ci.md)** - Git workflow and CI\n  - Commit guidelines (**NO AI attribution!**)\n  - PR workflow with gh CLI commands\n  - Branch strategy\n  - CI failure reproduction workflow\n  - Container setup and debugging\n\n- **[package-building.md](docs/package-building.md)** - Package Building Guide\n  - Building RPMs locally using CI containers\n  - Building DEBs locally\n  - Creating source tarballs and onedir artifacts\n\n- **[troubleshooting.md](docs/troubleshooting.md)** - Common issues and solutions\n  - Import order issues\n  - Module discovery problems\n  - ZeroMQ issues\n  - Python 3.11+ compatibility gotchas\n  - Container filesystem behavior\n  - Lazy loading behavior\n  - Common error messages and fixes\n\n## How to Use\n\n**Recommended approach:** Create a symlink from the root directory to the agent file you want to use.\n\n### Symlink (Recommended)\n\nCreating a symlink keeps your local setup in sync with updates to the canonical instructions:\n\n```bash\n# From the root of the Salt repository\nln -s agents/CLAUDE.md CLAUDE.md\n# or\nln -s agents/CURSOR.md .cursorrules\n# or\nln -s agents/GEMINI.md GEMINI.md\n# or\nln -s agents/COPILOT.md COPILOT.md\n```\n\n**Why symlink?**\n- Automatically receives updates when canonical files are updated\n- Links to `agents/docs/` work correctly\n- No maintenance required\n\n### Alternative: IDE-Specific Configuration\n\nSome tools may allow you to specify a custom path to instruction files in their settings. Consult your tool's documentation.\n\n**Note:** If you need to customize the instructions, it's better to propose changes to the canonical files in `agents/` so everyone benefits, rather than maintaining a personal copy.\n\n## MCP Servers\n\nThis directory also contains Model Context Protocol (MCP) servers that expose Salt development tools to AI agents:\n\n- **mcp/salt_test/** - Testing tools server\n  - Direct pytest execution (quick local testing)\n  - CI failure discovery (analyze PR failures)\n  - Container testing (reproduce CI failures)\n\nSee [mcp/README.md](mcp/README.md) for setup and usage instructions.\n\n### Quick Setup\n\n1. Install MCP SDK: `pip install mcp`\n2. Configure in `~/.config/claude-code/mcp_config.json` (see `mcp/mcp-config.json` for template)\n3. Set GitHub token for CI features: `export GITHUB_TOKEN=\"your_token\"`\n\nWith MCP configured, you can ask Claude:\n- \"What tests are failing in PR #68562?\"\n- \"Run the loader tests locally\"\n- \"Reproduce the failure from PR #68562 on debian-11\"\n\n## .gitignore\n\nThe root-level instruction files (CLAUDE.md, GEMINI.md, etc.) are intentionally ignored by git to prevent personal configurations from being committed. Only the canonical versions in this directory are tracked.\n\n## Contributing\n\nIf you discover better patterns, common issues, or ways to improve these instructions:\n\n1. Edit the appropriate file in this `agents/` directory\n2. Submit a pull request with your improvements\n3. Include a brief explanation of what the change helps agents understand better\n\n## File Naming Conventions\n\n- **CLAUDE.md** - For Claude-based tools\n- **GEMINI.md** - For Gemini-based tools\n- **CURSOR.md** - For Cursor IDE (may also be named `.cursorrules`)\n- **COPILOT.md** - For GitHub Copilot (may also be named `.github/copilot-instructions.md`)\n\nNote: Some tools may use different filenames. Check your tool's documentation for the correct filename and location.\n","agents/GEMINI.md":"# Salt Development Instructions for Gemini\n\nYou are assisting with development of Salt, a powerful infrastructure automation and configuration management system written in Python. This guide provides essential quick reference and links to detailed documentation.\n\n## Coding Philosophy\n\n**CRITICAL**: Write code as a CPython core developer:\n- Understand Python internals, memory management, GC\n- Know performance implications\n- Be aware of implementation details\n\n**Zen of Python**: Readability counts. Explicit > implicit. Simple > complex. Errors never pass silently.\n\n**TDD for Bug Fixes**:\n1. Write failing test first → 2. Verify it fails → 3. Fix bug (minimal changes) → 4. Verify test passes → 5. Run related tests\n\n---\n\n## Architecture Quick Reference\n\nSalt is a Python-based configuration management system:\n- **Master-Minion Architecture**: Central master controls distributed minions\n- **Event-Driven**: Real-time communication via event bus\n- **Plugin-Based**: Extensible through loader system\n\n**Key Module Types:**\n- **Execution Modules** (`salt/modules/`): CLI commands on minions (264+)\n- **State Modules** (`salt/states/`): Declarative configuration (126+)\n- **Utils** (`salt/utils/`): Shared utility functions (170+)\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete architecture.**\n\n---\n\n## Dunder Dictionaries\n\nSalt injects special variables into module scope:\n\n**Always Available:**\n- `__opts__` - Config options: `__opts__.get(\"id\")`, `__opts__[\"test\"]`\n- `__grains__` - System data: `__grains__.get(\"os_family\")`\n- `__pillar__` - Secure data: `__pillar__.get(\"password\")`\n- `__context__` - Per-run cache for expensive operations\n\n**Module-Specific:**\n- `__salt__` - Other execution modules: `__salt__[\"pkg.install\"](\"nginx\")`\n- `__utils__` - Utility functions: `__utils__[\"files.is_text\"](path)`\n- `__states__` - State modules (in state modules only)\n\n**Context Caching Pattern:**\n```python\nif \"pkg_list\" not in __context__:\n    __context__[\"pkg_list\"] = expensive_fetch()  # Once per run\nreturn __context__[\"pkg_list\"]\n```\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for loader system details.**\n\n---\n\n## Execution Module Structure\n\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Returns True to load, False or (False, reason) to skip\"\"\"\n    if not salt.utils.path.which(\"required_binary\"):\n        return False, \"required_binary not found\"\n    return True\n\ndef my_function(name, param=None):\n    \"\"\"\n    CLI Example:\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    if not name:\n        raise salt.exceptions.SaltInvocationError(\"name required\")\n    return __salt__[\"cmd.run\"](f\"command {name}\")\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete templates.**\n\n---\n\n## State Module Structure\n\n**Required Return:**\n```python\n{\n    \"name\": name,              # Resource identifier\n    \"result\": True/False/None, # Success/Failure/Test mode\n    \"changes\": {},             # {\"old\": ..., \"new\": ...}\n    \"comment\": \"\"              # What happened\n}\n```\n\n**State Flow:**\n1. Validate input → 2. Check current state → 3. If correct: return success → 4. If test mode: return `result=None` with proposed changes → 5. Make changes → 6. Return result\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete state templates.**\n\n---\n\n## Common Patterns\n\n### Logging\n```python\nlog = logging.getLogger(__name__)\n# Use lazy formatting (NOT f-strings)\nlog.debug(\"Processing: %s\", filename)  # Good\nlog.debug(f\"Processing: {filename}\")   # Bad\n```\n**Never log sensitive data.**\n\n### File Handling\n```python\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nApplies to modules, states, utils, and tests alike.\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,   # Operation failed\n    SaltInvocationError,     # Invalid arguments\n    CommandNotFoundError,    # Binary not found\n)\n```\n\n### Platform Detection\n```python\nimport salt.utils.platform\nif salt.utils.platform.is_windows():\n    # Windows-specific\nif __grains__.get(\"os_family\") == \"Debian\":\n    # Debian-based\n```\n\n---\n\n## Git Workflow\n\n### CRITICAL RULES\n\n1. **NO attribution lines** - **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n2. **Imperative mood** - \"Fix bug\", not \"Fixed bug\"\n3. **Reference issues** - Use `#NNNN`\n4. **Small commits** - One logical change per commit\n5. **Rebase before push** - Linear history\n\n**Good Commit:**\n```\nFix loader cache invalidation on module reload\n\nClear cache dict on reload to ensure fresh module imports.\n\nFixes #12345\n```\n\n**Bad (NEVER):**\n```\nFixed bugs\nCo-Authored-By: Claude <noreply@anthropic.com>\n```\n\n**See [agents/docs/git-and-ci.md](agents/docs/git-and-ci.md) for complete workflow.**\n\n---\n\n## Checklists\n\n### Execution Module\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform-specific\n- [ ] Input validation (`SaltInvocationError`)\n- [ ] Error handling (`CommandExecutionError`)\n- [ ] Context caching for expensive ops\n- [ ] Unit tests\n\n### State Module\n- [ ] Returns: name, result, changes, comment\n- [ ] `result=None` for test mode\n- [ ] Check current state first\n- [ ] Populate changes dict\n- [ ] Clear comments\n\n---\n\n## Detailed Documentation\n\n**Essential guides in `agents/docs/`:**\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - venv310/venv312 setup, dependencies, verification\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete architecture, module types, loader, event bus\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates, `__virtual__()` patterns, decorators\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test structure, templates, mocking, running tests\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit guidelines, PR workflow, CI reproduction\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Import issues, ZeroMQ, Python 3.11+, lazy loading\n\n---\n\n## Key Example Files\n\n- Simple: `salt/modules/test.py`\n- Complex: `salt/modules/file.py`\n- Package: `salt/modules/aptpkg.py`\n- Service: `salt/modules/systemd_service.py`\n- States: `salt/states/file.py`, `salt/states/pkg.py`\n\n---\n\n## Summary\n\n1. **Follow patterns** - Use existing modules as templates\n2. **Use dunders** - `__salt__`, `__opts__`, `__grains__`, `__context__`\n3. **Implement `__virtual__()`** - Declare dependencies\n4. **Handle errors** - Use Salt exception classes\n5. **Write tests** - Every function needs tests\n6. **Document** - Docstrings with CLI examples required\n7. **Think idempotent** - Safe to repeat\n8. **Cache wisely** - Use `__context__`\n\n**When in doubt, check existing modules and refer to detailed docs.**\n\n**Remember: NEVER add AI attribution lines to commits!**\n","agents/CLAUDE.md":"# Salt Development Instructions for Claude\n\nYou are assisting with development of Salt, a powerful infrastructure automation and configuration management system. This document provides essential quick reference information and links to detailed documentation.\n\n## Table of Contents\n\n1. [Coding Philosophy](#coding-philosophy)\n2. [Architecture Quick Reference](#architecture-quick-reference)\n3. [Dunder Dictionaries](#dunder-dictionaries)\n4. [Module Basics](#module-basics)\n5. [State Basics](#state-basics)\n6. [Git Workflow](#git-workflow)\n7. [Checklists](#checklists)\n8. [Detailed Documentation](#detailed-documentation)\n\n---\n\n## Coding Philosophy\n\n**CRITICAL**: Approach Salt development as a CPython core developer:\n- Know memory management and garbage collection implications\n- Understand performance trade-offs between implementations\n- Be aware of CPython implementation details and GIL impact\n\n**TDD for Bug Fixes**: Always write a failing test first, verify it fails, fix the bug, verify it passes, then run related tests.\n\n**Zen of Python**: Readability counts. Explicit is better than implicit. Simple is better than complex. Errors should never pass silently.\n\n---\n\n## Architecture Quick Reference\n\nSalt is a Python-based configuration management system using master-minion architecture with an event-driven plugin system.\n\n**Key Module Types:**\n- **Execution Modules** (`salt/modules/`) - CLI commands on minions (264+)\n- **State Modules** (`salt/states/`) - Declarative configuration (126+)\n- **Utils** (`salt/utils/`) - Shared utility functions (170+)\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete architecture details.**\n\n---\n\n## Dunder Dictionaries\n\nSalt injects special dictionaries into module scope via the loader:\n\n**Always Available:**\n- `__opts__` - Configuration options (test mode, minion ID, etc.)\n- `__grains__` - System information (OS, platform, custom data)\n- `__pillar__` - Secure data for this minion\n- `__context__` - Per-run cache (persists during Salt execution)\n\n**Module-Specific:**\n- `__salt__` - Access other execution modules\n- `__utils__` - Utility functions\n- `__states__` - Call other states (in state modules only)\n\n**Example: Context Caching**\n```python\nif \"cache_key\" not in __context__:\n    __context__[\"cache_key\"] = expensive_operation()  # Only once per run\nreturn __context__[\"cache_key\"]\n```\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for detailed loader system documentation.**\n\n---\n\n## Module Basics\n\n**Execution Module Structure:**\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Return True to load, False or (False, reason) to skip\"\"\"\n    if salt.utils.platform.is_windows():\n        return False, \"Not available on Windows\"\n    return True\n\ndef my_function(name, param=None):\n    \"\"\"\n    CLI Example::\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    if not name:\n        raise salt.exceptions.SaltInvocationError(\"name is required\")\n    return __salt__[\"cmd.run\"](f\"command {name}\")\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete templates and patterns.**\n\n---\n\n## State Basics\n\n**State Return Structure (REQUIRED):**\n```python\nret = {\n    \"name\": name,        # Name parameter\n    \"result\": False,     # True (success), False (fail), None (test mode)\n    \"changes\": {},       # Dict of changes: {\"old\": ..., \"new\": ...}\n    \"comment\": \"\"        # What happened\n}\n```\n\n**State Flow:**\n1. Validate input\n2. Check current state\n3. If already correct: return success\n4. If test mode (`__opts__[\"test\"]`): return `result=None` with proposed changes\n5. Make changes and return result\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete state templates.**\n\n---\n\n## Git Workflow\n\n### CRITICAL RULES\n\n1. **NO attribution lines** - **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n2. **Use imperative mood** - \"Fix bug\", not \"Fixed bug\" or \"Fixes bug\"\n3. **Reference issues** - Use `#NNNN` to reference GitHub issues\n4. **Small, focused commits** - One logical change per commit\n5. **Rebase before push** - Keep history linear and clean\n\n**Good Commit:**\n```\nFix loader cache invalidation on module reload\n\nWhen modules are reloaded, the loader cache was not properly\ninvalidated, causing stale references. Clear the cache dict\non reload to ensure fresh module imports.\n\nFixes #12345\n```\n\n**Bad Commit (NEVER DO THIS):**\n```\nFixed some bugs\n\nCo-Authored-By: Claude <noreply@anthropic.com>\n```\n\n**See [agents/docs/git-and-ci.md](agents/docs/git-and-ci.md) for complete git workflow and CI reproduction.**\n\n---\n\n## Checklists\n\n### Execution Module Checklist\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform/dependency specific\n- [ ] Input validation with `SaltInvocationError`\n- [ ] Error handling with `CommandExecutionError`\n- [ ] Context caching for expensive operations\n- [ ] `salt.utils.files.fopen()` for any file I/O, never bare `open()`\n- [ ] Unit tests\n\n### State Module Checklist\n- [ ] Returns dict with: name, result, changes, comment\n- [ ] `result=None` for test mode (`__opts__[\"test\"]`)\n- [ ] Check current state before changing\n- [ ] Populate changes dict with old/new values\n- [ ] Clear comment explaining outcome\n\n### Common Imports\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.args\nimport salt.utils.platform\nimport salt.utils.path\n\nlog = logging.getLogger(__name__)\n```\n\n### Logging\n```python\n# Use lazy formatting (NOT f-strings)\nlog.debug(\"Processing file: %s\", filename)  # Good\nlog.debug(f\"Processing file: {filename}\")   # Bad\n```\n\n**Never log sensitive data** (passwords, tokens, keys).\n\n### File Handling\n```python\n# Use salt.utils.files.fopen(), NOT the bare open() builtin\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nThis applies everywhere, including unit tests.\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Operation failed\n    SaltInvocationError,      # Invalid arguments\n    CommandNotFoundError,     # Binary not found\n)\n```\n\n---\n\n## Detailed Documentation\n\n**Essential guides in `agents/docs/`:**\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - Virtual environment setup (venv310 and venv312), platform-specific dependencies, installation verification\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete Salt architecture, all module types, loader system, event bus\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates for execution and state modules, `__virtual__()` patterns, decorators\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test structure, unit test templates, mocking patterns, running tests\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit guidelines, PR workflow, CI failure reproduction, container testing\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Import issues, module discovery, ZeroMQ, Python 3.11+ compatibility, lazy loading\n\n---\n\n## Key Example Files\n\n- Simple module: `salt/modules/test.py`\n- Complex module: `salt/modules/file.py`\n- Package manager: `salt/modules/aptpkg.py`\n- Service: `salt/modules/systemd_service.py`\n- State examples: `salt/states/file.py`, `salt/states/pkg.py`\n\n---\n\n## Summary\n\nWhen writing Salt code:\n\n1. **Follow the patterns** - Use existing modules as templates\n2. **Use dunders correctly** - `__salt__`, `__opts__`, `__grains__`, `__context__`\n3. **Implement `__virtual__()`** - Declare platform and dependency requirements\n4. **Handle errors properly** - Use Salt's exception classes\n5. **Write tests** - Every new function needs unit tests\n6. **Document thoroughly** - Docstrings with CLI examples are required\n7. **Think idempotent** - Operations should be safe to repeat\n8. **Cache wisely** - Use `__context__` for expensive operations\n\n**When in doubt, look at existing modules for patterns and refer to the detailed documentation.**\n\n**Remember: NEVER add AI attribution lines to commits!**\n","agents/docs/package-building.md":"# Package Building Guide\n\nThis guide describes how to build Salt packages (RPM, Deb, etc.) locally using the same methods as the CI/CD pipeline.\n\n## Overview\n\nSalt packages are built using the `tools` script (specifically `tools pkg`) running inside a CI container. This ensures the build environment matches the official release environment.\n\nThe general process is:\n1.  Enter the appropriate CI container\n2.  Setup the Python environment\n3.  Build the source tarball\n4.  Build the \"Onedir\" (a self-contained directory with Python and dependencies)\n5.  Build the final package (RPM/Deb) using the Onedir\n\n## Prerequisites\n\n-   Docker installed and running\n-   `python-tools-scripts` installed in your local environment (optional, but helpful for some commands)\n\n## Reference Workflows\n\nThe instructions in this guide are derived from the official GitHub Actions workflows. If you need to verify the current build process or versions, check these files:\n\n-   **`.github/workflows/ci.yml`**: The main entry point for CI builds. It defines the high-level orchestration.\n-   **`.github/workflows/build-salt-onedir.yml`**: Defines how the \"Onedir\" (relocatable Python environment) is built. Check this for `relenv` and `python` versions.\n-   **`.github/workflows/build-packages.yml`**: Defines how the final RPM/Deb/Windows/macOS packages are built using the Onedir.\n-   **`.github/actions/build-source-tarball/action.yml`**: The steps to create the initial source distribution.\n\n## Building RPMs (Linux)\n\n### 1. Enter the Build Container\n\nFrom the root of the Salt repository:\n\n```bash\ndocker run --rm -it \\\n  -v \"$(pwd):/salt\" \\\n  -w /salt \\\n  ghcr.io/saltstack/salt-ci-containers/testing:fedora-42 \\\n  bash\n```\n\n*Note: Use `fedora-42` for RPM builds. For Deb builds, use `debian-11` or similar.*\n\n### 2. Setup Python Environment (Inside Container)\n\nOnce inside the container:\n\n```bash\n# Detect python version (likely 3.10 or 3.14 depending on branch/container)\nPY_VER=$(python3 -c \"import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')\")\n\n# Create and activate venv\npython3 -m venv venv\nsource venv/bin/activate\n\n# Install build dependencies\npip install --upgrade pip setuptools\npip install -r requirements/static/ci/py${PY_VER}/tools.lock\npip install -e .\n```\n\n### 3. Build Source Tarball\n\nCreate the source distribution:\n\n```bash\npython3 -m tools pkg source-tarball\n```\n\nThis will create `dist/salt-<version>.tar.gz`.\n\n### 4. Build Salt Onedir\n\nThe \"Onedir\" is an artifact containing Salt and all its dependencies (including Python itself) in a relocatable directory.\n\n```bash\n# Get the version from the tarball\nSALT_VERSION=$(ls dist/salt-*.tar.gz | sed 's/dist\\/salt-//;s/.tar.gz//')\nmkdir -p artifacts\n\n# Build the Onedir\n# Note: CI uses specific pinned versions for relenv and python.\n# Check .github/workflows/build-salt-onedir.yml for current versions.\npython3 -m tools pkg build salt-onedir \"dist/salt-${SALT_VERSION}.tar.gz\" \\\n    --platform linux \\\n    --package-name artifacts/salt \\\n    --relenv-version 0.22.4\n\n# Cleanup and Archive\npython3 -m tools pkg pre-archive-cleanup artifacts/salt\ntar -cJf \"artifacts/salt-${SALT_VERSION}-onedir-linux-x86_64.tar.xz\" -C artifacts salt\n```\n\n### 5. Build RPM Package\n\nUse the Onedir artifact to build the final RPM.\n\n```bash\npython3 -m tools pkg build rpm \\\n    --relenv-version=0.22.4 \\\n    --python-version=3.10.19 \\\n    --onedir=\"salt-${SALT_VERSION}-onedir-linux-x86_64.tar.xz\"\n```\n\nThe RPMs will be generated in `~/rpmbuild/RPMS/x86_64/`.\n\n### 6. Retrieve Artifacts\n\nBefore exiting the container, copy the RPMs to the mounted volume:\n\n```bash\nmkdir -p artifacts/rpm\ncp -r ~/rpmbuild/RPMS/x86_64/*.rpm artifacts/rpm/\n```\n\n## Building DEBs (Linux)\n\nThe process is similar to RPMs but uses the `deb` command and a Debian container.\n\n1.  Use `ghcr.io/saltstack/salt-ci-containers/testing:debian-11` (or newer).\n2.  Follow steps 2-4 above (Source Tarball & Onedir).\n3.  Run `python3 -m tools pkg build deb ...` instead of `rpm`.\n\n## Building Windows/macOS Packages\n\nWindows and macOS packages are typically built on their respective host OSs in CI, not in Docker containers. Refer to `.github/workflows/build-packages.yml` for the specific steps and requirements (signing certificates, etc.).\n",".github/copilot-instructions.md":"../agents/COPILOT.md","agents/docs/architecture.md":"# Salt Architecture\n\n## What is Salt?\n\nSalt is a Python-based configuration management system using a master-minion architecture:\n- **Master**: Central server that manages configuration and sends commands\n- **Minion**: Agent running on managed systems that executes commands\n- **Event Bus**: Real-time communication system between components\n- **Loader System**: Dynamic plugin system for extending functionality\n\n## Module Types\n\nSalt uses a loader system to discover and load different types of modules:\n\n### Execution Modules (`salt/modules/`)\nFunctions executed on minions (e.g., `pkg.install`, `file.read`). There are 264+ execution modules providing the CLI commands available via Salt.\n\n**Examples:**\n- `salt/modules/pkg.py` - Package management (virtual module)\n- `salt/modules/file.py` - File operations\n- `salt/modules/cmd.py` - Command execution\n- `salt/modules/systemd_service.py` - systemd service management\n\n### State Modules (`salt/states/`)\nDeclarative configuration management (e.g., `pkg.installed`, `file.managed`). There are 126+ state modules providing idempotent configuration.\n\n**Examples:**\n- `salt/states/pkg.py` - Package installation states\n- `salt/states/file.py` - File management states\n- `salt/states/service.py` - Service management states\n\n### Runner Modules (`salt/runners/`)\nMaster-side operations (e.g., `salt-run jobs.list_jobs`). Runners execute on the Salt master.\n\n### Wheel Modules (`salt/wheel/`)\nMaster configuration management (key management, config). Used for managing the Salt master itself.\n\n### Grain Modules (`salt/grains/`)\nStatic system information collection. Grains provide facts about the minion system (OS, CPU, memory, etc.).\n\n### Pillar Modules (`salt/pillar/`)\nDynamic configuration data backends. Pillars provide secure, targeted data to minions.\n\n### Beacon Modules (`salt/beacons/`)\nSystem monitoring triggers (watch for events). Beacons monitor system events and fire events on the event bus.\n\n### Engine Modules (`salt/engines/`)\nLong-running processes on master or minion. Engines provide persistent functionality.\n\n### Returner Modules (`salt/returners/`)\nStore or send command output to external systems (databases, message queues, etc.).\n\n### Renderer Modules (`salt/renderers/`)\nTemplate engines (Jinja, Mako, YAML, JSON). Renderers process state files.\n\n### Matcher Modules (`salt/matchers/`)\nTarget matching (glob, pcre, grain, pillar, compound). Matchers determine which minions execute commands.\n\n### Utility Modules (`salt/utils/`)\nShared utility functions (170+ modules). Utilities provide common functionality across Salt.\n\n## Directory Structure\n\n```\nsalt/\n├── master.py, minion.py, state.py  # Core daemon implementations\n├── loader/                         # Plugin loader system (LazyLoader)\n├── modules/                        # 264+ execution modules\n├── states/                         # 126+ state modules\n├── runners/, wheel/                # Master operations and config\n├── beacons/, engines/              # Monitoring triggers, long-running processes\n├── grains/, pillar/                # System info, config data backends\n├── renderers/, returners/          # Template engines, return data storage\n├── matchers/, transport/           # Targeting systems, communication layer\n├── utils/                          # 170+ utility modules\n└── ext/                            # Vendored third-party code\n\ntests/\n├── pytests/\n│   ├── unit/                       # Fast tests, no daemons/network\n│   ├── functional/                 # End-to-end, no external deps\n│   ├── integration/                # With daemons/network\n│   ├── scenarios/                  # Scenario-based testing\n│   └── pkg/                        # Package install/upgrade tests\n└── support/                        # Test helpers and fixtures\n```\n\n## Key Architectural Components\n\n### State Compiler\n\n`salt/state.py` compiles YAML/Jinja templates into execution chunks. Uses **NetworkX** for dependency graph management with requisites:\n\n- `require`: Must run after\n- `watch`: Run if watched state changes\n- `onchanges`: Run only if watched state changes\n- `onfail`: Run if watched state fails\n- `listen`: Listen for notifications\n- `prereq`: Run before (with test mode check)\n\nAll state operations are idempotent and support test mode (`test=True`).\n\n### Transport Layer\n\n`salt/transport/` handles communication between master and minions:\n\n- **ZeroMQ** (default): High-performance messaging\n- **TCP**: Alternative transport\n- **WebSocket**: For browser-based access\n\n### Security\n\n- **PKI-based authentication** (`salt/crypt.py`)\n- **AES encryption** for all payloads\n- **Master key acceptance workflow** prevents unauthorized access\n- **External auth (eauth)** for Salt API access\n\n### Event Bus\n\n`salt/utils/event.py` provides ZeroMQ pub/sub with namespaced event tags for real-time communication between all components.\n\nEvents are namespaced with tags like:\n- `salt/job/<jid>/new` - New job started\n- `salt/job/<jid>/ret/<minion_id>` - Job return from minion\n- `salt/auth` - Authentication events\n- `salt/minion/<minion_id>/start` - Minion started\n\n### Loader System\n\nThe loader system (`salt/loader/`) dynamically discovers and loads modules at runtime. Key features:\n\n- **LazyLoader**: Loads modules on first access, not import\n- **Virtual names**: Modules can override their name via `__virtual__()`\n- **Dunder injection**: Injects `__salt__`, `__opts__`, `__grains__`, etc.\n- **Caching**: Modules are cached after first load\n\n## Async/Await Transition\n\nSalt is transitioning from **Tornado** coroutines to native **asyncio**:\n- Use native `async`/`await` syntax, not `@tornado.gen.coroutine`\n- Prefer asyncio patterns over Tornado patterns\n- Be aware of mixed async code during transition\n\n## Master-Minion Communication\n\n1. Minion authenticates with master via PKI\n2. Master accepts minion key\n3. Minion connects to master on port 4506 (request server)\n4. Master publishes commands on port 4505 (publish server)\n5. Minions subscribe to master's publish port\n6. Commands are encrypted with AES\n7. Results return via request server\n\n## File Server\n\nSalt includes a file server that serves files to minions:\n- `salt://` URLs reference files on the file server\n- Multiple backends: local, git, S3, HTTP, etc.\n- Files are cached on minions\n\nFor details on the Git backends and performance, see [GitFS Providers](gitfs-providers.md).\n\n## Targeting\n\nMinions can be targeted in multiple ways:\n- **Glob**: `*`, `web*.example.com`\n- **Grain**: `os:Ubuntu`, `kernel:Linux`\n- **Pillar**: `role:webserver`\n- **Compound**: `G@os:Ubuntu and webserver*`\n- **List**: `web1,web2,web3`\n- **PCRE**: Regular expressions\n\n## Performance Considerations\n\n### Lazy Loading\nModules only load on first access. Import-time side effects may not occur until first use of a module function.\n\n### Context Caching\nUse `__context__` to cache expensive operations within a Salt run (single execution).\n\n### Module Discovery\n- Module directories need `__init__.py`\n- Filename becomes namespace: `salt/modules/pkg.py` → `pkg.*`\n- Use `__virtualname__` for different name than filename\n- **Performance tip**: Filename should match virtualname when possible\n\n## Additional Resources\n\n### Documentation\n- Module development: `doc/topics/development/modules/developing.rst`\n- State writing guide: `doc/ref/states/writing.rst`\n- Contributing guidelines: `CONTRIBUTING.rst`\n\n### Example Modules\n- Simple execution module: `salt/modules/test.py`\n- Complex execution module: `salt/modules/file.py`\n- Package manager: `salt/modules/aptpkg.py`, `salt/modules/yumpkg.py`\n- Service manager: `salt/modules/systemd_service.py`\n- Simple state: `salt/states/pkg.py`\n- Complex state: `salt/states/file.py`\n","agents/docs/development-setup.md":"# Salt Development Environment Setup\n\nThis guide covers setting up your local development environment for Salt development.\n\n## Virtual Environment Requirements\n\nSalt development requires setting up **two** virtual environments:\n\n- **venv310 (Python 3.10)**: For running tests on 3006.x and 3007.x branches\n- **venv314 (Python 3.14)**: For running tests on master branch AND running pre-commit hooks\n\n## Prerequisites\n\n- Python 3.10 and Python 3.14 installed on your system\n- Git repository cloned\n- Internet connection for downloading dependencies\n\n## Setup venv310 (Python 3.10)\n\nThis environment is used for testing on 3006.x and 3007.x branches.\n\n```bash\n# Create virtual environment\npython3.10 -m venv venv310\n\n# Activate\nsource venv310/bin/activate\n\n# Upgrade pip\npip install --upgrade pip setuptools wheel\n\n# Install platform-specific dependencies (choose your OS)\n# Linux:\npip install -r requirements/static/pkg/py3.10/linux.lock\n\n# macOS:\npip install -r requirements/static/pkg/py3.10/darwin.lock\n\n# Windows:\npip install -r requirements/static/pkg/py3.10/windows.lock\n\n# Install pytest requirements\npip install -r requirements/pytest.txt\n\n# Install Salt in editable mode\npip install -e .\n\n# Install tools dependencies (for using tools/ commands)\npip install -r requirements/static/ci/py3.10/tools.lock\n\n# Install pre-commit and python-tools-scripts\npip install pre-commit python-tools-scripts\n\n# Deactivate\ndeactivate\n```\n\n## Setup venv314 (Python 3.14)\n\nThis environment is used for testing on master branch and running pre-commit hooks.\n\n```bash\n# Create virtual environment\npython3.14 -m venv venv314\n\n# Activate\nsource venv314/bin/activate\n\n# Upgrade pip\npip install --upgrade pip setuptools wheel\n\n# Install platform-specific dependencies (choose your OS)\n# Linux:\npip install -r requirements/static/pkg/py3.14/linux.lock\n\n# macOS:\npip install -r requirements/static/pkg/py3.14/darwin.lock\n\n# Windows:\npip install -r requirements/static/pkg/py3.14/windows.lock\n\n# Install pytest requirements\npip install -r requirements/pytest.txt\n\n# Install Salt in editable mode\npip install -e .\n\n# Install tools dependencies\npip install -r requirements/static/ci/py3.14/tools.lock\n\n# Install pre-commit and python-tools-scripts\npip install pre-commit python-tools-scripts\n\n# Install pre-commit hooks\npre-commit install\n\n# Deactivate\ndeactivate\n```\n\n## Environment Usage\n\n### venv310 is used for:\n- Running tests on 3006.x and 3007.x branches\n- Direct pytest execution on older branches\n- Tools commands (`python -m tools`)\n\n### venv314 is used for:\n- Running tests on master branch\n- Running pre-commit hooks\n- Code formatting and linting checks\n- Pre-commit validation before commits\n\n## Verify Installation\n\n```bash\n# Test Salt import (master / default dev)\n./venv314/bin/python -c \"import salt.version; print(salt.version.__version__)\"\n\n# Run a simple test\n./venv314/bin/pytest tests/pytests/unit/test_loader.py -v\n\n# Test tools (3006.x/3007.x workflow)\n./venv310/bin/python -m tools --help\n```\n\n## IMPORTANT: Using Virtual Environments\n\nWhen running tests or tools, always use the full path to the venv executable or activate the venv first. This ensures you're using the correct Python environment with all dependencies installed.\n\n**Good examples:**\n```bash\n./venv310/bin/pytest tests/pytests/unit/test_foo.py -v\n./venv314/bin/pre-commit run --files salt/loader/lazy.py\n./venv310/bin/python -m tools ts pytest run tests/pytests/unit/\n```\n\n**Alternatively, activate first:**\n```bash\nsource venv314/bin/activate\npytest tests/pytests/unit/test_foo.py -v\ndeactivate\n```\n\n## Troubleshooting\n\n### ModuleNotFoundError: No module named 'ptscripts'\n\nIf you see this error when running `python -m tools`, you need to install the tools dependencies:\n\n```bash\n./venv310/bin/pip install -r requirements/static/ci/py3.10/tools.lock\n./venv310/bin/pip install python-tools-scripts\n```\n\n### venv not found\n\nIf you get \"No such file or directory\" for venv paths, make sure you've created the virtual environment and are running commands from the repository root directory.\n\n### Permission errors\n\nOn some systems you may need to use `python3.10` and `python3.14` explicitly instead of just `python3`.\n\n### Pre-commit hooks not running\n\nMake sure you've run `pre-commit install` in the venv314 environment:\n\n```bash\nsource venv314/bin/activate\npre-commit install\ndeactivate\n```\n","agents/docs/testing.md":"# Salt Testing Guide\n\nSalt uses multiple test frameworks. All new code should include tests.\n\n## Test Directory Structure\n\n```\ntests/\n├── unit/               # Fast unit tests (no daemons, heavy mocking)\n│   ├── modules/        # Tests for salt/modules/\n│   ├── states/         # Tests for salt/states/\n│   └── utils/          # Tests for salt/utils/\n├── integration/        # Integration tests (with daemons)\n│   ├── modules/        # Tests for salt/modules/\n│   ├── states/         # Tests for salt/states/\n│   └── ...\n└── pytests/           # Pytest-based tests (newer style)\n    ├── unit/\n    ├── integration/\n    └── functional/\n```\n\n## Unit Test Templates\n\n### Legacy Style (TestCase)\n\n```python\n\"\"\"\nTests for salt.modules.mymodule\n\"\"\"\n\nimport pytest\n\nimport salt.modules.mymodule as mymodule\nfrom salt.exceptions import CommandExecutionError, SaltInvocationError\nfrom tests.support.mixins import LoaderModuleMockMixin\nfrom tests.support.mock import MagicMock, patch\nfrom tests.support.unit import TestCase\n\n\nclass MyModuleTestCase(TestCase, LoaderModuleMockMixin):\n    \"\"\"\n    Test cases for salt.modules.mymodule\n    \"\"\"\n\n    def setup_loader_modules(self):\n        \"\"\"\n        Setup loader modules - injects dunders\n        \"\"\"\n        return {\n            mymodule: {\n                \"__opts__\": {\"test\": False},\n                \"__grains__\": {\"os\": \"Linux\"},\n                \"__salt__\": {},\n            }\n        }\n\n    def test_simple_function(self):\n        \"\"\"\n        Test simple function\n        \"\"\"\n        result = mymodule.my_function(\"test\")\n        self.assertEqual(result, \"expected\")\n\n    def test_with_mock(self):\n        \"\"\"\n        Test with mocked subprocess call\n        \"\"\"\n        mock_cmd = MagicMock(return_value={\"retcode\": 0, \"stdout\": \"success\"})\n        with patch.dict(mymodule.__salt__, {\"cmd.run_all\": mock_cmd}):\n            result = mymodule.my_function(\"test\")\n            self.assertTrue(result)\n            mock_cmd.assert_called_once()\n\n    def test_error_handling(self):\n        \"\"\"\n        Test error conditions\n        \"\"\"\n        with self.assertRaises(SaltInvocationError):\n            mymodule.my_function(None)\n```\n\n### Pytest Style (Newer)\n\n```python\n\"\"\"\nTests for salt.modules.mymodule\n\"\"\"\n\nimport pytest\n\nimport salt.modules.mymodule as mymodule\nfrom salt.exceptions import SaltInvocationError\n\n\n@pytest.fixture\ndef configure_loader_modules():\n    \"\"\"\n    Setup module dunders\n    \"\"\"\n    return {\n        mymodule: {\n            \"__opts__\": {\"test\": False},\n            \"__grains__\": {\"os\": \"Linux\"},\n            \"__salt__\": {},\n        }\n    }\n\n\ndef test_simple_function():\n    \"\"\"\n    Test simple function\n    \"\"\"\n    result = mymodule.my_function(\"test\")\n    assert result == \"expected\"\n\n\ndef test_error_handling():\n    \"\"\"\n    Test error conditions\n    \"\"\"\n    with pytest.raises(SaltInvocationError):\n        mymodule.my_function(None)\n```\n\n## Mocking Patterns\n\n### Basic Mocking\n\n```python\nfrom tests.support.mock import MagicMock, patch, call\n\n# Mock a function call\nmock_func = MagicMock(return_value=\"result\")\nwith patch(\"salt.modules.mymodule.some_function\", mock_func):\n    result = mymodule.my_function()\n    mock_func.assert_called_once_with(\"expected_arg\")\n\n# Mock dunder dict\nwith patch.dict(mymodule.__salt__, {\"cmd.run\": MagicMock(return_value=\"output\")}):\n    result = mymodule.my_function()\n\n# Mock with side effects\nmock_func = MagicMock(side_effect=[\n    {\"retcode\": 1, \"stdout\": \"error\"},  # First call fails\n    {\"retcode\": 0, \"stdout\": \"success\"},  # Second call succeeds\n])\nwith patch.dict(mymodule.__salt__, {\"cmd.run_all\": mock_func}):\n    result = mymodule.my_function()\n```\n\n## Test Best Practices\n\n1. **One test per function behavior**: Test success cases, error cases, edge cases separately\n2. **Use descriptive test names**: `test_function_name_when_condition_then_expected_result`\n3. **Mock external dependencies**: File I/O, network calls, subprocess calls\n4. **Test error handling**: Verify exceptions are raised correctly\n5. **Test input validation**: Verify bad inputs are rejected\n6. **Keep tests fast**: Unit tests should run in milliseconds\n7. **Don't test implementation details**: Test the public API, not internal helpers\n\n## Running Tests\n\n### Using Nox (Recommended for CI Consistency)\n\n```bash\n# Run all tests\nnox -e test-3\n\n# Run specific test file\nnox -e test-3 -- tests/pytests/unit/test_loader.py\n\n# Run with pattern matching\nnox -e test-3 -- -k test_auth\n\n# Run specific test function\nnox -e test-3 -- tests/pytests/unit/test_loader.py::test_load_modules\n\n# Run last failed tests\nnox -e test-3 -- --lf\n\n# Run with specific Python version\nnox -e test-3.10\nnox -e test-3.14\n\n# Run with specific transport\nnox -e test-tcp-3\nnox -e test-zeromq-3\n\n# Run with coverage\nnox -e test-3 --coverage\nnox -e coverage-report\n```\n\n### Using Virtual Environment (Faster for Development)\n\nIf you have a local venv setup:\n\n```bash\n# Run tests directly with pytest (master branch: use venv312)\n./venv312/bin/pytest tests/pytests/unit/test_foo.py::test_bar -v\n\n# Run pre-commit checks on specific files\n./venv312/bin/pre-commit run --files salt/loader/lazy.py\n```\n\n## Container Testing (Reproduce CI Failures)\n\nTo reproduce CI failures locally using the same container environment:\n\n### 1. Download CI artifacts\n\n```bash\n./venv310/bin/python -m ptscripts ci download-artifacts --run-id <RUN-ID>\n```\n\n### 2. Create container\n\n```bash\n./venv310/bin/python -m ptscripts container create <IMAGE> --name <NAME>\ndocker start <NAME>\n```\n\n### 3. Setup container\n\n```bash\n# Decompress dependencies\ndocker exec <NAME> python3 -m nox -e decompress-dependencies -- linux x86_64\n\n# Create relenv toolchain symlink (Python 3.14+ only)\ndocker exec <NAME> bash -c \"mkdir -p /root/.local/relenv && ln -sf /root/.cache/relenv/toolchains /root/.local/relenv/toolchain\"\n```\n\n### 4. Run tests\n\n```bash\ndocker exec <NAME> python3 -m nox -e ci-test-onedir -- <TEST-PATH> --run-slow -x -v\n```\n\n### Example container images\n\n- `ghcr.io/saltstack/salt-ci-containers/testing:debian-11`\n- `ghcr.io/saltstack/salt-ci-containers/testing:ubuntu-22.04`\n- `ghcr.io/saltstack/salt-ci-containers/testing:rockylinux-9`\n\n## Linting and Formatting\n\n### Black (Code Formatting)\n\n```bash\n# Format all code\nblack .\n\n# Check without modifying\nblack --check .\n```\n\nBlack uses 88 character line length by default.\n\n### isort (Import Sorting)\n\n```bash\n# Sort all imports\nisort .\n\n# Check without modifying\nisort --check .\n```\n\nisort is configured with profile 3 and trailing commas in `pyproject.toml`.\n\n### Linting\n\n```bash\n# Lint both salt and tests\nnox -e lint\n\n# Lint Salt code only\nnox -e lint-salt\n\n# Lint tests only\nnox -e lint-tests\n```\n\n## Example Test Files\n\nFor reference, see:\n- Unit test example: `tests/unit/modules/test_systemd_service.py`\n- Complex mocking: `tests/unit/modules/test_cron.py`\n- Documentation: `doc/topics/tutorials/writing_tests.rst`\n","agents/docs/git-and-ci.md":"# Git Workflow and CI\n\n## Commit Guidelines\n\n### CRITICAL RULES\n\n1. **NO attribution lines** - **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n2. **Use imperative mood** - \"Fix bug\", not \"Fixed bug\" or \"Fixes bug\"\n3. **Reference issues** - Use `#NNNN` to reference GitHub issues\n4. **Small, focused commits** - One logical change per commit\n5. **Rebase before push** - Keep history linear and clean\n\n### Good Commit Message Example\n\n```\nFix loader cache invalidation on module reload\n\nWhen modules are reloaded, the loader cache was not properly\ninvalidated, causing stale references. Clear the cache dict\non reload to ensure fresh module imports.\n\nFixes #12345\n```\n\n### Bad Commit Message Example\n\n```\nFixed some bugs\n\nCo-Authored-By: Claude <noreply@anthropic.com>\n```\n\n**Why this is bad:**\n- Past tense instead of imperative mood\n- Vague description\n- Contains AI attribution (NEVER do this)\n\n## PR Workflow\n\n### Finding and Checking PRs\n\n```bash\n# Find PR by branch name\ngh pr list --repo saltstack/salt --head <BRANCH-NAME> --limit 5\n\n# Check PR status\ngh pr view <PR-NUMBER> --repo saltstack/salt\ngh pr checks <PR-NUMBER> --repo saltstack/salt\n\n# Watch CI run\ngh run watch <RUN-ID> --repo saltstack/salt\n```\n\n## Branch Strategy\n\n- **Main PR branch**: `master` (not `main`)\n- **Release branches**: `3006.x`, `3007.x`, etc.\n- **Merge-forward branches**: `merge/3007.x/master-YY-MM-DD`\n\n## CI Failure Reproduction\n\nWhen CI tests fail, you can reproduce the exact failure locally using containers.\n\n### Prerequisites\n\n- Docker installed and running\n- Virtual environment setup (see [development-setup.md](development-setup.md))\n- `ptscripts` installed (`pip install python-tools-scripts`)\n\n### Step 1: Download CI Artifacts\n\nFirst, get the CI artifacts from the failed run:\n\n```bash\n./venv310/bin/python -m ptscripts ci download-artifacts --run-id <RUN-ID>\n```\n\nThe run ID can be found in the GitHub Actions URL or via `gh run list`.\n\n### Step 2: Create Container\n\nCreate a container using the same image as CI:\n\n```bash\n./venv310/bin/python -m ptscripts container create <IMAGE> --name <NAME>\ndocker start <NAME>\n```\n\n**Example container images:**\n- `ghcr.io/saltstack/salt-ci-containers/testing:debian-11`\n- `ghcr.io/saltstack/salt-ci-containers/testing:ubuntu-22.04`\n- `ghcr.io/saltstack/salt-ci-containers/testing:ubuntu-24.04`\n- `ghcr.io/saltstack/salt-ci-containers/testing:rockylinux-8`\n- `ghcr.io/saltstack/salt-ci-containers/testing:rockylinux-9`\n- `ghcr.io/saltstack/salt-ci-containers/testing:fedora-40`\n- `ghcr.io/saltstack/salt-ci-containers/testing:amazonlinux-2`\n- `ghcr.io/saltstack/salt-ci-containers/testing:amazonlinux-2023`\n- `ghcr.io/saltstack/salt-ci-containers/testing:photonos-3`\n- `ghcr.io/saltstack/salt-ci-containers/testing:photonos-4`\n- `ghcr.io/saltstack/salt-ci-containers/testing:photonos-5`\n\n### Step 3: Setup Container\n\n```bash\n# Decompress dependencies\ndocker exec <NAME> python3 -m nox -e decompress-dependencies -- linux x86_64\n\n# Create relenv toolchain symlink (Python 3.14+ only)\ndocker exec <NAME> bash -c \"mkdir -p /root/.local/relenv && ln -sf /root/.cache/relenv/toolchains /root/.local/relenv/toolchain\"\n```\n\n### Step 4: Run Tests\n\n```bash\ndocker exec <NAME> python3 -m nox -e ci-test-onedir -- <TEST-PATH> --run-slow -x -v\n```\n\n**Examples:**\n```bash\n# Run specific test file\ndocker exec mycontainer python3 -m nox -e ci-test-onedir -- tests/pytests/unit/modules/test_pkg.py --run-slow -x -v\n\n# Run specific test function\ndocker exec mycontainer python3 -m nox -e ci-test-onedir -- tests/pytests/unit/modules/test_pkg.py::test_install --run-slow -x -v\n\n# Run with pattern matching\ndocker exec mycontainer python3 -m nox -e ci-test-onedir -- tests/pytests/unit/ -k test_systemd --run-slow -x -v\n```\n\n### Step 5: Debug in Container\n\nIf you need to debug interactively:\n\n```bash\n# Shell into container\ndocker exec -it <NAME> bash\n\n# Inside container, activate environment and run tests\ncd /salt\npython3 -m nox -e ci-test-onedir -- tests/pytests/unit/test_foo.py -x -v\n```\n\n### Step 6: Cleanup\n\n```bash\n# Stop and remove container\ndocker stop <NAME>\ndocker rm <NAME>\n\n# Clean artifacts\nrm -rf artifacts/ nox-*.zip nox.*.tar.*\n```\n\n## Container Filesystem Behavior\n\nWhen testing in containers, be aware:\n\n- Changes to `salt/` source are visible immediately (mounted volume)\n- Changes to Salt library may need artifact refresh:\n  ```bash\n  docker exec <NAME> cp /salt/salt/modules/foo.py \\\n      /salt/artifacts/salt/lib/python3.14/site-packages/salt/modules/\n  ```\n\n## Stale Artifacts\n\nAlways clean before downloading new CI artifacts:\n\n```bash\nrm -rf artifacts/ nox-*.zip nox.*.tar.*\n```\n\nOld artifacts can cause confusing test failures.\n\n## Common CI Issues\n\n### Tests Pass Locally But Fail in CI\n\nPossible causes:\n1. **Different Python version** - CI uses specific Python versions per branch\n2. **Platform differences** - CI runs on Linux, you might be on macOS/Windows\n3. **Missing dependencies** - CI containers have exact dependency versions\n4. **Timing issues** - CI environment may be slower/faster\n5. **Stale artifacts** - Clean and re-download\n\n### Container Setup Fails\n\nIf dependency decompression fails:\n1. Check you downloaded artifacts for correct run ID\n2. Ensure Docker has enough disk space\n3. Try removing old containers and images\n\n### Relenv Toolchain Issues\n\nFor Python 3.14+ containers, you must create the symlink:\n\n```bash\ndocker exec <NAME> bash -c \"mkdir -p /root/.local/relenv && ln -sf /root/.cache/relenv/toolchains /root/.local/relenv/toolchain\"\n```\n\nThis is a known path mismatch between relenv versions.\n\n## Using MCP Server for CI Workflows\n\nThe agents/mcp/salt_test MCP server provides tools for discovering CI failures:\n\n- `ci_pr_failures` - Get all failing tests from a PR\n- `ci_run_failures` - Get failures from specific CI run\n- `ci_failure_summary` - Get summary of recent failures\n\nSee [agents/mcp/salt_test/README.md](../mcp/salt_test/README.md) for details.\n\n## Best Practices\n\n1. **Always clean artifacts** before downloading new ones\n2. **Use exact CI container images** for reproduction\n3. **Note Python version** - use venv310 for 3006.x/3007.x, venv314 for master\n4. **Check run logs** in GitHub Actions for exact command that failed\n5. **Test locally first** before pushing to avoid CI churn\n6. **Keep commits small** for easier review and debugging\n","agents/docs/module-templates.md":"# Salt Module Templates\n\nThis document provides comprehensive templates for writing Salt modules.\n\n## Execution Module Template\n\nAll execution modules follow this basic structure:\n\n```python\n\"\"\"\nModule docstring explaining purpose\n\"\"\"\n\nimport logging\n\nimport salt.exceptions\nimport salt.utils.args\nimport salt.utils.data\nimport salt.utils.files\nimport salt.utils.path\nimport salt.utils.platform\n\n# Set up logging\nlog = logging.getLogger(__name__)\n\n# Module metadata\n__virtualname__ = \"mymodule\"  # Optional: override module name\n__proxyenabled__ = [\"*\"]  # Optional: enable for proxy minions\n__func_alias__ = {\n    \"list_\": \"list\"  # Optional: map function names to avoid Python keywords\n}\n\n\ndef __virtual__():\n    \"\"\"\n    Only load this module if requirements are met\n    Returns True, False, or (False, reason)\n    \"\"\"\n    if not salt.utils.path.which(\"mycommand\"):\n        return False, \"mycommand binary not found\"\n    return True\n\n\ndef my_function(name, param=None):\n    \"\"\"\n    Function description\n\n    name\n        Description of name parameter\n\n    param\n        Description of param parameter\n\n    CLI Example:\n\n    .. code-block:: bash\n\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    log.debug(\"Running my_function with name=%s\", name)\n\n    # Implementation\n    result = do_something(name, param)\n\n    return result\n```\n\n## State Module Template\n\nState modules provide idempotent, declarative configuration. They call execution modules to do the actual work.\n\n### State Return Structure\n\nEvery state function MUST return this exact structure:\n\n```python\nret = {\n    \"name\": name,        # The name parameter passed to the state\n    \"result\": result,    # True (success), False (failure), or None (test mode)\n    \"changes\": {},       # Dictionary of changes made\n    \"comment\": comment   # String or list describing what happened\n}\n```\n\n### Complete State Template\n\n```python\n\"\"\"\nState module docstring\n\"\"\"\n\nimport logging\n\nimport salt.exceptions\n\nlog = logging.getLogger(__name__)\n\n\ndef __virtual__():\n    \"\"\"\n    Only load if the corresponding execution module is available\n    \"\"\"\n    if \"mymod.do_thing\" in __salt__:\n        return True\n    return (False, \"mymod execution module not available\")\n\n\ndef managed(name, value=None, **kwargs):\n    \"\"\"\n    Ensure something is in the desired state\n\n    name\n        The resource identifier\n\n    value\n        The desired value\n\n    Example:\n\n    .. code-block:: yaml\n\n        /etc/myconfig:\n          mymod.managed:\n            - value: foo\n    \"\"\"\n    # Initialize return dict\n    ret = {\n        \"name\": name,\n        \"result\": False,\n        \"changes\": {},\n        \"comment\": \"\"\n    }\n\n    # 1. Validate input\n    if not name:\n        ret[\"comment\"] = \"Name is required\"\n        return ret\n\n    # 2. Check current state\n    current = __salt__[\"mymod.get_current\"](name)\n\n    # 3. Compare with desired state\n    if current == value:\n        ret[\"result\"] = True\n        ret[\"comment\"] = f\"{name} is already in the desired state\"\n        return ret\n\n    # 4. Handle test mode\n    if __opts__[\"test\"]:\n        ret[\"result\"] = None\n        ret[\"comment\"] = f\"{name} would be updated\"\n        ret[\"changes\"] = {\"old\": current, \"new\": value}\n        return ret\n\n    # 5. Make changes\n    try:\n        result = __salt__[\"mymod.set_value\"](name, value)\n    except Exception as exc:\n        ret[\"comment\"] = f\"Failed to update {name}: {exc}\"\n        return ret\n\n    # 6. Verify changes\n    new_value = __salt__[\"mymod.get_current\"](name)\n\n    # 7. Set result and changes\n    if new_value == value:\n        ret[\"result\"] = True\n        ret[\"changes\"] = {\"old\": current, \"new\": new_value}\n        ret[\"comment\"] = f\"{name} was updated\"\n    else:\n        ret[\"result\"] = False\n        ret[\"comment\"] = f\"Failed to update {name}\"\n\n    return ret\n```\n\n### State Flow Diagram\n\n```\n1. Validate Input → Invalid? Return with result=False\n                  ↓\n2. Check Current State\n                  ↓\n3. Already Correct? → Yes: Return with result=True, no changes\n                  ↓ No\n4. Test Mode? → Yes: Return with result=None, proposed changes\n                  ↓ No\n5. Make Changes\n                  ↓\n6. Verify Changes\n                  ↓\n7. Return Result (True/False) with changes dict\n```\n\n### State Result Values\n\n- **`result=True`**: State succeeded, system is in desired state\n- **`result=False`**: State failed, system is NOT in desired state\n- **`result=None`**: Test mode, changes would be made but weren't\n\n### Changes Dictionary\n\nThe `changes` dictionary should show what changed:\n\n```python\n# Simple change\nret[\"changes\"] = {\"old\": \"foo\", \"new\": \"bar\"}\n\n# Multiple changes\nret[\"changes\"] = {\n    \"user\": {\"old\": \"root\", \"new\": \"apache\"},\n    \"mode\": {\"old\": \"0644\", \"new\": \"0755\"},\n}\n\n# New resource created\nret[\"changes\"] = {\"created\": name}\n\n# Resource removed\nret[\"changes\"] = {\"removed\": name}\n```\n\n## The __virtual__ Function Patterns\n\nThe `__virtual__()` function determines whether a module should load on a particular system. It runs before any other module code and should be fast.\n\n### Return Values\n\n1. **`True`**: Load module with filename as name\n2. **`False`**: Don't load module\n3. **`(False, reason)`**: Don't load, with explanation\n4. **`\"string\"`**: Load module with this name (virtualname)\n5. **`(\"string\", reason)`**: Load with name, with explanation\n\n### Pattern 1: Simple Platform Check\n\n```python\ndef __virtual__():\n    \"\"\"\n    Only work on POSIX-like systems\n    \"\"\"\n    if salt.utils.platform.is_windows():\n        return (\n            False,\n            \"The file execution module cannot be loaded: only available on \"\n            \"non-Windows systems - use win_file instead.\",\n        )\n    return True\n```\n\n### Pattern 2: Virtual Name with OS Check\n\nUsed when multiple modules implement the same interface (e.g., `pkg` for different package managers).\n\n```python\n__virtualname__ = \"pkg\"\n\ndef __virtual__():\n    \"\"\"\n    Confirm this module is on a Debian-based system\n    \"\"\"\n    if __grains__.get(\"os_family\") == \"Debian\":\n        return __virtualname__\n    return False, \"The pkg module could not be loaded: unsupported OS family\"\n```\n\n### Pattern 3: Binary Availability Check\n\n```python\ndef __virtual__():\n    \"\"\"\n    Only load if systemctl is available\n    \"\"\"\n    if not salt.utils.path.which(\"systemctl\"):\n        return False, \"The systemd module could not be loaded: systemctl not found\"\n    return True\n```\n\n### Pattern 4: Complex Multi-Condition Check\n\n```python\n__virtualname__ = \"service\"\n\ndef __virtual__():\n    \"\"\"\n    Only work on systems that have been booted with systemd\n    \"\"\"\n    is_linux = __grains__.get(\"kernel\") == \"Linux\"\n    is_booted = salt.utils.systemd.booted(__context__)\n    is_offline = salt.utils.systemd.offline(__context__)\n\n    if is_linux and (is_booted or is_offline):\n        return __virtualname__\n\n    return (\n        False,\n        \"The systemd execution module failed to load: only available on Linux \"\n        \"systems which have been booted with systemd.\",\n    )\n```\n\n### Pattern 5: Check for Execution Module Availability\n\nUsed in state modules to verify required execution modules exist.\n\n```python\ndef __virtual__():\n    \"\"\"\n    Only make these states available if a pkg provider has been detected\n    \"\"\"\n    if \"pkg.install\" in __salt__:\n        return True\n    return (False, \"pkg module could not be loaded\")\n```\n\n### Best Practices for __virtual__\n\n- Keep `__virtual__()` fast - it runs on every module scan\n- Provide clear error messages explaining why the module didn't load\n- Use `salt.utils.platform` for platform detection\n- Check grains for OS-specific logic\n- Cache expensive checks in `__context__` if needed\n\n## Common Module Patterns\n\n### Decorators\n\nSalt provides decorators for common tasks:\n\n```python\nfrom salt.utils.decorators import depends, memoize\nfrom salt.utils.decorators.path import which\n\n# Ensure binary exists\n@which(\"systemctl\")\ndef restart_service(name):\n    \"\"\"Only available if systemctl is found\"\"\"\n    pass\n\n# Ensure Python module exists\n@depends(\"docker\")\ndef list_containers():\n    \"\"\"Only available if docker module is installed\"\"\"\n    pass\n\n# Memoize expensive operations\n@memoize\ndef get_system_info():\n    \"\"\"Cache result for lifetime of process\"\"\"\n    pass\n```\n\n### Function Aliases\n\nMap function names to avoid Python keywords:\n\n```python\n__func_alias__ = {\n    \"list_\": \"list\",      # my_module.list() → my_module.list_()\n    \"exec_\": \"exec\",      # my_module.exec() → my_module.exec_()\n}\n```\n\n### Cross-Calling Modules\n\n```python\ndef my_function():\n    \"\"\"Call other execution modules\"\"\"\n    # Get file contents\n    content = __salt__[\"file.read\"](\"/etc/hosts\")\n\n    # Run a command\n    output = __salt__[\"cmd.run\"](\"ls -la\")\n\n    # Install a package\n    result = __salt__[\"pkg.install\"](\"nginx\")\n\n    return result\n```\n\n### Argument Handling\n\n```python\nimport salt.utils.args\n\ndef my_function(*args, **kwargs):\n    \"\"\"Handle various argument formats\"\"\"\n    # Clean extra kwargs from state data\n    kwargs = salt.utils.args.clean_kwargs(**kwargs)\n\n    # Parse key=value arguments\n    kwargs.update(salt.utils.args.parse_input(args))\n\n    return kwargs\n```\n\n### Path Handling\n\n```python\nimport salt.utils.path\n\ndef my_function():\n    \"\"\"Handle paths safely\"\"\"\n    # Find binary in PATH\n    binary = salt.utils.path.which(\"nginx\")\n    if not binary:\n        return False\n\n    # Join paths correctly for OS\n    config_path = salt.utils.path.join(\"/etc\", \"nginx\", \"nginx.conf\")\n\n    return config_path\n```\n\n### File Handling\n\n**Always use `salt.utils.files.fopen()` instead of the bare `open()` builtin**\nwhen reading or writing files in Salt code (modules, states, utils, and\ntests alike). `saltpylint`'s `resource-leakage` check (`W8470`) fails CI on\nany bare `open()` call.\n\n```python\nimport salt.utils.files\n\ndef my_function(path):\n    \"\"\"Read/write files the Salt way\"\"\"\n    with salt.utils.files.fopen(path, \"r\") as fp_:\n        content = fp_.read()\n\n    with salt.utils.files.fopen(path, \"w\") as fp_:\n        fp_.write(content)\n\n    return content\n```\n\n```python\n# Bad - fails the resource-leakage lint check\nwith open(path, \"r\") as fp_:\n    content = fp_.read()\n```\n\n`fopen()` is a thin wrapper around `open()` that additionally normalizes\nline endings/encoding across platforms and ensures file handles are tracked\nand closed consistently. Use it for every `open()` call, including binary\nmode (`\"rb\"`/`\"wb\"`) and in unit tests -- the lint check applies there too.\n\n### Platform Detection\n\n```python\nimport salt.utils.platform\n\ndef my_function():\n    \"\"\"Handle platform differences\"\"\"\n    if salt.utils.platform.is_windows():\n        return \"C:\\\\Windows\"\n    elif salt.utils.platform.is_linux():\n        return \"/etc\"\n    elif salt.utils.platform.is_darwin():\n        return \"/Library\"\n\n    # Check specific OS family\n    if __grains__.get(\"os_family\") == \"Debian\":\n        return \"apt\"\n    elif __grains__.get(\"os_family\") == \"RedHat\":\n        return \"yum\"\n```\n\n### Context Caching Pattern\n\n```python\ndef _get_cached_value():\n    \"\"\"Safe context usage for standalone calls\"\"\"\n    try:\n        context = __context__\n    except NameError:\n        context = {}\n\n    if \"my_key\" not in context:\n        context[\"my_key\"] = expensive_operation()\n    return context.get(\"my_key\")\n```\n\n## Error Handling\n\nUse Salt's exception classes for consistent error handling:\n\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Command/operation failed\n    SaltInvocationError,      # Bad function arguments\n    MinionError,              # General minion error\n    CommandNotFoundError,     # Binary not found\n)\n\ndef my_function(path, required_param):\n    \"\"\"\n    Example with proper error handling\n    \"\"\"\n    # Validate required parameters\n    if not required_param:\n        raise SaltInvocationError(\"required_param is mandatory\")\n\n    # Validate parameter values\n    if not os.path.exists(path):\n        raise SaltInvocationError(f\"Path does not exist: {path}\")\n\n    # Check for required binary\n    binary = salt.utils.path.which(\"mytool\")\n    if not binary:\n        raise CommandNotFoundError(\"mytool binary not found in PATH\")\n\n    # Execute operation with error handling\n    try:\n        result = __salt__[\"cmd.run_all\"](f\"{binary} {path}\")\n    except Exception as exc:\n        raise CommandExecutionError(\n            f\"Failed to run mytool on {path}: {exc}\"\n        )\n\n    # Check command result\n    if result[\"retcode\"] != 0:\n        raise CommandExecutionError(\n            f\"mytool failed: {result['stderr']}\"\n        )\n\n    return result[\"stdout\"]\n```\n\n### When to Use Each Exception\n\n- **`SaltInvocationError`**: Invalid function arguments, missing required parameters\n- **`CommandExecutionError`**: Operation failed (file not found, command failed, etc.)\n- **`CommandNotFoundError`**: Required binary not in PATH\n- **`MinionError`**: General minion-side errors\n- **Standard exceptions**: Use Python's built-in exceptions when appropriate\n\n## Module Checklists\n\n### Execution Module Checklist\n\n- [ ] Proper imports (logging, salt.exceptions, salt.utils.*)\n- [ ] Logger configured: `log = logging.getLogger(__name__)`\n- [ ] `__virtual__()` function if platform/dependency specific\n- [ ] Docstrings on all functions with CLI examples\n- [ ] Input validation with proper exceptions\n- [ ] Error handling with CommandExecutionError/SaltInvocationError\n- [ ] Use `__context__` for caching expensive operations\n- [ ] Unit tests covering success and error cases\n\n### State Module Checklist\n\n- [ ] Returns correct dict structure (name, result, changes, comment)\n- [ ] Validates input parameters\n- [ ] Checks current state before making changes\n- [ ] Handles test mode (`__opts__[\"test\"]`)\n- [ ] Verifies changes after applying\n- [ ] Sets `result` correctly (True/False/None)\n- [ ] Populates `changes` dict with before/after\n- [ ] Provides clear `comment` explaining what happened\n\n## Reference Files\n\n- Simple example: `salt/modules/test.py`\n- Complex example: `salt/modules/file.py`\n- Platform-specific: `salt/modules/aptpkg.py`, `salt/modules/systemd_service.py`\n- State examples: `salt/states/file.py`, `salt/states/pkg.py`\n- Documentation: `doc/topics/development/modules/developing.rst`, `doc/ref/states/writing.rst`\n","agents/docs/troubleshooting.md":"# Subagent: troubleshooting\n\nPath: `agents/docs/troubleshooting.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/docs/troubleshooting.md)","agents/mcp/README.md":"# Subagent: readme\n\nPath: `agents/mcp/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/mcp/README.md)","agents/mcp/mcp-config.json":"# Mcp Skill\n\nPath: `agents/mcp/mcp-config.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/mcp/mcp-config.json)","agents/reports/silent_drop_audit_3008_to_master.md":"# Subagent: silent_drop_audit_3008_to_master\n\nPath: `agents/reports/silent_drop_audit_3008_to_master.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/reports/silent_drop_audit_3008_to_master.md)","agents/mcp/salt_test/README.md":"# Subagent: readme\n\nPath: `agents/mcp/salt_test/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/mcp/salt_test/README.md)","scripts/suse/yum/plugins/README.md":"# plugins Documentation\n\nPath: `scripts/suse/yum/plugins/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/scripts/suse/yum/plugins/README.md)","scripts/suse/zypper/plugins/commit/README.md":"# commit Documentation\n\nPath: `scripts/suse/zypper/plugins/commit/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/scripts/suse/zypper/plugins/commit/README.md)"},"items":[{"name":"mcp-config.json","path":"agents/mcp/mcp-config.json","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/mcp/mcp-config.json","title":"Mcp Skill","category":"anthropic-skill","format":"json","content":"# Mcp Skill\n\nPath: `agents/mcp/mcp-config.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/mcp/mcp-config.json)","tokens":300,"sizeBytes":159},{"name":"CLAUDE.md","path":"agents/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/CLAUDE.md","title":"agents - Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# Salt Development Instructions for Claude\n\nYou are assisting with development of Salt, a powerful infrastructure automation and configuration management system. This document provides essential quick reference information and links to detailed documentation.\n\n## Table of Contents\n\n1. [Coding Philosophy](#coding-philosophy)\n2. [Architecture Quick Reference](#architecture-quick-reference)\n3. [Dunder Dictionaries](#dunder-dictionaries)\n4. [Module Basics](#module-basics)\n5. [State Basics](#state-basics)\n6. [Git Workflow](#git-workflow)\n7. [Checklists](#checklists)\n8. [Detailed Documentation](#detailed-documentation)\n\n---\n\n## Coding Philosophy\n\n**CRITICAL**: Approach Salt development as a CPython core developer:\n- Know memory management and garbage collection implications\n- Understand performance trade-offs between implementations\n- Be aware of CPython implementation details and GIL impact\n\n**TDD for Bug Fixes**: Always write a failing test first, verify it fails, fix the bug, verify it passes, then run related tests.\n\n**Zen of Python**: Readability counts. Explicit is better than implicit. Simple is better than complex. Errors should never pass silently.\n\n---\n\n## Architecture Quick Reference\n\nSalt is a Python-based configuration management system using master-minion architecture with an event-driven plugin system.\n\n**Key Module Types:**\n- **Execution Modules** (`salt/modules/`) - CLI commands on minions (264+)\n- **State Modules** (`salt/states/`) - Declarative configuration (126+)\n- **Utils** (`salt/utils/`) - Shared utility functions (170+)\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete architecture details.**\n\n---\n\n## Dunder Dictionaries\n\nSalt injects special dictionaries into module scope via the loader:\n\n**Always Available:**\n- `__opts__` - Configuration options (test mode, minion ID, etc.)\n- `__grains__` - System information (OS, platform, custom data)\n- `__pillar__` - Secure data for this minion\n- `__context__` - Per-run cache (persists during Salt execution)\n\n**Module-Specific:**\n- `__salt__` - Access other execution modules\n- `__utils__` - Utility functions\n- `__states__` - Call other states (in state modules only)\n\n**Example: Context Caching**\n```python\nif \"cache_key\" not in __context__:\n    __context__[\"cache_key\"] = expensive_operation()  # Only once per run\nreturn __context__[\"cache_key\"]\n```\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for detailed loader system documentation.**\n\n---\n\n## Module Basics\n\n**Execution Module Structure:**\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Return True to load, False or (False, reason) to skip\"\"\"\n    if salt.utils.platform.is_windows():\n        return False, \"Not available on Windows\"\n    return True\n\ndef my_function(name, param=None):\n    \"\"\"\n    CLI Example::\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    if not name:\n        raise salt.exceptions.SaltInvocationError(\"name is required\")\n    return __salt__[\"cmd.run\"](f\"command {name}\")\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete templates and patterns.**\n\n---\n\n## State Basics\n\n**State Return Structure (REQUIRED):**\n```python\nret = {\n    \"name\": name,        # Name parameter\n    \"result\": False,     # True (success), False (fail), None (test mode)\n    \"changes\": {},       # Dict of changes: {\"old\": ..., \"new\": ...}\n    \"comment\": \"\"        # What happened\n}\n```\n\n**State Flow:**\n1. Validate input\n2. Check current state\n3. If already correct: return success\n4. If test mode (`__opts__[\"test\"]`): return `result=None` with proposed changes\n5. Make changes and return result\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete state templates.**\n\n---\n\n## Git Workflow\n\n### CRITICAL RULES\n\n1. **NO attribution lines** - **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n2. **Use imperative mood** - \"Fix bug\", not \"Fixed bug\" or \"Fixes bug\"\n3. **Reference issues** - Use `#NNNN` to reference GitHub issues\n4. **Small, focused commits** - One logical change per commit\n5. **Rebase before push** - Keep history linear and clean\n\n**Good Commit:**\n```\nFix loader cache invalidation on module reload\n\nWhen modules are reloaded, the loader cache was not properly\ninvalidated, causing stale references. Clear the cache dict\non reload to ensure fresh module imports.\n\nFixes #12345\n```\n\n**Bad Commit (NEVER DO THIS):**\n```\nFixed some bugs\n\nCo-Authored-By: Claude <noreply@anthropic.com>\n```\n\n**See [agents/docs/git-and-ci.md](agents/docs/git-and-ci.md) for complete git workflow and CI reproduction.**\n\n---\n\n## Checklists\n\n### Execution Module Checklist\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform/dependency specific\n- [ ] Input validation with `SaltInvocationError`\n- [ ] Error handling with `CommandExecutionError`\n- [ ] Context caching for expensive operations\n- [ ] `salt.utils.files.fopen()` for any file I/O, never bare `open()`\n- [ ] Unit tests\n\n### State Module Checklist\n- [ ] Returns dict with: name, result, changes, comment\n- [ ] `result=None` for test mode (`__opts__[\"test\"]`)\n- [ ] Check current state before changing\n- [ ] Populate changes dict with old/new values\n- [ ] Clear comment explaining outcome\n\n### Common Imports\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.args\nimport salt.utils.platform\nimport salt.utils.path\n\nlog = logging.getLogger(__name__)\n```\n\n### Logging\n```python\n# Use lazy formatting (NOT f-strings)\nlog.debug(\"Processing file: %s\", filename)  # Good\nlog.debug(f\"Processing file: {filename}\")   # Bad\n```\n\n**Never log sensitive data** (passwords, tokens, keys).\n\n### File Handling\n```python\n# Use salt.utils.files.fopen(), NOT the bare open() builtin\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nThis applies everywhere, including unit tests.\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Operation failed\n    SaltInvocationError,      # Invalid arguments\n    CommandNotFoundError,     # Binary not found\n)\n```\n\n---\n\n## Detailed Documentation\n\n**Essential guides in `agents/docs/`:**\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - Virtual environment setup (venv310 and venv312), platform-specific dependencies, installation verification\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete Salt architecture, all module types, loader system, event bus\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates for execution and state modules, `__virtual__()` patterns, decorators\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test structure, unit test templates, mocking patterns, running tests\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit guidelines, PR workflow, CI failure reproduction, container testing\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Import issues, module discovery, ZeroMQ, Python 3.11+ compatibility, lazy loading\n\n---\n\n## Key Example Files\n\n- Simple module: `salt/modules/test.py`\n- Complex module: `salt/modules/file.py`\n- Package manager: `salt/modules/aptpkg.py`\n- Service: `salt/modules/systemd_service.py`\n- State examples: `salt/states/file.py`, `salt/states/pkg.py`\n\n---\n\n## Summary\n\nWhen writing Salt code:\n\n1. **Follow the patterns** - Use existing modules as templates\n2. **Use dunders correctly** - `__salt__`, `__opts__`, `__grains__`, `__context__`\n3. **Implement `__virtual__()`** - Declare platform and dependency requirements\n4. **Handle errors properly** - Use Salt's exception classes\n5. **Write tests** - Every new function needs unit tests\n6. **Document thoroughly** - Docstrings with CLI examples are required\n7. **Think idempotent** - Operations should be safe to repeat\n8. **Cache wisely** - Use `__context__` for expensive operations\n\n**When in doubt, look at existing modules for patterns and refer to the detailed documentation.**\n\n**Remember: NEVER add AI attribution lines to commits!**\n","isInternal":false,"tokens":1977,"sizeBytes":8375},{"name":"GEMINI.md","path":"agents/GEMINI.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/GEMINI.md","title":"agents - Gemini Agent & Antigravity Instructions","category":"gemini-rule","format":"markdown","content":"# Salt Development Instructions for Gemini\n\nYou are assisting with development of Salt, a powerful infrastructure automation and configuration management system written in Python. This guide provides essential quick reference and links to detailed documentation.\n\n## Coding Philosophy\n\n**CRITICAL**: Write code as a CPython core developer:\n- Understand Python internals, memory management, GC\n- Know performance implications\n- Be aware of implementation details\n\n**Zen of Python**: Readability counts. Explicit > implicit. Simple > complex. Errors never pass silently.\n\n**TDD for Bug Fixes**:\n1. Write failing test first → 2. Verify it fails → 3. Fix bug (minimal changes) → 4. Verify test passes → 5. Run related tests\n\n---\n\n## Architecture Quick Reference\n\nSalt is a Python-based configuration management system:\n- **Master-Minion Architecture**: Central master controls distributed minions\n- **Event-Driven**: Real-time communication via event bus\n- **Plugin-Based**: Extensible through loader system\n\n**Key Module Types:**\n- **Execution Modules** (`salt/modules/`): CLI commands on minions (264+)\n- **State Modules** (`salt/states/`): Declarative configuration (126+)\n- **Utils** (`salt/utils/`): Shared utility functions (170+)\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete architecture.**\n\n---\n\n## Dunder Dictionaries\n\nSalt injects special variables into module scope:\n\n**Always Available:**\n- `__opts__` - Config options: `__opts__.get(\"id\")`, `__opts__[\"test\"]`\n- `__grains__` - System data: `__grains__.get(\"os_family\")`\n- `__pillar__` - Secure data: `__pillar__.get(\"password\")`\n- `__context__` - Per-run cache for expensive operations\n\n**Module-Specific:**\n- `__salt__` - Other execution modules: `__salt__[\"pkg.install\"](\"nginx\")`\n- `__utils__` - Utility functions: `__utils__[\"files.is_text\"](path)`\n- `__states__` - State modules (in state modules only)\n\n**Context Caching Pattern:**\n```python\nif \"pkg_list\" not in __context__:\n    __context__[\"pkg_list\"] = expensive_fetch()  # Once per run\nreturn __context__[\"pkg_list\"]\n```\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for loader system details.**\n\n---\n\n## Execution Module Structure\n\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Returns True to load, False or (False, reason) to skip\"\"\"\n    if not salt.utils.path.which(\"required_binary\"):\n        return False, \"required_binary not found\"\n    return True\n\ndef my_function(name, param=None):\n    \"\"\"\n    CLI Example:\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    if not name:\n        raise salt.exceptions.SaltInvocationError(\"name required\")\n    return __salt__[\"cmd.run\"](f\"command {name}\")\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete templates.**\n\n---\n\n## State Module Structure\n\n**Required Return:**\n```python\n{\n    \"name\": name,              # Resource identifier\n    \"result\": True/False/None, # Success/Failure/Test mode\n    \"changes\": {},             # {\"old\": ..., \"new\": ...}\n    \"comment\": \"\"              # What happened\n}\n```\n\n**State Flow:**\n1. Validate input → 2. Check current state → 3. If correct: return success → 4. If test mode: return `result=None` with proposed changes → 5. Make changes → 6. Return result\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete state templates.**\n\n---\n\n## Common Patterns\n\n### Logging\n```python\nlog = logging.getLogger(__name__)\n# Use lazy formatting (NOT f-strings)\nlog.debug(\"Processing: %s\", filename)  # Good\nlog.debug(f\"Processing: {filename}\")   # Bad\n```\n**Never log sensitive data.**\n\n### File Handling\n```python\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nApplies to modules, states, utils, and tests alike.\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,   # Operation failed\n    SaltInvocationError,     # Invalid arguments\n    CommandNotFoundError,    # Binary not found\n)\n```\n\n### Platform Detection\n```python\nimport salt.utils.platform\nif salt.utils.platform.is_windows():\n    # Windows-specific\nif __grains__.get(\"os_family\") == \"Debian\":\n    # Debian-based\n```\n\n---\n\n## Git Workflow\n\n### CRITICAL RULES\n\n1. **NO attribution lines** - **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n2. **Imperative mood** - \"Fix bug\", not \"Fixed bug\"\n3. **Reference issues** - Use `#NNNN`\n4. **Small commits** - One logical change per commit\n5. **Rebase before push** - Linear history\n\n**Good Commit:**\n```\nFix loader cache invalidation on module reload\n\nClear cache dict on reload to ensure fresh module imports.\n\nFixes #12345\n```\n\n**Bad (NEVER):**\n```\nFixed bugs\nCo-Authored-By: Claude <noreply@anthropic.com>\n```\n\n**See [agents/docs/git-and-ci.md](agents/docs/git-and-ci.md) for complete workflow.**\n\n---\n\n## Checklists\n\n### Execution Module\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform-specific\n- [ ] Input validation (`SaltInvocationError`)\n- [ ] Error handling (`CommandExecutionError`)\n- [ ] Context caching for expensive ops\n- [ ] Unit tests\n\n### State Module\n- [ ] Returns: name, result, changes, comment\n- [ ] `result=None` for test mode\n- [ ] Check current state first\n- [ ] Populate changes dict\n- [ ] Clear comments\n\n---\n\n## Detailed Documentation\n\n**Essential guides in `agents/docs/`:**\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - venv310/venv312 setup, dependencies, verification\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete architecture, module types, loader, event bus\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates, `__virtual__()` patterns, decorators\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test structure, templates, mocking, running tests\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit guidelines, PR workflow, CI reproduction\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Import issues, ZeroMQ, Python 3.11+, lazy loading\n\n---\n\n## Key Example Files\n\n- Simple: `salt/modules/test.py`\n- Complex: `salt/modules/file.py`\n- Package: `salt/modules/aptpkg.py`\n- Service: `salt/modules/systemd_service.py`\n- States: `salt/states/file.py`, `salt/states/pkg.py`\n\n---\n\n## Summary\n\n1. **Follow patterns** - Use existing modules as templates\n2. **Use dunders** - `__salt__`, `__opts__`, `__grains__`, `__context__`\n3. **Implement `__virtual__()`** - Declare dependencies\n4. **Handle errors** - Use Salt exception classes\n5. **Write tests** - Every function needs tests\n6. **Document** - Docstrings with CLI examples required\n7. **Think idempotent** - Safe to repeat\n8. **Cache wisely** - Use `__context__`\n\n**When in doubt, check existing modules and refer to detailed docs.**\n\n**Remember: NEVER add AI attribution lines to commits!**\n","isInternal":false,"tokens":1771,"sizeBytes":7186},{"name":"copilot-instructions.md","path":".github/copilot-instructions.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/.github/copilot-instructions.md","title":"GitHub Copilot Instructions","category":"copilot-instructions","format":"markdown","content":"../agents/COPILOT.md","isInternal":false,"tokens":7,"sizeBytes":20},{"name":"COPILOT.md","path":"agents/COPILOT.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/COPILOT.md","title":"agents - GitHub Copilot Context & Rules","category":"copilot-instructions","format":"markdown","content":"# GitHub Copilot Instructions for Salt Development\n\n## Project Context\n\nThis is the Salt project - a Python-based configuration management and remote execution system using master-minion architecture with a dynamic loader system for plugins.\n\n## Coding Philosophy\n\n**CRITICAL**: Write code as a CPython core developer:\n- Deep understanding of Python internals, memory management, GC\n- Know performance implications of different implementations\n\n**Zen of Python**: Readability counts. Explicit > implicit. Simple > complex. Errors never pass silently.\n\n**TDD for Bug Fixes**: Write failing test → verify fail → fix bug → verify pass → run related tests\n\n---\n\n## Git Workflow\n\n**CRITICAL: NO attribution lines**\n- **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n- Use imperative mood: \"Fix bug\" not \"Fixed bug\"\n- Reference issues: `#NNNN`\n- Small, focused commits\n- Rebase before push\n\n---\n\n## Architecture\n\n**Salt components:**\n- **Master-Minion**: Central master controls distributed minions\n- **Event Bus**: Real-time communication\n- **Execution Modules** (`salt/modules/`): CLI commands (264+)\n- **State Modules** (`salt/states/`): Declarative configuration (126+)\n- **Utils** (`salt/utils/`): Shared utilities (170+)\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete details.**\n\n---\n\n## Code Patterns\n\n### Execution Module\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Return True to load, False or (False, reason) to skip\"\"\"\n    if not salt.utils.path.which(\"required_binary\"):\n        return False, \"required_binary not found\"\n    return True\n\ndef function_name(param1, param2=None):\n    \"\"\"\n    CLI Example::\n        salt '*' module.function_name value\n    \"\"\"\n    if not param1:\n        raise salt.exceptions.SaltInvocationError(\"param1 required\")\n    return __salt__[\"cmd.run\"](f\"command {param1}\")\n```\n\n### State Module Return\n```python\ndef managed(name, value=None):\n    ret = {\"name\": name, \"result\": False, \"changes\": {}, \"comment\": \"\"}\n\n    # Validate → Check current → If correct return → If test mode return None → Apply → Return\n    if not name:\n        ret[\"comment\"] = \"Name required\"\n        return ret\n\n    current = __salt__[\"module.get\"](name)\n    if current == value:\n        ret[\"result\"] = True\n        ret[\"comment\"] = \"Already correct\"\n        return ret\n\n    if __opts__[\"test\"]:\n        ret[\"result\"] = None\n        ret[\"changes\"] = {\"old\": current, \"new\": value}\n        ret[\"comment\"] = \"Would change\"\n        return ret\n\n    try:\n        __salt__[\"module.set\"](name, value)\n        ret[\"result\"] = True\n        ret[\"changes\"] = {\"old\": current, \"new\": value}\n        ret[\"comment\"] = \"Updated\"\n    except Exception as exc:\n        ret[\"comment\"] = str(exc)\n\n    return ret\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete templates.**\n\n---\n\n## Dunder Variables\n\nSalt injects these into module scope:\n\n- `__opts__`: Config - `__opts__[\"test\"]`, `__opts__.get(\"id\")`\n- `__grains__`: System data - `__grains__.get(\"os_family\")`\n- `__pillar__`: Secure data - `__pillar__.get(\"password\")`\n- `__context__`: Per-run cache for expensive operations\n- `__salt__`: Execution modules - `__salt__[\"pkg.install\"](\"nginx\")`\n- `__utils__`: Utilities - `__utils__[\"files.is_text\"](path)`\n- `__states__`: State modules (states only)\n\n**Context caching pattern:**\n```python\nif \"pkg_list\" not in __context__:\n    __context__[\"pkg_list\"] = expensive_fetch()\nreturn __context__[\"pkg_list\"]\n```\n\n---\n\n## Required Elements\n\n### Logging\n```python\nlog = logging.getLogger(__name__)\nlog.debug(\"Processing: %s\", filename)  # Good - lazy formatting\nlog.debug(f\"Processing: {filename}\")   # Bad - f-strings evaluated early\n```\n**Never log secrets.**\n\n### File Handling\n```python\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nApplies to modules, states, utils, and tests alike.\n\n### Docstrings\nEvery function requires description, parameters, and CLI example:\n```python\ndef my_function(name, value=None):\n    \"\"\"\n    Description of functionality\n\n    name\n        Description of name parameter\n\n    CLI Example::\n        salt '*' module.my_function foo value=bar\n    \"\"\"\n```\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Operation failures\n    SaltInvocationError,      # Invalid arguments\n    CommandNotFoundError,     # Missing binaries\n)\n\nif not required_param:\n    raise SaltInvocationError(\"required_param must be provided\")\n```\n\n### __virtual__ Function\nDeclare platform/dependency requirements:\n```python\n# Platform check\ndef __virtual__():\n    if salt.utils.platform.is_windows():\n        return False, \"Not available on Windows\"\n    return True\n\n# Virtual name (for interface modules)\n__virtualname__ = \"pkg\"\ndef __virtual__():\n    if __grains__.get(\"os_family\") == \"Debian\":\n        return __virtualname__\n    return False\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for all patterns.**\n\n---\n\n## Development Environment\n\n**Two environments required:**\n- **venv310**: Testing 3006.x/3007.x branches\n- **venv312**: Testing master branch + pre-commit\n\n```bash\n# venv310\npython3.10 -m venv venv310 && source venv310/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.10/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.10/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && deactivate\n\n# venv312\npython3.12 -m venv venv312 && source venv312/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.11/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.11/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && pre-commit install && deactivate\n```\n\n**Always use full paths:** `./venv312/bin/pytest` (master); `./venv310/bin/pytest` (3006.x/3007.x)\n\n**See [agents/docs/development-setup.md](agents/docs/development-setup.md) for complete setup.**\n\n---\n\n## Testing\n\n### Pytest Fixture\n```python\nimport pytest\nimport salt.modules.mymodule as mymodule\n\n@pytest.fixture\ndef configure_loader_modules():\n    return {\n        mymodule: {\n            \"__opts__\": {\"test\": False},\n            \"__grains__\": {\"os\": \"Linux\"},\n            \"__salt__\": {},\n        }\n    }\n\ndef test_function():\n    result = mymodule.my_function(\"test\")\n    assert result == \"expected\"\n```\n\n### Running Tests\n```bash\n# Nox\nnox -e test-3 -- tests/pytests/unit/test_loader.py\nnox -e test-3 -- --lf  # Last failed\n\n# Direct (faster)\n./venv312/bin/pytest tests/pytests/unit/test_foo.py -v\n```\n\n**See [agents/docs/testing.md](agents/docs/testing.md) for complete guide.**\n\n---\n\n## Common Patterns\n\n### Platform Detection\n```python\nsalt.utils.platform.is_windows()\nsalt.utils.platform.is_linux()\n__grains__.get(\"os_family\")  # \"Debian\", \"RedHat\", etc.\n```\n\n### Run Commands\n```python\noutput = __salt__[\"cmd.run\"](\"ls -la\")\nresult = __salt__[\"cmd.run_all\"](\"cmd\")  # {\"retcode\": 0, \"stdout\": \"...\", \"stderr\": \"...\"}\n```\n\n### File Operations\n```python\ncontent = __salt__[\"file.read\"](path)\n__salt__[\"file.write\"](path, content)\n__salt__[\"file.file_exists\"](path)\n```\n\n---\n\n## Checklists\n\n### Execution Module\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform-specific\n- [ ] Input validation (`SaltInvocationError`)\n- [ ] Error handling (`CommandExecutionError`)\n- [ ] Context caching for expensive ops\n- [ ] Unit tests\n\n### State Module\n- [ ] Returns: name, result (True/False/None), changes, comment\n- [ ] result=None for test mode\n- [ ] Check current state before modifying\n- [ ] Populate changes dict with old/new\n\n---\n\n## Documentation\n\n**Essential guides in `agents/docs/`:**\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - venv310/venv312 setup, dependencies\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete architecture, module types, loader\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates, all patterns\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test templates, mocking, running tests\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit guidelines, PR workflow, CI reproduction\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Common issues and solutions\n\n**Key example files:** `salt/modules/test.py`, `salt/modules/file.py`, `salt/states/pkg.py`\n\n---\n\n## Key Principles\n\n- **Idempotency**: Safe to run multiple times\n- **Testing**: Every function needs tests\n- **Documentation**: Docstrings with CLI examples\n- **Error Handling**: Use Salt exception classes\n- **Performance**: Cache in `__context__`\n- **Security**: Validate inputs, never log secrets\n","isInternal":false,"tokens":2250,"sizeBytes":9187},{"name":"README.md","path":"scripts/suse/yum/plugins/README.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/scripts/suse/yum/plugins/README.md","title":"plugins Documentation","category":"plugin-manifest","format":"markdown","content":"# plugins Documentation\n\nPath: `scripts/suse/yum/plugins/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/scripts/suse/yum/plugins/README.md)","tokens":300,"sizeBytes":187},{"name":"README.md","path":"scripts/suse/zypper/plugins/commit/README.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/scripts/suse/zypper/plugins/commit/README.md","title":"commit Documentation","category":"plugin-manifest","format":"markdown","content":"# commit Documentation\n\nPath: `scripts/suse/zypper/plugins/commit/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/scripts/suse/zypper/plugins/commit/README.md)","tokens":300,"sizeBytes":206},{"name":"CURSOR.md","path":"agents/CURSOR.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/CURSOR.md","title":"Subagent: cursor","category":"subagent-persona","format":"markdown","content":"# Salt Development Rules for Cursor\n\nYou are working on Salt, a Python-based configuration management and remote execution system. Follow these rules when writing or modifying Salt code.\n\n## Critical Rules\n\n**CODING PHILOSOPHY:**\n- Write as a CPython core developer - know internals, memory management, GC, performance\n- Zen of Python: Readability counts. Explicit > implicit. Simple > complex\n- TDD for bugs: Write failing test → verify fail → fix → verify pass → run related tests\n\n**GIT WORKFLOW:**\n- **NEVER** add \"Generated with Claude Code\" or \"Co-Authored-By: Claude\" to commits\n- Use imperative mood: \"Fix bug\" not \"Fixed bug\"\n- Reference issues: `#NNNN`\n- Small, focused commits\n- Rebase before push\n\n**LINTING:**\n- Black: 88 char lines (`black .`)\n- isort: Profile 3, trailing commas (`isort .`)\n- Lint: `nox -e lint-salt`\n\n---\n\n## Architecture Quick Reference\n\n**Salt Components:**\n- Master-Minion architecture for distributed configuration management\n- Event-driven communication via event bus\n- Execution modules: CLI commands (`salt/modules/`) - 264+\n- State modules: Declarative config (`salt/states/`) - 126+\n- Utils: Shared functions (`salt/utils/`) - 170+\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete architecture.**\n\n---\n\n## Module Structure\n\n### Execution Module Skeleton\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Returns True/False/(False, reason)\"\"\"\n    if not salt.utils.path.which(\"required_cmd\"):\n        return False, \"required_cmd not found\"\n    return True\n\ndef my_function(name, param=None):\n    \"\"\"\n    CLI Example::\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    if not name:\n        raise salt.exceptions.SaltInvocationError(\"name required\")\n    return __salt__[\"cmd.run\"](f\"command {name}\")\n```\n\n### State Module Return Structure\n```python\nret = {\n    \"name\": name,        # Resource name\n    \"result\": False,     # True/False/None (test mode)\n    \"changes\": {},       # {\"old\": ..., \"new\": ...}\n    \"comment\": \"\"        # What happened\n}\n```\n\n**State flow:** Validate → Check current → If correct return → If test mode return None → Make changes → Return result\n\n**Complete templates: [agents/docs/module-templates.md](agents/docs/module-templates.md)**\n\n---\n\n## Dunder Dictionaries\n\n- `__opts__`: Config (`__opts__[\"test\"]`, `__opts__.get(\"id\")`)\n- `__grains__`: System info (`__grains__.get(\"os_family\")`)\n- `__pillar__`: Secure data (`__pillar__.get(\"password\")`)\n- `__context__`: Per-run cache - use for expensive operations\n- `__salt__`: Execution modules (`__salt__[\"pkg.install\"](\"nginx\")`)\n- `__utils__`: Utilities (`__utils__[\"files.is_text\"](path)`)\n- `__states__`: State modules (states only)\n\n**Context cache pattern:**\n```python\nif \"cache_key\" not in __context__:\n    __context__[\"cache_key\"] = expensive_operation()\nreturn __context__[\"cache_key\"]\n```\n\n---\n\n## Common Patterns\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Operation failed\n    SaltInvocationError,      # Bad arguments\n    CommandNotFoundError,     # Binary not found\n)\n```\n\n### Logging\n```python\nlog = logging.getLogger(__name__)\nlog.debug(\"Processing %s\", filename)  # Good - lazy formatting\nlog.debug(f\"Processing {filename}\")   # Bad - f-strings evaluated early\n```\n**Never log secrets.**\n\n### File Handling\n```python\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nApplies to modules, states, utils, and tests alike.\n\n### Platform Detection\n```python\nsalt.utils.platform.is_windows()\nsalt.utils.platform.is_linux()\n__grains__.get(\"os_family\")  # \"Debian\", \"RedHat\", etc.\n```\n\n### Decorators\n```python\nfrom salt.utils.decorators.path import which\nfrom salt.utils.decorators import depends, memoize\n\n@which(\"systemctl\")  # Require binary\n@depends(\"docker\")   # Require Python module\n@memoize            # Cache forever\n```\n\n---\n\n## Development Environment\n\n**Two environments required:**\n- **venv310**: Testing 3006.x/3007.x branches\n- **venv312**: Testing master branch + pre-commit\n\n```bash\n# venv310\npython3.10 -m venv venv310 && source venv310/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.10/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.10/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && deactivate\n\n# venv312\npython3.12 -m venv venv312 && source venv312/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.11/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.11/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && pre-commit install && deactivate\n```\n\n**Always use full paths:** `./venv312/bin/python`, `./venv312/bin/pytest` (master); `./venv310/...` for 3006.x/3007.x\n\n**Complete setup: [agents/docs/development-setup.md](agents/docs/development-setup.md)**\n\n---\n\n## Testing\n\n### Pytest Structure\n```python\nimport pytest\nimport salt.modules.mymodule as mymodule\n\n@pytest.fixture\ndef configure_loader_modules():\n    return {\n        mymodule: {\n            \"__opts__\": {\"test\": False},\n            \"__grains__\": {\"os\": \"Linux\"},\n            \"__salt__\": {},\n        }\n    }\n\ndef test_function():\n    result = mymodule.my_function(\"test\")\n    assert result == \"expected\"\n```\n\n### Running Tests\n```bash\n# Nox\nnox -e test-3 -- tests/pytests/unit/test_loader.py\nnox -e test-3 -- --lf  # Last failed\n\n# Direct (faster)\n./venv312/bin/pytest tests/pytests/unit/test_foo.py -v\n```\n\n**Complete guide: [agents/docs/testing.md](agents/docs/testing.md)**\n\n---\n\n## Code Checklist\n\n### Execution Module\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform-specific\n- [ ] Input validation (`SaltInvocationError`)\n- [ ] Error handling (`CommandExecutionError`)\n- [ ] Context caching for expensive ops\n- [ ] Unit tests\n\n### State Module\n- [ ] Returns: name, result (True/False/None), changes, comment\n- [ ] result=None for test mode\n- [ ] Check current state before modifying\n- [ ] Populate changes dict with old/new\n\n---\n\n## Quick Reference\n\n### Common Imports\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.args\nimport salt.utils.platform\nimport salt.utils.path\n\nlog = logging.getLogger(__name__)\n```\n\n### Run Commands\n```python\noutput = __salt__[\"cmd.run\"](\"ls -la\")\nresult = __salt__[\"cmd.run_all\"](\"cmd\")  # {\"retcode\": 0, \"stdout\": \"...\", \"stderr\": \"...\"}\n```\n\n### File Operations\n```python\ncontent = __salt__[\"file.read\"](path)\n__salt__[\"file.write\"](path, content)\nexists = __salt__[\"file.file_exists\"](path)\n```\n\n---\n\n## Documentation Links\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - venv setup, verification\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete architecture, all module types\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates, patterns\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test templates, mocking, container testing\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit rules, PR workflow, CI reproduction\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Common issues and solutions\n\n**Key example files:** `salt/modules/test.py`, `salt/modules/file.py`, `salt/states/pkg.py`\n\n---\n\n## Key Principles\n\n- **Idempotency**: Safe to run multiple times\n- **Testing**: Every function needs tests\n- **Documentation**: Docstrings with CLI examples\n- **Error Handling**: Use Salt exceptions\n- **Performance**: Cache in `__context__`\n- **Security**: Validate inputs, never log secrets\n","isInternal":false,"tokens":1995,"sizeBytes":8012},{"name":"architecture.md","path":"agents/docs/architecture.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/docs/architecture.md","title":"Subagent: architecture","category":"subagent-persona","format":"markdown","content":"# Salt Architecture\n\n## What is Salt?\n\nSalt is a Python-based configuration management system using a master-minion architecture:\n- **Master**: Central server that manages configuration and sends commands\n- **Minion**: Agent running on managed systems that executes commands\n- **Event Bus**: Real-time communication system between components\n- **Loader System**: Dynamic plugin system for extending functionality\n\n## Module Types\n\nSalt uses a loader system to discover and load different types of modules:\n\n### Execution Modules (`salt/modules/`)\nFunctions executed on minions (e.g., `pkg.install`, `file.read`). There are 264+ execution modules providing the CLI commands available via Salt.\n\n**Examples:**\n- `salt/modules/pkg.py` - Package management (virtual module)\n- `salt/modules/file.py` - File operations\n- `salt/modules/cmd.py` - Command execution\n- `salt/modules/systemd_service.py` - systemd service management\n\n### State Modules (`salt/states/`)\nDeclarative configuration management (e.g., `pkg.installed`, `file.managed`). There are 126+ state modules providing idempotent configuration.\n\n**Examples:**\n- `salt/states/pkg.py` - Package installation states\n- `salt/states/file.py` - File management states\n- `salt/states/service.py` - Service management states\n\n### Runner Modules (`salt/runners/`)\nMaster-side operations (e.g., `salt-run jobs.list_jobs`). Runners execute on the Salt master.\n\n### Wheel Modules (`salt/wheel/`)\nMaster configuration management (key management, config). Used for managing the Salt master itself.\n\n### Grain Modules (`salt/grains/`)\nStatic system information collection. Grains provide facts about the minion system (OS, CPU, memory, etc.).\n\n### Pillar Modules (`salt/pillar/`)\nDynamic configuration data backends. Pillars provide secure, targeted data to minions.\n\n### Beacon Modules (`salt/beacons/`)\nSystem monitoring triggers (watch for events). Beacons monitor system events and fire events on the event bus.\n\n### Engine Modules (`salt/engines/`)\nLong-running processes on master or minion. Engines provide persistent functionality.\n\n### Returner Modules (`salt/returners/`)\nStore or send command output to external systems (databases, message queues, etc.).\n\n### Renderer Modules (`salt/renderers/`)\nTemplate engines (Jinja, Mako, YAML, JSON). Renderers process state files.\n\n### Matcher Modules (`salt/matchers/`)\nTarget matching (glob, pcre, grain, pillar, compound). Matchers determine which minions execute commands.\n\n### Utility Modules (`salt/utils/`)\nShared utility functions (170+ modules). Utilities provide common functionality across Salt.\n\n## Directory Structure\n\n```\nsalt/\n├── master.py, minion.py, state.py  # Core daemon implementations\n├── loader/                         # Plugin loader system (LazyLoader)\n├── modules/                        # 264+ execution modules\n├── states/                         # 126+ state modules\n├── runners/, wheel/                # Master operations and config\n├── beacons/, engines/              # Monitoring triggers, long-running processes\n├── grains/, pillar/                # System info, config data backends\n├── renderers/, returners/          # Template engines, return data storage\n├── matchers/, transport/           # Targeting systems, communication layer\n├── utils/                          # 170+ utility modules\n└── ext/                            # Vendored third-party code\n\ntests/\n├── pytests/\n│   ├── unit/                       # Fast tests, no daemons/network\n│   ├── functional/                 # End-to-end, no external deps\n│   ├── integration/                # With daemons/network\n│   ├── scenarios/                  # Scenario-based testing\n│   └── pkg/                        # Package install/upgrade tests\n└── support/                        # Test helpers and fixtures\n```\n\n## Key Architectural Components\n\n### State Compiler\n\n`salt/state.py` compiles YAML/Jinja templates into execution chunks. Uses **NetworkX** for dependency graph management with requisites:\n\n- `require`: Must run after\n- `watch`: Run if watched state changes\n- `onchanges`: Run only if watched state changes\n- `onfail`: Run if watched state fails\n- `listen`: Listen for notifications\n- `prereq`: Run before (with test mode check)\n\nAll state operations are idempotent and support test mode (`test=True`).\n\n### Transport Layer\n\n`salt/transport/` handles communication between master and minions:\n\n- **ZeroMQ** (default): High-performance messaging\n- **TCP**: Alternative transport\n- **WebSocket**: For browser-based access\n\n### Security\n\n- **PKI-based authentication** (`salt/crypt.py`)\n- **AES encryption** for all payloads\n- **Master key acceptance workflow** prevents unauthorized access\n- **External auth (eauth)** for Salt API access\n\n### Event Bus\n\n`salt/utils/event.py` provides ZeroMQ pub/sub with namespaced event tags for real-time communication between all components.\n\nEvents are namespaced with tags like:\n- `salt/job/<jid>/new` - New job started\n- `salt/job/<jid>/ret/<minion_id>` - Job return from minion\n- `salt/auth` - Authentication events\n- `salt/minion/<minion_id>/start` - Minion started\n\n### Loader System\n\nThe loader system (`salt/loader/`) dynamically discovers and loads modules at runtime. Key features:\n\n- **LazyLoader**: Loads modules on first access, not import\n- **Virtual names**: Modules can override their name via `__virtual__()`\n- **Dunder injection**: Injects `__salt__`, `__opts__`, `__grains__`, etc.\n- **Caching**: Modules are cached after first load\n\n## Async/Await Transition\n\nSalt is transitioning from **Tornado** coroutines to native **asyncio**:\n- Use native `async`/`await` syntax, not `@tornado.gen.coroutine`\n- Prefer asyncio patterns over Tornado patterns\n- Be aware of mixed async code during transition\n\n## Master-Minion Communication\n\n1. Minion authenticates with master via PKI\n2. Master accepts minion key\n3. Minion connects to master on port 4506 (request server)\n4. Master publishes commands on port 4505 (publish server)\n5. Minions subscribe to master's publish port\n6. Commands are encrypted with AES\n7. Results return via request server\n\n## File Server\n\nSalt includes a file server that serves files to minions:\n- `salt://` URLs reference files on the file server\n- Multiple backends: local, git, S3, HTTP, etc.\n- Files are cached on minions\n\nFor details on the Git backends and performance, see [GitFS Providers](gitfs-providers.md).\n\n## Targeting\n\nMinions can be targeted in multiple ways:\n- **Glob**: `*`, `web*.example.com`\n- **Grain**: `os:Ubuntu`, `kernel:Linux`\n- **Pillar**: `role:webserver`\n- **Compound**: `G@os:Ubuntu and webserver*`\n- **List**: `web1,web2,web3`\n- **PCRE**: Regular expressions\n\n## Performance Considerations\n\n### Lazy Loading\nModules only load on first access. Import-time side effects may not occur until first use of a module function.\n\n### Context Caching\nUse `__context__` to cache expensive operations within a Salt run (single execution).\n\n### Module Discovery\n- Module directories need `__init__.py`\n- Filename becomes namespace: `salt/modules/pkg.py` → `pkg.*`\n- Use `__virtualname__` for different name than filename\n- **Performance tip**: Filename should match virtualname when possible\n\n## Additional Resources\n\n### Documentation\n- Module development: `doc/topics/development/modules/developing.rst`\n- State writing guide: `doc/ref/states/writing.rst`\n- Contributing guidelines: `CONTRIBUTING.rst`\n\n### Example Modules\n- Simple execution module: `salt/modules/test.py`\n- Complex execution module: `salt/modules/file.py`\n- Package manager: `salt/modules/aptpkg.py`, `salt/modules/yumpkg.py`\n- Service manager: `salt/modules/systemd_service.py`\n- Simple state: `salt/states/pkg.py`\n- Complex state: `salt/states/file.py`\n","isInternal":false,"tokens":1759,"sizeBytes":7837},{"name":"development-setup.md","path":"agents/docs/development-setup.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/docs/development-setup.md","title":"Subagent: development-setup","category":"subagent-persona","format":"markdown","content":"# Salt Development Environment Setup\n\nThis guide covers setting up your local development environment for Salt development.\n\n## Virtual Environment Requirements\n\nSalt development requires setting up **two** virtual environments:\n\n- **venv310 (Python 3.10)**: For running tests on 3006.x and 3007.x branches\n- **venv314 (Python 3.14)**: For running tests on master branch AND running pre-commit hooks\n\n## Prerequisites\n\n- Python 3.10 and Python 3.14 installed on your system\n- Git repository cloned\n- Internet connection for downloading dependencies\n\n## Setup venv310 (Python 3.10)\n\nThis environment is used for testing on 3006.x and 3007.x branches.\n\n```bash\n# Create virtual environment\npython3.10 -m venv venv310\n\n# Activate\nsource venv310/bin/activate\n\n# Upgrade pip\npip install --upgrade pip setuptools wheel\n\n# Install platform-specific dependencies (choose your OS)\n# Linux:\npip install -r requirements/static/pkg/py3.10/linux.lock\n\n# macOS:\npip install -r requirements/static/pkg/py3.10/darwin.lock\n\n# Windows:\npip install -r requirements/static/pkg/py3.10/windows.lock\n\n# Install pytest requirements\npip install -r requirements/pytest.txt\n\n# Install Salt in editable mode\npip install -e .\n\n# Install tools dependencies (for using tools/ commands)\npip install -r requirements/static/ci/py3.10/tools.lock\n\n# Install pre-commit and python-tools-scripts\npip install pre-commit python-tools-scripts\n\n# Deactivate\ndeactivate\n```\n\n## Setup venv314 (Python 3.14)\n\nThis environment is used for testing on master branch and running pre-commit hooks.\n\n```bash\n# Create virtual environment\npython3.14 -m venv venv314\n\n# Activate\nsource venv314/bin/activate\n\n# Upgrade pip\npip install --upgrade pip setuptools wheel\n\n# Install platform-specific dependencies (choose your OS)\n# Linux:\npip install -r requirements/static/pkg/py3.14/linux.lock\n\n# macOS:\npip install -r requirements/static/pkg/py3.14/darwin.lock\n\n# Windows:\npip install -r requirements/static/pkg/py3.14/windows.lock\n\n# Install pytest requirements\npip install -r requirements/pytest.txt\n\n# Install Salt in editable mode\npip install -e .\n\n# Install tools dependencies\npip install -r requirements/static/ci/py3.14/tools.lock\n\n# Install pre-commit and python-tools-scripts\npip install pre-commit python-tools-scripts\n\n# Install pre-commit hooks\npre-commit install\n\n# Deactivate\ndeactivate\n```\n\n## Environment Usage\n\n### venv310 is used for:\n- Running tests on 3006.x and 3007.x branches\n- Direct pytest execution on older branches\n- Tools commands (`python -m tools`)\n\n### venv314 is used for:\n- Running tests on master branch\n- Running pre-commit hooks\n- Code formatting and linting checks\n- Pre-commit validation before commits\n\n## Verify Installation\n\n```bash\n# Test Salt import (master / default dev)\n./venv314/bin/python -c \"import salt.version; print(salt.version.__version__)\"\n\n# Run a simple test\n./venv314/bin/pytest tests/pytests/unit/test_loader.py -v\n\n# Test tools (3006.x/3007.x workflow)\n./venv310/bin/python -m tools --help\n```\n\n## IMPORTANT: Using Virtual Environments\n\nWhen running tests or tools, always use the full path to the venv executable or activate the venv first. This ensures you're using the correct Python environment with all dependencies installed.\n\n**Good examples:**\n```bash\n./venv310/bin/pytest tests/pytests/unit/test_foo.py -v\n./venv314/bin/pre-commit run --files salt/loader/lazy.py\n./venv310/bin/python -m tools ts pytest run tests/pytests/unit/\n```\n\n**Alternatively, activate first:**\n```bash\nsource venv314/bin/activate\npytest tests/pytests/unit/test_foo.py -v\ndeactivate\n```\n\n## Troubleshooting\n\n### ModuleNotFoundError: No module named 'ptscripts'\n\nIf you see this error when running `python -m tools`, you need to install the tools dependencies:\n\n```bash\n./venv310/bin/pip install -r requirements/static/ci/py3.10/tools.lock\n./venv310/bin/pip install python-tools-scripts\n```\n\n### venv not found\n\nIf you get \"No such file or directory\" for venv paths, make sure you've created the virtual environment and are running commands from the repository root directory.\n\n### Permission errors\n\nOn some systems you may need to use `python3.10` and `python3.14` explicitly instead of just `python3`.\n\n### Pre-commit hooks not running\n\nMake sure you've run `pre-commit install` in the venv314 environment:\n\n```bash\nsource venv314/bin/activate\npre-commit install\ndeactivate\n```\n","isInternal":false,"tokens":1051,"sizeBytes":4365},{"name":"git-and-ci.md","path":"agents/docs/git-and-ci.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/docs/git-and-ci.md","title":"Subagent: git-and-ci","category":"subagent-persona","format":"markdown","content":"# Git Workflow and CI\n\n## Commit Guidelines\n\n### CRITICAL RULES\n\n1. **NO attribution lines** - **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n2. **Use imperative mood** - \"Fix bug\", not \"Fixed bug\" or \"Fixes bug\"\n3. **Reference issues** - Use `#NNNN` to reference GitHub issues\n4. **Small, focused commits** - One logical change per commit\n5. **Rebase before push** - Keep history linear and clean\n\n### Good Commit Message Example\n\n```\nFix loader cache invalidation on module reload\n\nWhen modules are reloaded, the loader cache was not properly\ninvalidated, causing stale references. Clear the cache dict\non reload to ensure fresh module imports.\n\nFixes #12345\n```\n\n### Bad Commit Message Example\n\n```\nFixed some bugs\n\nCo-Authored-By: Claude <noreply@anthropic.com>\n```\n\n**Why this is bad:**\n- Past tense instead of imperative mood\n- Vague description\n- Contains AI attribution (NEVER do this)\n\n## PR Workflow\n\n### Finding and Checking PRs\n\n```bash\n# Find PR by branch name\ngh pr list --repo saltstack/salt --head <BRANCH-NAME> --limit 5\n\n# Check PR status\ngh pr view <PR-NUMBER> --repo saltstack/salt\ngh pr checks <PR-NUMBER> --repo saltstack/salt\n\n# Watch CI run\ngh run watch <RUN-ID> --repo saltstack/salt\n```\n\n## Branch Strategy\n\n- **Main PR branch**: `master` (not `main`)\n- **Release branches**: `3006.x`, `3007.x`, etc.\n- **Merge-forward branches**: `merge/3007.x/master-YY-MM-DD`\n\n## CI Failure Reproduction\n\nWhen CI tests fail, you can reproduce the exact failure locally using containers.\n\n### Prerequisites\n\n- Docker installed and running\n- Virtual environment setup (see [development-setup.md](development-setup.md))\n- `ptscripts` installed (`pip install python-tools-scripts`)\n\n### Step 1: Download CI Artifacts\n\nFirst, get the CI artifacts from the failed run:\n\n```bash\n./venv310/bin/python -m ptscripts ci download-artifacts --run-id <RUN-ID>\n```\n\nThe run ID can be found in the GitHub Actions URL or via `gh run list`.\n\n### Step 2: Create Container\n\nCreate a container using the same image as CI:\n\n```bash\n./venv310/bin/python -m ptscripts container create <IMAGE> --name <NAME>\ndocker start <NAME>\n```\n\n**Example container images:**\n- `ghcr.io/saltstack/salt-ci-containers/testing:debian-11`\n- `ghcr.io/saltstack/salt-ci-containers/testing:ubuntu-22.04`\n- `ghcr.io/saltstack/salt-ci-containers/testing:ubuntu-24.04`\n- `ghcr.io/saltstack/salt-ci-containers/testing:rockylinux-8`\n- `ghcr.io/saltstack/salt-ci-containers/testing:rockylinux-9`\n- `ghcr.io/saltstack/salt-ci-containers/testing:fedora-40`\n- `ghcr.io/saltstack/salt-ci-containers/testing:amazonlinux-2`\n- `ghcr.io/saltstack/salt-ci-containers/testing:amazonlinux-2023`\n- `ghcr.io/saltstack/salt-ci-containers/testing:photonos-3`\n- `ghcr.io/saltstack/salt-ci-containers/testing:photonos-4`\n- `ghcr.io/saltstack/salt-ci-containers/testing:photonos-5`\n\n### Step 3: Setup Container\n\n```bash\n# Decompress dependencies\ndocker exec <NAME> python3 -m nox -e decompress-dependencies -- linux x86_64\n\n# Create relenv toolchain symlink (Python 3.14+ only)\ndocker exec <NAME> bash -c \"mkdir -p /root/.local/relenv && ln -sf /root/.cache/relenv/toolchains /root/.local/relenv/toolchain\"\n```\n\n### Step 4: Run Tests\n\n```bash\ndocker exec <NAME> python3 -m nox -e ci-test-onedir -- <TEST-PATH> --run-slow -x -v\n```\n\n**Examples:**\n```bash\n# Run specific test file\ndocker exec mycontainer python3 -m nox -e ci-test-onedir -- tests/pytests/unit/modules/test_pkg.py --run-slow -x -v\n\n# Run specific test function\ndocker exec mycontainer python3 -m nox -e ci-test-onedir -- tests/pytests/unit/modules/test_pkg.py::test_install --run-slow -x -v\n\n# Run with pattern matching\ndocker exec mycontainer python3 -m nox -e ci-test-onedir -- tests/pytests/unit/ -k test_systemd --run-slow -x -v\n```\n\n### Step 5: Debug in Container\n\nIf you need to debug interactively:\n\n```bash\n# Shell into container\ndocker exec -it <NAME> bash\n\n# Inside container, activate environment and run tests\ncd /salt\npython3 -m nox -e ci-test-onedir -- tests/pytests/unit/test_foo.py -x -v\n```\n\n### Step 6: Cleanup\n\n```bash\n# Stop and remove container\ndocker stop <NAME>\ndocker rm <NAME>\n\n# Clean artifacts\nrm -rf artifacts/ nox-*.zip nox.*.tar.*\n```\n\n## Container Filesystem Behavior\n\nWhen testing in containers, be aware:\n\n- Changes to `salt/` source are visible immediately (mounted volume)\n- Changes to Salt library may need artifact refresh:\n  ```bash\n  docker exec <NAME> cp /salt/salt/modules/foo.py \\\n      /salt/artifacts/salt/lib/python3.14/site-packages/salt/modules/\n  ```\n\n## Stale Artifacts\n\nAlways clean before downloading new CI artifacts:\n\n```bash\nrm -rf artifacts/ nox-*.zip nox.*.tar.*\n```\n\nOld artifacts can cause confusing test failures.\n\n## Common CI Issues\n\n### Tests Pass Locally But Fail in CI\n\nPossible causes:\n1. **Different Python version** - CI uses specific Python versions per branch\n2. **Platform differences** - CI runs on Linux, you might be on macOS/Windows\n3. **Missing dependencies** - CI containers have exact dependency versions\n4. **Timing issues** - CI environment may be slower/faster\n5. **Stale artifacts** - Clean and re-download\n\n### Container Setup Fails\n\nIf dependency decompression fails:\n1. Check you downloaded artifacts for correct run ID\n2. Ensure Docker has enough disk space\n3. Try removing old containers and images\n\n### Relenv Toolchain Issues\n\nFor Python 3.14+ containers, you must create the symlink:\n\n```bash\ndocker exec <NAME> bash -c \"mkdir -p /root/.local/relenv && ln -sf /root/.cache/relenv/toolchains /root/.local/relenv/toolchain\"\n```\n\nThis is a known path mismatch between relenv versions.\n\n## Using MCP Server for CI Workflows\n\nThe agents/mcp/salt_test MCP server provides tools for discovering CI failures:\n\n- `ci_pr_failures` - Get all failing tests from a PR\n- `ci_run_failures` - Get failures from specific CI run\n- `ci_failure_summary` - Get summary of recent failures\n\nSee [agents/mcp/salt_test/README.md](../mcp/salt_test/README.md) for details.\n\n## Best Practices\n\n1. **Always clean artifacts** before downloading new ones\n2. **Use exact CI container images** for reproduction\n3. **Note Python version** - use venv310 for 3006.x/3007.x, venv314 for master\n4. **Check run logs** in GitHub Actions for exact command that failed\n5. **Test locally first** before pushing to avoid CI churn\n6. **Keep commits small** for easier review and debugging\n","isInternal":false,"tokens":1739,"sizeBytes":6385},{"name":"module-templates.md","path":"agents/docs/module-templates.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/docs/module-templates.md","title":"Subagent: module-templates","category":"subagent-persona","format":"markdown","content":"# Salt Module Templates\n\nThis document provides comprehensive templates for writing Salt modules.\n\n## Execution Module Template\n\nAll execution modules follow this basic structure:\n\n```python\n\"\"\"\nModule docstring explaining purpose\n\"\"\"\n\nimport logging\n\nimport salt.exceptions\nimport salt.utils.args\nimport salt.utils.data\nimport salt.utils.files\nimport salt.utils.path\nimport salt.utils.platform\n\n# Set up logging\nlog = logging.getLogger(__name__)\n\n# Module metadata\n__virtualname__ = \"mymodule\"  # Optional: override module name\n__proxyenabled__ = [\"*\"]  # Optional: enable for proxy minions\n__func_alias__ = {\n    \"list_\": \"list\"  # Optional: map function names to avoid Python keywords\n}\n\n\ndef __virtual__():\n    \"\"\"\n    Only load this module if requirements are met\n    Returns True, False, or (False, reason)\n    \"\"\"\n    if not salt.utils.path.which(\"mycommand\"):\n        return False, \"mycommand binary not found\"\n    return True\n\n\ndef my_function(name, param=None):\n    \"\"\"\n    Function description\n\n    name\n        Description of name parameter\n\n    param\n        Description of param parameter\n\n    CLI Example:\n\n    .. code-block:: bash\n\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    log.debug(\"Running my_function with name=%s\", name)\n\n    # Implementation\n    result = do_something(name, param)\n\n    return result\n```\n\n## State Module Template\n\nState modules provide idempotent, declarative configuration. They call execution modules to do the actual work.\n\n### State Return Structure\n\nEvery state function MUST return this exact structure:\n\n```python\nret = {\n    \"name\": name,        # The name parameter passed to the state\n    \"result\": result,    # True (success), False (failure), or None (test mode)\n    \"changes\": {},       # Dictionary of changes made\n    \"comment\": comment   # String or list describing what happened\n}\n```\n\n### Complete State Template\n\n```python\n\"\"\"\nState module docstring\n\"\"\"\n\nimport logging\n\nimport salt.exceptions\n\nlog = logging.getLogger(__name__)\n\n\ndef __virtual__():\n    \"\"\"\n    Only load if the corresponding execution module is available\n    \"\"\"\n    if \"mymod.do_thing\" in __salt__:\n        return True\n    return (False, \"mymod execution module not available\")\n\n\ndef managed(name, value=None, **kwargs):\n    \"\"\"\n    Ensure something is in the desired state\n\n    name\n        The resource identifier\n\n    value\n        The desired value\n\n    Example:\n\n    .. code-block:: yaml\n\n        /etc/myconfig:\n          mymod.managed:\n            - value: foo\n    \"\"\"\n    # Initialize return dict\n    ret = {\n        \"name\": name,\n        \"result\": False,\n        \"changes\": {},\n        \"comment\": \"\"\n    }\n\n    # 1. Validate input\n    if not name:\n        ret[\"comment\"] = \"Name is required\"\n        return ret\n\n    # 2. Check current state\n    current = __salt__[\"mymod.get_current\"](name)\n\n    # 3. Compare with desired state\n    if current == value:\n        ret[\"result\"] = True\n        ret[\"comment\"] = f\"{name} is already in the desired state\"\n        return ret\n\n    # 4. Handle test mode\n    if __opts__[\"test\"]:\n        ret[\"result\"] = None\n        ret[\"comment\"] = f\"{name} would be updated\"\n        ret[\"changes\"] = {\"old\": current, \"new\": value}\n        return ret\n\n    # 5. Make changes\n    try:\n        result = __salt__[\"mymod.set_value\"](name, value)\n    except Exception as exc:\n        ret[\"comment\"] = f\"Failed to update {name}: {exc}\"\n        return ret\n\n    # 6. Verify changes\n    new_value = __salt__[\"mymod.get_current\"](name)\n\n    # 7. Set result and changes\n    if new_value == value:\n        ret[\"result\"] = True\n        ret[\"changes\"] = {\"old\": current, \"new\": new_value}\n        ret[\"comment\"] = f\"{name} was updated\"\n    else:\n        ret[\"result\"] = False\n        ret[\"comment\"] = f\"Failed to update {name}\"\n\n    return ret\n```\n\n### State Flow Diagram\n\n```\n1. Validate Input → Invalid? Return with result=False\n                  ↓\n2. Check Current State\n                  ↓\n3. Already Correct? → Yes: Return with result=True, no changes\n                  ↓ No\n4. Test Mode? → Yes: Return with result=None, proposed changes\n                  ↓ No\n5. Make Changes\n                  ↓\n6. Verify Changes\n                  ↓\n7. Return Result (True/False) with changes dict\n```\n\n### State Result Values\n\n- **`result=True`**: State succeeded, system is in desired state\n- **`result=False`**: State failed, system is NOT in desired state\n- **`result=None`**: Test mode, changes would be made but weren't\n\n### Changes Dictionary\n\nThe `changes` dictionary should show what changed:\n\n```python\n# Simple change\nret[\"changes\"] = {\"old\": \"foo\", \"new\": \"bar\"}\n\n# Multiple changes\nret[\"changes\"] = {\n    \"user\": {\"old\": \"root\", \"new\": \"apache\"},\n    \"mode\": {\"old\": \"0644\", \"new\": \"0755\"},\n}\n\n# New resource created\nret[\"changes\"] = {\"created\": name}\n\n# Resource removed\nret[\"changes\"] = {\"removed\": name}\n```\n\n## The __virtual__ Function Patterns\n\nThe `__virtual__()` function determines whether a module should load on a particular system. It runs before any other module code and should be fast.\n\n### Return Values\n\n1. **`True`**: Load module with filename as name\n2. **`False`**: Don't load module\n3. **`(False, reason)`**: Don't load, with explanation\n4. **`\"string\"`**: Load module with this name (virtualname)\n5. **`(\"string\", reason)`**: Load with name, with explanation\n\n### Pattern 1: Simple Platform Check\n\n```python\ndef __virtual__():\n    \"\"\"\n    Only work on POSIX-like systems\n    \"\"\"\n    if salt.utils.platform.is_windows():\n        return (\n            False,\n            \"The file execution module cannot be loaded: only available on \"\n            \"non-Windows systems - use win_file instead.\",\n        )\n    return True\n```\n\n### Pattern 2: Virtual Name with OS Check\n\nUsed when multiple modules implement the same interface (e.g., `pkg` for different package managers).\n\n```python\n__virtualname__ = \"pkg\"\n\ndef __virtual__():\n    \"\"\"\n    Confirm this module is on a Debian-based system\n    \"\"\"\n    if __grains__.get(\"os_family\") == \"Debian\":\n        return __virtualname__\n    return False, \"The pkg module could not be loaded: unsupported OS family\"\n```\n\n### Pattern 3: Binary Availability Check\n\n```python\ndef __virtual__():\n    \"\"\"\n    Only load if systemctl is available\n    \"\"\"\n    if not salt.utils.path.which(\"systemctl\"):\n        return False, \"The systemd module could not be loaded: systemctl not found\"\n    return True\n```\n\n### Pattern 4: Complex Multi-Condition Check\n\n```python\n__virtualname__ = \"service\"\n\ndef __virtual__():\n    \"\"\"\n    Only work on systems that have been booted with systemd\n    \"\"\"\n    is_linux = __grains__.get(\"kernel\") == \"Linux\"\n    is_booted = salt.utils.systemd.booted(__context__)\n    is_offline = salt.utils.systemd.offline(__context__)\n\n    if is_linux and (is_booted or is_offline):\n        return __virtualname__\n\n    return (\n        False,\n        \"The systemd execution module failed to load: only available on Linux \"\n        \"systems which have been booted with systemd.\",\n    )\n```\n\n### Pattern 5: Check for Execution Module Availability\n\nUsed in state modules to verify required execution modules exist.\n\n```python\ndef __virtual__():\n    \"\"\"\n    Only make these states available if a pkg provider has been detected\n    \"\"\"\n    if \"pkg.install\" in __salt__:\n        return True\n    return (False, \"pkg module could not be loaded\")\n```\n\n### Best Practices for __virtual__\n\n- Keep `__virtual__()` fast - it runs on every module scan\n- Provide clear error messages explaining why the module didn't load\n- Use `salt.utils.platform` for platform detection\n- Check grains for OS-specific logic\n- Cache expensive checks in `__context__` if needed\n\n## Common Module Patterns\n\n### Decorators\n\nSalt provides decorators for common tasks:\n\n```python\nfrom salt.utils.decorators import depends, memoize\nfrom salt.utils.decorators.path import which\n\n# Ensure binary exists\n@which(\"systemctl\")\ndef restart_service(name):\n    \"\"\"Only available if systemctl is found\"\"\"\n    pass\n\n# Ensure Python module exists\n@depends(\"docker\")\ndef list_containers():\n    \"\"\"Only available if docker module is installed\"\"\"\n    pass\n\n# Memoize expensive operations\n@memoize\ndef get_system_info():\n    \"\"\"Cache result for lifetime of process\"\"\"\n    pass\n```\n\n### Function Aliases\n\nMap function names to avoid Python keywords:\n\n```python\n__func_alias__ = {\n    \"list_\": \"list\",      # my_module.list() → my_module.list_()\n    \"exec_\": \"exec\",      # my_module.exec() → my_module.exec_()\n}\n```\n\n### Cross-Calling Modules\n\n```python\ndef my_function():\n    \"\"\"Call other execution modules\"\"\"\n    # Get file contents\n    content = __salt__[\"file.read\"](\"/etc/hosts\")\n\n    # Run a command\n    output = __salt__[\"cmd.run\"](\"ls -la\")\n\n    # Install a package\n    result = __salt__[\"pkg.install\"](\"nginx\")\n\n    return result\n```\n\n### Argument Handling\n\n```python\nimport salt.utils.args\n\ndef my_function(*args, **kwargs):\n    \"\"\"Handle various argument formats\"\"\"\n    # Clean extra kwargs from state data\n    kwargs = salt.utils.args.clean_kwargs(**kwargs)\n\n    # Parse key=value arguments\n    kwargs.update(salt.utils.args.parse_input(args))\n\n    return kwargs\n```\n\n### Path Handling\n\n```python\nimport salt.utils.path\n\ndef my_function():\n    \"\"\"Handle paths safely\"\"\"\n    # Find binary in PATH\n    binary = salt.utils.path.which(\"nginx\")\n    if not binary:\n        return False\n\n    # Join paths correctly for OS\n    config_path = salt.utils.path.join(\"/etc\", \"nginx\", \"nginx.conf\")\n\n    return config_path\n```\n\n### File Handling\n\n**Always use `salt.utils.files.fopen()` instead of the bare `open()` builtin**\nwhen reading or writing files in Salt code (modules, states, utils, and\ntests alike). `saltpylint`'s `resource-leakage` check (`W8470`) fails CI on\nany bare `open()` call.\n\n```python\nimport salt.utils.files\n\ndef my_function(path):\n    \"\"\"Read/write files the Salt way\"\"\"\n    with salt.utils.files.fopen(path, \"r\") as fp_:\n        content = fp_.read()\n\n    with salt.utils.files.fopen(path, \"w\") as fp_:\n        fp_.write(content)\n\n    return content\n```\n\n```python\n# Bad - fails the resource-leakage lint check\nwith open(path, \"r\") as fp_:\n    content = fp_.read()\n```\n\n`fopen()` is a thin wrapper around `open()` that additionally normalizes\nline endings/encoding across platforms and ensures file handles are tracked\nand closed consistently. Use it for every `open()` call, including binary\nmode (`\"rb\"`/`\"wb\"`) and in unit tests -- the lint check applies there too.\n\n### Platform Detection\n\n```python\nimport salt.utils.platform\n\ndef my_function():\n    \"\"\"Handle platform differences\"\"\"\n    if salt.utils.platform.is_windows():\n        return \"C:\\\\Windows\"\n    elif salt.utils.platform.is_linux():\n        return \"/etc\"\n    elif salt.utils.platform.is_darwin():\n        return \"/Library\"\n\n    # Check specific OS family\n    if __grains__.get(\"os_family\") == \"Debian\":\n        return \"apt\"\n    elif __grains__.get(\"os_family\") == \"RedHat\":\n        return \"yum\"\n```\n\n### Context Caching Pattern\n\n```python\ndef _get_cached_value():\n    \"\"\"Safe context usage for standalone calls\"\"\"\n    try:\n        context = __context__\n    except NameError:\n        context = {}\n\n    if \"my_key\" not in context:\n        context[\"my_key\"] = expensive_operation()\n    return context.get(\"my_key\")\n```\n\n## Error Handling\n\nUse Salt's exception classes for consistent error handling:\n\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Command/operation failed\n    SaltInvocationError,      # Bad function arguments\n    MinionError,              # General minion error\n    CommandNotFoundError,     # Binary not found\n)\n\ndef my_function(path, required_param):\n    \"\"\"\n    Example with proper error handling\n    \"\"\"\n    # Validate required parameters\n    if not required_param:\n        raise SaltInvocationError(\"required_param is mandatory\")\n\n    # Validate parameter values\n    if not os.path.exists(path):\n        raise SaltInvocationError(f\"Path does not exist: {path}\")\n\n    # Check for required binary\n    binary = salt.utils.path.which(\"mytool\")\n    if not binary:\n        raise CommandNotFoundError(\"mytool binary not found in PATH\")\n\n    # Execute operation with error handling\n    try:\n        result = __salt__[\"cmd.run_all\"](f\"{binary} {path}\")\n    except Exception as exc:\n        raise CommandExecutionError(\n            f\"Failed to run mytool on {path}: {exc}\"\n        )\n\n    # Check command result\n    if result[\"retcode\"] != 0:\n        raise CommandExecutionError(\n            f\"mytool failed: {result['stderr']}\"\n        )\n\n    return result[\"stdout\"]\n```\n\n### When to Use Each Exception\n\n- **`SaltInvocationError`**: Invalid function arguments, missing required parameters\n- **`CommandExecutionError`**: Operation failed (file not found, command failed, etc.)\n- **`CommandNotFoundError`**: Required binary not in PATH\n- **`MinionError`**: General minion-side errors\n- **Standard exceptions**: Use Python's built-in exceptions when appropriate\n\n## Module Checklists\n\n### Execution Module Checklist\n\n- [ ] Proper imports (logging, salt.exceptions, salt.utils.*)\n- [ ] Logger configured: `log = logging.getLogger(__name__)`\n- [ ] `__virtual__()` function if platform/dependency specific\n- [ ] Docstrings on all functions with CLI examples\n- [ ] Input validation with proper exceptions\n- [ ] Error handling with CommandExecutionError/SaltInvocationError\n- [ ] Use `__context__` for caching expensive operations\n- [ ] Unit tests covering success and error cases\n\n### State Module Checklist\n\n- [ ] Returns correct dict structure (name, result, changes, comment)\n- [ ] Validates input parameters\n- [ ] Checks current state before making changes\n- [ ] Handles test mode (`__opts__[\"test\"]`)\n- [ ] Verifies changes after applying\n- [ ] Sets `result` correctly (True/False/None)\n- [ ] Populates `changes` dict with before/after\n- [ ] Provides clear `comment` explaining what happened\n\n## Reference Files\n\n- Simple example: `salt/modules/test.py`\n- Complex example: `salt/modules/file.py`\n- Platform-specific: `salt/modules/aptpkg.py`, `salt/modules/systemd_service.py`\n- State examples: `salt/states/file.py`, `salt/states/pkg.py`\n- Documentation: `doc/topics/development/modules/developing.rst`, `doc/ref/states/writing.rst`\n","isInternal":false,"tokens":3357,"sizeBytes":14259},{"name":"package-building.md","path":"agents/docs/package-building.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/docs/package-building.md","title":"Subagent: package-building","category":"subagent-persona","format":"markdown","content":"# Package Building Guide\n\nThis guide describes how to build Salt packages (RPM, Deb, etc.) locally using the same methods as the CI/CD pipeline.\n\n## Overview\n\nSalt packages are built using the `tools` script (specifically `tools pkg`) running inside a CI container. This ensures the build environment matches the official release environment.\n\nThe general process is:\n1.  Enter the appropriate CI container\n2.  Setup the Python environment\n3.  Build the source tarball\n4.  Build the \"Onedir\" (a self-contained directory with Python and dependencies)\n5.  Build the final package (RPM/Deb) using the Onedir\n\n## Prerequisites\n\n-   Docker installed and running\n-   `python-tools-scripts` installed in your local environment (optional, but helpful for some commands)\n\n## Reference Workflows\n\nThe instructions in this guide are derived from the official GitHub Actions workflows. If you need to verify the current build process or versions, check these files:\n\n-   **`.github/workflows/ci.yml`**: The main entry point for CI builds. It defines the high-level orchestration.\n-   **`.github/workflows/build-salt-onedir.yml`**: Defines how the \"Onedir\" (relocatable Python environment) is built. Check this for `relenv` and `python` versions.\n-   **`.github/workflows/build-packages.yml`**: Defines how the final RPM/Deb/Windows/macOS packages are built using the Onedir.\n-   **`.github/actions/build-source-tarball/action.yml`**: The steps to create the initial source distribution.\n\n## Building RPMs (Linux)\n\n### 1. Enter the Build Container\n\nFrom the root of the Salt repository:\n\n```bash\ndocker run --rm -it \\\n  -v \"$(pwd):/salt\" \\\n  -w /salt \\\n  ghcr.io/saltstack/salt-ci-containers/testing:fedora-42 \\\n  bash\n```\n\n*Note: Use `fedora-42` for RPM builds. For Deb builds, use `debian-11` or similar.*\n\n### 2. Setup Python Environment (Inside Container)\n\nOnce inside the container:\n\n```bash\n# Detect python version (likely 3.10 or 3.14 depending on branch/container)\nPY_VER=$(python3 -c \"import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')\")\n\n# Create and activate venv\npython3 -m venv venv\nsource venv/bin/activate\n\n# Install build dependencies\npip install --upgrade pip setuptools\npip install -r requirements/static/ci/py${PY_VER}/tools.lock\npip install -e .\n```\n\n### 3. Build Source Tarball\n\nCreate the source distribution:\n\n```bash\npython3 -m tools pkg source-tarball\n```\n\nThis will create `dist/salt-<version>.tar.gz`.\n\n### 4. Build Salt Onedir\n\nThe \"Onedir\" is an artifact containing Salt and all its dependencies (including Python itself) in a relocatable directory.\n\n```bash\n# Get the version from the tarball\nSALT_VERSION=$(ls dist/salt-*.tar.gz | sed 's/dist\\/salt-//;s/.tar.gz//')\nmkdir -p artifacts\n\n# Build the Onedir\n# Note: CI uses specific pinned versions for relenv and python.\n# Check .github/workflows/build-salt-onedir.yml for current versions.\npython3 -m tools pkg build salt-onedir \"dist/salt-${SALT_VERSION}.tar.gz\" \\\n    --platform linux \\\n    --package-name artifacts/salt \\\n    --relenv-version 0.22.4\n\n# Cleanup and Archive\npython3 -m tools pkg pre-archive-cleanup artifacts/salt\ntar -cJf \"artifacts/salt-${SALT_VERSION}-onedir-linux-x86_64.tar.xz\" -C artifacts salt\n```\n\n### 5. Build RPM Package\n\nUse the Onedir artifact to build the final RPM.\n\n```bash\npython3 -m tools pkg build rpm \\\n    --relenv-version=0.22.4 \\\n    --python-version=3.10.19 \\\n    --onedir=\"salt-${SALT_VERSION}-onedir-linux-x86_64.tar.xz\"\n```\n\nThe RPMs will be generated in `~/rpmbuild/RPMS/x86_64/`.\n\n### 6. Retrieve Artifacts\n\nBefore exiting the container, copy the RPMs to the mounted volume:\n\n```bash\nmkdir -p artifacts/rpm\ncp -r ~/rpmbuild/RPMS/x86_64/*.rpm artifacts/rpm/\n```\n\n## Building DEBs (Linux)\n\nThe process is similar to RPMs but uses the `deb` command and a Debian container.\n\n1.  Use `ghcr.io/saltstack/salt-ci-containers/testing:debian-11` (or newer).\n2.  Follow steps 2-4 above (Source Tarball & Onedir).\n3.  Run `python3 -m tools pkg build deb ...` instead of `rpm`.\n\n## Building Windows/macOS Packages\n\nWindows and macOS packages are typically built on their respective host OSs in CI, not in Docker containers. Refer to `.github/workflows/build-packages.yml` for the specific steps and requirements (signing certificates, etc.).\n","isInternal":false,"tokens":1128,"sizeBytes":4263},{"name":"testing.md","path":"agents/docs/testing.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/docs/testing.md","title":"Subagent: testing","category":"subagent-persona","format":"markdown","content":"# Salt Testing Guide\n\nSalt uses multiple test frameworks. All new code should include tests.\n\n## Test Directory Structure\n\n```\ntests/\n├── unit/               # Fast unit tests (no daemons, heavy mocking)\n│   ├── modules/        # Tests for salt/modules/\n│   ├── states/         # Tests for salt/states/\n│   └── utils/          # Tests for salt/utils/\n├── integration/        # Integration tests (with daemons)\n│   ├── modules/        # Tests for salt/modules/\n│   ├── states/         # Tests for salt/states/\n│   └── ...\n└── pytests/           # Pytest-based tests (newer style)\n    ├── unit/\n    ├── integration/\n    └── functional/\n```\n\n## Unit Test Templates\n\n### Legacy Style (TestCase)\n\n```python\n\"\"\"\nTests for salt.modules.mymodule\n\"\"\"\n\nimport pytest\n\nimport salt.modules.mymodule as mymodule\nfrom salt.exceptions import CommandExecutionError, SaltInvocationError\nfrom tests.support.mixins import LoaderModuleMockMixin\nfrom tests.support.mock import MagicMock, patch\nfrom tests.support.unit import TestCase\n\n\nclass MyModuleTestCase(TestCase, LoaderModuleMockMixin):\n    \"\"\"\n    Test cases for salt.modules.mymodule\n    \"\"\"\n\n    def setup_loader_modules(self):\n        \"\"\"\n        Setup loader modules - injects dunders\n        \"\"\"\n        return {\n            mymodule: {\n                \"__opts__\": {\"test\": False},\n                \"__grains__\": {\"os\": \"Linux\"},\n                \"__salt__\": {},\n            }\n        }\n\n    def test_simple_function(self):\n        \"\"\"\n        Test simple function\n        \"\"\"\n        result = mymodule.my_function(\"test\")\n        self.assertEqual(result, \"expected\")\n\n    def test_with_mock(self):\n        \"\"\"\n        Test with mocked subprocess call\n        \"\"\"\n        mock_cmd = MagicMock(return_value={\"retcode\": 0, \"stdout\": \"success\"})\n        with patch.dict(mymodule.__salt__, {\"cmd.run_all\": mock_cmd}):\n            result = mymodule.my_function(\"test\")\n            self.assertTrue(result)\n            mock_cmd.assert_called_once()\n\n    def test_error_handling(self):\n        \"\"\"\n        Test error conditions\n        \"\"\"\n        with self.assertRaises(SaltInvocationError):\n            mymodule.my_function(None)\n```\n\n### Pytest Style (Newer)\n\n```python\n\"\"\"\nTests for salt.modules.mymodule\n\"\"\"\n\nimport pytest\n\nimport salt.modules.mymodule as mymodule\nfrom salt.exceptions import SaltInvocationError\n\n\n@pytest.fixture\ndef configure_loader_modules():\n    \"\"\"\n    Setup module dunders\n    \"\"\"\n    return {\n        mymodule: {\n            \"__opts__\": {\"test\": False},\n            \"__grains__\": {\"os\": \"Linux\"},\n            \"__salt__\": {},\n        }\n    }\n\n\ndef test_simple_function():\n    \"\"\"\n    Test simple function\n    \"\"\"\n    result = mymodule.my_function(\"test\")\n    assert result == \"expected\"\n\n\ndef test_error_handling():\n    \"\"\"\n    Test error conditions\n    \"\"\"\n    with pytest.raises(SaltInvocationError):\n        mymodule.my_function(None)\n```\n\n## Mocking Patterns\n\n### Basic Mocking\n\n```python\nfrom tests.support.mock import MagicMock, patch, call\n\n# Mock a function call\nmock_func = MagicMock(return_value=\"result\")\nwith patch(\"salt.modules.mymodule.some_function\", mock_func):\n    result = mymodule.my_function()\n    mock_func.assert_called_once_with(\"expected_arg\")\n\n# Mock dunder dict\nwith patch.dict(mymodule.__salt__, {\"cmd.run\": MagicMock(return_value=\"output\")}):\n    result = mymodule.my_function()\n\n# Mock with side effects\nmock_func = MagicMock(side_effect=[\n    {\"retcode\": 1, \"stdout\": \"error\"},  # First call fails\n    {\"retcode\": 0, \"stdout\": \"success\"},  # Second call succeeds\n])\nwith patch.dict(mymodule.__salt__, {\"cmd.run_all\": mock_func}):\n    result = mymodule.my_function()\n```\n\n## Test Best Practices\n\n1. **One test per function behavior**: Test success cases, error cases, edge cases separately\n2. **Use descriptive test names**: `test_function_name_when_condition_then_expected_result`\n3. **Mock external dependencies**: File I/O, network calls, subprocess calls\n4. **Test error handling**: Verify exceptions are raised correctly\n5. **Test input validation**: Verify bad inputs are rejected\n6. **Keep tests fast**: Unit tests should run in milliseconds\n7. **Don't test implementation details**: Test the public API, not internal helpers\n\n## Running Tests\n\n### Using Nox (Recommended for CI Consistency)\n\n```bash\n# Run all tests\nnox -e test-3\n\n# Run specific test file\nnox -e test-3 -- tests/pytests/unit/test_loader.py\n\n# Run with pattern matching\nnox -e test-3 -- -k test_auth\n\n# Run specific test function\nnox -e test-3 -- tests/pytests/unit/test_loader.py::test_load_modules\n\n# Run last failed tests\nnox -e test-3 -- --lf\n\n# Run with specific Python version\nnox -e test-3.10\nnox -e test-3.14\n\n# Run with specific transport\nnox -e test-tcp-3\nnox -e test-zeromq-3\n\n# Run with coverage\nnox -e test-3 --coverage\nnox -e coverage-report\n```\n\n### Using Virtual Environment (Faster for Development)\n\nIf you have a local venv setup:\n\n```bash\n# Run tests directly with pytest (master branch: use venv312)\n./venv312/bin/pytest tests/pytests/unit/test_foo.py::test_bar -v\n\n# Run pre-commit checks on specific files\n./venv312/bin/pre-commit run --files salt/loader/lazy.py\n```\n\n## Container Testing (Reproduce CI Failures)\n\nTo reproduce CI failures locally using the same container environment:\n\n### 1. Download CI artifacts\n\n```bash\n./venv310/bin/python -m ptscripts ci download-artifacts --run-id <RUN-ID>\n```\n\n### 2. Create container\n\n```bash\n./venv310/bin/python -m ptscripts container create <IMAGE> --name <NAME>\ndocker start <NAME>\n```\n\n### 3. Setup container\n\n```bash\n# Decompress dependencies\ndocker exec <NAME> python3 -m nox -e decompress-dependencies -- linux x86_64\n\n# Create relenv toolchain symlink (Python 3.14+ only)\ndocker exec <NAME> bash -c \"mkdir -p /root/.local/relenv && ln -sf /root/.cache/relenv/toolchains /root/.local/relenv/toolchain\"\n```\n\n### 4. Run tests\n\n```bash\ndocker exec <NAME> python3 -m nox -e ci-test-onedir -- <TEST-PATH> --run-slow -x -v\n```\n\n### Example container images\n\n- `ghcr.io/saltstack/salt-ci-containers/testing:debian-11`\n- `ghcr.io/saltstack/salt-ci-containers/testing:ubuntu-22.04`\n- `ghcr.io/saltstack/salt-ci-containers/testing:rockylinux-9`\n\n## Linting and Formatting\n\n### Black (Code Formatting)\n\n```bash\n# Format all code\nblack .\n\n# Check without modifying\nblack --check .\n```\n\nBlack uses 88 character line length by default.\n\n### isort (Import Sorting)\n\n```bash\n# Sort all imports\nisort .\n\n# Check without modifying\nisort --check .\n```\n\nisort is configured with profile 3 and trailing commas in `pyproject.toml`.\n\n### Linting\n\n```bash\n# Lint both salt and tests\nnox -e lint\n\n# Lint Salt code only\nnox -e lint-salt\n\n# Lint tests only\nnox -e lint-tests\n```\n\n## Example Test Files\n\nFor reference, see:\n- Unit test example: `tests/unit/modules/test_systemd_service.py`\n- Complex mocking: `tests/unit/modules/test_cron.py`\n- Documentation: `doc/topics/tutorials/writing_tests.rst`\n","isInternal":false,"tokens":1703,"sizeBytes":6992},{"name":"troubleshooting.md","path":"agents/docs/troubleshooting.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/docs/troubleshooting.md","title":"Subagent: troubleshooting","category":"subagent-persona","format":"markdown","content":"# Subagent: troubleshooting\n\nPath: `agents/docs/troubleshooting.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/docs/troubleshooting.md)","tokens":300,"sizeBytes":183},{"name":"README.md","path":"agents/mcp/README.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/mcp/README.md","title":"Subagent: readme","category":"subagent-persona","format":"markdown","content":"# Subagent: readme\n\nPath: `agents/mcp/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/mcp/README.md)","tokens":300,"sizeBytes":154},{"name":"README.md","path":"agents/mcp/salt_test/README.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/mcp/salt_test/README.md","title":"Subagent: readme","category":"subagent-persona","format":"markdown","content":"# Subagent: readme\n\nPath: `agents/mcp/salt_test/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/mcp/salt_test/README.md)","tokens":300,"sizeBytes":174},{"name":"README.md","path":"agents/README.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/README.md","title":"Subagent: readme","category":"subagent-persona","format":"markdown","content":"# AI Agent Instructions for Salt Development\n\nThis directory contains instruction files for various AI coding assistants to help them better understand and work with the Salt codebase.\n\n## Purpose\n\nThese files provide AI agents with:\n- Salt's architecture and design patterns\n- Coding conventions and best practices\n- Testing guidelines and requirements\n- Common pitfalls to avoid\n\n## Available Instructions\n\n- **CLAUDE.md** - Instructions for Anthropic's Claude (Claude Code, Claude.ai, etc.)\n- **GEMINI.md** - Instructions for Google's Gemini\n- **CURSOR.md** - Instructions for Cursor IDE\n- **COPILOT.md** - Instructions for GitHub Copilot\n\nEach instruction file provides a quick reference and links to detailed documentation (see below).\n\n## Detailed Documentation\n\nThe `docs/` directory contains comprehensive guides that are referenced by all agent instruction files. This provides a single source of truth for detailed information:\n\n- **[development-setup.md](docs/development-setup.md)** - Complete virtual environment setup\n  - venv310 and venv312 setup instructions\n  - Platform-specific dependencies\n  - Installation verification steps\n  - Common troubleshooting\n\n- **[architecture.md](docs/architecture.md)** - Salt architecture deep dive\n  - Master-minion architecture\n  - All module types (execution, state, grains, pillar, beacon, engine, etc.)\n  - Directory structure\n  - Key components (state compiler, transport layer, event bus, loader system)\n\n- **[module-templates.md](docs/module-templates.md)** - Complete code templates\n  - Full execution module template with all patterns\n  - Full state module template with flow diagram\n  - All `__virtual__()` patterns (5 variations)\n  - Error handling examples\n  - Common decorators and utilities\n\n- **[testing.md](docs/testing.md)** - Comprehensive testing guide\n  - Test directory structure\n  - Unit test templates (legacy and pytest styles)\n  - Mocking patterns with examples\n  - Running tests (Nox, venv, container)\n  - Container testing for CI reproduction\n  - Linting and formatting\n\n- **[git-and-ci.md](docs/git-and-ci.md)** - Git workflow and CI\n  - Commit guidelines (**NO AI attribution!**)\n  - PR workflow with gh CLI commands\n  - Branch strategy\n  - CI failure reproduction workflow\n  - Container setup and debugging\n\n- **[package-building.md](docs/package-building.md)** - Package Building Guide\n  - Building RPMs locally using CI containers\n  - Building DEBs locally\n  - Creating source tarballs and onedir artifacts\n\n- **[troubleshooting.md](docs/troubleshooting.md)** - Common issues and solutions\n  - Import order issues\n  - Module discovery problems\n  - ZeroMQ issues\n  - Python 3.11+ compatibility gotchas\n  - Container filesystem behavior\n  - Lazy loading behavior\n  - Common error messages and fixes\n\n## How to Use\n\n**Recommended approach:** Create a symlink from the root directory to the agent file you want to use.\n\n### Symlink (Recommended)\n\nCreating a symlink keeps your local setup in sync with updates to the canonical instructions:\n\n```bash\n# From the root of the Salt repository\nln -s agents/CLAUDE.md CLAUDE.md\n# or\nln -s agents/CURSOR.md .cursorrules\n# or\nln -s agents/GEMINI.md GEMINI.md\n# or\nln -s agents/COPILOT.md COPILOT.md\n```\n\n**Why symlink?**\n- Automatically receives updates when canonical files are updated\n- Links to `agents/docs/` work correctly\n- No maintenance required\n\n### Alternative: IDE-Specific Configuration\n\nSome tools may allow you to specify a custom path to instruction files in their settings. Consult your tool's documentation.\n\n**Note:** If you need to customize the instructions, it's better to propose changes to the canonical files in `agents/` so everyone benefits, rather than maintaining a personal copy.\n\n## MCP Servers\n\nThis directory also contains Model Context Protocol (MCP) servers that expose Salt development tools to AI agents:\n\n- **mcp/salt_test/** - Testing tools server\n  - Direct pytest execution (quick local testing)\n  - CI failure discovery (analyze PR failures)\n  - Container testing (reproduce CI failures)\n\nSee [mcp/README.md](mcp/README.md) for setup and usage instructions.\n\n### Quick Setup\n\n1. Install MCP SDK: `pip install mcp`\n2. Configure in `~/.config/claude-code/mcp_config.json` (see `mcp/mcp-config.json` for template)\n3. Set GitHub token for CI features: `export GITHUB_TOKEN=\"your_token\"`\n\nWith MCP configured, you can ask Claude:\n- \"What tests are failing in PR #68562?\"\n- \"Run the loader tests locally\"\n- \"Reproduce the failure from PR #68562 on debian-11\"\n\n## .gitignore\n\nThe root-level instruction files (CLAUDE.md, GEMINI.md, etc.) are intentionally ignored by git to prevent personal configurations from being committed. Only the canonical versions in this directory are tracked.\n\n## Contributing\n\nIf you discover better patterns, common issues, or ways to improve these instructions:\n\n1. Edit the appropriate file in this `agents/` directory\n2. Submit a pull request with your improvements\n3. Include a brief explanation of what the change helps agents understand better\n\n## File Naming Conventions\n\n- **CLAUDE.md** - For Claude-based tools\n- **GEMINI.md** - For Gemini-based tools\n- **CURSOR.md** - For Cursor IDE (may also be named `.cursorrules`)\n- **COPILOT.md** - For GitHub Copilot (may also be named `.github/copilot-instructions.md`)\n\nNote: Some tools may use different filenames. Check your tool's documentation for the correct filename and location.\n","isInternal":false,"tokens":1230,"sizeBytes":5424},{"name":"silent_drop_audit_3008_to_master.md","path":"agents/reports/silent_drop_audit_3008_to_master.md","rawUrl":"https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/reports/silent_drop_audit_3008_to_master.md","title":"Subagent: silent_drop_audit_3008_to_master","category":"subagent-persona","format":"markdown","content":"# Subagent: silent_drop_audit_3008_to_master\n\nPath: `agents/reports/silent_drop_audit_3008_to_master.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/reports/silent_drop_audit_3008_to_master.md)","tokens":300,"sizeBytes":240}],"systemPromptSnippet":"<yakaai_skills repo=\"saltstack/salt\">\n<!-- File: agents/mcp/mcp-config.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Mcp Skill\n\nPath: `agents/mcp/mcp-config.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/mcp/mcp-config.json)\n\n<!-- File: agents/CLAUDE.md (Tokens: ~1977 | Category: claude-rule) -->\n# Salt Development Instructions for Claude\n\nYou are assisting with development of Salt, a powerful infrastructure automation and configuration management system. This document provides essential quick reference information and links to detailed documentation.\n\n## Table of Contents\n\n1. [Coding Philosophy](#coding-philosophy)\n2. [Architecture Quick Reference](#architecture-quick-reference)\n3. [Dunder Dictionaries](#dunder-dictionaries)\n4. [Module Basics](#module-basics)\n5. [State Basics](#state-basics)\n6. [Git Workflow](#git-workflow)\n7. [Checklists](#checklists)\n8. [Detailed Documentation](#detailed-documentation)\n\n---\n\n## Coding Philosophy\n\n**CRITICAL**: Approach Salt development as a CPython core developer:\n- Know memory management and garbage collection implications\n- Understand performance trade-offs between implementations\n- Be aware of CPython implementation details and GIL impact\n\n**TDD for Bug Fixes**: Always write a failing test first, verify it fails, fix the bug, verify it passes, then run related tests.\n\n**Zen of Python**: Readability counts. Explicit is better than implicit. Simple is better than complex. Errors should never pass silently.\n\n---\n\n## Architecture Quick Reference\n\nSalt is a Python-based configuration management system using master-minion architecture with an event-driven plugin system.\n\n**Key Module Types:**\n- **Execution Modules** (`salt/modules/`) - CLI commands on minions (264+)\n- **State Modules** (`salt/states/`) - Declarative configuration (126+)\n- **Utils** (`salt/utils/`) - Shared utility functions (170+)\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete architecture details.**\n\n---\n\n## Dunder Dictionaries\n\nSalt injects special dictionaries into module scope via the loader:\n\n**Always Available:**\n- `__opts__` - Configuration options (test mode, minion ID, etc.)\n- `__grains__` - System information (OS, platform, custom data)\n- `__pillar__` - Secure data for this minion\n- `__context__` - Per-run cache (persists during Salt execution)\n\n**Module-Specific:**\n- `__salt__` - Access other execution modules\n- `__utils__` - Utility functions\n- `__states__` - Call other states (in state modules only)\n\n**Example: Context Caching**\n```python\nif \"cache_key\" not in __context__:\n    __context__[\"cache_key\"] = expensive_operation()  # Only once per run\nreturn __context__[\"cache_key\"]\n```\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for detailed loader system documentation.**\n\n---\n\n## Module Basics\n\n**Execution Module Structure:**\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Return True to load, False or (False, reason) to skip\"\"\"\n    if salt.utils.platform.is_windows():\n        return False, \"Not available on Windows\"\n    return True\n\ndef my_function(name, param=None):\n    \"\"\"\n    CLI Example::\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    if not name:\n        raise salt.exceptions.SaltInvocationError(\"name is required\")\n    return __salt__[\"cmd.run\"](f\"command {name}\")\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete templates and patterns.**\n\n---\n\n## State Basics\n\n**State Return Structure (REQUIRED):**\n```python\nret = {\n    \"name\": name,        # Name parameter\n    \"result\": False,     # True (success), False (fail), None (test mode)\n    \"changes\": {},       # Dict of changes: {\"old\": ..., \"new\": ...}\n    \"comment\": \"\"        # What happened\n}\n```\n\n**State Flow:**\n1. Validate input\n2. Check current state\n3. If already correct: return success\n4. If test mode (`__opts__[\"test\"]`): return `result=None` with proposed changes\n5. Make changes and return result\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete state templates.**\n\n---\n\n## Git Workflow\n\n### CRITICAL RULES\n\n1. **NO attribution lines** - **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n2. **Use imperative mood** - \"Fix bug\", not \"Fixed bug\" or \"Fixes bug\"\n3. **Reference issues** - Use `#NNNN` to reference GitHub issues\n4. **Small, focused commits** - One logical change per commit\n5. **Rebase before push** - Keep history linear and clean\n\n**Good Commit:**\n```\nFix loader cache invalidation on module reload\n\nWhen modules are reloaded, the loader cache was not properly\ninvalidated, causing stale references. Clear the cache dict\non reload to ensure fresh module imports.\n\nFixes #12345\n```\n\n**Bad Commit (NEVER DO THIS):**\n```\nFixed some bugs\n\nCo-Authored-By: Claude <noreply@anthropic.com>\n```\n\n**See [agents/docs/git-and-ci.md](agents/docs/git-and-ci.md) for complete git workflow and CI reproduction.**\n\n---\n\n## Checklists\n\n### Execution Module Checklist\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform/dependency specific\n- [ ] Input validation with `SaltInvocationError`\n- [ ] Error handling with `CommandExecutionError`\n- [ ] Context caching for expensive operations\n- [ ] `salt.utils.files.fopen()` for any file I/O, never bare `open()`\n- [ ] Unit tests\n\n### State Module Checklist\n- [ ] Returns dict with: name, result, changes, comment\n- [ ] `result=None` for test mode (`__opts__[\"test\"]`)\n- [ ] Check current state before changing\n- [ ] Populate changes dict with old/new values\n- [ ] Clear comment explaining outcome\n\n### Common Imports\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.args\nimport salt.utils.platform\nimport salt.utils.path\n\nlog = logging.getLogger(__name__)\n```\n\n### Logging\n```python\n# Use lazy formatting (NOT f-strings)\nlog.debug(\"Processing file: %s\", filename)  # Good\nlog.debug(f\"Processing file: {filename}\")   # Bad\n```\n\n**Never log sensitive data** (passwords, tokens, keys).\n\n### File Handling\n```python\n# Use salt.utils.files.fopen(), NOT the bare open() builtin\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nThis applies everywhere, including unit tests.\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Operation failed\n    SaltInvocationError,      # Invalid arguments\n    CommandNotFoundError,     # Binary not found\n)\n```\n\n---\n\n## Detailed Documentation\n\n**Essential guides in `agents/docs/`:**\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - Virtual environment setup (venv310 and venv312), platform-specific dependencies, installation verification\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete Salt architecture, all module types, loader system, event bus\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates for execution and state modules, `__virtual__()` patterns, decorators\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test structure, unit test templates, mocking patterns, running tests\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit guidelines, PR workflow, CI failure reproduction, container testing\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Import issues, module discovery, ZeroMQ, Python 3.11+ compatibility, lazy loading\n\n---\n\n## Key Example Files\n\n- Simple module: `salt/modules/test.py`\n- Complex module: `salt/modules/file.py`\n- Package manager: `salt/modules/aptpkg.py`\n- Service: `salt/modules/systemd_service.py`\n- State examples: `salt/states/file.py`, `salt/states/pkg.py`\n\n---\n\n## Summary\n\nWhen writing Salt code:\n\n1. **Follow the patterns** - Use existing modules as templates\n2. **Use dunders correctly** - `__salt__`, `__opts__`, `__grains__`, `__context__`\n3. **Implement `__virtual__()`** - Declare platform and dependency requirements\n4. **Handle errors properly** - Use Salt's exception classes\n5. **Write tests** - Every new function needs unit tests\n6. **Document thoroughly** - Docstrings with CLI examples are required\n7. **Think idempotent** - Operations should be safe to repeat\n8. **Cache wisely** - Use `__context__` for expensive operations\n\n**When in doubt, look at existing modules for patterns and refer to the detailed documentation.**\n\n**Remember: NEVER add AI attribution lines to commits!**\n\n<!-- File: agents/GEMINI.md (Tokens: ~1771 | Category: gemini-rule) -->\n# Salt Development Instructions for Gemini\n\nYou are assisting with development of Salt, a powerful infrastructure automation and configuration management system written in Python. This guide provides essential quick reference and links to detailed documentation.\n\n## Coding Philosophy\n\n**CRITICAL**: Write code as a CPython core developer:\n- Understand Python internals, memory management, GC\n- Know performance implications\n- Be aware of implementation details\n\n**Zen of Python**: Readability counts. Explicit > implicit. Simple > complex. Errors never pass silently.\n\n**TDD for Bug Fixes**:\n1. Write failing test first → 2. Verify it fails → 3. Fix bug (minimal changes) → 4. Verify test passes → 5. Run related tests\n\n---\n\n## Architecture Quick Reference\n\nSalt is a Python-based configuration management system:\n- **Master-Minion Architecture**: Central master controls distributed minions\n- **Event-Driven**: Real-time communication via event bus\n- **Plugin-Based**: Extensible through loader system\n\n**Key Module Types:**\n- **Execution Modules** (`salt/modules/`): CLI commands on minions (264+)\n- **State Modules** (`salt/states/`): Declarative configuration (126+)\n- **Utils** (`salt/utils/`): Shared utility functions (170+)\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete architecture.**\n\n---\n\n## Dunder Dictionaries\n\nSalt injects special variables into module scope:\n\n**Always Available:**\n- `__opts__` - Config options: `__opts__.get(\"id\")`, `__opts__[\"test\"]`\n- `__grains__` - System data: `__grains__.get(\"os_family\")`\n- `__pillar__` - Secure data: `__pillar__.get(\"password\")`\n- `__context__` - Per-run cache for expensive operations\n\n**Module-Specific:**\n- `__salt__` - Other execution modules: `__salt__[\"pkg.install\"](\"nginx\")`\n- `__utils__` - Utility functions: `__utils__[\"files.is_text\"](path)`\n- `__states__` - State modules (in state modules only)\n\n**Context Caching Pattern:**\n```python\nif \"pkg_list\" not in __context__:\n    __context__[\"pkg_list\"] = expensive_fetch()  # Once per run\nreturn __context__[\"pkg_list\"]\n```\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for loader system details.**\n\n---\n\n## Execution Module Structure\n\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Returns True to load, False or (False, reason) to skip\"\"\"\n    if not salt.utils.path.which(\"required_binary\"):\n        return False, \"required_binary not found\"\n    return True\n\ndef my_function(name, param=None):\n    \"\"\"\n    CLI Example:\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    if not name:\n        raise salt.exceptions.SaltInvocationError(\"name required\")\n    return __salt__[\"cmd.run\"](f\"command {name}\")\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete templates.**\n\n---\n\n## State Module Structure\n\n**Required Return:**\n```python\n{\n    \"name\": name,              # Resource identifier\n    \"result\": True/False/None, # Success/Failure/Test mode\n    \"changes\": {},             # {\"old\": ..., \"new\": ...}\n    \"comment\": \"\"              # What happened\n}\n```\n\n**State Flow:**\n1. Validate input → 2. Check current state → 3. If correct: return success → 4. If test mode: return `result=None` with proposed changes → 5. Make changes → 6. Return result\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete state templates.**\n\n---\n\n## Common Patterns\n\n### Logging\n```python\nlog = logging.getLogger(__name__)\n# Use lazy formatting (NOT f-strings)\nlog.debug(\"Processing: %s\", filename)  # Good\nlog.debug(f\"Processing: {filename}\")   # Bad\n```\n**Never log sensitive data.**\n\n### File Handling\n```python\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nApplies to modules, states, utils, and tests alike.\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,   # Operation failed\n    SaltInvocationError,     # Invalid arguments\n    CommandNotFoundError,    # Binary not found\n)\n```\n\n### Platform Detection\n```python\nimport salt.utils.platform\nif salt.utils.platform.is_windows():\n    # Windows-specific\nif __grains__.get(\"os_family\") == \"Debian\":\n    # Debian-based\n```\n\n---\n\n## Git Workflow\n\n### CRITICAL RULES\n\n1. **NO attribution lines** - **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n2. **Imperative mood** - \"Fix bug\", not \"Fixed bug\"\n3. **Reference issues** - Use `#NNNN`\n4. **Small commits** - One logical change per commit\n5. **Rebase before push** - Linear history\n\n**Good Commit:**\n```\nFix loader cache invalidation on module reload\n\nClear cache dict on reload to ensure fresh module imports.\n\nFixes #12345\n```\n\n**Bad (NEVER):**\n```\nFixed bugs\nCo-Authored-By: Claude <noreply@anthropic.com>\n```\n\n**See [agents/docs/git-and-ci.md](agents/docs/git-and-ci.md) for complete workflow.**\n\n---\n\n## Checklists\n\n### Execution Module\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform-specific\n- [ ] Input validation (`SaltInvocationError`)\n- [ ] Error handling (`CommandExecutionError`)\n- [ ] Context caching for expensive ops\n- [ ] Unit tests\n\n### State Module\n- [ ] Returns: name, result, changes, comment\n- [ ] `result=None` for test mode\n- [ ] Check current state first\n- [ ] Populate changes dict\n- [ ] Clear comments\n\n---\n\n## Detailed Documentation\n\n**Essential guides in `agents/docs/`:**\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - venv310/venv312 setup, dependencies, verification\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete architecture, module types, loader, event bus\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates, `__virtual__()` patterns, decorators\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test structure, templates, mocking, running tests\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit guidelines, PR workflow, CI reproduction\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Import issues, ZeroMQ, Python 3.11+, lazy loading\n\n---\n\n## Key Example Files\n\n- Simple: `salt/modules/test.py`\n- Complex: `salt/modules/file.py`\n- Package: `salt/modules/aptpkg.py`\n- Service: `salt/modules/systemd_service.py`\n- States: `salt/states/file.py`, `salt/states/pkg.py`\n\n---\n\n## Summary\n\n1. **Follow patterns** - Use existing modules as templates\n2. **Use dunders** - `__salt__`, `__opts__`, `__grains__`, `__context__`\n3. **Implement `__virtual__()`** - Declare dependencies\n4. **Handle errors** - Use Salt exception classes\n5. **Write tests** - Every function needs tests\n6. **Document** - Docstrings with CLI examples required\n7. **Think idempotent** - Safe to repeat\n8. **Cache wisely** - Use `__context__`\n\n**When in doubt, check existing modules and refer to detailed docs.**\n\n**Remember: NEVER add AI attribution lines to commits!**\n\n<!-- File: .github/copilot-instructions.md (Tokens: ~7 | Category: copilot-instructions) -->\n../agents/COPILOT.md\n\n<!-- File: agents/COPILOT.md (Tokens: ~2250 | Category: copilot-instructions) -->\n# GitHub Copilot Instructions for Salt Development\n\n## Project Context\n\nThis is the Salt project - a Python-based configuration management and remote execution system using master-minion architecture with a dynamic loader system for plugins.\n\n## Coding Philosophy\n\n**CRITICAL**: Write code as a CPython core developer:\n- Deep understanding of Python internals, memory management, GC\n- Know performance implications of different implementations\n\n**Zen of Python**: Readability counts. Explicit > implicit. Simple > complex. Errors never pass silently.\n\n**TDD for Bug Fixes**: Write failing test → verify fail → fix bug → verify pass → run related tests\n\n---\n\n## Git Workflow\n\n**CRITICAL: NO attribution lines**\n- **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n- Use imperative mood: \"Fix bug\" not \"Fixed bug\"\n- Reference issues: `#NNNN`\n- Small, focused commits\n- Rebase before push\n\n---\n\n## Architecture\n\n**Salt components:**\n- **Master-Minion**: Central master controls distributed minions\n- **Event Bus**: Real-time communication\n- **Execution Modules** (`salt/modules/`): CLI commands (264+)\n- **State Modules** (`salt/states/`): Declarative configuration (126+)\n- **Utils** (`salt/utils/`): Shared utilities (170+)\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete details.**\n\n---\n\n## Code Patterns\n\n### Execution Module\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Return True to load, False or (False, reason) to skip\"\"\"\n    if not salt.utils.path.which(\"required_binary\"):\n        return False, \"required_binary not found\"\n    return True\n\ndef function_name(param1, param2=None):\n    \"\"\"\n    CLI Example::\n        salt '*' module.function_name value\n    \"\"\"\n    if not param1:\n        raise salt.exceptions.SaltInvocationError(\"param1 required\")\n    return __salt__[\"cmd.run\"](f\"command {param1}\")\n```\n\n### State Module Return\n```python\ndef managed(name, value=None):\n    ret = {\"name\": name, \"result\": False, \"changes\": {}, \"comment\": \"\"}\n\n    # Validate → Check current → If correct return → If test mode return None → Apply → Return\n    if not name:\n        ret[\"comment\"] = \"Name required\"\n        return ret\n\n    current = __salt__[\"module.get\"](name)\n    if current == value:\n        ret[\"result\"] = True\n        ret[\"comment\"] = \"Already correct\"\n        return ret\n\n    if __opts__[\"test\"]:\n        ret[\"result\"] = None\n        ret[\"changes\"] = {\"old\": current, \"new\": value}\n        ret[\"comment\"] = \"Would change\"\n        return ret\n\n    try:\n        __salt__[\"module.set\"](name, value)\n        ret[\"result\"] = True\n        ret[\"changes\"] = {\"old\": current, \"new\": value}\n        ret[\"comment\"] = \"Updated\"\n    except Exception as exc:\n        ret[\"comment\"] = str(exc)\n\n    return ret\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for complete templates.**\n\n---\n\n## Dunder Variables\n\nSalt injects these into module scope:\n\n- `__opts__`: Config - `__opts__[\"test\"]`, `__opts__.get(\"id\")`\n- `__grains__`: System data - `__grains__.get(\"os_family\")`\n- `__pillar__`: Secure data - `__pillar__.get(\"password\")`\n- `__context__`: Per-run cache for expensive operations\n- `__salt__`: Execution modules - `__salt__[\"pkg.install\"](\"nginx\")`\n- `__utils__`: Utilities - `__utils__[\"files.is_text\"](path)`\n- `__states__`: State modules (states only)\n\n**Context caching pattern:**\n```python\nif \"pkg_list\" not in __context__:\n    __context__[\"pkg_list\"] = expensive_fetch()\nreturn __context__[\"pkg_list\"]\n```\n\n---\n\n## Required Elements\n\n### Logging\n```python\nlog = logging.getLogger(__name__)\nlog.debug(\"Processing: %s\", filename)  # Good - lazy formatting\nlog.debug(f\"Processing: {filename}\")   # Bad - f-strings evaluated early\n```\n**Never log secrets.**\n\n### File Handling\n```python\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nApplies to modules, states, utils, and tests alike.\n\n### Docstrings\nEvery function requires description, parameters, and CLI example:\n```python\ndef my_function(name, value=None):\n    \"\"\"\n    Description of functionality\n\n    name\n        Description of name parameter\n\n    CLI Example::\n        salt '*' module.my_function foo value=bar\n    \"\"\"\n```\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Operation failures\n    SaltInvocationError,      # Invalid arguments\n    CommandNotFoundError,     # Missing binaries\n)\n\nif not required_param:\n    raise SaltInvocationError(\"required_param must be provided\")\n```\n\n### __virtual__ Function\nDeclare platform/dependency requirements:\n```python\n# Platform check\ndef __virtual__():\n    if salt.utils.platform.is_windows():\n        return False, \"Not available on Windows\"\n    return True\n\n# Virtual name (for interface modules)\n__virtualname__ = \"pkg\"\ndef __virtual__():\n    if __grains__.get(\"os_family\") == \"Debian\":\n        return __virtualname__\n    return False\n```\n\n**See [agents/docs/module-templates.md](agents/docs/module-templates.md) for all patterns.**\n\n---\n\n## Development Environment\n\n**Two environments required:**\n- **venv310**: Testing 3006.x/3007.x branches\n- **venv312**: Testing master branch + pre-commit\n\n```bash\n# venv310\npython3.10 -m venv venv310 && source venv310/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.10/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.10/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && deactivate\n\n# venv312\npython3.12 -m venv venv312 && source venv312/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.11/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.11/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && pre-commit install && deactivate\n```\n\n**Always use full paths:** `./venv312/bin/pytest` (master); `./venv310/bin/pytest` (3006.x/3007.x)\n\n**See [agents/docs/development-setup.md](agents/docs/development-setup.md) for complete setup.**\n\n---\n\n## Testing\n\n### Pytest Fixture\n```python\nimport pytest\nimport salt.modules.mymodule as mymodule\n\n@pytest.fixture\ndef configure_loader_modules():\n    return {\n        mymodule: {\n            \"__opts__\": {\"test\": False},\n            \"__grains__\": {\"os\": \"Linux\"},\n            \"__salt__\": {},\n        }\n    }\n\ndef test_function():\n    result = mymodule.my_function(\"test\")\n    assert result == \"expected\"\n```\n\n### Running Tests\n```bash\n# Nox\nnox -e test-3 -- tests/pytests/unit/test_loader.py\nnox -e test-3 -- --lf  # Last failed\n\n# Direct (faster)\n./venv312/bin/pytest tests/pytests/unit/test_foo.py -v\n```\n\n**See [agents/docs/testing.md](agents/docs/testing.md) for complete guide.**\n\n---\n\n## Common Patterns\n\n### Platform Detection\n```python\nsalt.utils.platform.is_windows()\nsalt.utils.platform.is_linux()\n__grains__.get(\"os_family\")  # \"Debian\", \"RedHat\", etc.\n```\n\n### Run Commands\n```python\noutput = __salt__[\"cmd.run\"](\"ls -la\")\nresult = __salt__[\"cmd.run_all\"](\"cmd\")  # {\"retcode\": 0, \"stdout\": \"...\", \"stderr\": \"...\"}\n```\n\n### File Operations\n```python\ncontent = __salt__[\"file.read\"](path)\n__salt__[\"file.write\"](path, content)\n__salt__[\"file.file_exists\"](path)\n```\n\n---\n\n## Checklists\n\n### Execution Module\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform-specific\n- [ ] Input validation (`SaltInvocationError`)\n- [ ] Error handling (`CommandExecutionError`)\n- [ ] Context caching for expensive ops\n- [ ] Unit tests\n\n### State Module\n- [ ] Returns: name, result (True/False/None), changes, comment\n- [ ] result=None for test mode\n- [ ] Check current state before modifying\n- [ ] Populate changes dict with old/new\n\n---\n\n## Documentation\n\n**Essential guides in `agents/docs/`:**\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - venv310/venv312 setup, dependencies\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete architecture, module types, loader\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates, all patterns\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test templates, mocking, running tests\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit guidelines, PR workflow, CI reproduction\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Common issues and solutions\n\n**Key example files:** `salt/modules/test.py`, `salt/modules/file.py`, `salt/states/pkg.py`\n\n---\n\n## Key Principles\n\n- **Idempotency**: Safe to run multiple times\n- **Testing**: Every function needs tests\n- **Documentation**: Docstrings with CLI examples\n- **Error Handling**: Use Salt exception classes\n- **Performance**: Cache in `__context__`\n- **Security**: Validate inputs, never log secrets\n\n<!-- File: scripts/suse/yum/plugins/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# plugins Documentation\n\nPath: `scripts/suse/yum/plugins/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/scripts/suse/yum/plugins/README.md)\n\n<!-- File: scripts/suse/zypper/plugins/commit/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# commit Documentation\n\nPath: `scripts/suse/zypper/plugins/commit/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/scripts/suse/zypper/plugins/commit/README.md)\n\n<!-- File: agents/CURSOR.md (Tokens: ~1995 | Category: subagent-persona) -->\n# Salt Development Rules for Cursor\n\nYou are working on Salt, a Python-based configuration management and remote execution system. Follow these rules when writing or modifying Salt code.\n\n## Critical Rules\n\n**CODING PHILOSOPHY:**\n- Write as a CPython core developer - know internals, memory management, GC, performance\n- Zen of Python: Readability counts. Explicit > implicit. Simple > complex\n- TDD for bugs: Write failing test → verify fail → fix → verify pass → run related tests\n\n**GIT WORKFLOW:**\n- **NEVER** add \"Generated with Claude Code\" or \"Co-Authored-By: Claude\" to commits\n- Use imperative mood: \"Fix bug\" not \"Fixed bug\"\n- Reference issues: `#NNNN`\n- Small, focused commits\n- Rebase before push\n\n**LINTING:**\n- Black: 88 char lines (`black .`)\n- isort: Profile 3, trailing commas (`isort .`)\n- Lint: `nox -e lint-salt`\n\n---\n\n## Architecture Quick Reference\n\n**Salt Components:**\n- Master-Minion architecture for distributed configuration management\n- Event-driven communication via event bus\n- Execution modules: CLI commands (`salt/modules/`) - 264+\n- State modules: Declarative config (`salt/states/`) - 126+\n- Utils: Shared functions (`salt/utils/`) - 170+\n\n**See [agents/docs/architecture.md](agents/docs/architecture.md) for complete architecture.**\n\n---\n\n## Module Structure\n\n### Execution Module Skeleton\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.platform\n\nlog = logging.getLogger(__name__)\n\ndef __virtual__():\n    \"\"\"Returns True/False/(False, reason)\"\"\"\n    if not salt.utils.path.which(\"required_cmd\"):\n        return False, \"required_cmd not found\"\n    return True\n\ndef my_function(name, param=None):\n    \"\"\"\n    CLI Example::\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    if not name:\n        raise salt.exceptions.SaltInvocationError(\"name required\")\n    return __salt__[\"cmd.run\"](f\"command {name}\")\n```\n\n### State Module Return Structure\n```python\nret = {\n    \"name\": name,        # Resource name\n    \"result\": False,     # True/False/None (test mode)\n    \"changes\": {},       # {\"old\": ..., \"new\": ...}\n    \"comment\": \"\"        # What happened\n}\n```\n\n**State flow:** Validate → Check current → If correct return → If test mode return None → Make changes → Return result\n\n**Complete templates: [agents/docs/module-templates.md](agents/docs/module-templates.md)**\n\n---\n\n## Dunder Dictionaries\n\n- `__opts__`: Config (`__opts__[\"test\"]`, `__opts__.get(\"id\")`)\n- `__grains__`: System info (`__grains__.get(\"os_family\")`)\n- `__pillar__`: Secure data (`__pillar__.get(\"password\")`)\n- `__context__`: Per-run cache - use for expensive operations\n- `__salt__`: Execution modules (`__salt__[\"pkg.install\"](\"nginx\")`)\n- `__utils__`: Utilities (`__utils__[\"files.is_text\"](path)`)\n- `__states__`: State modules (states only)\n\n**Context cache pattern:**\n```python\nif \"cache_key\" not in __context__:\n    __context__[\"cache_key\"] = expensive_operation()\nreturn __context__[\"cache_key\"]\n```\n\n---\n\n## Common Patterns\n\n### Error Handling\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Operation failed\n    SaltInvocationError,      # Bad arguments\n    CommandNotFoundError,     # Binary not found\n)\n```\n\n### Logging\n```python\nlog = logging.getLogger(__name__)\nlog.debug(\"Processing %s\", filename)  # Good - lazy formatting\nlog.debug(f\"Processing {filename}\")   # Bad - f-strings evaluated early\n```\n**Never log secrets.**\n\n### File Handling\n```python\nimport salt.utils.files\nwith salt.utils.files.fopen(path, \"r\") as fp_:   # Good\n    content = fp_.read()\nwith open(path, \"r\") as fp_:                     # Bad - fails lint (resource-leakage)\n    content = fp_.read()\n```\nApplies to modules, states, utils, and tests alike.\n\n### Platform Detection\n```python\nsalt.utils.platform.is_windows()\nsalt.utils.platform.is_linux()\n__grains__.get(\"os_family\")  # \"Debian\", \"RedHat\", etc.\n```\n\n### Decorators\n```python\nfrom salt.utils.decorators.path import which\nfrom salt.utils.decorators import depends, memoize\n\n@which(\"systemctl\")  # Require binary\n@depends(\"docker\")   # Require Python module\n@memoize            # Cache forever\n```\n\n---\n\n## Development Environment\n\n**Two environments required:**\n- **venv310**: Testing 3006.x/3007.x branches\n- **venv312**: Testing master branch + pre-commit\n\n```bash\n# venv310\npython3.10 -m venv venv310 && source venv310/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.10/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.10/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && deactivate\n\n# venv312\npython3.12 -m venv venv312 && source venv312/bin/activate\npip install --upgrade pip setuptools wheel\npip install -r requirements/static/pkg/py3.11/linux.lock  # or darwin.lock/windows.lock\npip install -r requirements/pytest.txt -r requirements/static/ci/py3.11/tools.lock\npip install pre-commit python-tools-scripts && pip install -e . && pre-commit install && deactivate\n```\n\n**Always use full paths:** `./venv312/bin/python`, `./venv312/bin/pytest` (master); `./venv310/...` for 3006.x/3007.x\n\n**Complete setup: [agents/docs/development-setup.md](agents/docs/development-setup.md)**\n\n---\n\n## Testing\n\n### Pytest Structure\n```python\nimport pytest\nimport salt.modules.mymodule as mymodule\n\n@pytest.fixture\ndef configure_loader_modules():\n    return {\n        mymodule: {\n            \"__opts__\": {\"test\": False},\n            \"__grains__\": {\"os\": \"Linux\"},\n            \"__salt__\": {},\n        }\n    }\n\ndef test_function():\n    result = mymodule.my_function(\"test\")\n    assert result == \"expected\"\n```\n\n### Running Tests\n```bash\n# Nox\nnox -e test-3 -- tests/pytests/unit/test_loader.py\nnox -e test-3 -- --lf  # Last failed\n\n# Direct (faster)\n./venv312/bin/pytest tests/pytests/unit/test_foo.py -v\n```\n\n**Complete guide: [agents/docs/testing.md](agents/docs/testing.md)**\n\n---\n\n## Code Checklist\n\n### Execution Module\n- [ ] `log = logging.getLogger(__name__)`\n- [ ] Docstrings with CLI examples\n- [ ] `__virtual__()` if platform-specific\n- [ ] Input validation (`SaltInvocationError`)\n- [ ] Error handling (`CommandExecutionError`)\n- [ ] Context caching for expensive ops\n- [ ] Unit tests\n\n### State Module\n- [ ] Returns: name, result (True/False/None), changes, comment\n- [ ] result=None for test mode\n- [ ] Check current state before modifying\n- [ ] Populate changes dict with old/new\n\n---\n\n## Quick Reference\n\n### Common Imports\n```python\nimport logging\nimport salt.exceptions\nimport salt.utils.args\nimport salt.utils.platform\nimport salt.utils.path\n\nlog = logging.getLogger(__name__)\n```\n\n### Run Commands\n```python\noutput = __salt__[\"cmd.run\"](\"ls -la\")\nresult = __salt__[\"cmd.run_all\"](\"cmd\")  # {\"retcode\": 0, \"stdout\": \"...\", \"stderr\": \"...\"}\n```\n\n### File Operations\n```python\ncontent = __salt__[\"file.read\"](path)\n__salt__[\"file.write\"](path, content)\nexists = __salt__[\"file.file_exists\"](path)\n```\n\n---\n\n## Documentation Links\n\n- **[agents/docs/development-setup.md](agents/docs/development-setup.md)** - venv setup, verification\n- **[agents/docs/architecture.md](agents/docs/architecture.md)** - Complete architecture, all module types\n- **[agents/docs/module-templates.md](agents/docs/module-templates.md)** - Complete templates, patterns\n- **[agents/docs/testing.md](agents/docs/testing.md)** - Test templates, mocking, container testing\n- **[agents/docs/git-and-ci.md](agents/docs/git-and-ci.md)** - Commit rules, PR workflow, CI reproduction\n- **[agents/docs/troubleshooting.md](agents/docs/troubleshooting.md)** - Common issues and solutions\n\n**Key example files:** `salt/modules/test.py`, `salt/modules/file.py`, `salt/states/pkg.py`\n\n---\n\n## Key Principles\n\n- **Idempotency**: Safe to run multiple times\n- **Testing**: Every function needs tests\n- **Documentation**: Docstrings with CLI examples\n- **Error Handling**: Use Salt exceptions\n- **Performance**: Cache in `__context__`\n- **Security**: Validate inputs, never log secrets\n\n<!-- File: agents/docs/architecture.md (Tokens: ~1759 | Category: subagent-persona) -->\n# Salt Architecture\n\n## What is Salt?\n\nSalt is a Python-based configuration management system using a master-minion architecture:\n- **Master**: Central server that manages configuration and sends commands\n- **Minion**: Agent running on managed systems that executes commands\n- **Event Bus**: Real-time communication system between components\n- **Loader System**: Dynamic plugin system for extending functionality\n\n## Module Types\n\nSalt uses a loader system to discover and load different types of modules:\n\n### Execution Modules (`salt/modules/`)\nFunctions executed on minions (e.g., `pkg.install`, `file.read`). There are 264+ execution modules providing the CLI commands available via Salt.\n\n**Examples:**\n- `salt/modules/pkg.py` - Package management (virtual module)\n- `salt/modules/file.py` - File operations\n- `salt/modules/cmd.py` - Command execution\n- `salt/modules/systemd_service.py` - systemd service management\n\n### State Modules (`salt/states/`)\nDeclarative configuration management (e.g., `pkg.installed`, `file.managed`). There are 126+ state modules providing idempotent configuration.\n\n**Examples:**\n- `salt/states/pkg.py` - Package installation states\n- `salt/states/file.py` - File management states\n- `salt/states/service.py` - Service management states\n\n### Runner Modules (`salt/runners/`)\nMaster-side operations (e.g., `salt-run jobs.list_jobs`). Runners execute on the Salt master.\n\n### Wheel Modules (`salt/wheel/`)\nMaster configuration management (key management, config). Used for managing the Salt master itself.\n\n### Grain Modules (`salt/grains/`)\nStatic system information collection. Grains provide facts about the minion system (OS, CPU, memory, etc.).\n\n### Pillar Modules (`salt/pillar/`)\nDynamic configuration data backends. Pillars provide secure, targeted data to minions.\n\n### Beacon Modules (`salt/beacons/`)\nSystem monitoring triggers (watch for events). Beacons monitor system events and fire events on the event bus.\n\n### Engine Modules (`salt/engines/`)\nLong-running processes on master or minion. Engines provide persistent functionality.\n\n### Returner Modules (`salt/returners/`)\nStore or send command output to external systems (databases, message queues, etc.).\n\n### Renderer Modules (`salt/renderers/`)\nTemplate engines (Jinja, Mako, YAML, JSON). Renderers process state files.\n\n### Matcher Modules (`salt/matchers/`)\nTarget matching (glob, pcre, grain, pillar, compound). Matchers determine which minions execute commands.\n\n### Utility Modules (`salt/utils/`)\nShared utility functions (170+ modules). Utilities provide common functionality across Salt.\n\n## Directory Structure\n\n```\nsalt/\n├── master.py, minion.py, state.py  # Core daemon implementations\n├── loader/                         # Plugin loader system (LazyLoader)\n├── modules/                        # 264+ execution modules\n├── states/                         # 126+ state modules\n├── runners/, wheel/                # Master operations and config\n├── beacons/, engines/              # Monitoring triggers, long-running processes\n├── grains/, pillar/                # System info, config data backends\n├── renderers/, returners/          # Template engines, return data storage\n├── matchers/, transport/           # Targeting systems, communication layer\n├── utils/                          # 170+ utility modules\n└── ext/                            # Vendored third-party code\n\ntests/\n├── pytests/\n│   ├── unit/                       # Fast tests, no daemons/network\n│   ├── functional/                 # End-to-end, no external deps\n│   ├── integration/                # With daemons/network\n│   ├── scenarios/                  # Scenario-based testing\n│   └── pkg/                        # Package install/upgrade tests\n└── support/                        # Test helpers and fixtures\n```\n\n## Key Architectural Components\n\n### State Compiler\n\n`salt/state.py` compiles YAML/Jinja templates into execution chunks. Uses **NetworkX** for dependency graph management with requisites:\n\n- `require`: Must run after\n- `watch`: Run if watched state changes\n- `onchanges`: Run only if watched state changes\n- `onfail`: Run if watched state fails\n- `listen`: Listen for notifications\n- `prereq`: Run before (with test mode check)\n\nAll state operations are idempotent and support test mode (`test=True`).\n\n### Transport Layer\n\n`salt/transport/` handles communication between master and minions:\n\n- **ZeroMQ** (default): High-performance messaging\n- **TCP**: Alternative transport\n- **WebSocket**: For browser-based access\n\n### Security\n\n- **PKI-based authentication** (`salt/crypt.py`)\n- **AES encryption** for all payloads\n- **Master key acceptance workflow** prevents unauthorized access\n- **External auth (eauth)** for Salt API access\n\n### Event Bus\n\n`salt/utils/event.py` provides ZeroMQ pub/sub with namespaced event tags for real-time communication between all components.\n\nEvents are namespaced with tags like:\n- `salt/job/<jid>/new` - New job started\n- `salt/job/<jid>/ret/<minion_id>` - Job return from minion\n- `salt/auth` - Authentication events\n- `salt/minion/<minion_id>/start` - Minion started\n\n### Loader System\n\nThe loader system (`salt/loader/`) dynamically discovers and loads modules at runtime. Key features:\n\n- **LazyLoader**: Loads modules on first access, not import\n- **Virtual names**: Modules can override their name via `__virtual__()`\n- **Dunder injection**: Injects `__salt__`, `__opts__`, `__grains__`, etc.\n- **Caching**: Modules are cached after first load\n\n## Async/Await Transition\n\nSalt is transitioning from **Tornado** coroutines to native **asyncio**:\n- Use native `async`/`await` syntax, not `@tornado.gen.coroutine`\n- Prefer asyncio patterns over Tornado patterns\n- Be aware of mixed async code during transition\n\n## Master-Minion Communication\n\n1. Minion authenticates with master via PKI\n2. Master accepts minion key\n3. Minion connects to master on port 4506 (request server)\n4. Master publishes commands on port 4505 (publish server)\n5. Minions subscribe to master's publish port\n6. Commands are encrypted with AES\n7. Results return via request server\n\n## File Server\n\nSalt includes a file server that serves files to minions:\n- `salt://` URLs reference files on the file server\n- Multiple backends: local, git, S3, HTTP, etc.\n- Files are cached on minions\n\nFor details on the Git backends and performance, see [GitFS Providers](gitfs-providers.md).\n\n## Targeting\n\nMinions can be targeted in multiple ways:\n- **Glob**: `*`, `web*.example.com`\n- **Grain**: `os:Ubuntu`, `kernel:Linux`\n- **Pillar**: `role:webserver`\n- **Compound**: `G@os:Ubuntu and webserver*`\n- **List**: `web1,web2,web3`\n- **PCRE**: Regular expressions\n\n## Performance Considerations\n\n### Lazy Loading\nModules only load on first access. Import-time side effects may not occur until first use of a module function.\n\n### Context Caching\nUse `__context__` to cache expensive operations within a Salt run (single execution).\n\n### Module Discovery\n- Module directories need `__init__.py`\n- Filename becomes namespace: `salt/modules/pkg.py` → `pkg.*`\n- Use `__virtualname__` for different name than filename\n- **Performance tip**: Filename should match virtualname when possible\n\n## Additional Resources\n\n### Documentation\n- Module development: `doc/topics/development/modules/developing.rst`\n- State writing guide: `doc/ref/states/writing.rst`\n- Contributing guidelines: `CONTRIBUTING.rst`\n\n### Example Modules\n- Simple execution module: `salt/modules/test.py`\n- Complex execution module: `salt/modules/file.py`\n- Package manager: `salt/modules/aptpkg.py`, `salt/modules/yumpkg.py`\n- Service manager: `salt/modules/systemd_service.py`\n- Simple state: `salt/states/pkg.py`\n- Complex state: `salt/states/file.py`\n\n<!-- File: agents/docs/development-setup.md (Tokens: ~1051 | Category: subagent-persona) -->\n# Salt Development Environment Setup\n\nThis guide covers setting up your local development environment for Salt development.\n\n## Virtual Environment Requirements\n\nSalt development requires setting up **two** virtual environments:\n\n- **venv310 (Python 3.10)**: For running tests on 3006.x and 3007.x branches\n- **venv314 (Python 3.14)**: For running tests on master branch AND running pre-commit hooks\n\n## Prerequisites\n\n- Python 3.10 and Python 3.14 installed on your system\n- Git repository cloned\n- Internet connection for downloading dependencies\n\n## Setup venv310 (Python 3.10)\n\nThis environment is used for testing on 3006.x and 3007.x branches.\n\n```bash\n# Create virtual environment\npython3.10 -m venv venv310\n\n# Activate\nsource venv310/bin/activate\n\n# Upgrade pip\npip install --upgrade pip setuptools wheel\n\n# Install platform-specific dependencies (choose your OS)\n# Linux:\npip install -r requirements/static/pkg/py3.10/linux.lock\n\n# macOS:\npip install -r requirements/static/pkg/py3.10/darwin.lock\n\n# Windows:\npip install -r requirements/static/pkg/py3.10/windows.lock\n\n# Install pytest requirements\npip install -r requirements/pytest.txt\n\n# Install Salt in editable mode\npip install -e .\n\n# Install tools dependencies (for using tools/ commands)\npip install -r requirements/static/ci/py3.10/tools.lock\n\n# Install pre-commit and python-tools-scripts\npip install pre-commit python-tools-scripts\n\n# Deactivate\ndeactivate\n```\n\n## Setup venv314 (Python 3.14)\n\nThis environment is used for testing on master branch and running pre-commit hooks.\n\n```bash\n# Create virtual environment\npython3.14 -m venv venv314\n\n# Activate\nsource venv314/bin/activate\n\n# Upgrade pip\npip install --upgrade pip setuptools wheel\n\n# Install platform-specific dependencies (choose your OS)\n# Linux:\npip install -r requirements/static/pkg/py3.14/linux.lock\n\n# macOS:\npip install -r requirements/static/pkg/py3.14/darwin.lock\n\n# Windows:\npip install -r requirements/static/pkg/py3.14/windows.lock\n\n# Install pytest requirements\npip install -r requirements/pytest.txt\n\n# Install Salt in editable mode\npip install -e .\n\n# Install tools dependencies\npip install -r requirements/static/ci/py3.14/tools.lock\n\n# Install pre-commit and python-tools-scripts\npip install pre-commit python-tools-scripts\n\n# Install pre-commit hooks\npre-commit install\n\n# Deactivate\ndeactivate\n```\n\n## Environment Usage\n\n### venv310 is used for:\n- Running tests on 3006.x and 3007.x branches\n- Direct pytest execution on older branches\n- Tools commands (`python -m tools`)\n\n### venv314 is used for:\n- Running tests on master branch\n- Running pre-commit hooks\n- Code formatting and linting checks\n- Pre-commit validation before commits\n\n## Verify Installation\n\n```bash\n# Test Salt import (master / default dev)\n./venv314/bin/python -c \"import salt.version; print(salt.version.__version__)\"\n\n# Run a simple test\n./venv314/bin/pytest tests/pytests/unit/test_loader.py -v\n\n# Test tools (3006.x/3007.x workflow)\n./venv310/bin/python -m tools --help\n```\n\n## IMPORTANT: Using Virtual Environments\n\nWhen running tests or tools, always use the full path to the venv executable or activate the venv first. This ensures you're using the correct Python environment with all dependencies installed.\n\n**Good examples:**\n```bash\n./venv310/bin/pytest tests/pytests/unit/test_foo.py -v\n./venv314/bin/pre-commit run --files salt/loader/lazy.py\n./venv310/bin/python -m tools ts pytest run tests/pytests/unit/\n```\n\n**Alternatively, activate first:**\n```bash\nsource venv314/bin/activate\npytest tests/pytests/unit/test_foo.py -v\ndeactivate\n```\n\n## Troubleshooting\n\n### ModuleNotFoundError: No module named 'ptscripts'\n\nIf you see this error when running `python -m tools`, you need to install the tools dependencies:\n\n```bash\n./venv310/bin/pip install -r requirements/static/ci/py3.10/tools.lock\n./venv310/bin/pip install python-tools-scripts\n```\n\n### venv not found\n\nIf you get \"No such file or directory\" for venv paths, make sure you've created the virtual environment and are running commands from the repository root directory.\n\n### Permission errors\n\nOn some systems you may need to use `python3.10` and `python3.14` explicitly instead of just `python3`.\n\n### Pre-commit hooks not running\n\nMake sure you've run `pre-commit install` in the venv314 environment:\n\n```bash\nsource venv314/bin/activate\npre-commit install\ndeactivate\n```\n\n<!-- File: agents/docs/git-and-ci.md (Tokens: ~1739 | Category: subagent-persona) -->\n# Git Workflow and CI\n\n## Commit Guidelines\n\n### CRITICAL RULES\n\n1. **NO attribution lines** - **NEVER** add \"Generated with Claude Code\", \"Co-Authored-By: Claude\", or any AI attribution\n2. **Use imperative mood** - \"Fix bug\", not \"Fixed bug\" or \"Fixes bug\"\n3. **Reference issues** - Use `#NNNN` to reference GitHub issues\n4. **Small, focused commits** - One logical change per commit\n5. **Rebase before push** - Keep history linear and clean\n\n### Good Commit Message Example\n\n```\nFix loader cache invalidation on module reload\n\nWhen modules are reloaded, the loader cache was not properly\ninvalidated, causing stale references. Clear the cache dict\non reload to ensure fresh module imports.\n\nFixes #12345\n```\n\n### Bad Commit Message Example\n\n```\nFixed some bugs\n\nCo-Authored-By: Claude <noreply@anthropic.com>\n```\n\n**Why this is bad:**\n- Past tense instead of imperative mood\n- Vague description\n- Contains AI attribution (NEVER do this)\n\n## PR Workflow\n\n### Finding and Checking PRs\n\n```bash\n# Find PR by branch name\ngh pr list --repo saltstack/salt --head <BRANCH-NAME> --limit 5\n\n# Check PR status\ngh pr view <PR-NUMBER> --repo saltstack/salt\ngh pr checks <PR-NUMBER> --repo saltstack/salt\n\n# Watch CI run\ngh run watch <RUN-ID> --repo saltstack/salt\n```\n\n## Branch Strategy\n\n- **Main PR branch**: `master` (not `main`)\n- **Release branches**: `3006.x`, `3007.x`, etc.\n- **Merge-forward branches**: `merge/3007.x/master-YY-MM-DD`\n\n## CI Failure Reproduction\n\nWhen CI tests fail, you can reproduce the exact failure locally using containers.\n\n### Prerequisites\n\n- Docker installed and running\n- Virtual environment setup (see [development-setup.md](development-setup.md))\n- `ptscripts` installed (`pip install python-tools-scripts`)\n\n### Step 1: Download CI Artifacts\n\nFirst, get the CI artifacts from the failed run:\n\n```bash\n./venv310/bin/python -m ptscripts ci download-artifacts --run-id <RUN-ID>\n```\n\nThe run ID can be found in the GitHub Actions URL or via `gh run list`.\n\n### Step 2: Create Container\n\nCreate a container using the same image as CI:\n\n```bash\n./venv310/bin/python -m ptscripts container create <IMAGE> --name <NAME>\ndocker start <NAME>\n```\n\n**Example container images:**\n- `ghcr.io/saltstack/salt-ci-containers/testing:debian-11`\n- `ghcr.io/saltstack/salt-ci-containers/testing:ubuntu-22.04`\n- `ghcr.io/saltstack/salt-ci-containers/testing:ubuntu-24.04`\n- `ghcr.io/saltstack/salt-ci-containers/testing:rockylinux-8`\n- `ghcr.io/saltstack/salt-ci-containers/testing:rockylinux-9`\n- `ghcr.io/saltstack/salt-ci-containers/testing:fedora-40`\n- `ghcr.io/saltstack/salt-ci-containers/testing:amazonlinux-2`\n- `ghcr.io/saltstack/salt-ci-containers/testing:amazonlinux-2023`\n- `ghcr.io/saltstack/salt-ci-containers/testing:photonos-3`\n- `ghcr.io/saltstack/salt-ci-containers/testing:photonos-4`\n- `ghcr.io/saltstack/salt-ci-containers/testing:photonos-5`\n\n### Step 3: Setup Container\n\n```bash\n# Decompress dependencies\ndocker exec <NAME> python3 -m nox -e decompress-dependencies -- linux x86_64\n\n# Create relenv toolchain symlink (Python 3.14+ only)\ndocker exec <NAME> bash -c \"mkdir -p /root/.local/relenv && ln -sf /root/.cache/relenv/toolchains /root/.local/relenv/toolchain\"\n```\n\n### Step 4: Run Tests\n\n```bash\ndocker exec <NAME> python3 -m nox -e ci-test-onedir -- <TEST-PATH> --run-slow -x -v\n```\n\n**Examples:**\n```bash\n# Run specific test file\ndocker exec mycontainer python3 -m nox -e ci-test-onedir -- tests/pytests/unit/modules/test_pkg.py --run-slow -x -v\n\n# Run specific test function\ndocker exec mycontainer python3 -m nox -e ci-test-onedir -- tests/pytests/unit/modules/test_pkg.py::test_install --run-slow -x -v\n\n# Run with pattern matching\ndocker exec mycontainer python3 -m nox -e ci-test-onedir -- tests/pytests/unit/ -k test_systemd --run-slow -x -v\n```\n\n### Step 5: Debug in Container\n\nIf you need to debug interactively:\n\n```bash\n# Shell into container\ndocker exec -it <NAME> bash\n\n# Inside container, activate environment and run tests\ncd /salt\npython3 -m nox -e ci-test-onedir -- tests/pytests/unit/test_foo.py -x -v\n```\n\n### Step 6: Cleanup\n\n```bash\n# Stop and remove container\ndocker stop <NAME>\ndocker rm <NAME>\n\n# Clean artifacts\nrm -rf artifacts/ nox-*.zip nox.*.tar.*\n```\n\n## Container Filesystem Behavior\n\nWhen testing in containers, be aware:\n\n- Changes to `salt/` source are visible immediately (mounted volume)\n- Changes to Salt library may need artifact refresh:\n  ```bash\n  docker exec <NAME> cp /salt/salt/modules/foo.py \\\n      /salt/artifacts/salt/lib/python3.14/site-packages/salt/modules/\n  ```\n\n## Stale Artifacts\n\nAlways clean before downloading new CI artifacts:\n\n```bash\nrm -rf artifacts/ nox-*.zip nox.*.tar.*\n```\n\nOld artifacts can cause confusing test failures.\n\n## Common CI Issues\n\n### Tests Pass Locally But Fail in CI\n\nPossible causes:\n1. **Different Python version** - CI uses specific Python versions per branch\n2. **Platform differences** - CI runs on Linux, you might be on macOS/Windows\n3. **Missing dependencies** - CI containers have exact dependency versions\n4. **Timing issues** - CI environment may be slower/faster\n5. **Stale artifacts** - Clean and re-download\n\n### Container Setup Fails\n\nIf dependency decompression fails:\n1. Check you downloaded artifacts for correct run ID\n2. Ensure Docker has enough disk space\n3. Try removing old containers and images\n\n### Relenv Toolchain Issues\n\nFor Python 3.14+ containers, you must create the symlink:\n\n```bash\ndocker exec <NAME> bash -c \"mkdir -p /root/.local/relenv && ln -sf /root/.cache/relenv/toolchains /root/.local/relenv/toolchain\"\n```\n\nThis is a known path mismatch between relenv versions.\n\n## Using MCP Server for CI Workflows\n\nThe agents/mcp/salt_test MCP server provides tools for discovering CI failures:\n\n- `ci_pr_failures` - Get all failing tests from a PR\n- `ci_run_failures` - Get failures from specific CI run\n- `ci_failure_summary` - Get summary of recent failures\n\nSee [agents/mcp/salt_test/README.md](../mcp/salt_test/README.md) for details.\n\n## Best Practices\n\n1. **Always clean artifacts** before downloading new ones\n2. **Use exact CI container images** for reproduction\n3. **Note Python version** - use venv310 for 3006.x/3007.x, venv314 for master\n4. **Check run logs** in GitHub Actions for exact command that failed\n5. **Test locally first** before pushing to avoid CI churn\n6. **Keep commits small** for easier review and debugging\n\n<!-- File: agents/docs/module-templates.md (Tokens: ~3357 | Category: subagent-persona) -->\n# Salt Module Templates\n\nThis document provides comprehensive templates for writing Salt modules.\n\n## Execution Module Template\n\nAll execution modules follow this basic structure:\n\n```python\n\"\"\"\nModule docstring explaining purpose\n\"\"\"\n\nimport logging\n\nimport salt.exceptions\nimport salt.utils.args\nimport salt.utils.data\nimport salt.utils.files\nimport salt.utils.path\nimport salt.utils.platform\n\n# Set up logging\nlog = logging.getLogger(__name__)\n\n# Module metadata\n__virtualname__ = \"mymodule\"  # Optional: override module name\n__proxyenabled__ = [\"*\"]  # Optional: enable for proxy minions\n__func_alias__ = {\n    \"list_\": \"list\"  # Optional: map function names to avoid Python keywords\n}\n\n\ndef __virtual__():\n    \"\"\"\n    Only load this module if requirements are met\n    Returns True, False, or (False, reason)\n    \"\"\"\n    if not salt.utils.path.which(\"mycommand\"):\n        return False, \"mycommand binary not found\"\n    return True\n\n\ndef my_function(name, param=None):\n    \"\"\"\n    Function description\n\n    name\n        Description of name parameter\n\n    param\n        Description of param parameter\n\n    CLI Example:\n\n    .. code-block:: bash\n\n        salt '*' mymodule.my_function foo param=bar\n    \"\"\"\n    log.debug(\"Running my_function with name=%s\", name)\n\n    # Implementation\n    result = do_something(name, param)\n\n    return result\n```\n\n## State Module Template\n\nState modules provide idempotent, declarative configuration. They call execution modules to do the actual work.\n\n### State Return Structure\n\nEvery state function MUST return this exact structure:\n\n```python\nret = {\n    \"name\": name,        # The name parameter passed to the state\n    \"result\": result,    # True (success), False (failure), or None (test mode)\n    \"changes\": {},       # Dictionary of changes made\n    \"comment\": comment   # String or list describing what happened\n}\n```\n\n### Complete State Template\n\n```python\n\"\"\"\nState module docstring\n\"\"\"\n\nimport logging\n\nimport salt.exceptions\n\nlog = logging.getLogger(__name__)\n\n\ndef __virtual__():\n    \"\"\"\n    Only load if the corresponding execution module is available\n    \"\"\"\n    if \"mymod.do_thing\" in __salt__:\n        return True\n    return (False, \"mymod execution module not available\")\n\n\ndef managed(name, value=None, **kwargs):\n    \"\"\"\n    Ensure something is in the desired state\n\n    name\n        The resource identifier\n\n    value\n        The desired value\n\n    Example:\n\n    .. code-block:: yaml\n\n        /etc/myconfig:\n          mymod.managed:\n            - value: foo\n    \"\"\"\n    # Initialize return dict\n    ret = {\n        \"name\": name,\n        \"result\": False,\n        \"changes\": {},\n        \"comment\": \"\"\n    }\n\n    # 1. Validate input\n    if not name:\n        ret[\"comment\"] = \"Name is required\"\n        return ret\n\n    # 2. Check current state\n    current = __salt__[\"mymod.get_current\"](name)\n\n    # 3. Compare with desired state\n    if current == value:\n        ret[\"result\"] = True\n        ret[\"comment\"] = f\"{name} is already in the desired state\"\n        return ret\n\n    # 4. Handle test mode\n    if __opts__[\"test\"]:\n        ret[\"result\"] = None\n        ret[\"comment\"] = f\"{name} would be updated\"\n        ret[\"changes\"] = {\"old\": current, \"new\": value}\n        return ret\n\n    # 5. Make changes\n    try:\n        result = __salt__[\"mymod.set_value\"](name, value)\n    except Exception as exc:\n        ret[\"comment\"] = f\"Failed to update {name}: {exc}\"\n        return ret\n\n    # 6. Verify changes\n    new_value = __salt__[\"mymod.get_current\"](name)\n\n    # 7. Set result and changes\n    if new_value == value:\n        ret[\"result\"] = True\n        ret[\"changes\"] = {\"old\": current, \"new\": new_value}\n        ret[\"comment\"] = f\"{name} was updated\"\n    else:\n        ret[\"result\"] = False\n        ret[\"comment\"] = f\"Failed to update {name}\"\n\n    return ret\n```\n\n### State Flow Diagram\n\n```\n1. Validate Input → Invalid? Return with result=False\n                  ↓\n2. Check Current State\n                  ↓\n3. Already Correct? → Yes: Return with result=True, no changes\n                  ↓ No\n4. Test Mode? → Yes: Return with result=None, proposed changes\n                  ↓ No\n5. Make Changes\n                  ↓\n6. Verify Changes\n                  ↓\n7. Return Result (True/False) with changes dict\n```\n\n### State Result Values\n\n- **`result=True`**: State succeeded, system is in desired state\n- **`result=False`**: State failed, system is NOT in desired state\n- **`result=None`**: Test mode, changes would be made but weren't\n\n### Changes Dictionary\n\nThe `changes` dictionary should show what changed:\n\n```python\n# Simple change\nret[\"changes\"] = {\"old\": \"foo\", \"new\": \"bar\"}\n\n# Multiple changes\nret[\"changes\"] = {\n    \"user\": {\"old\": \"root\", \"new\": \"apache\"},\n    \"mode\": {\"old\": \"0644\", \"new\": \"0755\"},\n}\n\n# New resource created\nret[\"changes\"] = {\"created\": name}\n\n# Resource removed\nret[\"changes\"] = {\"removed\": name}\n```\n\n## The __virtual__ Function Patterns\n\nThe `__virtual__()` function determines whether a module should load on a particular system. It runs before any other module code and should be fast.\n\n### Return Values\n\n1. **`True`**: Load module with filename as name\n2. **`False`**: Don't load module\n3. **`(False, reason)`**: Don't load, with explanation\n4. **`\"string\"`**: Load module with this name (virtualname)\n5. **`(\"string\", reason)`**: Load with name, with explanation\n\n### Pattern 1: Simple Platform Check\n\n```python\ndef __virtual__():\n    \"\"\"\n    Only work on POSIX-like systems\n    \"\"\"\n    if salt.utils.platform.is_windows():\n        return (\n            False,\n            \"The file execution module cannot be loaded: only available on \"\n            \"non-Windows systems - use win_file instead.\",\n        )\n    return True\n```\n\n### Pattern 2: Virtual Name with OS Check\n\nUsed when multiple modules implement the same interface (e.g., `pkg` for different package managers).\n\n```python\n__virtualname__ = \"pkg\"\n\ndef __virtual__():\n    \"\"\"\n    Confirm this module is on a Debian-based system\n    \"\"\"\n    if __grains__.get(\"os_family\") == \"Debian\":\n        return __virtualname__\n    return False, \"The pkg module could not be loaded: unsupported OS family\"\n```\n\n### Pattern 3: Binary Availability Check\n\n```python\ndef __virtual__():\n    \"\"\"\n    Only load if systemctl is available\n    \"\"\"\n    if not salt.utils.path.which(\"systemctl\"):\n        return False, \"The systemd module could not be loaded: systemctl not found\"\n    return True\n```\n\n### Pattern 4: Complex Multi-Condition Check\n\n```python\n__virtualname__ = \"service\"\n\ndef __virtual__():\n    \"\"\"\n    Only work on systems that have been booted with systemd\n    \"\"\"\n    is_linux = __grains__.get(\"kernel\") == \"Linux\"\n    is_booted = salt.utils.systemd.booted(__context__)\n    is_offline = salt.utils.systemd.offline(__context__)\n\n    if is_linux and (is_booted or is_offline):\n        return __virtualname__\n\n    return (\n        False,\n        \"The systemd execution module failed to load: only available on Linux \"\n        \"systems which have been booted with systemd.\",\n    )\n```\n\n### Pattern 5: Check for Execution Module Availability\n\nUsed in state modules to verify required execution modules exist.\n\n```python\ndef __virtual__():\n    \"\"\"\n    Only make these states available if a pkg provider has been detected\n    \"\"\"\n    if \"pkg.install\" in __salt__:\n        return True\n    return (False, \"pkg module could not be loaded\")\n```\n\n### Best Practices for __virtual__\n\n- Keep `__virtual__()` fast - it runs on every module scan\n- Provide clear error messages explaining why the module didn't load\n- Use `salt.utils.platform` for platform detection\n- Check grains for OS-specific logic\n- Cache expensive checks in `__context__` if needed\n\n## Common Module Patterns\n\n### Decorators\n\nSalt provides decorators for common tasks:\n\n```python\nfrom salt.utils.decorators import depends, memoize\nfrom salt.utils.decorators.path import which\n\n# Ensure binary exists\n@which(\"systemctl\")\ndef restart_service(name):\n    \"\"\"Only available if systemctl is found\"\"\"\n    pass\n\n# Ensure Python module exists\n@depends(\"docker\")\ndef list_containers():\n    \"\"\"Only available if docker module is installed\"\"\"\n    pass\n\n# Memoize expensive operations\n@memoize\ndef get_system_info():\n    \"\"\"Cache result for lifetime of process\"\"\"\n    pass\n```\n\n### Function Aliases\n\nMap function names to avoid Python keywords:\n\n```python\n__func_alias__ = {\n    \"list_\": \"list\",      # my_module.list() → my_module.list_()\n    \"exec_\": \"exec\",      # my_module.exec() → my_module.exec_()\n}\n```\n\n### Cross-Calling Modules\n\n```python\ndef my_function():\n    \"\"\"Call other execution modules\"\"\"\n    # Get file contents\n    content = __salt__[\"file.read\"](\"/etc/hosts\")\n\n    # Run a command\n    output = __salt__[\"cmd.run\"](\"ls -la\")\n\n    # Install a package\n    result = __salt__[\"pkg.install\"](\"nginx\")\n\n    return result\n```\n\n### Argument Handling\n\n```python\nimport salt.utils.args\n\ndef my_function(*args, **kwargs):\n    \"\"\"Handle various argument formats\"\"\"\n    # Clean extra kwargs from state data\n    kwargs = salt.utils.args.clean_kwargs(**kwargs)\n\n    # Parse key=value arguments\n    kwargs.update(salt.utils.args.parse_input(args))\n\n    return kwargs\n```\n\n### Path Handling\n\n```python\nimport salt.utils.path\n\ndef my_function():\n    \"\"\"Handle paths safely\"\"\"\n    # Find binary in PATH\n    binary = salt.utils.path.which(\"nginx\")\n    if not binary:\n        return False\n\n    # Join paths correctly for OS\n    config_path = salt.utils.path.join(\"/etc\", \"nginx\", \"nginx.conf\")\n\n    return config_path\n```\n\n### File Handling\n\n**Always use `salt.utils.files.fopen()` instead of the bare `open()` builtin**\nwhen reading or writing files in Salt code (modules, states, utils, and\ntests alike). `saltpylint`'s `resource-leakage` check (`W8470`) fails CI on\nany bare `open()` call.\n\n```python\nimport salt.utils.files\n\ndef my_function(path):\n    \"\"\"Read/write files the Salt way\"\"\"\n    with salt.utils.files.fopen(path, \"r\") as fp_:\n        content = fp_.read()\n\n    with salt.utils.files.fopen(path, \"w\") as fp_:\n        fp_.write(content)\n\n    return content\n```\n\n```python\n# Bad - fails the resource-leakage lint check\nwith open(path, \"r\") as fp_:\n    content = fp_.read()\n```\n\n`fopen()` is a thin wrapper around `open()` that additionally normalizes\nline endings/encoding across platforms and ensures file handles are tracked\nand closed consistently. Use it for every `open()` call, including binary\nmode (`\"rb\"`/`\"wb\"`) and in unit tests -- the lint check applies there too.\n\n### Platform Detection\n\n```python\nimport salt.utils.platform\n\ndef my_function():\n    \"\"\"Handle platform differences\"\"\"\n    if salt.utils.platform.is_windows():\n        return \"C:\\\\Windows\"\n    elif salt.utils.platform.is_linux():\n        return \"/etc\"\n    elif salt.utils.platform.is_darwin():\n        return \"/Library\"\n\n    # Check specific OS family\n    if __grains__.get(\"os_family\") == \"Debian\":\n        return \"apt\"\n    elif __grains__.get(\"os_family\") == \"RedHat\":\n        return \"yum\"\n```\n\n### Context Caching Pattern\n\n```python\ndef _get_cached_value():\n    \"\"\"Safe context usage for standalone calls\"\"\"\n    try:\n        context = __context__\n    except NameError:\n        context = {}\n\n    if \"my_key\" not in context:\n        context[\"my_key\"] = expensive_operation()\n    return context.get(\"my_key\")\n```\n\n## Error Handling\n\nUse Salt's exception classes for consistent error handling:\n\n```python\nfrom salt.exceptions import (\n    CommandExecutionError,    # Command/operation failed\n    SaltInvocationError,      # Bad function arguments\n    MinionError,              # General minion error\n    CommandNotFoundError,     # Binary not found\n)\n\ndef my_function(path, required_param):\n    \"\"\"\n    Example with proper error handling\n    \"\"\"\n    # Validate required parameters\n    if not required_param:\n        raise SaltInvocationError(\"required_param is mandatory\")\n\n    # Validate parameter values\n    if not os.path.exists(path):\n        raise SaltInvocationError(f\"Path does not exist: {path}\")\n\n    # Check for required binary\n    binary = salt.utils.path.which(\"mytool\")\n    if not binary:\n        raise CommandNotFoundError(\"mytool binary not found in PATH\")\n\n    # Execute operation with error handling\n    try:\n        result = __salt__[\"cmd.run_all\"](f\"{binary} {path}\")\n    except Exception as exc:\n        raise CommandExecutionError(\n            f\"Failed to run mytool on {path}: {exc}\"\n        )\n\n    # Check command result\n    if result[\"retcode\"] != 0:\n        raise CommandExecutionError(\n            f\"mytool failed: {result['stderr']}\"\n        )\n\n    return result[\"stdout\"]\n```\n\n### When to Use Each Exception\n\n- **`SaltInvocationError`**: Invalid function arguments, missing required parameters\n- **`CommandExecutionError`**: Operation failed (file not found, command failed, etc.)\n- **`CommandNotFoundError`**: Required binary not in PATH\n- **`MinionError`**: General minion-side errors\n- **Standard exceptions**: Use Python's built-in exceptions when appropriate\n\n## Module Checklists\n\n### Execution Module Checklist\n\n- [ ] Proper imports (logging, salt.exceptions, salt.utils.*)\n- [ ] Logger configured: `log = logging.getLogger(__name__)`\n- [ ] `__virtual__()` function if platform/dependency specific\n- [ ] Docstrings on all functions with CLI examples\n- [ ] Input validation with proper exceptions\n- [ ] Error handling with CommandExecutionError/SaltInvocationError\n- [ ] Use `__context__` for caching expensive operations\n- [ ] Unit tests covering success and error cases\n\n### State Module Checklist\n\n- [ ] Returns correct dict structure (name, result, changes, comment)\n- [ ] Validates input parameters\n- [ ] Checks current state before making changes\n- [ ] Handles test mode (`__opts__[\"test\"]`)\n- [ ] Verifies changes after applying\n- [ ] Sets `result` correctly (True/False/None)\n- [ ] Populates `changes` dict with before/after\n- [ ] Provides clear `comment` explaining what happened\n\n## Reference Files\n\n- Simple example: `salt/modules/test.py`\n- Complex example: `salt/modules/file.py`\n- Platform-specific: `salt/modules/aptpkg.py`, `salt/modules/systemd_service.py`\n- State examples: `salt/states/file.py`, `salt/states/pkg.py`\n- Documentation: `doc/topics/development/modules/developing.rst`, `doc/ref/states/writing.rst`\n\n<!-- File: agents/docs/package-building.md (Tokens: ~1128 | Category: subagent-persona) -->\n# Package Building Guide\n\nThis guide describes how to build Salt packages (RPM, Deb, etc.) locally using the same methods as the CI/CD pipeline.\n\n## Overview\n\nSalt packages are built using the `tools` script (specifically `tools pkg`) running inside a CI container. This ensures the build environment matches the official release environment.\n\nThe general process is:\n1.  Enter the appropriate CI container\n2.  Setup the Python environment\n3.  Build the source tarball\n4.  Build the \"Onedir\" (a self-contained directory with Python and dependencies)\n5.  Build the final package (RPM/Deb) using the Onedir\n\n## Prerequisites\n\n-   Docker installed and running\n-   `python-tools-scripts` installed in your local environment (optional, but helpful for some commands)\n\n## Reference Workflows\n\nThe instructions in this guide are derived from the official GitHub Actions workflows. If you need to verify the current build process or versions, check these files:\n\n-   **`.github/workflows/ci.yml`**: The main entry point for CI builds. It defines the high-level orchestration.\n-   **`.github/workflows/build-salt-onedir.yml`**: Defines how the \"Onedir\" (relocatable Python environment) is built. Check this for `relenv` and `python` versions.\n-   **`.github/workflows/build-packages.yml`**: Defines how the final RPM/Deb/Windows/macOS packages are built using the Onedir.\n-   **`.github/actions/build-source-tarball/action.yml`**: The steps to create the initial source distribution.\n\n## Building RPMs (Linux)\n\n### 1. Enter the Build Container\n\nFrom the root of the Salt repository:\n\n```bash\ndocker run --rm -it \\\n  -v \"$(pwd):/salt\" \\\n  -w /salt \\\n  ghcr.io/saltstack/salt-ci-containers/testing:fedora-42 \\\n  bash\n```\n\n*Note: Use `fedora-42` for RPM builds. For Deb builds, use `debian-11` or similar.*\n\n### 2. Setup Python Environment (Inside Container)\n\nOnce inside the container:\n\n```bash\n# Detect python version (likely 3.10 or 3.14 depending on branch/container)\nPY_VER=$(python3 -c \"import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')\")\n\n# Create and activate venv\npython3 -m venv venv\nsource venv/bin/activate\n\n# Install build dependencies\npip install --upgrade pip setuptools\npip install -r requirements/static/ci/py${PY_VER}/tools.lock\npip install -e .\n```\n\n### 3. Build Source Tarball\n\nCreate the source distribution:\n\n```bash\npython3 -m tools pkg source-tarball\n```\n\nThis will create `dist/salt-<version>.tar.gz`.\n\n### 4. Build Salt Onedir\n\nThe \"Onedir\" is an artifact containing Salt and all its dependencies (including Python itself) in a relocatable directory.\n\n```bash\n# Get the version from the tarball\nSALT_VERSION=$(ls dist/salt-*.tar.gz | sed 's/dist\\/salt-//;s/.tar.gz//')\nmkdir -p artifacts\n\n# Build the Onedir\n# Note: CI uses specific pinned versions for relenv and python.\n# Check .github/workflows/build-salt-onedir.yml for current versions.\npython3 -m tools pkg build salt-onedir \"dist/salt-${SALT_VERSION}.tar.gz\" \\\n    --platform linux \\\n    --package-name artifacts/salt \\\n    --relenv-version 0.22.4\n\n# Cleanup and Archive\npython3 -m tools pkg pre-archive-cleanup artifacts/salt\ntar -cJf \"artifacts/salt-${SALT_VERSION}-onedir-linux-x86_64.tar.xz\" -C artifacts salt\n```\n\n### 5. Build RPM Package\n\nUse the Onedir artifact to build the final RPM.\n\n```bash\npython3 -m tools pkg build rpm \\\n    --relenv-version=0.22.4 \\\n    --python-version=3.10.19 \\\n    --onedir=\"salt-${SALT_VERSION}-onedir-linux-x86_64.tar.xz\"\n```\n\nThe RPMs will be generated in `~/rpmbuild/RPMS/x86_64/`.\n\n### 6. Retrieve Artifacts\n\nBefore exiting the container, copy the RPMs to the mounted volume:\n\n```bash\nmkdir -p artifacts/rpm\ncp -r ~/rpmbuild/RPMS/x86_64/*.rpm artifacts/rpm/\n```\n\n## Building DEBs (Linux)\n\nThe process is similar to RPMs but uses the `deb` command and a Debian container.\n\n1.  Use `ghcr.io/saltstack/salt-ci-containers/testing:debian-11` (or newer).\n2.  Follow steps 2-4 above (Source Tarball & Onedir).\n3.  Run `python3 -m tools pkg build deb ...` instead of `rpm`.\n\n## Building Windows/macOS Packages\n\nWindows and macOS packages are typically built on their respective host OSs in CI, not in Docker containers. Refer to `.github/workflows/build-packages.yml` for the specific steps and requirements (signing certificates, etc.).\n\n<!-- File: agents/docs/testing.md (Tokens: ~1703 | Category: subagent-persona) -->\n# Salt Testing Guide\n\nSalt uses multiple test frameworks. All new code should include tests.\n\n## Test Directory Structure\n\n```\ntests/\n├── unit/               # Fast unit tests (no daemons, heavy mocking)\n│   ├── modules/        # Tests for salt/modules/\n│   ├── states/         # Tests for salt/states/\n│   └── utils/          # Tests for salt/utils/\n├── integration/        # Integration tests (with daemons)\n│   ├── modules/        # Tests for salt/modules/\n│   ├── states/         # Tests for salt/states/\n│   └── ...\n└── pytests/           # Pytest-based tests (newer style)\n    ├── unit/\n    ├── integration/\n    └── functional/\n```\n\n## Unit Test Templates\n\n### Legacy Style (TestCase)\n\n```python\n\"\"\"\nTests for salt.modules.mymodule\n\"\"\"\n\nimport pytest\n\nimport salt.modules.mymodule as mymodule\nfrom salt.exceptions import CommandExecutionError, SaltInvocationError\nfrom tests.support.mixins import LoaderModuleMockMixin\nfrom tests.support.mock import MagicMock, patch\nfrom tests.support.unit import TestCase\n\n\nclass MyModuleTestCase(TestCase, LoaderModuleMockMixin):\n    \"\"\"\n    Test cases for salt.modules.mymodule\n    \"\"\"\n\n    def setup_loader_modules(self):\n        \"\"\"\n        Setup loader modules - injects dunders\n        \"\"\"\n        return {\n            mymodule: {\n                \"__opts__\": {\"test\": False},\n                \"__grains__\": {\"os\": \"Linux\"},\n                \"__salt__\": {},\n            }\n        }\n\n    def test_simple_function(self):\n        \"\"\"\n        Test simple function\n        \"\"\"\n        result = mymodule.my_function(\"test\")\n        self.assertEqual(result, \"expected\")\n\n    def test_with_mock(self):\n        \"\"\"\n        Test with mocked subprocess call\n        \"\"\"\n        mock_cmd = MagicMock(return_value={\"retcode\": 0, \"stdout\": \"success\"})\n        with patch.dict(mymodule.__salt__, {\"cmd.run_all\": mock_cmd}):\n            result = mymodule.my_function(\"test\")\n            self.assertTrue(result)\n            mock_cmd.assert_called_once()\n\n    def test_error_handling(self):\n        \"\"\"\n        Test error conditions\n        \"\"\"\n        with self.assertRaises(SaltInvocationError):\n            mymodule.my_function(None)\n```\n\n### Pytest Style (Newer)\n\n```python\n\"\"\"\nTests for salt.modules.mymodule\n\"\"\"\n\nimport pytest\n\nimport salt.modules.mymodule as mymodule\nfrom salt.exceptions import SaltInvocationError\n\n\n@pytest.fixture\ndef configure_loader_modules():\n    \"\"\"\n    Setup module dunders\n    \"\"\"\n    return {\n        mymodule: {\n            \"__opts__\": {\"test\": False},\n            \"__grains__\": {\"os\": \"Linux\"},\n            \"__salt__\": {},\n        }\n    }\n\n\ndef test_simple_function():\n    \"\"\"\n    Test simple function\n    \"\"\"\n    result = mymodule.my_function(\"test\")\n    assert result == \"expected\"\n\n\ndef test_error_handling():\n    \"\"\"\n    Test error conditions\n    \"\"\"\n    with pytest.raises(SaltInvocationError):\n        mymodule.my_function(None)\n```\n\n## Mocking Patterns\n\n### Basic Mocking\n\n```python\nfrom tests.support.mock import MagicMock, patch, call\n\n# Mock a function call\nmock_func = MagicMock(return_value=\"result\")\nwith patch(\"salt.modules.mymodule.some_function\", mock_func):\n    result = mymodule.my_function()\n    mock_func.assert_called_once_with(\"expected_arg\")\n\n# Mock dunder dict\nwith patch.dict(mymodule.__salt__, {\"cmd.run\": MagicMock(return_value=\"output\")}):\n    result = mymodule.my_function()\n\n# Mock with side effects\nmock_func = MagicMock(side_effect=[\n    {\"retcode\": 1, \"stdout\": \"error\"},  # First call fails\n    {\"retcode\": 0, \"stdout\": \"success\"},  # Second call succeeds\n])\nwith patch.dict(mymodule.__salt__, {\"cmd.run_all\": mock_func}):\n    result = mymodule.my_function()\n```\n\n## Test Best Practices\n\n1. **One test per function behavior**: Test success cases, error cases, edge cases separately\n2. **Use descriptive test names**: `test_function_name_when_condition_then_expected_result`\n3. **Mock external dependencies**: File I/O, network calls, subprocess calls\n4. **Test error handling**: Verify exceptions are raised correctly\n5. **Test input validation**: Verify bad inputs are rejected\n6. **Keep tests fast**: Unit tests should run in milliseconds\n7. **Don't test implementation details**: Test the public API, not internal helpers\n\n## Running Tests\n\n### Using Nox (Recommended for CI Consistency)\n\n```bash\n# Run all tests\nnox -e test-3\n\n# Run specific test file\nnox -e test-3 -- tests/pytests/unit/test_loader.py\n\n# Run with pattern matching\nnox -e test-3 -- -k test_auth\n\n# Run specific test function\nnox -e test-3 -- tests/pytests/unit/test_loader.py::test_load_modules\n\n# Run last failed tests\nnox -e test-3 -- --lf\n\n# Run with specific Python version\nnox -e test-3.10\nnox -e test-3.14\n\n# Run with specific transport\nnox -e test-tcp-3\nnox -e test-zeromq-3\n\n# Run with coverage\nnox -e test-3 --coverage\nnox -e coverage-report\n```\n\n### Using Virtual Environment (Faster for Development)\n\nIf you have a local venv setup:\n\n```bash\n# Run tests directly with pytest (master branch: use venv312)\n./venv312/bin/pytest tests/pytests/unit/test_foo.py::test_bar -v\n\n# Run pre-commit checks on specific files\n./venv312/bin/pre-commit run --files salt/loader/lazy.py\n```\n\n## Container Testing (Reproduce CI Failures)\n\nTo reproduce CI failures locally using the same container environment:\n\n### 1. Download CI artifacts\n\n```bash\n./venv310/bin/python -m ptscripts ci download-artifacts --run-id <RUN-ID>\n```\n\n### 2. Create container\n\n```bash\n./venv310/bin/python -m ptscripts container create <IMAGE> --name <NAME>\ndocker start <NAME>\n```\n\n### 3. Setup container\n\n```bash\n# Decompress dependencies\ndocker exec <NAME> python3 -m nox -e decompress-dependencies -- linux x86_64\n\n# Create relenv toolchain symlink (Python 3.14+ only)\ndocker exec <NAME> bash -c \"mkdir -p /root/.local/relenv && ln -sf /root/.cache/relenv/toolchains /root/.local/relenv/toolchain\"\n```\n\n### 4. Run tests\n\n```bash\ndocker exec <NAME> python3 -m nox -e ci-test-onedir -- <TEST-PATH> --run-slow -x -v\n```\n\n### Example container images\n\n- `ghcr.io/saltstack/salt-ci-containers/testing:debian-11`\n- `ghcr.io/saltstack/salt-ci-containers/testing:ubuntu-22.04`\n- `ghcr.io/saltstack/salt-ci-containers/testing:rockylinux-9`\n\n## Linting and Formatting\n\n### Black (Code Formatting)\n\n```bash\n# Format all code\nblack .\n\n# Check without modifying\nblack --check .\n```\n\nBlack uses 88 character line length by default.\n\n### isort (Import Sorting)\n\n```bash\n# Sort all imports\nisort .\n\n# Check without modifying\nisort --check .\n```\n\nisort is configured with profile 3 and trailing commas in `pyproject.toml`.\n\n### Linting\n\n```bash\n# Lint both salt and tests\nnox -e lint\n\n# Lint Salt code only\nnox -e lint-salt\n\n# Lint tests only\nnox -e lint-tests\n```\n\n## Example Test Files\n\nFor reference, see:\n- Unit test example: `tests/unit/modules/test_systemd_service.py`\n- Complex mocking: `tests/unit/modules/test_cron.py`\n- Documentation: `doc/topics/tutorials/writing_tests.rst`\n\n<!-- File: agents/docs/troubleshooting.md (Tokens: ~300 | Category: subagent-persona) -->\n# Subagent: troubleshooting\n\nPath: `agents/docs/troubleshooting.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/docs/troubleshooting.md)\n\n<!-- File: agents/mcp/README.md (Tokens: ~300 | Category: subagent-persona) -->\n# Subagent: readme\n\nPath: `agents/mcp/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/mcp/README.md)\n\n<!-- File: agents/mcp/salt_test/README.md (Tokens: ~300 | Category: subagent-persona) -->\n# Subagent: readme\n\nPath: `agents/mcp/salt_test/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/mcp/salt_test/README.md)\n\n<!-- File: agents/README.md (Tokens: ~1230 | Category: subagent-persona) -->\n# AI Agent Instructions for Salt Development\n\nThis directory contains instruction files for various AI coding assistants to help them better understand and work with the Salt codebase.\n\n## Purpose\n\nThese files provide AI agents with:\n- Salt's architecture and design patterns\n- Coding conventions and best practices\n- Testing guidelines and requirements\n- Common pitfalls to avoid\n\n## Available Instructions\n\n- **CLAUDE.md** - Instructions for Anthropic's Claude (Claude Code, Claude.ai, etc.)\n- **GEMINI.md** - Instructions for Google's Gemini\n- **CURSOR.md** - Instructions for Cursor IDE\n- **COPILOT.md** - Instructions for GitHub Copilot\n\nEach instruction file provides a quick reference and links to detailed documentation (see below).\n\n## Detailed Documentation\n\nThe `docs/` directory contains comprehensive guides that are referenced by all agent instruction files. This provides a single source of truth for detailed information:\n\n- **[development-setup.md](docs/development-setup.md)** - Complete virtual environment setup\n  - venv310 and venv312 setup instructions\n  - Platform-specific dependencies\n  - Installation verification steps\n  - Common troubleshooting\n\n- **[architecture.md](docs/architecture.md)** - Salt architecture deep dive\n  - Master-minion architecture\n  - All module types (execution, state, grains, pillar, beacon, engine, etc.)\n  - Directory structure\n  - Key components (state compiler, transport layer, event bus, loader system)\n\n- **[module-templates.md](docs/module-templates.md)** - Complete code templates\n  - Full execution module template with all patterns\n  - Full state module template with flow diagram\n  - All `__virtual__()` patterns (5 variations)\n  - Error handling examples\n  - Common decorators and utilities\n\n- **[testing.md](docs/testing.md)** - Comprehensive testing guide\n  - Test directory structure\n  - Unit test templates (legacy and pytest styles)\n  - Mocking patterns with examples\n  - Running tests (Nox, venv, container)\n  - Container testing for CI reproduction\n  - Linting and formatting\n\n- **[git-and-ci.md](docs/git-and-ci.md)** - Git workflow and CI\n  - Commit guidelines (**NO AI attribution!**)\n  - PR workflow with gh CLI commands\n  - Branch strategy\n  - CI failure reproduction workflow\n  - Container setup and debugging\n\n- **[package-building.md](docs/package-building.md)** - Package Building Guide\n  - Building RPMs locally using CI containers\n  - Building DEBs locally\n  - Creating source tarballs and onedir artifacts\n\n- **[troubleshooting.md](docs/troubleshooting.md)** - Common issues and solutions\n  - Import order issues\n  - Module discovery problems\n  - ZeroMQ issues\n  - Python 3.11+ compatibility gotchas\n  - Container filesystem behavior\n  - Lazy loading behavior\n  - Common error messages and fixes\n\n## How to Use\n\n**Recommended approach:** Create a symlink from the root directory to the agent file you want to use.\n\n### Symlink (Recommended)\n\nCreating a symlink keeps your local setup in sync with updates to the canonical instructions:\n\n```bash\n# From the root of the Salt repository\nln -s agents/CLAUDE.md CLAUDE.md\n# or\nln -s agents/CURSOR.md .cursorrules\n# or\nln -s agents/GEMINI.md GEMINI.md\n# or\nln -s agents/COPILOT.md COPILOT.md\n```\n\n**Why symlink?**\n- Automatically receives updates when canonical files are updated\n- Links to `agents/docs/` work correctly\n- No maintenance required\n\n### Alternative: IDE-Specific Configuration\n\nSome tools may allow you to specify a custom path to instruction files in their settings. Consult your tool's documentation.\n\n**Note:** If you need to customize the instructions, it's better to propose changes to the canonical files in `agents/` so everyone benefits, rather than maintaining a personal copy.\n\n## MCP Servers\n\nThis directory also contains Model Context Protocol (MCP) servers that expose Salt development tools to AI agents:\n\n- **mcp/salt_test/** - Testing tools server\n  - Direct pytest execution (quick local testing)\n  - CI failure discovery (analyze PR failures)\n  - Container testing (reproduce CI failures)\n\nSee [mcp/README.md](mcp/README.md) for setup and usage instructions.\n\n### Quick Setup\n\n1. Install MCP SDK: `pip install mcp`\n2. Configure in `~/.config/claude-code/mcp_config.json` (see `mcp/mcp-config.json` for template)\n3. Set GitHub token for CI features: `export GITHUB_TOKEN=\"your_token\"`\n\nWith MCP configured, you can ask Claude:\n- \"What tests are failing in PR #68562?\"\n- \"Run the loader tests locally\"\n- \"Reproduce the failure from PR #68562 on debian-11\"\n\n## .gitignore\n\nThe root-level instruction files (CLAUDE.md, GEMINI.md, etc.) are intentionally ignored by git to prevent personal configurations from being committed. Only the canonical versions in this directory are tracked.\n\n## Contributing\n\nIf you discover better patterns, common issues, or ways to improve these instructions:\n\n1. Edit the appropriate file in this `agents/` directory\n2. Submit a pull request with your improvements\n3. Include a brief explanation of what the change helps agents understand better\n\n## File Naming Conventions\n\n- **CLAUDE.md** - For Claude-based tools\n- **GEMINI.md** - For Gemini-based tools\n- **CURSOR.md** - For Cursor IDE (may also be named `.cursorrules`)\n- **COPILOT.md** - For GitHub Copilot (may also be named `.github/copilot-instructions.md`)\n\nNote: Some tools may use different filenames. Check your tool's documentation for the correct filename and location.\n\n<!-- File: agents/reports/silent_drop_audit_3008_to_master.md (Tokens: ~300 | Category: subagent-persona) -->\n# Subagent: silent_drop_audit_3008_to_master\n\nPath: `agents/reports/silent_drop_audit_3008_to_master.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/saltstack/salt/HEAD/agents/reports/silent_drop_audit_3008_to_master.md)\n</yakaai_skills>"}