--- trigger: always_on --- ## 0) Язык и стиль общения (СТРОГО) - ИИ-агент **ВСЕГДА отвечает на русском языке** - Английский допускается ТОЛЬКО для: - имён библиотек - имён классов, функций, переменных - CLI-команд - Тон: инженерный, практичный, без воды и маркетинга --- ## 1) Базовые принципы (НЕ ОБСУЖДАЮТСЯ) Проект разрабатывается строго по принципам: - **SOLID** - **KISS** - **DRY** Правила приоритета: - красиво vs просто → **простота** - умно vs поддерживаемо → **поддерживаемость** - магия vs явность → **явность** --- ## 2) Контекст проекта - ОС: **Astra Linux 1.8** - Python: **3.11.2** - Django: **3.x (указано 3.14)** - Django REST Framework (DRF) - Celery - PostgreSQL **15.10** - Apache **2.4.57** - mod_wsgi **4.9.4** ### Инструменты разработки - **uv** - **виртуальная среда** - **pre-commit** - **Gitea Actions (CI)** - Тесты: `django test` - Линтинг: `ruff` - **ЗАПРЕЩЕНО** создавать тестовые скрипты для демонстрации, все должно быть исправлено в рамках проекта --- ## 3) Окружение и команды (СТРОГО) Все команды: - выполняются **только внутри виртуальной среды** - используют **uv** - считаются выполняемыми из корня проекта ### ❌ Запрещено - `pip`, `python -m pip` - `poetry`, `pipenv`, `pipx` - системные команды вне venv ### ✅ Разрешено - `uv venv` - `source .venv/bin/activate` - `uv add / uv remove / uv sync` - `uv run ` Пример: ```bash uv run python manage.py test ``` --- ## 4) Архитектура и слои ответственности (КРИТИЧНО) ### 4.1 View (DRF) View отвечает ТОЛЬКО за: - приём HTTP-запроса - проверку прав доступа - работу с serializer - вызов сервисного слоя ❌ Запрещено: - бизнес-логика - сложные условия - транзакции - сложная работа с ORM --- ### 4.2 Serializer Serializer отвечает за: - валидацию данных - преобразование вход/выход Допускается: - field-level validation - object-level validation ❌ Запрещено: - бизнес-правила - side-effects - сложная логика в `save()` --- ### 4.3 Сервисный слой (Business Logic) - **ВСЯ бизнес-логика живёт здесь** - Сервисы: - не зависят от HTTP - легко тестируются - управляют транзакциями - Сервис определяет *что* делать, а не *как* отдать ответ Рекомендуемый паттерн: ```python class EntityService: @classmethod def do_something(cls, *, data): ... ``` --- ### 4.4 Модели (ORM) Модели должны быть: - простыми - декларативными Допускается: - `__str__` - простые computed properties - минимальные helper-методы ❌ Запрещено: - бизнес-логика - workflow - сигналы как логика - условия, зависящие от сценариев 👉 **Любые исключения — ТОЛЬКО после обсуждения в чате.** --- ## 5) Celery - Task = **thin wrapper** - Task вызывает сервис, а не содержит логику - Таски: - идемпотентны - логируют начало и завершение - Ретраи: - только для временных ошибок - с backoff --- ## 6) База данных и миграции - Любое изменение моделей → миграции обязательны - Миграции: - детерминированные - без ручной магии без причины Проверка перед коммитом: ```bash uv run python manage.py makemigrations --check --dry-run ``` PostgreSQL: - транзакции использовать осознанно - `select_for_update()` при гонках - Raw SQL — только с объяснением --- ## 7) Тестирование - Любая бизнес-логика → тесты - В первую очередь тестируется сервисный слой - API — happy path + edge cases Запуск: ```bash uv run python manage.py test ``` --- ## 8) pre-commit (обязателен) - Любой код обязан проходить pre-commit - Агент обязан учитывать проверки форматирования и линтинга ```bash pre-commit run --all-files ``` --- ## 9) CI (Gitea Actions) - Используется **Gitea Actions** - ❌ GitHub Actions запрещены - Любые изменения: - не должны ломать CI - Если меняются: - зависимости - команды тестов - миграции → агент обязан указать необходимость правок workflow --- ## 10) Apache + mod_wsgi - Используется **ТОЛЬКО WSGI** - ASGI запрещён без отдельного обсуждения - Любые изменения в `wsgi.py`, путях, статике: - сопровождаются пояснением - требуют перезапуска Apache ```bash systemctl restart apache2 ``` Учитывать ограничения и права Astra Linux. --- ## 11) Работа с репозиторием - Минимальный diff — приоритет - ❌ Не коммитить: - `.venv` - артефакты - дампы БД - Массовый рефакторинг — только по явному запросу --- ## 12) Anti-patterns (ЗАПРЕЩЕНО) - Fat Models - God Views - Бизнес-логика в Serializers - Сигналы как workflow - Магия в `save()` - Прямые импорты моделей между apps - Сложная логика в queryset как бизнес-правило --- ## 13) Формат ответа ИИ-агента (ОБЯЗАТЕЛЬНЫЙ) Каждый ответ должен содержать: 1. **Что меняем** 2. **Файлы / патч** 3. **Команды (через uv)** 4. **Проверки (tests / pre-commit / CI)** 5. **Риски / замечания** --- ## 14) Исключения - ИИ-агент **НЕ внедряет исключения сам** - Агент: - описывает стандартное решение - объясняет, почему оно не подходит - запрашивает разрешение в чате ## 15) Структура проекта и миксины (ОБЯЗАТЕЛЬНО) ### 15.0 Правило Core-First (КРИТИЧНО) **ПЕРЕД созданием любого нового компонента** агент ОБЯЗАН проверить модуль `apps.core`: ``` src/apps/core/ ├── mixins.py # Model mixins (TimestampMixin, SoftDeleteMixin, etc.) ├── services.py # BaseService, BackgroundJobService ├── views.py # Health checks, BackgroundJob API ├── viewsets.py # BaseViewSet, ReadOnlyViewSet ├── exceptions.py # APIError, NotFoundError, ValidationError ├── permissions.py # IsOwner, IsAdminOrReadOnly, etc. ├── pagination.py # CursorPagination ├── filters.py # BaseFilterSet ├── cache.py # cache_result, invalidate_cache ├── tasks.py # BaseTask для Celery ├── logging.py # StructuredLogger ├── middleware.py # RequestIDMiddleware ├── signals.py # SignalDispatcher ├── responses.py # APIResponse wrapper ├── openapi.py # api_docs decorator └── management/commands/base.py # BaseAppCommand ``` **Порядок действий:** 1. Проверить `apps.core` на наличие нужного базового класса/миксина 2. Наследоваться от существующего, а не создавать с нуля 3. Если нужного нет — обсудить добавление в core ❌ **ЗАПРЕЩЕНО:** создавать дублирующую функциональность в app-модулях --- ### 15.1 Model Mixins При создании моделей **ОБЯЗАТЕЛЬНО** использовать миксины из `apps.core.mixins`: | Миксин | Когда использовать | Поля | |--------|-------------------|------| | `TimestampMixin` | **ВСЕГДА** для любой модели | `created_at`, `updated_at` | | `UUIDPrimaryKeyMixin` | Когда нужен UUID вместо int ID | `id` (UUID) | | `SoftDeleteMixin` | Когда нельзя физически удалять | `is_deleted`, `deleted_at` | | `AuditMixin` | Когда нужно знать кто создал/изменил | `created_by`, `updated_by` | | `OrderableMixin` | Для сортируемых списков | `order` | | `StatusMixin` | Для моделей со статусами | `status` | | `SlugMixin` | Для URL-friendly идентификаторов | `slug` | **Пример правильного использования:** ```python from apps.core.mixins import TimestampMixin, SoftDeleteMixin, AuditMixin class Document(TimestampMixin, SoftDeleteMixin, AuditMixin, models.Model): """Документ с историей и мягким удалением.""" title = models.CharField(max_length=200) class Meta: ordering = ['-created_at'] ``` **Порядок наследования миксинов:** 1. `UUIDPrimaryKeyMixin` (если нужен) 2. `TimestampMixin` 3. `SoftDeleteMixin` (если нужен) 4. `AuditMixin` (если нужен) 5. `OrderableMixin` / `StatusMixin` / `SlugMixin` 6. `models.Model` (последним) --- ### 15.2 Management Commands Все management commands наследуются от `BaseAppCommand`: ```python from apps.core.management.commands.base import BaseAppCommand class Command(BaseAppCommand): help = 'Описание команды' use_transaction = True # Обернуть в транзакцию def add_arguments(self, parser): super().add_arguments(parser) # Добавляет --dry-run, --silent parser.add_argument('--my-arg', type=str) def execute_command(self, *args, **options): items = MyModel.objects.all() for item in self.progress_iter(items, desc="Обработка"): if not self.dry_run: self.process(item) return "Обработано успешно" ``` **Возможности BaseAppCommand:** - `--dry-run` — тестовый запуск без изменений - `--silent` — минимальный вывод - `self.progress_iter()` — прогресс-бар - `self.timed_operation()` — измерение времени - `self.confirm()` — подтверждение - `self.log_info/success/warning/error()` — логирование --- ### 15.3 Background Jobs (Celery) Для отслеживания статуса фоновых задач использовать `BackgroundJob`: ```python # В сервисе при запуске задачи from apps.core.services import BackgroundJobService job = BackgroundJobService.create_job( task_id=task.id, task_name="apps.myapp.tasks.process_data", user_id=request.user.id, ) # В Celery таске from apps.core.models import BackgroundJob @shared_task(bind=True) def my_task(self, data): job = BackgroundJob.objects.get(task_id=self.request.id) job.mark_started() for i, item in enumerate(items): process(item) job.update_progress(i * 100 // len(items), "Обработка...") job.complete(result={"processed": len(items)}) ``` **API эндпоинты:** - `GET /api/v1/jobs/` — список задач пользователя - `GET /api/v1/jobs/{task_id}/` — статус конкретной задачи --- ### 15.4 Factories (тестирование) Все фабрики используют `factory_boy` + `faker`: ```python import factory from faker import Faker fake = Faker("ru_RU") class MyModelFactory(factory.django.DjangoModelFactory): class Meta: model = MyModel name = factory.LazyAttribute(lambda _: fake.word()) email = factory.LazyAttribute(lambda _: fake.unique.email()) ``` **Правила:** - Никакого хардкода в тестах (`"test@example.com"` → `fake.email()`) - Использовать `fake.unique.*` для уникальных полей - Локаль: `Faker("ru_RU")` для русских данных