18 KiB
2026-06-05
Orchestrator — CI-гейт качества закрыт ✅ (продолжение self-hosting ORCH-7)
Главный итог
CI у orchestrator теперь честно зелёный. Дефект изоляции тестов (тесты протекали друг в друга через синглтон settings) закрыт. Гейт качества для self-hosting работает по-настоящему.
Что было сделано
- PR #27 (ветка
fix/isolate-webhook-tests-from-plane) — три коммита:7bbab9c— изоляция 3 webhook-тестов от живого Plane API (моки через ИСТОЧНИКsrc.plane_sync.*)e856e09— миграцияtest_plane_webhook_generates_sequential_idsпод новый контракт (задача создаётся при переходе В In Progress, а не наwork_item.created)1baae81— autouse-фикстура вtests/conftest.py, сбрасывает webhook-секрет (+ db_path) на синглтоне settings перед каждым тестом
- Проверено лично на проде: полный
pytest tests/в чистом docker-окружении = 294 passed, 0 failed (было 10 failed). CI run 547/548 = success (впервые). - 401/HMAC/dedup тесты ЖИВЫ — фикстура их не сломала (они перекрывают её своим monkeypatch). Off-limits зона не тронута: правки только в
tests/conftest.pyиtests/test_webhooks.py, src/ чист.
Ключевой урок — интерференция тестов через синглтон settings
Settings(Pydantic, src/config.py) читает env ОДИН раз при создании объектаsettings.test_webhooks.pyставит секрет жёсткоos.environ["..."]=""на import-time; другие файлы — черезsetdefault;test_webhook_dedup.pyставит реальный"s3cr3t"через monkeypatch.- В ПОЛНОМ прогоне
pytest tests/секрет протекал между файлами → HMAC включался → webhook-тесты ловилиassert 401 == 200. По отдельности файл зелёный, вместе — красный. - Решение-паттерн: autouse-фикстура в conftest, сброс протекающего состояния синглтона через monkeypatch перед каждым тестом. НЕ менять сам механизм/защищённые тесты — только навести порядок с изоляцией.
db_pathтоже протекал (get_task_by_repo_branch возвращал stale) — Dev законно добавил и его сброс.
Мерж ВЫПОЛНЕН ✅ (05.06, Слава дал «да»)
- PR #27 → MERGED в main (HTTP 200). main HEAD =
d0a3424Merge PR #27. Все 3 коммита фиксов в истории main. - PR #26 → CLOSED без мержа (поглощён #27).
- Нюанс: CI на самом main после merge-commit отдельным прогоном не появился (workflow триггерится на push/PR-ветки; merge-commit мог не дёрнуть). Код в main = ровно то, что прогонялось зелёным (294 passed). Гейт на будущих PR работает.
- Пункт №1 self-hosting (CI-гейт) ЗАКРЫТ. Осталось: удалить отработанные ветки fix/isolate, ci/add (косметика).
Staging-среда для orchestrator (ORCH-30..35) — дизайн оформлен, задачи в Backlog
Решения (гибрид C)
- Агенты на staging: ПО ДЕФОЛТУ заглушки (быстро/дёшево/детерминированно — гейт проверяет работоспособность ОРКа, не качество LLM) + режим
full-realпо флагу (настоящий Claude CLI черезclaude-cli-proxy). - Plane/Gitea на staging — настоящие, но в песочнице (ORCH-SANDBOX проект + orchestrator-sandbox репо).
DEPLOY_REQUIRE_MANUAL_APPROVE=trueна старте — хук тормозит после зелёного staging, ждёт «go» Славы.- Деплоем рулит ВНЕШНИЙ хост-хук, не сам контейнер орка (орк не может убить свой процесс на середине деплоя; rollback через PREV_IMG, образец
enduro-deploy-hook.sh). - Задачи только в Backlog — конвейер стартует ТОЛЬКО при переходе В In Progress (Feature 1). Backlog безопасен.
Задачи (все Backlog, project ORCH id 8da6aa25-a60e-44d6-a1e2-d8ae59aa7d6a)
- ORCH-31 id=5a68a13c-3f3b-4d9e-91f7-24b0794bed06 — staging-инфра: контейнер
orchestrator-staging(порт 8501), изолированная БД черезORCH_DB_PATH/volume./data/staging, профиль composestaging - ORCH-32 id=3dadcf0d-f1b6-4302-8075-8d428ace01f6 — песочница: Plane
ORCH-SANDBOX+ Giteaorchestrator-sandbox, реестр через envORCH_PROJECTS_JSON(код не трогать) - ORCH-33 id=a11a7c0f-a78a-4309-9aa5-1c43d4355d0f — тест-сьют (smoke + доступы + e2e), режим full-real
- ORCH-34 id=47fe9e75-d7ff-4ecb-bb99-67af9b23ab67 — хост-деплой-хук
orchestrator-deploy-hook.sh(build candidate→staging-гейт→promote→health 10×6с→auto-rollback), пересекается с ORCH-21 - ORCH-35 id=4ead9be7-e1bf-4a28-8c76-88bda1c1fc2c — стадия
deploy-stagingпередdeploy-prod
Разведка инфры (факты для точных ТЗ)
- compose: один service
orchestrator,network_mode: host, порт 8500 в команде uvicorn (uvicorn src.main:app --host 0.0.0.0 --port 8500). Staging → просто другой порт 8501 + профильstaging. ORCH_DB_PATHУЖЕ параметризован (config.py:43 дефолт/app/data/orchestrator.db) → изоляция БД через него + отдельный volume./data/staging.- Реестр проектов через env
ORCH_PROJECTS_JSON(JSON-массив) → sandbox добавляется в.env.staging, код менять не надо. - В Plane sandbox-проекта ещё НЕТ (есть ag_proj, First, ET, ORCH). В Gitea sandbox-репо ещё НЕТ (enduro-trails, openclaw-vault, orchestrator, wiki). Заведу сама через API при выкате Этапа 2.
- ТЗ готово:
tasks/orchestrator/DEV_TASK_ORCH31_STAGING_INFRA.md. Запускать Dev на ORCH-31 ТОЛЬКО когда репо свободно (не толкать двух Dev на один git одновременно).
Грабли/правила (подтверждены этой сессией)
- Файлы памяти: durable → ТОЛЬКО
memory/YYYY-MM-DD.md(append). Сегодня2026-06-05.md. - Dev: код/тесты/конфиги → ТОЛЬКО в Gitea-репо orchestrator через PR. Push в main запрещён (pre-receive hook). Dev НЕ мержит — мержит ревьюер (Стрим/Слава).
- Инфра вне репо (
.env.stagingреальный, Plane/Gitea sandbox-сущности, хост-деплой-хук, поднятие контейнера) — делаю я через installer/API, НЕ Dev через PR. В репо только.env.staging.example. - ⚠️ Dev: НЕ регистрировать раннеров, НЕ nohup. Раннер
mva154-runner-orchуже есть. - ⚠️ Off-limits без согласования: HMAC-механизм, 9 HMAC/401 тестов, src/config.py settings, check_reviewer_verdict, check_deploy_status, merge-gate gitea.py, PLANE_STATES, set_issue_done, launcher, queue, stages.py mapping, _parse_tests_verdict, check_tests_local (только DEPRECATED, не удалять).
- ⚠️ Грабля push: после push
git log origin/main..origin/<branch>ДОЛЖЕН показать коммит ДО отчёта «PR готов». - ⚠️ Plane API: дёргать через base64-stdin+docker cp в контейнер orchestrator. URL
http://localhost:8091БЕЗ /api/v1 (суффикс добавляет код!), заголовокX-API-Key, токенORCH_PLANE_API_TOKEN. Без /api/v1 Plane вернёт HTML(200) вместо JSON. - Прогон тестов в чистом окружении (как CI):
docker run --rm -v /home/slin/repos/orchestrator:/code:ro -w /code -e PYTHONPATH=/code --entrypoint python3 $(docker inspect orchestrator --format '{{.Config.Image}}') -m pytest tests/ -q - Эндпоинт Actions:
GET /api/v1/repos/admin/orchestrator/actions/tasks, токенdocker exec orchestrator printenv ORCH_GITEA_TOKEN. - Сервер:
slin@82.22.50.71(sshpass, pw motoZ@yaz2010), репо/home/slin/repos/orchestrator, контейнерorchestratorпорт 8500, Gitea https://git.mva154.duckdns.org. - Модель Dev:
tokenator/claude-sonnet-4-6(vibecode кредиты кончились; альт tokenator/claude-opus-4-8). - ElevenLabs TTS квота кончилась → отвечать ТЕКСТОМ.
Уборка раннеров (TODO, ждёт ОК Славы)
3 процесса act-runner: systemd act-runner.service (рабочий, mva154-runner), PID 3828601 (старый mva154-runner с 19.05), PID 4091018 (mva154-runner-orch через nohup — НЕ переживёт reboot). План: systemd-unit вместо nohup, прибить лишние. Принести Славе на ОК.
Следующие шаги
- [ждёт кивка] Мерж PR #27 в main, закрыть #26 → CI-гейт (пункт self-hosting) закрыт.
- Запустить Dev на ORCH-31 (staging-инфра) когда репо свободно.
- Этап 2 (ORCH-32): завести ORCH-SANDBOX в Plane + orchestrator-sandbox в Gitea при выкате.
- Уборка раннеров (план Славе на ОК).
Продолжение сессии (флаш #2)
PR #27 СМЕРЖЕН в main ✅ / #26 закрыт
- PR #27 merged (HTTP 200), main HEAD =
d0a3424"Merge PR #27". Все три коммита фиксов в истории main (изоляция Plane + seq-тест + изоляция секрета). - PR #26 закрыт без мержа (поглощён #27). Подтверждено API: #27 state=closed merged=True; #26 state=closed merged=False.
- CI-гейт качества orchestrator закрыт по-настоящему → главный блокер self-hosting (ORCH-7) снят.
- Грабля по экранированию: при мерже через Gitea API двойной SSH-эскейп кавычек поехал → передавала скрипт через base64 (echo b64 | base64 -d | bash). Рабочий приём для сложных команд через sshpass.
- ⚠️ Косметика-TODO: удалить отработанные ветки
fix/isolate-webhook-tests-from-planeиci/add-gitea-workflow(обе merged/closed).
Этап 1 (ORCH-31) — staging-инфра ГОТОВА, PR #28 (ждёт мержа на момент флаша)
- Dev отработал, ветка
feature/ORCH-31-staging-infra, PR #28 open, mergeable. - 4 файла, прод-блок compose НЕ тронут (diff чистый:
@@ -25,3 +25,39 @@— чистое добавление в конце, прод-строки 1-25 без изменений). - Проверено на проде лично: обычный
docker compose configпоказывает ТОЛЬКОorchestrator; staging виден ЛИШЬ с--profile staging→ случайно не поднимется. Контейнераorchestrator-stagingна проде нет (крутится только прод, Up 15h). БД изолирована через volume./data/staging(на хосте ещё не создан — создастся при первом запуске). - ⚠️ Нюанс:
docker compose config --profile stagingбез реального.env.stagingне выводит детали порта 8501 — это норма, реальная валидация при выкате с настоящим.env.staging(Этап 2). - Развилка (ждёт ОК Славы): мержить #28 сейчас (чистый инфра-код в main, ничего не запускается — staging «спящий чертёж») ИЛИ ждать. Я ЗА мерж сейчас (вариант 1) — main маленькими безопасными кусками.
- На хосте куча
orchestrator.db.bak-deploy*— бэкапы прод-БД от прошлых деплоев 03-04.06, НЕ наша история, не трогать.
Объяснила Славе что проверяют 294 теста (для контекста)
Группы тестов orchestrator (страховочная сетка перед самодеплоем): гейт качества (41), Telegram-трекер (28), resilience/переживание рестарта (26), usage+queue (23+19), движок этапов конвейера (22), запуск агентов через Claude CLI (18), webhooks Plane/Gitea + дедуп + подпись (16+9+8), маршрутизация проектов после инцидента 02.06 (16), git-worktree изоляция веток (9), мелочёвка (task.md, комменты, ротация логов, вердикты). Тесты НЕ вшиты в docker-образ — только в репо (правильно).
Время
Сессия идёт глубокой ночью UTC (~04:40-04:50), у Славы утро ~07:40 UTC+3. Несколько раз предлагала паузу — Слава продолжает работать.
Следующий шаг (на момент флаша)
Жду ОК Славы: мержить PR #28 → идти на Этап 2 (ORCH-32: песочница Plane ORCH-SANDBOX + Gitea orchestrator-sandbox + реальный .env.staging + поднятие staging-контейнера через installer).
Продолжение (флаш #3) — STAGING-СТЕНД ПОДНЯТ 🚀
PR #28 смержен → Этапы 1+2 выкачены, staging живой на проде
- PR #28 merged (main HEAD
3b68a29). ⚠️ Грабля: при мерже через Gitea API заголовокAuthorizationсглючил →token is required(HTTP 401); фикс —AUTH = "token " + TOKотдельной переменной, не инлайнить. Со второго раза HTTP 200. - Этап 2 (ORCH-32) песочница создана: Gitea-репо
admin/orchestrator-sandbox(auto-init main) + Plane-проект "ORCH Sandbox" (identifier SANDBOX, id 8c5a3025-4f9d-4190-b79f-fa06276bb27e). Имя в Plane БЕЗ дефиса — Plane не пускает спецсимволы в name (ошибка "Project name cannot contain special characters"). - Токены — вариант 1 (Слава подтвердил): общие с продом, изоляция по проекту/репо.
.env.stagingсобран (скрипт из прод-.env + staging-оверрайды), бэкап прод-.env сделан (.env.bak-before-staging-*), в .gitignore (IGNORED ok), прод .env цел (23 строки).
Изоляция staging (КЛЮЧЕВОЕ — проверено лично, всё зелёное)
- Реестр
ORCH_PROJECTS_JSON= ТОЛЬКО sandbox →known_plane_project_ids()= {8c5a3025...}, боевые ORCH(8da6aa25)/ET(7a79f0a9) = False → события по ним фильтр ORCH-6 режет вignored. Staging физически не реагирует на боевые задачи. - БД физически раздельная: volume
./data/staging→data/staging/orchestrator.db(32KB свежая). Прод-БД не трогает. - Telegram пустой (Слава: вариант 1, staging молчит) — ORCH_TELEGRAM_CHAT_ID="" и BOT_TOKEN="". Не спамит боевой канал.
- ORCH_STAGING=true пометка.
- Подъём:
sg docker -c "docker compose --profile staging up -d --build orchestrator-staging".
Проверки выката (все ✅)
- staging 8501 /health=ok, /queue=ok (queued/running/done/failed=0, preflight_ok, Claude Code 2.1.142).
- прод 8500 /health=ok, контейнер Up 16h БЕЗ рестарта (не дёрнулся).
- реестр: sandbox=True, боевой ORCH=False, боевой ET=False.
Важные технические факты (для след. этапов)
- Gitea-webhook прод-репо
orchestratorжёстко шлёт наhttp://localhost:8500/webhook/gitea. Sandbox-репоorchestrator-sandboxпока БЕЗ хуков (настрою на 8501 в Этапе 3 при e2e). - Plane-webhooks через workspace API = 404 (Plane настраивает их через UI/инстанс plane-app-*, не через API). Прод получает Plane-события webhook'ом на 8500. Staging боевые Plane-события НЕ получит, пока ему отдельно не настроят webhook → плюс к безопасности.
- Орк роутеры:
/webhook/plane,/webhook/gitea(main.py:92-93, prefix /webhook). - 8500=прод (uvicorn pid живой), 8501=staging.
ORCH_REPOS_DIRв .env=/home/slin/repos, но compose env-секция перебивает на /repos → внутри контейнера /repos (правильно).- Образ staging:
orchestrator-orchestrator-staging:latest, контейнерorchestrator-staging.
Следующий шаг
Этап 3 (ORCH-33): тест-сьют staging — smoke + проверка доступов (Plane sandbox + Gitea sandbox реальными вызовами) + e2e (задача в SANDBOX → ветка в orchestrator-sandbox → статусы+комменты в Plane, верификация по API), режим full-real. Перед e2e: настроить Gitea-webhook sandbox-репо на localhost:8501. Это код для Dev (тест-скрипт) + моя инфра (webhook).