master/tasks.md

14 KiB
Raw Blame History

План работ: 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
  • Статус: 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