Files
wiki/memory/2026-06-05.md
2026-06-05 08:40:01 +03:00

18 KiB
Raw Blame History

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)
    • 1baae81autouse-фикстура в 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 = d0a3424 Merge 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, профиль compose staging
  • ORCH-32 id=3dadcf0d-f1b6-4302-8075-8d428ace01f6 — песочница: Plane ORCH-SANDBOX + Gitea orchestrator-sandbox, реестр через env ORCH_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, прибить лишние. Принести Славе на ОК.

Следующие шаги

  1. [ждёт кивка] Мерж PR #27 в main, закрыть #26 → CI-гейт (пункт self-hosting) закрыт.
  2. Запустить Dev на ORCH-31 (staging-инфра) когда репо свободно.
  3. Этап 2 (ORCH-32): завести ORCH-SANDBOX в Plane + orchestrator-sandbox в Gitea при выкате.
  4. Уборка раннеров (план Славе на ОК).

Продолжение сессии (флаш #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 = ТОЛЬКО sandboxknown_plane_project_ids() = {8c5a3025...}, боевые ORCH(8da6aa25)/ET(7a79f0a9) = False → события по ним фильтр ORCH-6 режет в ignored. Staging физически не реагирует на боевые задачи.
  • БД физически раздельная: volume ./data/stagingdata/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).