docs(history): LESSONS ORCH-036+053 — bootstrap-деплой, merge-конфликт, reconciler в проде
This commit is contained in:
120
docs/history/LESSONS_ORCH-036-053.md
Normal file
120
docs/history/LESSONS_ORCH-036-053.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# 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`
|
||||
Reference in New Issue
Block a user