# План работ: web-python-skelet ## Контекст - Источник требований: `AGENTS.md` и ADR `docs/001`-`docs/004` - Текущее состояние: в `adapter/`, `domain/`, `usecase/`, `repository/`, `test/` пока только `__init__.py` - Отсутствуют рабочие каталоги и файлы из целевой структуры: `adapter/config/`, `adapter/otel/`, `adapter/di/`, `adapter/http/fastapi/`, `config/`, `main.py` - Ограничения: `docs/` и `tasks.md` не добавлять в git; коммиты не делать; работать по одной задаче - ADR пока покрывают архитектуру, новые ADR нужны только если по ходу работ изменится решение ## Правила выполнения - Каждую задачу выполнять отдельным заходом, без параллельной реализации - Каждый субагент отдает diff, список измененных файлов и проверку, но не делает commit - Если в задаче всплывает архитектурное изменение, остановиться и вынести вопрос на согласование ## Очередь задач ### T01. Базовый каркас домена и usecase - Исполнитель: `primary-agent` (scaffolding) - Статус: completed - Зависимости: нет - Commit required: no - Scope: создать базовые файлы и контракты в `domain/`, `usecase/`, `repository/` - Файлы: `domain/error.py`, `domain/user.py`, `usecase/interface.py`, `usecase/user.py`, `repository/user.py` - Критерии приемки: зависимости направлены внутрь; в `domain/` и `usecase/` нет FastAPI/OTel; есть пример сущности, ошибок, портов и простого usecase ### T02. Конфиг из YAML и env - Субагент: `feature-developer` - Статус: completed - Зависимости: `T01` - Commit required: no - Scope: собрать typed-config слой в `adapter/config/` и подготовить базовые yaml-файлы - Файлы: `adapter/config/*`, `config/app.yaml` - Критерии приемки: конфиг собирается в одну dataclass-структуру; секреты читаются из env; парсинг и валидация не протекают в inner layers ### T03. Observability порты и OTel adapter - Субагент: `feature-developer` - Статус: completed - Зависимости: `T01`, `T02` - Commit required: no - Scope: реализовать логгер, метрики, трейсинг и bootstrap OTel в `adapter/otel/` через интерфейсы из `usecase/interface.py` - Файлы: `adapter/otel/*`, `config/otel-collector.yaml` - Критерии приемки: inner layers знают только интерфейсы; OTLP exporter настраивается из конфига; нет кастомного trace middleware ### T04. Composition root и lifetime singleton-объектов - Субагент: `feature-developer` - Статус: completed - Зависимости: `T01`, `T02`, `T03` - Commit required: no - Scope: собрать контейнер и startup wiring в `adapter/di/` - Файлы: `adapter/di/container.py`, `adapter/di/__init__.py` - Критерии приемки: repository/usecase создаются один раз на старте; контейнер хранит инстансы явно; нет пересоздания на HTTP-запрос ### T05. FastAPI adapter как заменяемый web layer - Субагент: `feature-developer` - Статус: completed - Зависимости: `T04` - Commit required: no - Scope: поднять HTTP adapter в `adapter/http/fastapi/` с app factory, lifespan, dependencies, middleware и router ` /api/v1` - Файлы: `adapter/http/fastapi/*`, `main.py` - Критерии приемки: FastAPI изолирован в adapter-слое; handlers тонкие; request logging и metrics middleware подключены; usecase/repository берутся из контейнера ### T06. Локальный runtime и compose-окружение - Субагент: `feature-developer` - Статус: completed - Зависимости: `T02`, `T03`, `T05` - Commit required: no - Scope: добавить контейнерный runtime для сервиса и compose-окружение с OTel и UI для просмотра логов, метрик и трейсов - Файлы: `Dockerfile`, `docker-compose.yml` - Ограничения: не трогать репозиторный `config/app.yaml`; docker должен прокидывать свой runtime-config внутрь контейнера; Dockerfile только с двумя стадиями `build` и `run` - Критерии приемки: `make compose-build` и `make compose-up` опираются на существующие файлы; сервис поднимается в контейнере; OTel telemetry уходит в dockerized stack; есть UI для просмотра логов, метрик и трейсов; для локального docker-окружения достаточно только `Dockerfile` и `docker-compose.yml` ### T07. Тесты на lifetimes, config и HTTP smoke - Субагент: `test-engineer` - Статус: pending - Зависимости: `T01`, `T02`, `T03`, `T04`, `T05`, `T06` - Commit required: no - Scope: покрыть тестами ключевые архитектурные гарантии - Файлы: `test/*` - Критерии приемки: есть тест на singleton lifetime для repository/usecase; есть тест merge YAML+env; есть smoke-тест для ` /api/v1`; тесты не тянут FastAPI/OTel в inner layers ### T08. Архитектурный и boundary review - Субагент: `code-reviewer` - Статус: pending - Зависимости: `T07` - Commit required: no - Scope: проверить импорты, соблюдение слоев, startup lifetimes и заменяемость web adapter - Файлы: весь измененный код - Критерии приемки: dependency direction не нарушен; FastAPI и OTel не протекают в `domain/` и `usecase/`; замечания сформулированы как точечные правки или подтверждение готовности к review ### T09. Конфигурируемый runtime observability - Субагент: `feature-developer` - Статус: completed - Зависимости: `T03`, `T04`, `T05` - Commit required: no - Scope: сделать конфигурируемый runtime observability с настраиваемым sink и форматом логов, плюс отдельными флагами для метрик и трейсов - Файлы: `adapter/config/*`, `adapter/observability/*`, `adapter/otel/*`, `adapter/di/*`, `adapter/http/fastapi/*`, `config/app.yaml`, при необходимости `main.py` - Конфиг: `logging.output=stdout|file|otel`, `logging.format=text|json`, `logging.file_path=...`, `metrics.enabled=true|false`, `tracing.enabled=true|false` - Решение: вынести выбор runtime в отдельный adapter-layer factory; `domain/` и `usecase/` не менять; для `stdout` и `file` поддержать оба формата `text` и `json`; `logging.file_path` использовать только при `logging.output=file`; при отключенных метриках и трейсах использовать `Noop`-реализации; OTel runtime поднимать только если нужен хотя бы для одного сигнала - Критерии приемки: при `logging.output=stdout` логи идут в stdout в формате `text` или `json` по конфигу; при `logging.output=file` логи пишутся в файл по пути `logging.file_path` в формате `text` или `json`; при `logging.output=otel` логи уходят в collector; `metrics.enabled=false` отключает метрики и metrics middleware; `tracing.enabled=false` отключает FastAPI instrumentation и tracer runtime; DI продолжает отдавать единый runtime через контейнер; внутренние слои по-прежнему знают только порты ### T10. ADR: раннее подключение OTel к FastAPI - Исполнитель: `primary-agent` (docs) - Статус: completed - Зависимости: нет - Commit required: no - Scope: зафиксировать правило, что FastAPI OTel instrumentation выполняется до первой сборки `middleware_stack` - Файлы: `docs/005-fastapi-otel-early-instrumentation.md` - Критерии приемки: ADR занимает 10-20 строк; описаны context, decision, consequences; решение не переписывает историю прошлых ADR ### T11. Перенос FastAPI OTel bootstrap в app factory - Субагент: `feature-developer` - Статус: completed - Зависимости: `T10` - Commit required: no - Scope: перенести создание container, установку `FastAPIInstrumentor.instrument_app(...)` из `lifespan` в `create_app`, оставив в `lifespan` только shutdown - Файлы: `adapter/http/fastapi/app.py`, `adapter/http/fastapi/lifespan.py`, при необходимости `adapter/http/fastapi/dependencies.py` - Ограничения: не использовать ручной rebuild `app.middleware_stack`; не менять `domain/` и `usecase/`; не добавлять бизнес-логику; сохранить singleton-lifetime container - Критерии приемки: instrumentation происходит до первой сборки middleware stack; `OpenTelemetryMiddleware` попадает в runtime stack без workaround; shutdown закрывает instrumentation и runtime один раз; compose-конфиг продолжает работать ### T12. Регрессионная проверка HTTP telemetry wiring - Субагент: `test-engineer` - Статус: pending - Зависимости: `T11` - Commit required: no - Scope: добавить проверку, что раннее instrumentation wiring сохраняется и не требует ручного rebuild middleware stack - Файлы: `test/*` - Ограничения: без реального collector; проверять через ASGI/lifespan или локальные assertions по app runtime; не тянуть FastAPI и OTel в inner-layer тесты - Критерии приемки: тест подтверждает, что при включенных metrics/tracing `OpenTelemetryMiddleware` присутствует в runtime stack; тест не зависит от внешнего OTel collector; существующие архитектурные границы не нарушены