14 KiB
14 KiB
План работ: 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 volumero, lambda-tools volumero - 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 policychat=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 - Статус: pending
- Зависимости:
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