Files
orchestrator/docs/history/LESSONS_ORCH-036-053.md

8.8 KiB
Raw Blame History

Lessons Learned — 2026-06-06 (вечер): ORCH-36 + ORCH-53 → прод (эпик ORCH-54)

Итог

Закрыты две задачи эпика ORCH-54 (автономное внедрение): ORCH-36 (исполняемый самодеплой стадии deploy) и ORCH-53 (sweeper/reconciler потерянных webhook). Обе прошли конвейер через рабочий merge-gate (ORCH-43), но финальный мерж+деплой потребовал ручного разрыва bootstrap-цикла — задача, добавляющая автодеплой, сама не может задеплоить себя через старую логику. Reconciler доказал себя в первую секунду после старта — разблокировал две реально застрявшие задачи (ORCH-036 и ET-013).

Эпик ORCH-54: 4 из 6 в проде (ORCH-40 права, ORCH-43 merge-gate, ORCH-36 деплой, ORCH-53 reconciler). Осталось: ORCH-51 (окно/HA), обкатка полностью автономного деплоя.


1. 🔴 Bootstrap-парадокс самодеплоя (ORCH-36)

Симптом

ORCH-36 застряла в петле deploy → development:

QG check_deploy_status — failed: Deploy log not found (14-deploy-log.md)
→ deployer verdict FAILED, rolled back deploy → development

deployer запускался (exit 0), но не писал 14-deploy-log.md → гейт FAILED → откат → снова deployer → бесконечный цикл (jobs 140→142→143...).

Корень

Классический bootstrap самохостинга: новая deploy-логика лежит в ветке, старая работает в проде. ORCH-36 учит deployer писать лог по результату РЕАЛЬНОГО деплоя (через хост-хук), но прод-deployer работает по СТАРОМУ промпту, который для self-репо реального деплоя не делает и SUCCESS-лог не пишет. Нет лога → FAILED → откат.

Урок

Self-репо не может задеплоить сам себя через старую логику. Нужен разовый ручной разрыв цикла: домержить + задеплоить руками ОДИН раз, дальше конвейер катит своей же новой логикой. Тот же паттерн был у ORCH-40/43. Это структурное свойство любой задачи, меняющей deploy/merge-механику самого оркестратора — закладывать ручной bootstrap-шаг в план.


2. 🔴 Merge-конфликт при последовательном ручном мерже двух задач

Симптом

PR #56 (ORCH-53) смержен первым — чисто. PR #55 (ORCH-36) сразу после → CONFLICT 409: .env.example, CHANGELOG.md, docs/architecture/README.md, docs/operations/INFRA.md, src/config.py.

Корень

После мержа PR #56 main ушёл вперёд → PR #55 валидировался против СТАРОГО main (точки ответвления), а мержится в НОВЫЙ. Это ровно класс «main ушёл вперёд», который чинит merge-gate (ORCH-43) — но при РУЧНОМ мерже через Gitea API merge-gate не участвует.

Решение

  • merge main→ветку, НЕ rebase. Rebase 9 коммитов = 9 потенциальных конфликт-разборов; один merge-коммит = ОДИН разбор. Быстрее и безопаснее для большого набора коммитов.
  • Конфликт в src/config.py был чисто аддитивный: ветка ORCH-36 добавляла блок self_deploy_* настроек, main (ORCH-53) — блок reconcile_*. Нужны ОБА блока → склеить, убрав только git-маркеры (<<<<<<</=======/>>>>>>>). Обязательно после — python3 -c 'import ast; ast.parse(...)' для проверки синтаксиса.
  • docs/.env/CHANGELOG конфликты — тоже аддитивные (обе стороны добавляют строки) → union.

Грабли

⚠️ grep -rE '^(<<<<<<<|=======|>>>>>>>)' по docs/work-items/*/13-test-report.md даёт ЛОЖНЫЕ срабатывания — там ======= это markdown-разделители таблиц/секций, не git-конфликты. Проверять реальные конфликтные файлы поимённо, не доверять глобальному grep.


3. Review-гейт поймал 2 реальных P1 ДО прода (ORCH-36)

reviewer завернул первую версию (verdict: REQUEST_CHANGES), конвейер сам откатил dev→review→fix→APPROVED. Два P1:

  1. sentinel-маркеры self-deploy (initiated/result/approve-requested) не чистились на rollback → при возврате задачи человек ставит Approved, а устаревший маркер ломает фазу B.
  2. нет .env.example для новых флагов + процедуры «approve→деплой» в INFRA.md.

Урок: merge-gate + review отрабатывают как задумано — брак не уходит в прод автономно. Это и есть ценность эпика: система фильтрует сама.


4. 🔥 Reconciler доказал себя мгновенно (ORCH-53)

В первую секунду после рестарта прода (21:24 UTC):

reconciler: ORCH-036 development разблокирована (потерян webhook)
reconciler: ET-013 development разблокирована (потерян webhook)

Sweeper нашёл и разблокировал ДВЕ реально застрявшие задачи — включая саму ORCH-036 из bootstrap-петли, и старое зависание ET-013 (enduro-trails). Ручной heartbeat-watchdog, который раньше держал Стрим, больше не нужен — система чинит застревания сама.


5. Операционные мелочи (закрепить)

  • Заголовки ORCH-задач ≤80 символов. QG-0 (check title length) заворачивает старт конвейера, если длиннее. ORCH-53 был 83 символа → завернул на старте → подрезали до 71.
  • Developer-таймаут 1800с (30 мин) мал для мясных задач. 1-й заход developer'а ORCH-36 (деплой-хук + Telegram-кнопка + callback) упёрся в лимит → SIGKILL (exit -9). Спас resilience-ретрай (ORCH-1b): attempt 2, наработки в worktree между попытками сохранились. Если упирается систематически — поднять agent_timeout_seconds (override per-agent) или дробить задачу.
  • Время хоста ≠ UTC. Файлы worktree датируются по мск (+3), БД/системное — UTC. Не баг, но путает сверки etime/updated_at/finished_at. Сверять по одному источнику.
  • Gitea merge auth: заголовок строго Authorization: token <ORCH_GITEA_TOKEN> (формат token , буквально). НЕ маскировать токен плейсхолдером *** → иначе 401. POST /repos/admin/orchestrator/pulls/{N}/merge, body {"Do":"merge"}.
  • approve прод-деплоя 8500 = Telegram-кнопка (решение Owner), флаг DEPLOY_REQUIRE_MANUAL_APPROVE=true по дефолту.
  • max_concurrency=1 оставлен сознательно (решение Owner): одна БД/очередь на все проекты, последовательное выполнение надёжнее. НЕ поднимать без явного запроса.

Состояние прода после деплоя (21:24 UTC, main 1ff8d85)

  • src/self_deploy.py — в проде (исполняемый деплой, 3 фазы A/B/C)
  • src/reconciler.py — в проде (фоновый sweeper, уже разблокировал 2 задачи)
  • uid 1000, health {"status":"ok"}, preflight True (Claude Code 2.1.142)
  • Деплой-скрипт с авто-rollback: исходник в workspace temp/deploy_36_53.sh