310 lines
41 KiB
Markdown
310 lines
41 KiB
Markdown
# 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 = `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` = ТОЛЬКО 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).
|
||
|
||
|
||
---
|
||
|
||
## Staging-среда orchestrator — Этапы 2-3 ВЫКАЧЕНЫ ✅ (05.06, утро)
|
||
|
||
### Этап 1 (ORCH-31) — закрыт
|
||
- **PR #28 смержен** в main, main HEAD `3b68a29` "Merge PR #28", коммит ORCH-31 `6c1e5ff` в main.
|
||
- ⚠️ Грабля мержа: заголовок авторизации Gitea при первом заходе пришёл как `***` (санитайзер) → HTTP 401 `token is required`. Фикс: явно `token <tok>` → HTTP 200 merged. Запомнить: при мерже через Gitea API проверять, что заголовок реально `token <значение>`, а не затёртый.
|
||
|
||
### Этап 2 (ORCH-32) — песочница создана + staging-контейнер ПОДНЯТ
|
||
- **Gitea-репо песочницы:** `admin/orchestrator-sandbox` (main, auto-init).
|
||
- **Plane-проект песочницы:** name "ORCH Sandbox" (Plane НЕ пускает дефис/спецсимволы в name → без дефиса), identifier `SANDBOX`, **project_id `8c5a3025-4f9d-4190-b79f-fa06276bb27e`**.
|
||
- **Токены — вариант 1 (решение Славы):** общие с продом, изоляция по проекту/репо + раздельная БД (НЕ отдельные токены).
|
||
- **Telegram staging — вариант 1 (решение Славы):** пустой/выключенный, staging молчит в телегу (смотрим по логам/API). Никакого спама в боевой канал.
|
||
- **`.env.staging` собран** на проде (`/home/slin/repos/orchestrator/.env.staging`, в .gitignore, НЕ коммитится): реестр `ORCH_PROJECTS_JSON` = ТОЛЬКО sandbox (боевые ET/ORCH → ignored фильтром ORCH-6), Telegram пустой, БД через volume, `ORCH_STAGING=true`. Прод `.env` (23 строки) цел, бэкап сделан.
|
||
- **Контейнер `orchestrator-staging` поднят на 8501** (`docker compose --profile staging up -d`), образ `orchestrator-orchestrator-staging:latest`. Проверено лично: `/health`=ok, `/queue`=ok (Claude Code 2.1.142 виден), прод `orchestrator` Up 16h БЕЗ рестарта.
|
||
- **Изоляция подтверждена:** staging знает ТОЛЬКО sandbox (`known_plane_project_ids()`={8c5a3025...}); боевые ORCH(8da6aa25)/ET(7a79f0a9) → False; БД физически раздельная `data/staging/orchestrator.db`.
|
||
|
||
### Webhook-изоляция (важная находка)
|
||
- **Gitea webhook прод-репо `orchestrator`** шлёт жёстко на `localhost:8500/webhook/gitea` (прод). Sandbox-репо пока БЕЗ хуков (правильно).
|
||
- **Plane webhooks через workspace API недоступны** (404) — настраиваются на уровне Plane-инстанса/UI (`plane-app-*` контейнеры). Прод-Plane шлёт на 8500. Staging боевые Plane-события НЕ получит, пока webhook в Plane не настроен на 8501.
|
||
- **Главный ключ изоляции:** `ORCH_PROJECTS_JSON` staging = только sandbox → даже если событие прилетит, всё не-sandbox → `ignored` (фильтр ORCH-6).
|
||
|
||
### Этап 3 (ORCH-33) — тест-сьют ГОТОВ, PR #29 (ждёт мержа)
|
||
- Dev написал `scripts/staging_check.py` (smoke A + доступы B + e2e C, exit-code, cleanup в finally) + `docs/STAGING_CHECK.md`. Ветка `feature/ORCH-33-staging-testsuite`. **PR #29 open.**
|
||
- **Проверено лично на проде: 10/10 PASS, exit 0.** Файлы только новые (src/tests/compose/.env не тронуты). Боевое НЕ задето (нет staging-check веток в orchestrator/enduro-trails, нет тест-задач в боевом ORCH). Песочница чиста после cleanup (sandbox-репо = только main, Plane SANDBOX = 0 задач). Прод 8500 жив.
|
||
- **Ключевое архитектурное знание (порядок старта конвейера):** при старте сначала resolve проекта → подтяг name/desc из Plane → QG-0 валидация → **создаётся work_item_id + ветка + начальные доки + строка задачи в БД** → ТОЛЬКО ПОТОМ enqueue аналитика (Claude CLI через launcher). Значит e2e проверяет РАННИЕ артефакты (ветка/доки) ДО запуска LLM — быстро, без расхода кредитов. Аналитика не ждём.
|
||
- **Режима заглушек агентов (гибрид C) в коде НЕТ** — но для e2e он и не нужен (проверяем раннюю стадию до LLM).
|
||
|
||
### Открытый нюанс (не блокер)
|
||
- Bot-токены агентов (`ORCH_PLANE_BOT_ANALYST` и пр.) НЕ добавлены членами в SANDBOX-проект → `add_comment` от их имени = 403. Dev заменил проверку «коммент в Plane» на надёжный ранний артефакт «analyst job в очереди». Чтобы закрыть полностью — добавить бот-аккаунты в SANDBOX через Plane API (моя инфра-работа, мелкая).
|
||
|
||
### Следующие шаги
|
||
1. Мерж PR #29 (Этап 3) — предложено + добавить ботов в SANDBOX.
|
||
2. **Этап 4 (ORCH-34):** хост-деплой-хук `orchestrator-deploy-hook.sh` (промоут staging→prod, health 10×6с=60с, auto-rollback PREV_IMG). Образец `/home/slin/bin/enduro-deploy-hook.sh`.
|
||
3. **Этап 5 (ORCH-35):** стадия `deploy-staging` перед `deploy-prod` в конвейере.
|
||
|
||
### Идентификаторы (Этап 2-3)
|
||
- Plane SANDBOX project_id: `8c5a3025-4f9d-4190-b79f-fa06276bb27e`, identifier `SANDBOX`
|
||
- Gitea sandbox-репо: `admin/orchestrator-sandbox`
|
||
- staging порт 8501, контейнер `orchestrator-staging`, профиль compose `staging`, образ `orchestrator-orchestrator-staging:latest`, БД `data/staging/orchestrator.db`, `.env.staging` (в .gitignore)
|
||
- main HEAD после Этапа 1: `3b68a29`; PR #28 merged, PR #29 open (Этап 3)
|
||
- ТЗ: `tasks/orchestrator/DEV_TASK_ORCH33_STAGING_TESTSUITE.md`; отчёт `tasks/orchestrator/reports/dev-2026-06-05-orch33-staging-testsuite.md`
|
||
|
||
---
|
||
|
||
## Этап 3 закрыт + боты в SANDBOX (05.06, продолжение)
|
||
|
||
### PR #29 смержен → Этап 3 в main
|
||
- **PR #29 merged** (HTTP 200, AUTH="token "+TOK — урок учтён). main HEAD `93169f1`, коммит сьюта `94334bd`.
|
||
|
||
### Нюанс с бот-комментами закрыт (Plane membership)
|
||
- **Корень 403 при `add_comment` бот-токеном:** бот-токен ВИДИТ проект (GET 200), но НЕ может постить комменты, пока его owner НЕ член проекта. Воспроизвела: бот POST comment=403, админ POST comment=201.
|
||
- **7 бот-аккаунтов Plane (workspace members):** Analyst `c925cbcd-a8dc-4506-978e-15354ce3ad31`, Developer `bfe59a48-a7cd-4ce1-a78b-83f8891a4aba`, Reviewer `9836a30d-1294-430d-8109-ceb3e784d1d8`, Tester `7cdfc9e7-c552-4fd1-b4a7-3b1f03e72751`, Architect `6d82b825-c8b2-447c-bca0-c4d34c6200ac`, Deployer `ebf17448-d544-465e-8b0e-57581f76507c`, Стрим `468040f8-f609-406a-84d2-a119880a5e8e`. (+ admin-токен owner = `mva154` daf4d3f4..., + системный `Plane` 43c79cc2...).
|
||
- **Identity бот-токена узнаётся через** `GET {PBASE}/users/me/` с `X-API-Key: <bot-token>` → возвращает id+display_name.
|
||
- **Добавление члена проекта Plane (РАБОЧИЙ формат):** `POST /workspaces/{slug}/projects/{pid}/members/` body `{"member": <user_id>, "role": 15}` ПООДИНОЧКЕ. ⚠️ Формат `{"members":[{...}]}` и `member_id` НЕ работают (400 "member: This field is required"). role 15 = member.
|
||
- **Все 7 ботов добавлены членами SANDBOX** (теперь 8 members). Верифицировано: Analyst и Developer боты постят комменты → HTTP 201. Нюанс Этапа 3 устранён.
|
||
|
||
### Дальше
|
||
Этап 4 (ORCH-34): хост-деплой-хук `orchestrator-deploy-hook.sh` (промоут staging→prod, health 10×6с=60с, auto-rollback по PREV_IMG). Образец `/home/slin/bin/enduro-deploy-hook.sh`.
|
||
|
||
|
||
---
|
||
|
||
## Этап 4 (ORCH-34) деплой-хук — ГОТОВО ✅ + чистка Plane (05.06, продолжение)
|
||
|
||
### Закрыты в Plane (Backlog → Done НАПРЯМУЮ, минуя In Progress)
|
||
- ✅ ORCH-30, ORCH-31, ORCH-32, ORCH-33 → Done (верифицированы).
|
||
- ⚠️ **Важная тонкость безопасности:** боевой webhook ORCH-проекта идёт на прод (8500). Переход задачи **В In Progress запустил бы конвейер** по ней! Поэтому выполненные задачи двигать **Backlog → Done напрямую, минуя In Progress**. Прод не дёрнулся.
|
||
- **State id Done** в проекте ORCH: `3738cd3c-7610-4907-ba5e-26b9a248d9c0`.
|
||
- ⚠️ **In Progress id ПЛАВАЕТ** — был `b873d9eb...` (в памяти), стал `e331bfb3...`. ВСЕГДА запрашивать states свежим API-вызовом, НЕ по памяти.
|
||
- ОстаютсяBacklog: ORCH-34 (закрою после мержа), ORCH-35 (не начат).
|
||
|
||
### Бот-аккаунты добавлены в SANDBOX (нюанс Этапа 3 закрыт)
|
||
- 403 при коммите бот-токеном был НЕ из-за доступа к проекту (GET 200 у всех 7 ботов), а из-за **членства**: чтобы постить комменты, владелец бот-токена должен быть **member** проекта.
|
||
- **Plane API формат добавления члена:** `POST /projects/<id>/members/` с телом `{"member": <user_id>, "role": 15}` — ПООДИНОЧКЕ (не массивом), поле `member` (НЕ `member_id`!), role 15 = member.
|
||
- Добавила всех 7 владельцев бот-токенов в SANDBOX → коммент бот-токеном теперь 201 (было 403). Проверено воспроизведением.
|
||
- user-id ботов узнаются через workspace-members API.
|
||
|
||
### Этап 4 — деплой-хук (проверено мной на проде, оба сценария)
|
||
- Dev: `scripts/orchestrator-deploy-hook.sh` (176 строк) + `docs/DEPLOY_HOOK.md`. Ветка `feature/ORCH-34-deploy-hook`, коммит `a6cbacb`. **PR #30 open.**
|
||
- **Happy-path ✅:** deploy → health ok за 1 попытку → exit 0.
|
||
- **Авто-rollback ✅ (главное):** Я сама подсунула битый образ (busybox `exit 1`, перетегла как staging-образ) → хук прождал 10×6с=60с (HTTP 000) → САМ откатился на PREV_IMG → рестарт → post-health 5×3с → поднялся → staging снова ok. **Реальный exit-code хука = 1** при фейле (проверила без пайпа `| tail`, который съедал rc).
|
||
- **Heredoc-грабля Dev:** при передаче скрипта через heredoc терялись кавычки в `grep`/`curl -w` → health давал «not ready» при HTTP 200. Фикс Dev: `cat > файл` из локального файла, не heredoc. ЗАПОМНИТЬ для будущих shell-скриптов через heredoc.
|
||
- **Прод цел:** StartedAt `2026-06-04T13:08:13Z` не менялся (Up 17h), 8500 ok. Файлы только новые, staging восстановлен, временные теги убраны.
|
||
- **Параметризация:** дефолт БЕЗОПАСНЫЙ (TARGET_SERVICE=orchestrator-staging, PORT=8501, profile staging). Прод поддержан через override env, но НЕ дефолт. Отдельный PREV_IMAGE_FILE для staging.
|
||
|
||
### Этап 5 (ORCH-35) — последний, ПЛАН (ещё не спущен Dev)
|
||
- Цель: встроить стадию **`deploy-staging` ПЕРЕД `deploy-prod`** в конвейер (`src/stages.py`), тестер привязать к staging-эндпоинту 8501.
|
||
- ⚠️ **Самый рискованный этап:** трогает боевой конвейер деплоя, часть `src/` в off-limits. Перед спуском Dev — ЖИВАЯ разведка по коду стадий (как устроены deploy-стадии, точки встройки), показать план Славе.
|
||
- Из дизайна §4/§7: промоут с **ручным approve на старте** (`DEPLOY_REQUIRE_MANUAL_APPROVE=true`) — хук тормозит после зелёного staging, ждёт «go» Славы. Полный авто — позже.
|
||
- Из дизайна §6: агенты на staging — **гибрид C** (заглушки по умолчанию + full-real по флагу). Режима заглушек в коде ПОКА НЕТ (выяснено на Этапе 3) — возможно, понадобится на Этапе 5 или отдельной задачей.
|
||
|
||
### Идентификаторы (обновление)
|
||
- main HEAD после Этапа 3 (PR #29 merged): `93169f1`
|
||
- PR #30 (Этап 4) — open, коммит `a6cbacb`
|
||
- Done state id (ORCH project): `3738cd3c-7610-4907-ba5e-26b9a248d9c0`
|
||
- ТЗ Этап 4: `tasks/orchestrator/DEV_TASK_ORCH34_DEPLOY_HOOK.md`; отчёт `tasks/orchestrator/reports/dev-2026-06-05-orch34-deploy-hook.md`
|
||
- Деплой-хук: `scripts/orchestrator-deploy-hook.sh`, образец enduro: `/home/slin/bin/enduro-deploy-hook.sh`
|
||
|
||
---
|
||
|
||
## ORCH-36 (Вариант B самодеплоя) заведён в Backlog + критерии «доверия к автоматике» (05.06)
|
||
|
||
### ORCH-36 создан в Plane Backlog
|
||
- **id `84a6c09d-7a03-4cb7-a58b-3d8963c565a5`**, seq 36, name "ORCH-36: Исполняемый самодеплой — стадия deploy дёргает хост-хук (Вариант B)".
|
||
- Суть: стадия `deploy` реально дёргает хост-хук `orchestrator-deploy-hook.sh` (ORCH-34) через ssh → промоут в прод (8500) с health+авто-rollback. На старте — ОБЯЗАТЕЛЬНЫЙ ручной approve (`DEPLOY_REQUIRE_MANUAL_APPROVE=true`). Делается ПОСЛЕ ORCH-35 (Вариант A — staging-гейт ворота).
|
||
- Слава: "позже вернёмся" → лежит в бэклоге детально описанной.
|
||
|
||
### «Доверие к автоматике» — ФОРМАЛЬНЫЕ критерии (для флага true→false)
|
||
Переход `DEPLOY_REQUIRE_MANUAL_APPROVE` true→false (manual approve → полный авто) — ТОЛЬКО когда ВСЕ 5 закрыты (метрики набираются в режиме ручного approve):
|
||
1. **≥10 успешных промоутов подряд** (staging зелёный → approve → прод поднялся, откат не нужен).
|
||
2. **Zero false-negative (критично):** staging-гейт НИ РАЗУ не пропустил битый деплой как «зелёный».
|
||
3. **Авто-rollback проверен в бою ≥2-3 раза:** recovery rate 100%, MTTR <60с.
|
||
4. **Ни одного «молчаливого» деплоя:** каждый промоут/откат → Plane + Telegram.
|
||
5. **Период наблюдения:** ≥10 деплоев ИЛИ ≥2 недели без инцидентов в manual-approve.
|
||
Когда 5/5 → осознанное переключение флага отдельным шагом.
|
||
|
||
### Этап 5 (ORCH-35) = Вариант A (решение по объёму ждёт Славу: только A или A+B)
|
||
- Вариант A: стадия `deploy-staging` МЕЖДУ testing и deploy, QG `check_staging_status` (образец `check_deploy_status`), deployer гоняет `staging_check.py` против 8501. Прод-деплой недостижим пока staging не зелёный. Off-limits: не ломать существующие QG/rollback.
|
||
- Полный план разведки — в `tasks/orchestrator/DESIGN_STAGING_ENV.md` (раздел "РАЗВЕДКА КОДА ДЛЯ ЭТАПА 5").
|
||
|
||
|
||
---
|
||
|
||
## Этап 5 (ORCH-35) — staging-гейт встроен ✅, проверено лично + найден архитектурный нюанс (05.06 ~07:30 UTC)
|
||
|
||
### Что сделано Dev (PR #31, ветка `feature/ORCH-35-staging-gate`, коммит `e0b6e92`, НЕ мержен)
|
||
Вставлена стадия `deploy-staging` МЕЖДУ `testing` и `deploy`. Новая цепочка: `testing → deploy-staging → deploy → done`.
|
||
- `src/stages.py` — `STAGE_TRANSITIONS`: `testing→deploy-staging` (agent=deployer, qg=check_tests_passed), `deploy-staging→deploy` (agent=deployer, qg=check_staging_status), `deploy→done` без изменений
|
||
- `src/qg/checks.py` — новый `check_staging_status` + `_parse_staging_status` (парсит `staging_status: SUCCESS|FAILED` из `15-staging-log.md`), зарегистрирован в `QG_CHECKS`
|
||
- `src/stage_engine.py` — rollback-ветка: FAILED на deploy-staging → откат на `development` (по образцу БАГ-8 deploy)
|
||
- `.openclaw/agents/deployer.md` — НОВЫЙ файл (в репо его не было), инструкция для обеих стадий deployer
|
||
- `tests/test_qg.py`, `tests/test_stage_engine.py` — обновлены + 18 новых staging-тестов
|
||
|
||
### Проверено МНОЙ лично на проде (не со слов Dev)
|
||
- **312 passed, 0 failed** — прогнала весь набор сама. Способ: прод-образ орка (там все зависимости) + примонтировала исходники ветки поверх → честный прогон в реальном окружении. (В прод-контейнере `tests/` нет — образ собран без них; чистый python-образ не имеет зависимостей — поэтому монтирование в прод-образ.)
|
||
- Цепочка вживую: `get_next_stage("testing")=="deploy-staging"`, `("deploy-staging")=="deploy"`, `("deploy")=="done"`; `get_qg_for_stage("deploy-staging")=="check_staging_status"` ✓
|
||
- QG-логика вживую: SUCCESS→(True), FAILED→(False), missing→(False) ✓
|
||
- Rollback staging-провала → `development` (3 теста) ✓
|
||
- git diff: только код (`.openclaw/agents/deployer.md`, `src/qg/checks.py`, `src/stage_engine.py`, `src/stages.py`, `tests/*`). off-limits (.env/compose/staging_check.py/deploy-hook.sh) не тронуты. Прод 8500 не задет.
|
||
|
||
### ⚠️ АРХИТЕКТУРНЫЙ НЮАНС (раскопала сама, ЖДЁТ решения Славы перед мержем)
|
||
**Промпты агентов (`deployer.md` и пр.) живут В КАЖДОМ репозитории задач, а не в репо orchestrator.**
|
||
- `launcher.py` делает `cat {system_prompt}` ВНУТРИ `work_path` (= клон ЦЕЛЕВОГО репо задачи). Путь `.openclaw/agents/deployer.md`.
|
||
- **enduro-trails-репо** имеет полный набор промптов (analyst/architect/deployer/developer/reviewer/tester.md от 04.06), но deployer.md там СТАРЫЙ — про staging-гейт не знает.
|
||
- **orchestrator-репо** — Dev положил новый deployer.md с staging-инструкцией (правильно для self-hosting: орк деплоит сам себя, work_path=клон orchestrator).
|
||
|
||
**Проблема:** стадия `deploy-staging` ГЛОБАЛЬНАЯ (в STAGE_TRANSITIONS) → применяется ко ВСЕМ проектам конвейера. Но:
|
||
1. У enduro deployer-промпт не знает про staging → не напишет `15-staging-log.md` → гейт FAIL → enduro-задачи откатятся на `development` и застрянут
|
||
2. `staging_check.py --base-url localhost:8501` = staging ОРКА. У enduro своего staging нет. Гейт концептуально orchestrator-специфичный.
|
||
|
||
**Варианты (предложены Славе):**
|
||
- 🅰️ (моя рекомендация) `deploy-staging` УСЛОВНЫЙ — гейт только для self-hosting (проект orchestrator), для остальных стадия пропускается/пустой pass. Правильная инкапсуляция.
|
||
- 🅱️ оставить как есть + обновить enduro deployer.md + поднять enduro-staging (большой объём, выходит за рамки)
|
||
- 🅲️ заморозить мерж как known-issue (активных enduro-задач нет), мержить после A
|
||
|
||
**СТАТУС: жду решение Славы. PR #31 НЕ мержен. ORCH-35 НЕ закрыт.**
|
||
|
||
### Identifiers Этапа 5
|
||
- PR `#31` в Gitea (admin/orchestrator), ветка `feature/ORCH-35-staging-gate`, коммит `e0b6e92`, НЕ merged
|
||
- ORCH-35 issue id: `4ead9be7-e1bf-4a28-8c76-88bda1c1fc2c`
|
||
- Done state id: `3738cd3c-7610-4907-ba5e-26b9a248d9c0`
|
||
- Новый артефакт стадии: `docs/work-items/<wi>/15-staging-log.md`, машинное поле `staging_status:`
|
||
- Прогон тестов: образ прод-орка + монтирование исходников ветки → 312 passed
|
||
|
||
## Вариант B вынесен в ORCH-36 (Backlog) — исполняемый самодеплой
|
||
Создан ORCH-36 «Исполняемый самодеплой — стадия deploy дёргает хост-хук (Вариант B)» в Backlog с детальным описанием. Внутри зашиты 5 метрик «доверия к автоматике» для переключения флага manual approve → авто:
|
||
1. ≥10 успешных промоутов подряд (откат не понадобился)
|
||
2. Zero false-negative — staging-гейт ни разу не пропустил битый деплой (критично)
|
||
3. Авто-rollback в бою ≥2-3 раза (recovery 100%, MTTR <60с)
|
||
4. Ни одного «молчаливого» деплоя (каждый → Plane + Telegram)
|
||
5. Период ≥10 деплоев ИЛИ ≥2 недели без инцидентов
|
||
Связи: ORCH-7/21/34/35. Ждёт когда вернёмся.
|