master/tasks.md

482 lines
39 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# План работ: master-service MVP sandbox
## Контекст
- Источники требований: `docs/master.md`, `meetings/meeting_1_document.md`, `README.md`, `docs/*`
- Базовый template уже готов: typed config, DI container, observability, FastAPI adapter и versioned API под `/api/v1`
- Текущая цель: минимальное управление Docker sandbox без auth
- MVP API: `POST /api/v1/create`
- Sandbox policy: TTL `300` секунд, одна активная sandbox на `chat_id`, повторный `create` переиспользует активную сессию
- Volume policy: chat volume `rw`, dependencies volume `ro`, lambda-tools volume `ro`
- Host paths вычисляются из typed config, а HTTP request передает только `chat_id`
- Cleanup выполняется периодическим in-process loop внутри master-service
## Вне текущего scope
- auth и access control
- p2p lease и WebSocket transport
- workspace/chat CRUD API
- chat files, artifacts, S3, quota и retention policy
- центральная БД и multi-node orchestration
## Правила выполнения
- Выполняем по одной задаче
- Коммиты не делаем
- Если по ходу нужна смена архитектуры, останавливаемся и согласуем решение
- `domain/` и `usecase/` не импортируют Docker, FastAPI, OpenTelemetry, env или YAML
- Inner layers работают только через минимальные domain сущности и usecase порты
## Очередь задач
### M01. ADR и минимальный sandbox scaffolding
- Исполнитель: `primary-agent`
- Статус: completed
- Зависимости: нет
- Commit required: no
- Scope: зафиксировать MVP-решение в ADR и создать минимальные сущности, ошибки и usecase-контракты для sandbox orchestration
- Файлы: `docs/006-mvp-docker-sandbox-orchestration.md`, `domain/sandbox.py`, `domain/error.py`, `usecase/interface.py`, `usecase/sandbox.py`
- Критерии приемки: в `domain/` есть минимальная `SandboxSession` и sandbox-ошибки; в `usecase/` есть порты `SandboxSessionRepository`, `SandboxRuntime` и `Clock`; созданы скелеты `CreateSandbox` и `CleanupExpiredSandboxes`; ADR занимает 10-20 строк
### M02. Typed config для sandbox runtime
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M01`
- Commit required: no
- Scope: расширить typed-config слоем `sandbox` с настройками image, TTL, cleanup interval, host paths и container mount paths
- Файлы: `adapter/config/model.py`, `adapter/config/loader.py`, `config/app.yaml`
- Решение: chat host path строится как путь под общим `sandbox.chats_root/<chat_id>`; request не передает host path напрямую
- Критерии приемки: конфиг собирается в typed dataclass-дерево; дефолтный TTL равен `300`; есть отдельные настройки для `chats_root`, `dependencies_host_path`, `lambda_tools_host_path`, `chat_mount_path`, `dependencies_mount_path`, `lambda_tools_mount_path`, `cleanup_interval_seconds`; inner layers не читают env
### M03. Docker runtime adapter для sandbox lifecycle
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M01`, `M02`
- Commit required: no
- Scope: реализовать outer adapter над Docker для создания и остановки sandbox контейнера с нужными labels и mount policy
- Файлы: `adapter/docker/runtime.py`, `adapter/docker/__init__.py`
- Ограничения: все Docker-детали остаются в `adapter/`; runtime не должен протекать во внутренние слои
- Критерии приемки: runtime умеет создать sandbox container по входным параметрам usecase; chat volume монтируется как `rw`; dependency и lambda-tools volumes монтируются как `ro`; контейнер получает labels с `session_id`, `chat_id` и `expires_at`; runtime переводит ошибки Docker в понятные исключения адаптера
### M04. In-memory session repository и usecase `CreateSandbox`
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M01`, `M02`, `M03`
- Commit required: no
- Scope: реализовать in-memory registry активных sandbox-сессий и usecase создания sandbox с логикой reuse по `chat_id`
- Файлы: `repository/sandbox_session.py`, `usecase/sandbox.py`, `adapter/di/container.py`
- Решение: если по `chat_id` есть неистекшая сессия, usecase возвращает ее без нового container start; если сессия истекла, usecase инициирует stop старой sandbox и создает новую
- Критерии приемки: одна активная sandbox на `chat_id`; TTL-логика использует порт `Clock`; usecase не импортирует Docker; container wiring остается singleton-based
### M05. Cleanup expired sandboxes и lifecycle wiring
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M04`
- Commit required: no
- Scope: реализовать usecase cleanup просроченных sandbox и подключить периодический cleanup loop в FastAPI lifecycle
- Файлы: `usecase/sandbox.py`, `adapter/di/container.py`, `adapter/http/fastapi/app.py`, при необходимости `adapter/http/fastapi/dependencies.py`
- Ограничения: не ломать ADR про раннее OTel instrumentation; lifecycle loop должен стартовать и останавливаться один раз
- Критерии приемки: cleanup находит истекшие сессии, останавливает sandbox через runtime и удаляет их из registry; интервал cleanup берется из конфига; shutdown корректно завершает фоновую задачу
### M06. HTTP endpoint `POST /api/v1/create`
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M04`
- Commit required: no
- Scope: добавить минимальную HTTP ручку для создания или переиспользования sandbox без auth
- Файлы: `adapter/http/fastapi/schemas.py`, `adapter/http/fastapi/dependencies.py`, `adapter/http/fastapi/routers/v1/router.py`, при необходимости `adapter/di/container.py`
- Request: `{ "chat_id": "..." }`
- Response: `session_id`, `chat_id`, `container_id`, `status`, `expires_at`
- Критерии приемки: router остается тонким; handler только переводит HTTP input в команду usecase и маппит ошибки в HTTP; endpoint живет под `/api/v1/create`; auth не добавляется
### M07. Тесты для create, reuse, TTL и mount policy
- Субагент: `test-engineer`
- Статус: completed
- Зависимости: `M03`, `M04`, `M05`, `M06`
- Commit required: no
- Scope: покрыть тестами ключевое поведение MVP без запуска реального production Docker stack
- Файлы: `test/*`
- Критерии приемки: есть unit-тесты для `CreateSandbox` и `CleanupExpiredSandboxes` с fake clock; есть HTTP smoke-тест для `POST /api/v1/create`; есть adapter-level тест с mock Docker client на mount policy `chat=rw`, `deps=ro`, `tools=ro`; тесты не тащат FastAPI или Docker в inner-layer тесты
### M08. Архитектурный и boundary review по MVP sandbox
- Субагент: `code-reviewer`
- Статус: completed
- Зависимости: `M07`
- Commit required: no
- Scope: проверить соблюдение clean architecture, dependency direction и соответствие MVP-ограничениям
- Файлы: весь измененный код
- Критерии приемки: Docker остается только во внешнем adapter; FastAPI не протекает в `domain/` и `usecase/`; TTL и mount policy читаются как явные, тестируемые правила; замечания сформулированы как точечные правки или подтверждение готовности
## Follow-up после M08 review
### M09. Сериализация lifecycle sandbox по `chat_id`
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M08`
- Commit required: no
- Scope: убрать гонки между параллельными `create` и cleanup для одного `chat_id`
- Файлы: `usecase/interface.py`, `usecase/sandbox.py`, `repository/sandbox_lock.py` или другой outer-layer lock implementation, `adapter/di/container.py`
- Решение: ввести явный usecase-port для process-local lock по `chat_id`; outer-layer реализация держит per-chat lock registry; `CreateSandbox` и `CleanupExpiredSandboxes` выполняют мутации сессии под этим lock
- Критерии приемки: для одного `chat_id` не поднимаются два sandbox при concurrent create; create-vs-cleanup не оставляет orphan container; locking не протекает в HTTP и Docker adapter как бизнес-логика
### M10. Устойчивый cleanup и вынос blocking cleanup из event loop
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M09`
- Commit required: no
- Scope: сделать cleanup устойчивым к частичным ошибкам и не блокировать FastAPI event loop синхронным Docker stop
- Файлы: `usecase/sandbox.py`, `adapter/http/fastapi/app.py`
- Решение: `CleanupExpiredSandboxes` обрабатывает stop/delete по каждой сессии отдельно и продолжает batch; HTTP cleanup loop выносит blocking cleanup work в thread через adapter-layer orchestration
- Критерии приемки: ошибка на одной expired session не мешает чистить остальные; background cleanup loop не умирает после ошибки; blocking cleanup больше не выполняется прямо в event loop
### M11. Удаление не-MVP user surface из приложения
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M08`
- Commit required: no
- Scope: убрать из runtime app неотносящиеся к MVP user endpoint и seed user wiring
- Файлы: `adapter/http/fastapi/routers/v1/router.py`, `adapter/http/fastapi/dependencies.py`, `adapter/http/fastapi/schemas.py`, `adapter/di/container.py`
- Решение: оставить в MVP только `health` и sandbox API; примерный user code может остаться в репозитории как template, но не должен быть подключен в runtime app
- Критерии приемки: `GET /api/v1/users/{user_id}` больше не опубликован; container не создает seeded user repository/usecase для runtime app; незапрошенная user-surface area исчезает
### M12. Регрессионные тесты на race conditions и cleanup resilience
- Субагент: `test-engineer`
- Статус: completed
- Зависимости: `M09`, `M10`, `M11`
- Commit required: no
- Scope: добавить тесты на новые гарантии после review fixes
- Файлы: `test/*`
- Критерии приемки: есть тест на duplicate create для одного `chat_id`; есть тест на create-vs-cleanup race или эквивалентную сериализацию; есть тест, что cleanup продолжает batch после stop failure; HTTP smoke/regression тесты обновлены под удаление user endpoint
### M13. Повторный boundary review после fix-pass
- Субагент: `code-reviewer`
- Статус: completed
- Зависимости: `M12`
- Commit required: no
- Scope: проверить, что must-fix и should-fix замечания из `M08` закрыты без нарушения clean architecture
- Файлы: весь измененный код после `M09`-`M12`
- Критерии приемки: нет гонки на one-sandbox-per-chat; cleanup не блокирует event loop и не валится на первом stop failure; runtime app не публикует лишний user API; замечания сведены к minor или отсутствуют
### M14. Починка mypy-типизации тестов после sandbox MVP
- Субагент: `test-engineer`
- Статус: completed
- Зависимости: `M07`
- Commit required: no
- Scope: устранить текущие ошибки `make pre-commit` в test-suite без изменения production behavior
- Файлы: `test/test_docker_runtime.py`, `test/test_create_http.py`, при необходимости общие test helpers в `test/*`
- Ошибки: несовместимый fake Docker client для `DockerSandboxRuntime`, неточная типизация `run_calls` и ASGI message payload, использование `object` вместо типизированных test doubles для `AppRepositories`, `AppUsecases`, `AppContainer`
- Решение: сделать test doubles типизированными через совместимые fake classes или локальные protocols; убрать `object` и неиндексируемые `dict[str, object]` там, где mypy не может вывести типы
- Критерии приемки: `uv run mypy .` проходит; `make pre-commit` доходит как минимум до pytest stage; production code не меняется или меняется только при явной необходимости для testability
## Follow-up после M13 review
### M15. Канонизация и валидация `chat_id`
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M13`
- Commit required: no
- Scope: сделать `chat_id` строго UUID и убрать path alias/whole-root mount риск через неканоничные значения
- Файлы: `adapter/http/fastapi/schemas.py`, `adapter/docker/runtime.py`, при необходимости `usecase/sandbox.py` и тесты в `test/*`
- Решение: принять `chat_id` как UUID на HTTP boundary, использовать его каноничную строковую форму дальше в usecase/repository/path construction и не принимать произвольные path-like строки
- Критерии приемки: не-UUID значения отклоняются на HTTP boundary с `400/422`; UUID используется как единое каноничное значение для lock key, repository key и filesystem path; появляются регрессионные тесты на invalid `chat_id`
### M16. Lifecycle reconciliation на startup/shutdown
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M13`
- Commit required: no
- Scope: устранить restart-gap между in-memory registry и уже запущенными Docker containers
- Файлы: `adapter/docker/runtime.py`, `adapter/di/container.py`, `adapter/http/fastapi/app.py`, при необходимости новые outer-layer helper files и тесты в `test/*`
- Решение: основная стратегия — reconciliation по Docker labels на startup, чтобы после restart master-service продолжал видеть уже запущенные sandbox и не поднимал дубликаты; graceful shutdown cleanup остается опциональным дополнением
- Критерии приемки: после restart master-service может восстановить/синхронизировать state по Docker labels без потери работающих agent containers; one-sandbox-per-chat не нарушается из-за пустого in-memory registry; lifecycle policy явно зафиксирована и покрыта тестами
### M17. Управление жизненным циклом per-chat locks
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M13`
- Commit required: no
- Scope: ограничить неограниченный рост registry locks по числу когда-либо увиденных `chat_id`
- Файлы: `repository/sandbox_lock.py`, при необходимости тесты в `test/*`
- Решение: добавить eviction/ref-count/weakref policy во внешнем lock registry без нарушения сериализации lifecycle для активного `chat_id`
- Критерии приемки: registry locks не растет бесконечно без причины; сериализация для активных чатов сохраняется; поведение покрыто тестами
### M18. Перевести sandbox ids на UUID types
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M15`
- Commit required: no
- Scope: сделать `chat_id` и `session_id` типом `UUID` внутри sandbox scope, оставив `container_id` строкой как внешний Docker identifier
- Файлы: `domain/sandbox.py`, `usecase/interface.py`, `usecase/sandbox.py`, `repository/sandbox_session.py`, `adapter/http/fastapi/*`, `adapter/docker/runtime.py`, `adapter/di/container.py`, `test/*`
- Решение: HTTP boundary принимает/возвращает UUID, usecase и repository работают с UUID objects, Docker labels продолжают сериализоваться в строки через `str(uuid)`
- Критерии приемки: внутри sandbox flow `chat_id` и `session_id` больше не строки; `container_id` остается `str`; pydantic корректно сериализует UUID в response; `make pre-commit` проходит
## Follow-up после issue #11 observability
### M19. ADR и observability contracts для sandbox lifecycle
- Исполнитель: `primary-agent`
- Статус: completed
- Зависимости: `M18`
- Commit required: yes
- Commit message: `add sandbox observability contracts`
- Scope: зафиксировать sandbox lifecycle observability policy в ADR-lite и подготовить минимальные контракты для traces и current-state metrics без нарушения clean architecture
- Файлы: `docs/008-sandbox-lifecycle-observability.md`, `usecase/interface.py`, `repository/sandbox_session.py`, `adapter/otel/metrics.py`, `adapter/observability/noop.py`
- Решение: добавить в `Metrics` порт операцию `set(...)` для gauge-like current-state сигналов; добавить в `SandboxSessionRepository` `count_active()` как источник truth для `sandbox.active.count`
- Критерии приемки: ADR занимает 10-20 строк; inner layers по-прежнему знают только порты `Logger`/`Metrics`/`Tracer`; current-state метрика активных sandbox выражается без OTel imports во внутреннем слое
### M20. Трейсы и метрики в sandbox usecases
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M19`
- Commit required: yes
- Commit message: `instrument sandbox usecases`
- Scope: добавить spans и ключевые lifecycle metrics в `CreateSandbox` и `CleanupExpiredSandboxes`
- Файлы: `usecase/sandbox.py`, `adapter/di/container.py`, при необходимости тесты в `test/*`
- Решение: usecase получает `Metrics` и `Tracer` через конструктор; `CreateSandbox` и `CleanupExpiredSandboxes` публикуют `sandbox.create.total`, `sandbox.cleanup.total`, `sandbox.cleanup.error.total` и обновляют `sandbox.active.count` после мутаций registry
- Критерии приемки: есть spans `usecase.create_sandbox` и `usecase.cleanup_expired_sandboxes`; span attrs и metric attrs включают ключевые lifecycle identifiers/result fields; reuse/replace/cleanup paths наблюдаемы без OTel imports в usecase
### M21. Трейсы и runtime metrics в Docker adapter и reconciliation
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M19`
- Commit required: yes
- Commit message: `instrument sandbox docker runtime`
- Scope: добавить observability в `DockerSandboxRuntime` и reconciliation path для Docker operations и current-state sync
- Файлы: `adapter/docker/runtime.py`, `adapter/sandbox/reconciliation.py`, `adapter/di/container.py`, при необходимости тесты в `test/*`
- Решение: `DockerSandboxRuntime` получает `Metrics` и `Tracer`; create/stop/list публикуют duration histograms `sandbox.runtime.create.duration_ms`, `sandbox.runtime.stop.duration_ms`, `sandbox.runtime.list_active.duration_ms`, error counter `sandbox.runtime.error.total` и span attrs по chat/session/container; reconciliation обновляет `sandbox.active.count` по registry snapshot
- Критерии приемки: Docker adapter остается во внешнем слое; ошибки Docker операций отражаются в spans и metrics; после startup reconciliation current-state метрика активных sandbox синхронизирована с registry
### M22. Тесты на sandbox observability
- Субагент: `test-engineer`
- Статус: completed
- Зависимости: `M20`, `M21`
- Commit required: yes
- Commit message: `add sandbox observability tests`
- Scope: покрыть regression tests новую observability policy без реального OTel backend
- Файлы: `test/test_sandbox_usecase.py`, `test/test_docker_runtime.py`, при необходимости новые focused tests в `test/*`
- Решение: использовать типизированные fake metrics/tracer implementations и проверить names/attrs ключевых spans и metrics на create/reuse/replace/cleanup/runtime paths
- Критерии приемки: тесты подтверждают spans и metrics на usecase и adapter paths; constructor wiring обновлен без mypy regressions; `make typecheck` и релевантный `pytest` проходят
### M23. Boundary review для sandbox observability
- Субагент: `code-reviewer`
- Статус: in_progress
- Зависимости: `M22`
- Commit required: no
- Scope: проверить, что observability изменения закрывают issue #11 и FR-034 без нарушения clean architecture
- Файлы: весь измененный код после `M19`-`M22`
- Критерии приемки: inner layers не импортируют OTel; Docker-specific tracing остается в `adapter/docker/`; current-state и duration metrics достаточно покрывают sandbox lifecycle; замечания сведены к minor или отсутствуют
## Follow-up после M23 boundary review
### M24. Исправить replace trace identity в CreateSandbox
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M23`
- Commit required: yes
- Commit message: `fix sandbox replace trace identity`
- Scope: устранить смешение old/new sandbox identifiers в replace path usecase tracing
- Файлы: `usecase/sandbox.py`, при необходимости точечные тесты в `test/*`
- Решение: сохранять старые и новые sandbox identifiers в отдельных span attrs или child spans так, чтобы replace success и replace failure оставались однозначно трассируемыми
- Критерии приемки: replace path не перетирает previous/new identifiers; при replace failure span остается консистентным и отражает обе стороны lifecycle
### M25. Добрать failure-path observability regression tests
- Субагент: `test-engineer`
- Статус: completed
- Зависимости: `M24`
- Commit required: yes
- Commit message: `add sandbox observability failure tests`
- Scope: покрыть tests для replace-failure trace, cleanup error metrics/spans и Docker stop observability
- Файлы: `test/test_sandbox_usecase.py`, `test/test_docker_runtime.py`, при необходимости другие focused tests в `test/*`
- Решение: использовать presence-based assertions и проверять ключевые span/metric contracts без brittle exact-order checks
- Критерии приемки: есть тест на replace failure tracing; есть тест на `sandbox.cleanup.error.total`; есть тесты на Docker stop observability для success/error/not_found или эквивалентного набора outcome paths
### M26. Повторный boundary review для sandbox observability
- Субагент: `code-reviewer`
- Статус: in_progress
- Зависимости: `M25`
- Commit required: no
- Scope: подтвердить, что follow-up fixes закрыли M23 замечания без новых boundary нарушений
- Файлы: весь измененный код после `M24`-`M25`
- Критерии приемки: нет замечаний по replace tracing identity и missing failure-path observability coverage; clean architecture по-прежнему соблюдена
## Follow-up после M26 boundary review
### M27. Компенсация save failure после runtime.create
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M26`
- Commit required: yes
- Commit message: `fix sandbox create rollback gap`
- Scope: не оставлять untracked running container и неконсистентный `sandbox.active.count` при падении `repository.save()` после успешного `runtime.create()`
- Файлы: `usecase/sandbox.py`, при необходимости точечные тесты в `test/*`
- Решение: сделать create/replace path registry-safe через rollback или другой явный compensation path без нарушения clean architecture
- Критерии приемки: save failure не оставляет новый container в runtime без registry state; `sandbox.active.count` отражает финальное committed state; replace и fresh-create failure paths консистентны
### M28. Регрессии на rollback и startup failure observability
- Субагент: `test-engineer`
- Статус: completed
- Зависимости: `M27`
- Commit required: yes
- Commit message: `add sandbox rollback regression tests`
- Scope: покрыть tests для save-failure rollback и startup observability failure paths
- Файлы: `test/test_sandbox_usecase.py`, `test/test_docker_runtime.py`, `test/test_create_http.py`, при необходимости другие focused tests в `test/*`
- Решение: добавить tests на fresh-create/replace save failure compensation, `list_active` failure observability и reconciliation failure span/metric expectations где применимо
- Критерии приемки: rollback path покрыт; list/reconciliation failure observability не регрессирует; tests остаются presence-based и стабильными
### M29. Финальный boundary review для sandbox observability
- Субагент: `code-reviewer`
- Статус: completed
- Зависимости: `M28`
- Commit required: no
- Scope: подтвердить, что M27-M28 закрыли remaining M26 замечания
- Файлы: весь измененный код после `M27`-`M28`
- Критерии приемки: нет замечаний по rollback gap и startup failure observability coverage; sandbox observability slice приемлем as-is
## Follow-up: sandbox HTTP delete, runtime endpoint, agent env и request volume
### M30. ADR и контракты sandbox runtime params/control
- Исполнитель: `primary-agent`
- Статус: completed
- Зависимости: нет
- Commit required: no
- Scope: зафиксировать решение в ADR-lite и подготовить минимальные domain/usecase контракты для `agent_id`, host volume, endpoint и delete sandbox
- Файлы: `docs/009-sandbox-http-control-and-runtime-params.md`, `domain/sandbox.py`, `domain/error.py`, `usecase/interface.py`, `usecase/sandbox.py`
- Решение: create принимает `agent_id` и absolute `volume_host_path`; reuse разрешен только при совпадении параметров; delete выполняется по `chat_id`; response содержит Docker-network endpoint `ip:port`
- Критерии приемки: ADR занимает 10-20 строк; в domain есть endpoint/session metadata; usecase contracts не импортируют Docker/FastAPI; есть stubs для `DeleteSandbox` без бизнес-логики
### M31. Typed config для sandbox network и runtime port
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M30`
- Commit required: no
- Scope: добавить typed-config настройки Docker network, agent service port и mount target для request volume
- Файлы: `adapter/config/model.py`, `adapter/config/loader.py`, `config/app.yaml`, при необходимости `config/docker-compose.yml`, tests config loader
- Решение: добавить `sandbox.network_name: sandbox`, `sandbox.agent_service_port: 8000`, `sandbox.volume_mount_path: /workspace/volume`; network должен существовать заранее
- Критерии приемки: config собирается в dataclass tree; env overrides поддержаны; inner layers не читают env/YAML; defaults отражены в локальном config
### M32. Docker runtime для env, volume, network endpoint и delete
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M30`, `M31`
- Commit required: no
- Scope: обновить Docker adapter create/delete и reconciliation под новые sandbox runtime параметры
- Файлы: `adapter/docker/runtime.py`, `adapter/sandbox/reconciliation.py`, при необходимости focused tests helpers
- Решение: `containers.run` получает `environment={'AGENT_ID': agent_id}`, `network=config.sandbox.network_name`, extra bind mount `volume_host_path -> volume_mount_path`; после start runtime достает IP из configured network и возвращает endpoint; delete делает `remove(force=True)` и NotFound считает идемпотентным успехом; labels содержат `agent_id`, `volume_host_path` и endpoint port metadata для reconciliation
- Критерии приемки: Docker details остаются в adapter; mount policy сохраняет chat `rw`, deps/tools `ro`, request volume `rw`; create возвращает endpoint; startup reconciliation восстанавливает новые session fields; observability duration/error metrics не регрессируют
### M33. CreateSandbox параметры и conflict semantics
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M30`, `M32`
- Commit required: no
- Scope: обновить create usecase под `agent_id`, `volume_host_path`, endpoint response и reuse conflict rules
- Файлы: `usecase/sandbox.py`, `domain/error.py`, `adapter/di/container.py`, при необходимости `repository/sandbox_session.py`
- Решение: активная сессия переиспользуется только если `agent_id` и `volume_host_path` совпадают; при mismatch usecase поднимает sandbox conflict error; replace/cleanup rollback paths сохраняют прежнюю консистентность `sandbox.active.count`
- Критерии приемки: одна active sandbox на `chat_id`; mismatch не стартует новый контейнер; endpoint возвращается через domain session; usecase не импортирует Docker/FastAPI
### M34. DeleteSandbox usecase и DI wiring
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M30`, `M32`
- Commit required: no
- Scope: реализовать usecase удаления sandbox по `chat_id` и подключить его в container
- Файлы: `usecase/sandbox.py`, `usecase/interface.py`, `adapter/di/container.py`
- Решение: под per-chat lock найти active session, вызвать `runtime.delete(container_id)`, удалить registry entry и обновить `sandbox.active.count`; missing session возвращает idempotent `not_found` result
- Критерии приемки: delete-vs-create сериализован тем же locker; NotFound runtime path не ломает идемпотентность; lifecycle metrics/logs/traces отражают deleted/not_found/error outcomes
### M35. HTTP schemas/routes для create params и delete endpoint
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M33`, `M34`
- Commit required: no
- Scope: обновить FastAPI adapter под расширенный create request/response и добавить delete endpoint без auth
- Файлы: `adapter/http/fastapi/schemas.py`, `adapter/http/fastapi/dependencies.py`, `adapter/http/fastapi/routers/v1/router.py`
- Решение: `POST /api/v1/create` принимает `chat_id`, `agent_id`, `volume_host_path`; response содержит `agent_id`, `volume_host_path`, `endpoint`; `DELETE /api/v1/sandboxes/{chat_id}` возвращает `deleted` или `not_found`; conflict мапится в `409`
- Критерии приемки: router остается тонким; HTTP models остаются в FastAPI adapter; path/request validation не переносится во внутренние слои; auth не добавляется
### M36. Тесты для delete, endpoint, env и volume mapping
- Субагент: `test-engineer`
- Статус: completed
- Зависимости: `M31`, `M32`, `M33`, `M34`, `M35`
- Commit required: no
- Scope: покрыть новую sandbox control surface без реального production Docker stack
- Файлы: `test/test_sandbox_usecase.py`, `test/test_docker_runtime.py`, `test/test_create_http.py`, при необходимости новые focused tests
- Критерии приемки: есть tests на create request params, reuse match, reuse mismatch `409`, delete `deleted/not_found`, Docker env `AGENT_ID`, request volume bind `rw`, configured network, endpoint IP/port extraction и reconciliation новых labels; `make typecheck` и relevant pytest проходят
### M37. Boundary review для sandbox HTTP control changes
- Субагент: `code-reviewer`
- Статус: completed
- Зависимости: `M36`
- Commit required: no
- Scope: проверить clean architecture, boundary rules и соответствие согласованному MVP scope
- Файлы: весь измененный код после `M30`-`M36`
- Критерии приемки: Docker/FastAPI не протекают во внутренние слои; absolute host path явно ограничен как MVP-риск; dependency direction сохранен; delete/create race не нарушает one-sandbox-per-chat; замечания сведены к minor или отсутствуют
## Follow-up после M37 boundary review
### M38. Исправить rollback endpoint failure и canonical volume path
- Субагент: `feature-developer`
- Статус: completed
- Зависимости: `M37`
- Commit required: no
- Scope: закрыть must/should-fix замечания M37 без смены архитектуры
- Файлы: `adapter/docker/runtime.py`, `adapter/http/fastapi/schemas.py`, `usecase/sandbox.py`, при необходимости focused tests helpers
- Решение: при ошибке после успешного `containers.run` удалить новый container до `SandboxStartError`; canonicalize `volume_host_path` на HTTP boundary; сделать `agent_id` и `volume_host_path` обязательными в `CreateSandboxCommand`
- Критерии приемки: endpoint extraction failure не оставляет untracked running container; повторный create с эквивалентным path не конфликтует из-за raw/canonical mismatch; inner create command больше не допускает пустые default params
### M39. Регрессии для M37 review fixes
- Субагент: `test-engineer`
- Статус: completed
- Зависимости: `M38`
- Commit required: no
- Scope: добавить tests на rollback после endpoint failure и canonical volume path reuse, обновить тесты под required command params
- Файлы: `test/test_docker_runtime.py`, `test/test_create_http.py`, `test/test_sandbox_usecase.py`
- Критерии приемки: endpoint failure удаляет созданный container; HTTP canonicalizes volume path до usecase command; `CreateSandboxCommand` без params больше не используется; `make lint`, `make typecheck`, `make test` проходят
### M40. Повторный boundary review для sandbox HTTP control changes
- Субагент: `code-reviewer`
- Статус: completed
- Зависимости: `M39`
- Commit required: no
- Scope: подтвердить, что M38-M39 закрыли M37 findings без новых boundary нарушений
- Файлы: весь измененный код после `M38`-`M39`
- Критерии приемки: M37 must/should-fix закрыты; clean architecture соблюдена; замечания сведены к minor или отсутствуют