# Coding Conventions

See [REVIEW.md](REVIEW.md) for the review checklist applied to all code changes.

## Comments Policy

**Do NOT write single-line comments that describe what the next line does — they are bloat.**

Comments are allowed ONLY to describe complex algorithms in detail, when the explanation requires at least 4-5 lines. Self-documenting code with clear variable and function names is preferred.

```python
# BAD — obvious from the code
# Check if scene exists
if scene_id not in scene_map:
    raise SceneNotFoundError(scene_id)

# BAD — restating the code
# Set the user's current scene
state.current_scene = scene_id

# GOOD — no comment needed, the code is self-explanatory
if scene_id not in scene_map:
    raise SceneNotFoundError(scene_id)

state.current_scene = scene_id

# GOOD — complex algorithm that needs explanation
# Flag resolution uses a layered override strategy:
# 1. Global flags provide base values shared across all scenes
# 2. Scene-local flags override globals for the current scene
# 3. Variables are promoted to flags, overriding any static definitions
# 4. Conditional flags (rule engine) apply last, enabling dynamic
#    behavior based on resolved variable state
for source in [global_flags, scene_flags, variable_flags, conditional_flags]:
    resolved.update(source)
```

## Imports

Absolute imports only. Three groups separated by blank lines:

```python
# 1. Standard library
import logging
from typing import Any, TYPE_CHECKING
from collections.abc import Callable, Awaitable
from dataclasses import dataclass, field

# 2. Third-party
from aiogram import Bot, Router
from pydantic import BaseModel, Field

# 3. Local
from ui_router.schema import UIRouter, Scene
from ui_router.exceptions import UIRouterError
```

Use `TYPE_CHECKING` guard for imports needed only by type annotations (prevents circular dependencies):

```python
from __future__ import annotations

if TYPE_CHECKING:
    from ui_router.navigation_storage import NavigationStorage
```

## Type Hints

- **All public functions and methods must have return type annotations**
- Use `X | None` instead of `Optional[X]`, `X | Y` instead of `Union[X, Y]`
- Use `TYPE_CHECKING` for forward references and circular imports
- Use `Annotated[..., Discriminator(...)]` for Pydantic discriminated unions
- Use `Literal["a", "b"]` for constrained string values
- Use `Protocol` for interface definitions (not ABC)

## Naming

| Entity | Convention | Example |
|---|---|---|
| Classes | PascalCase + domain suffix | `NavigationService`, `FlagResolver`, `GotoSceneAction` |
| Functions/methods | snake_case | `resolve_scene_flags`, `get_flag_value` |
| Private members | `_` prefix | `_resolved_flags`, `_initialize_variables` |
| Constants | UPPER_SNAKE_CASE | `STATELESS_EVENT_TYPES`, `UI_CALLBACK_DATA_KEY` |
| Enums | StrEnum with lowercase values | `EventType.MESSAGE = "message"` |
| Type aliases | PascalCase | `ActionInstruction = Annotated[...]` |

**Class suffixes by role:**
- `*Service` — business logic (NavigationService, ContentService)
- `*Resolver` — data resolution (FlagResolver, ContentResolver)
- `*Executor` — action execution (UIRouterExecutor, ActionExecutor)
- `*Registry` — function storage (MasterRegistry, GetterRegistry)
- `*Manager` — lifecycle management (NavigationManager, CallbackDataManager)
- `*Adapter` — framework integration (AiogramAdapter)
- `*Action` — action types (GotoSceneAction, SendMessageAction)

## Class Design

| Purpose | Use | Example |
|---|---|---|
| Schema / config | Pydantic `BaseModel` | `Scene`, `Handler`, `UIRouter` |
| Runtime state | `@dataclass` | `NavigationState`, `EventData` |
| Service / executor | Plain class with DI | `UIRouterExecutor`, `HandlerService` |
| Interface / contract | `Protocol` | `NavigationStorage`, `VariableRepository` |

- **Composition over inheritance** — inject dependencies via constructor
- **Ordered initialization** — comment initialization steps in constructors when order matters
- Use `Field(default_factory=list)` for mutable Pydantic defaults
- Use `@model_validator(mode="after")` for cross-field validation

## Async

- All I/O operations must be `async`
- Support both sync and async callables where needed (check with `inspect.iscoroutinefunction` or `hasattr(result, "__await__")`)
- Use `asyncio.create_task()` for background work, wrap in `try/except RuntimeError` for event loop safety
- Never block the event loop with sync I/O

## Error Handling

- Use the project's exception hierarchy (rooted at `UIRouterError`)
- Every custom exception stores relevant context (action_type, field, scene_id, etc.)
- Raise specific exceptions, never bare `Exception`
- Use `logger.exception()` for error logging (preserves traceback)
- Recover or re-raise — never silently swallow exceptions

## Docstrings

- Language: **Russian** (project convention)
- Style: Google-style with `Args:`, `Returns:`, `Raises:` sections
- Include `Warning:` / `Note:` admonitions where relevant
- No type information in docstrings (use annotations instead)
- Required on: public classes, public methods with non-obvious behavior, modules

```python
class FlagResolver:
    """
    Централизованный резолв флагов из нескольких источников.

    Порядок резолва (каждый следующий переопределяет предыдущий):
    1. Глобальные флаги
    2. Локальные флаги сцены
    3. Переменные как флаги
    4. Conditional flags (rule engine)

    Warning:
        Результат кэшируется в ExecutionContext на время запроса.
    """
```

## Logging

- Per-module logger: `logger = logging.getLogger(__name__)`
- Use appropriate levels: `debug` for flow tracing, `info` for state changes, `warning` for recoverable issues, `error`/`exception` for failures
- Use `%s` formatting (not f-strings) in log calls for lazy evaluation
- Include contextual data in log messages: `logger.debug("[navigate_to] current=%s, target=%s", current, target)`
- Optional structlog integration via `StructlogMiddleware` for production

## Testing

- Framework: pytest with `asyncio_mode="auto"` (no need for `@pytest.mark.asyncio`)
- Fixture chain: `navigation_storage` → `variable_repository` → `event_bus` → `event_scheduler` → `shared_services`
- Helper functions: `_make_scene()`, `_goto()`, `_handler()` for test data construction
- Organization: class-based grouping (`TestAiogramAdapterCreation`, `TestSchemaValidation`)
- Test docstrings: English, describe what is being verified
- Test names: `test_<what>_<expected_behavior>`

## Formatting (ruff)

- Line length: 120
- Quote style: double
- Indent: spaces
- Target: Python 3.13
- Rule set: ALL (with specific ignores, see `pyproject.toml`)
