# AGENTS.md - Polytropos

## Project Overview

Polytropos is a Python build backend for Odoo modules that enables writing code once and
using it across multiple Odoo versions. It follows PEP 517/660 standards and generates
`__manifest__.py` dynamically at build time.

## Documentation as Source of Truth

The documentation in `docs/`, `CONTRIBUTING.md`, and this file is the **source of
truth** for design decisions. If you make changes to documentation, ensure tests verify
those design decisions and that the implementation passes them.

## Change Checklist

Whenever a change is requested, ALWAYS update all three:

1. **Implementation**: Code changes in `src/`
2. **Tests**: Update/add tests in `tests/`
3. **Documentation**: Update relevant docs in `docs/` and this file if needed

Run lint and typecheck after every change:

## Environment

-   Use **uv** for all Python package management
-   Activate venv: `source .venv/bin/activate` (or use `uv sync`, or `uv run` directly)
-   Use **plumbum** for CLI and subprocess execution
-   Nix devshell available: `nix develop --impure` or with direnv
-   **If binaries not found**: Use `direnv exec . <command>` (e.g.,
    `direnv exec . pre-commit run -a`)

## Naming Conventions

All code and documentation **must be in English**.

### Package and Module Names

-   **Package name** (Python): Use hyphens between words, e.g.,
    `odoo-addon-example-module`
-   **Module name** (Odoo manifest): Use underscores between words, e.g.,
    `example_module`
-   The system automatically converts hyphens to underscores when extracting the module
    name

## Build/Lint/Test/Docs Commands

```bash
# Run all tests
uv run pytest

# Run a single test (most common way to debug)
uv run pytest tests/test_build.py::TestBuildIntegration::test_basic_build -v

# Run tests with coverage
uv run pytest --cov=polytropos --cov-report=term-missing

# Run tests in parallel (default, configured in pyproject.toml)
uv run pytest -n auto

# Lint/Type check
ruff check src/
uv run mypy src/

# Fix linting automatically
ruff check src/ --fix

# Build sdist
uv build --sdist

# Build wheel for specific Odoo release
uv build --config-setting odoo_release=17.0 --wheel

# Build sdist
uv build --config-setting odoo_release=18.0 --wheel

# Build editable install
uv pip install -e . --config-setting odoo_release=17.0

# Documentation
uv run mkdocs serve
uv run mkdocs build

# Test docs links and code blocks
uv run python -m mktestdocs

# CLI
polytropos init my_module
```

## Code Style

-   **Imports**: ALWAYS at top of file, grouped: stdlib → third-party → local. NEVER
    inside functions.
-   **Relative imports**: Use relative imports within the package (e.g.,
    `from .conftest import ...`)
-   **Prefer flat over nested**: No deep nesting, avoid premature abstractions
-   **Explicit over implicit**: Clear code, no clever tricks
-   **Type hints**: Python 3.11+ style (`|`, `None`), avoid `Any` when possible
-   **Naming**: snake_case functions/vars, PascalCase classes, SCREAMING_SNAKE_CASE
    constants
-   **Line length**: Let ruff handle it (E501 ignored)
-   **String formatting**: Use f-strings for simple cases, `pprint.pformat` for
    structured output

## Docstrings

-   Use **Google-style** docstrings with markdown syntax
-   Include Args, Returns, Raises sections for functions
-   Keep brief one-line summaries for simple functions
-   In docs and docstrings, **filenames in backticks**: Always surround file names like
    `__manifest__.py`, `pyproject.toml`, `.gitignore` with backticks to prevent markdown
    bold formatting (e.g., use `__manifest__.py` not **manifest**.py)

Example:

```python
def get_odoo_release(config_settings: dict[str, Any] | None) -> str:
    """Get Odoo release with fallback.

    Args:
        config_settings: PEP 517 config_settings

    Returns:
        Odoo release string (e.g., "17.0")

    Raises:
        ValueError: If release cannot be determined
    """
```

## Error Handling

-   Raise `ValueError` for invalid configuration or missing required data
-   Raise `FileNotFoundError` for missing files
-   Use warnings.warn() for deprecation notices or config conflicts
-   Validate early, fail fast with clear error messages

## Testing

-   Prefer **integration tests** over unit tests
-   Use `tests/conftest.py` helper `build_file_tree()` for fixtures
-   Use `textwrap.dedent` for multiline strings in tests
-   Test real flows: create module tree, build, verify output
-   pytest config goes in `tests/conftest.py`
-   Use descriptive test names: `test_xxx_when_yyy_zzz`

## Documentation

-   Use MkDocs with Material theme
-   Test docs with `mktestdocs`
-   Keep docs in `docs/` directory
-   Generate LLM-friendly docs: `uv run mkdocs llms`
-   **Terminology**: Use **Odoo release** (e.g., "17.0") not "Odoo version" when
    referring to Odoo major versions
-   **Code block titles**: Add `title="pyproject.toml"` or similar to code blocks that
    refer to a file
-   Use simplified English for documentation

## Configuration

TOML with PEP 440 specifiers for version overrides:

```toml title="pyproject.toml"
[tool.polytropos]
default_odoo_release = "19.0"

[[tool.polytropos.manifest]]
depends = ["base"]

[[tool.polytropos.manifest]]
releases = ">=18"
application = true
```

## Version Handling

-   **Module version**: X.Y or X.Y.Z from `pyproject.toml [project].version`
-   **Odoo release**: A.B (e.g., "17.0", "18.0")
-   **Full version**: A.B.X.Y.Z (built from module_version + odoo_release, always 5
    components for manifestoo-core compatibility)
-   Resolution order: config_settings → ODOO_RELEASE env var → default_odoo_release

## Nix/Devshell

-   Devshell follows
    [numtide/devshell schema](https://numtide.github.io/devshell/modules_schema.html)
-   Nix code follows
    [blueprint standard](https://numtide.github.io/blueprint/main/getting-started/folder_structure/)

## GitLab CI

-   **DO NOT edit files inside `.gitlab/ci/`**: These come from the shared precommix
    template and are managed externally.
-   **Edit only `.gitlab-ci.yml`**: Project-specific CI configuration lives here.

## Key Design Patterns

1.  **Deep Merge**: `_deep_merge()` - dicts recurse, lists append, scalars override
2.  **Version Resolution**: config_settings → env var → installed odoo package
3.  **Version Building**: `module_version` (X.Y) + `odoo_release` (A.B) → `A.B.X.Y`

## What NOT to Do

-   Don't add comments unless required
-   Don't create complex class hierarchies (flat modules preferred)
-   Don't implement features "just in case"
-   Don't add separate config sections for assets/data - use unified manifest overrides
-   Don't use emojis in code/docs
