master/tasks.md
2026-04-03 09:49:14 +03:00

29 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
  • Статус: 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