diff --git a/memory/2026-06-05.md b/memory/2026-06-05.md index fd97305..8aad084 100644 --- a/memory/2026-06-05.md +++ b/memory/2026-06-05.md @@ -232,3 +232,25 @@ - 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"). diff --git a/tasks/orchestrator/DEV_TASK_ORCH35_STAGING_GATE.md b/tasks/orchestrator/DEV_TASK_ORCH35_STAGING_GATE.md new file mode 100644 index 0000000..1f31f7d --- /dev/null +++ b/tasks/orchestrator/DEV_TASK_ORCH35_STAGING_GATE.md @@ -0,0 +1,62 @@ +# DEV TASK — ORCH-35: стадия `deploy-staging` как обязательные ворота перед прод-деплоем (Вариант A) + +**Проект:** orchestrator | **Сервер:** slin@82.22.50.71 (pw motoZ@yaz2010) | **Репо:** /home/slin/repos/orchestrator +**Ветка:** `feature/ORCH-35-staging-gate` из свежего origin/main. `git fetch origin && git checkout main && git pull --ff-only && git checkout -b feature/ORCH-35-staging-gate`. +**Этап:** 5 (финальный) из 5. После Этапов 1-4: staging живой на 8501, песочница рабочая, тест-сьют `scripts/staging_check.py` в main, деплой-хук в main. + +## ЦЕЛЬ (Вариант A — «ворота», БЕЗ исполняемого деплоя) +Вставить в конвейер новую стадию **`deploy-staging` МЕЖДУ `testing` и `deploy`**. На ней прогоняется staging-тест-сьют против живого staging (8501) и пишется машинный вердикт. **Прод-деплой (`deploy`) недостижим, пока `deploy-staging`-гейт не зелёный.** Это «бумажные ворота»: реальный docker-деплой в прод НЕ делается (это Вариант B = ORCH-36, отдельная задача — НЕ трогать). + +Новая цепочка: `...testing → deploy-staging → deploy → done`. + +## ⛔ OFF-LIMITS / КРИТИЧНО (это БОЕВОЙ конвейер) +- **НЕ ломать существующие QG:** `check_deploy_status`, `check_tests_passed`, `_parse_deploy_status`, БАГ-8 rollback (`deploy`-вердикт FAILED → откат на `development` в `stage_engine.py` ~525), terminal-sync `deploy→done` (~260). ТОЛЬКО ДОБАВЛЯТЬ, не переписывать существующее. +- **НЕ дёргать реальный docker / хост-хук / ssh-деплой в прод.** Это Вариант B (ORCH-36). Здесь только прогон staging-сьюта + вердикт. +- **НЕ трогать** `.env`, `.env.staging`, `docker-compose.yml`, `scripts/staging_check.py`, `scripts/orchestrator-deploy-hook.sh`. +- PR push в main ЗАПРЕЩЁН, НЕ мержить. Коммит в ветку + PR. +- НЕ регистрировать раннеров, НЕ nohup. Раннер mva154-runner-orch уже есть. +- Если факт разошёлся с ТЗ — СТОП, отчёт, не угадывать. + +## ТОЧНЫЕ ТОЧКИ ВСТРОЙКИ (разведано 05.06) + +### 1. `src/stages.py` — `STAGE_TRANSITIONS` +Сейчас: +``` +"testing": {"next": "deploy", "agent": "deployer", "qg": "check_tests_passed"}, +"deploy": {"next": "done", "agent": None, "qg": "check_deploy_status"}, +``` +Стать (вставить `deploy-staging` между ними, СОХРАНИВ порядок ключей — `get_previous_stage` использует порядок dict для rollback): +``` +"testing": {"next": "deploy-staging", "agent": "deployer", "qg": "check_tests_passed"}, +"deploy-staging": {"next": "deploy", "agent": "deployer", "qg": "check_staging_status"}, +"deploy": {"next": "done", "agent": None, "qg": "check_deploy_status"}, +``` +⚠️ Семантика поля `agent` = «кто запускается при выходе ИЗ стадии» (см. docstring stages.py). На стадии `deploy-staging` отрабатывает deployer-агент (гоняет сьют + пишет вердикт), при выходе из неё агента нет → `deploy` имеет `agent:None` как сейчас. ВНИМАТЕЛЬНО: проверь, что переход `testing→deploy-staging` запускает нужного агента и не сломал текущую логику `get_agent_for_stage`. Обнови docstring-цепочку вверху файла. + +### 2. `src/qg/checks.py` — новый QG `check_staging_status` +- Создай функцию по ОБРАЗЦУ `check_deploy_status` (строка 407) + `_parse_deploy_status` (350) + `_deploy_log_from_main` (374). Сигнатура: `check_staging_status(repo: str, work_item_id: str, branch: str | None = None) -> tuple[bool, str]`. +- Парсит ТОЛЬКО машинное поле `staging_status:` (SUCCESS→pass, FAILED→fail, нет поля/нет frontmatter→fail) из нового лог-файла `15-staging-log.md` (читать из origin/main так же, как deploy-log — сделай аналог `_staging_log_from_main` или параметризуй существующий хелпер по имени файла, НЕ ломая deploy-версию). +- Зарегистрируй в `QG_CHECKS` (строка 444): добавь `"check_staging_status": check_staging_status,`. +- `_run_qg` (`stage_engine.py:77`): новый QG идёт по общей ветке `check_fn(repo, work_item_id, branch)` — спец-обработка НЕ нужна (как у `check_deploy_status`). Проверь, что попадает в общий путь. + +### 3. `src/usage.py` — `AGENT_ARTIFACT` (если нужно для логов/ссылок) +- Сейчас `"deployer": ("Deploy log", "14-deploy-log.md")`. Для стадии `deploy-staging` deployer пишет `15-staging-log.md`. Артефакт-маппинг сейчас по агенту, а deployer теперь на ДВУХ стадиях (deploy-staging и deploy) — реши аккуратно: либо маппинг по стадии, либо deployer на staging-стадии явно пишет `15-staging-log.md`. НЕ сломай существующую логику `14-deploy-log.md`. Если простого решения нет — опиши в отчёте и оставь deploy-log как есть, главное чтобы `check_staging_status` нашёл свой файл. + +### 4. Промпт deployer-агента (`.openclaw/agents/deployer.md` в репо) +- Найди файл (`system_prompt` указан в `launcher.py:108` как `.openclaw/agents/deployer.md`). Если он есть — добавь инструкцию: на стадии `deploy-staging` агент гоняет `python3 scripts/staging_check.py --base-url http://localhost:8501 --mode stub`, и по результату (exit 0 = все PASS) пишет в `docs/work-items//15-staging-log.md` frontmatter `staging_status: SUCCESS` (или `FAILED`), затем мержит артефакт в main (как 14-deploy-log). Если файла промпта нет в репо — опиши в отчёте, где он реально живёт, и согласуй (СТОП). + +### 5. `tests/` — обновить под новую цепочку +- `tests/test_stage_engine.py`, `tests/test_qg.py` (и любые, что упоминают `STAGE_TRANSITIONS`/`testing→deploy`/`check_deploy_status`) — обнови ожидания на новую цепочку `testing→deploy-staging→deploy→done`. Добавь тест на `check_staging_status` (SUCCESS/FAILED/missing — как для deploy). Добавь тест, что `deploy-staging` FAILED откатывает корректно (если в движке есть rollback-ветка для staging — или подтверди, что generic-rollback через `get_previous_stage` сработает: previous(`deploy-staging`)=`testing`... ⚠️ проверь, КУДА должен откатываться провал staging — на `development` как deploy, или на `testing`? Реши по логике: провал staging = код плох → откат на `development`, как у deploy-БАГ-8. Если для этого нужна ветка в stage_engine — добавь по образцу БАГ-8, НЕ ломая deploy-ветку). + +## ПРОВЕРКА (обязательный пруф в отчёт) +1. **Юнит-тесты зелёные:** `pytest tests/ -q` — ВСЁ passed (включая новые тесты staging-гейта). Покажи итог (N passed). Существующие тесты deploy НЕ red. +2. **Цепочка стадий:** показать, что `get_next_stage("testing")=="deploy-staging"`, `get_next_stage("deploy-staging")=="deploy"`, `get_next_stage("deploy")=="done"`. И `get_qg_for_stage("deploy-staging")=="check_staging_status"`. +3. **QG-логика:** `check_staging_status` на файле с `staging_status: SUCCESS`→(True,...), `FAILED`→(False,...), без поля→(False,...). Покажи прогон. +4. **Rollback staging-провала:** тест/прогон, что FAILED на `deploy-staging` откатывает задачу куда решено (development), не застревает и не идёт в `deploy`. +5. **Прод НЕ задет:** изменения только в коде (`src/stages.py`, `src/qg/checks.py`, возможно `src/usage.py`, `.openclaw/agents/deployer.md`, `tests/`). НЕ трогать .env/compose/staging_check.py/deploy-hook.sh. `git diff --name-only origin/main..ветка` — показать список. +6. `git log --oneline origin/main..origin/feature/ORCH-35-staging-gate` — коммит виден ПОСЛЕ push. + +⚠️ Это ворота-гейт, реального деплоя НЕТ — НЕ запускай ничего против боевого прода (8500), НЕ дёргай docker-рестарты. Юнит-тесты + статический прогон QG достаточно. Живой e2e staging-сьют уже покрыт ORCH-33. + +## РЕЗУЛЬТАТ +Изменения: `src/stages.py` (+стадия), `src/qg/checks.py` (+check_staging_status, +registry), при необходимости `src/usage.py` и `.openclaw/agents/deployer.md`, `tests/` (обновлены + новые). Все тесты зелёные. PR в Gitea. Отчёт → `tasks/orchestrator/reports/dev-2026-06-05-orch35-staging-gate.md`. Коммит: `feat(pipeline): add deploy-staging gate before prod deploy (ORCH-35)`.