This commit was merged in pull request #42.
This commit is contained in:
128
docs/history/LESSONS_2026-06-05.md
Normal file
128
docs/history/LESSONS_2026-06-05.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Lessons Learned — 2026-06-05 (вечер): ORCH-17/45/47 + деплой прода
|
||||
|
||||
## Итог дня
|
||||
Закрыты три задачи (ORCH-17, ORCH-45, ORCH-47), два прод-гейта стали умнее, заведено
|
||||
4 системных задачи в бэклог (ORCH-44/46/48 + B6). Главный сквозной урок: **конвейер не мог
|
||||
провести эти задачи автономно из-за дыр в самом конвейере** — потребовались ручные merge и
|
||||
ребилды прода. Корни задокументированы, чинятся отдельными задачами.
|
||||
|
||||
---
|
||||
|
||||
## 1. ORCH-17 — approve-ping links (закрыта вручную)
|
||||
Подробный разбор: `docs/history/LESSONS_ORCH-017.md`. Кратко: косметика (2 ссылки)
|
||||
застряла 5 раз, объективный дедлок shared-гейта, ручной merge PR #37 (`26c6f267`).
|
||||
|
||||
---
|
||||
|
||||
## 2. ORCH-45 — CI-гонка в `check_ci_green` (исправлена, в проде)
|
||||
|
||||
### Проблема
|
||||
`check_ci_green` делал **один** запрос статуса CI сразу после developer. Если CI ещё
|
||||
`pending` 1-3 секунды (реальный кейс: опрос 17:58:54 → pending, CI позеленел 17:58:55) —
|
||||
гейт возвращал False **один раз** и задача застревала насмерть с зелёным CI.
|
||||
|
||||
### Решение (PR #39, merge `982698c4`)
|
||||
Поллинг с ретраем: `success`/`failure` — терминальны (сразу), `pending` → ждать
|
||||
`CI_POLL_INTERVAL_S`(10с) до `CI_POLL_MAX_ATTEMPTS`(12) раз, истёк лимит → явный
|
||||
`False` с причиной "CI still pending after Ns" (не виснет молча). Параметры в `config.py`
|
||||
как env `ORCH_CI_POLL_*`. ADR-0004. +5 тестов (мок httpx + time.sleep).
|
||||
|
||||
---
|
||||
|
||||
## 3. ORCH-47 — тестер-гейт игнорил `result:` (исправлен, в проде)
|
||||
|
||||
### Проблема (уловка-22)
|
||||
`check_tests_passed`/`_parse_tests_verdict` читал только `verdict:`/`status:` из frontmatter
|
||||
`13-test-report.md`, но промпт tester-агента велит писать `result: PASS|FAIL`. Честный тестер
|
||||
(`result: PASS`, без `verdict:`) → гейт «No machine-readable verdict» → ложный FAIL → петля
|
||||
dev↔review↔tester → Blocked. **И сама ORCH-47 (которая это чинит) попала в тот же капкан:**
|
||||
в проде крутился старый гейт → не понимал её собственный `result: PASS` → 3 круга петли.
|
||||
Змея кусает хвост: чтобы пройти гейт автономно, фикс уже должен быть в проде.
|
||||
|
||||
### Решение (PR #40, merge `5d04de9e`)
|
||||
`result:` добавлен как равноправное поле наряду с `verdict:`/`status:`. Любое одно непустое
|
||||
поле достаточно. Negative-токен (BLOCKED/FAILED) в ЛЮБОМ поле авторитетен (ET-013 кейс
|
||||
сохранён). Token sets заморожены для обратной совместимости. ADR-001. +6 тестов (68 passed).
|
||||
После деплоя ручной `advance_stage` пнул застрявшую task → гейт принял `result: PASS` →
|
||||
прошёл testing. Петля исчезла навсегда.
|
||||
|
||||
### Остаточная находка → B6 / ORCH-48
|
||||
На staging деплоер дал 9/10 PASS, завалил **B6 Registry isolation**: staging-реестр видит
|
||||
боевые ET+ORCH вместо одного sandbox (нарушает «staging — только sandbox»). Деплоер честно
|
||||
поставил FAILED и НЕ стал натягивать зелёнку (вне мандата) → откат by design. К фиксу гейта
|
||||
отношения не имеет (E2E против sandbox прошёл). Заведена ORCH-48.
|
||||
|
||||
---
|
||||
|
||||
## 4. ДЕПЛОЙ ПРОДА — как правильно (важная операционная памятка)
|
||||
|
||||
### `/app` запечён в образ, НЕ volume
|
||||
`docker-compose.yml`: `build: .` + `COPY src/ ./src/`. Поэтому `git pull` + рестарт с
|
||||
`--no-build` **НЕ довозит код** — нужен `docker compose build orchestrator`. Деплой-хук
|
||||
(`scripts/orchestrator-deploy-hook.sh`) по дефолту целит в **staging** (by design) — для
|
||||
прода нужны env `TARGET_SERVICE=orchestrator TARGET_PORT=8500 COMPOSE_PROFILE=''`.
|
||||
|
||||
### Порты/профили
|
||||
- prod orchestrator = порт **8500** (`/health` → `{"status":"ok"}`), `network_mode: host`,
|
||||
профиль prod = пустой (стартует обычным `docker compose up -d orchestrator`).
|
||||
- staging = порт **8501**, профиль `staging` (стартует только `--profile staging`).
|
||||
|
||||
### Рабочая последовательность деплоя (проверена дважды 05.06)
|
||||
1. `sudo chown -R slin:slin /home/slin/repos/orchestrator` (см. грабля ниже).
|
||||
2. `git checkout main && git reset --hard origin/main && git clean -fd -e '*.bak*' -e '.deploy-prev-image-prod'`.
|
||||
3. `docker compose build orchestrator`.
|
||||
4. `docker compose up -d orchestrator` + health-loop на :8500.
|
||||
5. **Проверка claude-auth** (ребилд её ломает — см. ниже).
|
||||
6. Проверка что новый код активен в `/app` (grep маркера правки).
|
||||
|
||||
### ⚠️ ГРАБЛЯ: хост-репо рассинхронизирован с git (агенты пишут под root)
|
||||
Хост-репо `/home/slin/repos/orchestrator` оказывался на feature-ветке (не main), а рабочая
|
||||
копия засеяна untracked+modified файлами, созданными агентами **под uid=0 (root-owned)** прямо
|
||||
в репо. → `git pull --ff-only` падал `Permission denied` / `would be overwritten`, обычный
|
||||
`rm` под slin не мог снести root-файлы. **Лечение:** `sudo chown -R slin:slin <repo>` →
|
||||
проверить что modified=совпадает-с-main и untracked=уже-в-main (дубликаты, не теряем) →
|
||||
`git reset --hard origin/main` + `git clean`. **Хук это НЕ разруливает** — сверять состояние
|
||||
хост-репо перед каждым деплоем.
|
||||
|
||||
### ⚠️ ГРАБЛЯ: ребилд ломает claude-auth (проверять ВСЕГДА)
|
||||
Пересоздание контейнера может root-овнить `/home/slin/.claude/.credentials.json` и сделать
|
||||
`/root/.claude` пустышкой → агенты падают `Not logged in`. Защита — монтирование creds в
|
||||
compose (`/home/slin/.claude` + `.claude.json`), launcher форсит `HOME=/home/slin`.
|
||||
**После каждого ребилда боевая проверка:**
|
||||
`docker exec orchestrator bash -c 'cd /tmp && HOME=/home/slin /opt/claude-code/bin/claude.exe --print "ОК"'`
|
||||
(timeout 90с). 05.06 auth пережил оба ребилда — защита держит.
|
||||
|
||||
---
|
||||
|
||||
## 5. ЗАПУСК конвейера и Gitea API
|
||||
|
||||
### Старт конвейера = Plane Backlog → In Progress
|
||||
Конвейер стартует штатно переводом задачи в Plane из Backlog в **In Progress** (код:
|
||||
`webhooks/plane.py handle_status_start` — «pipeline is started when Slava moves the issue
|
||||
into In Progress»). Webhook создаёт task-row, заводит ветку, запускает analyst. Никаких
|
||||
ручных вставок в БД.
|
||||
|
||||
### QG-0: лимит заголовка 80 символов
|
||||
При старте задача с заголовком >80 символов заворачивается на QG-0 («Title слишком длинный»)
|
||||
и уходит в Blocked. Чинить — укоротить `name` (суть в заголовок, детали в description),
|
||||
вернуть в Backlog, снова In Progress.
|
||||
|
||||
### Gitea API грабли
|
||||
- **merge/create PR** требуют заголовок `Authorization: token <ORCH_GITEA_TOKEN>` (форма
|
||||
с префиксом `token `), иначе 401 "token is required".
|
||||
- **heredoc через ssh+docker exec глотает вывод** python-скрипта. Надёжный путь: написать
|
||||
`.py` локально → `base64 -w0` → `ssh "echo <b64> | base64 -d > /tmp/x.py"` → `docker cp`
|
||||
→ `docker exec python3 /tmp/x.py`. Это же обходит экранирование кириллицы/скобок.
|
||||
|
||||
---
|
||||
|
||||
## Состояние прод-гейтов после 05.06
|
||||
- ✅ `check_ci_green` — поллинг с ретраем (ORCH-45)
|
||||
- ✅ `check_tests_passed` — читает `result:`/`verdict:`/`status:` (ORCH-47)
|
||||
|
||||
## Бэклог (high) после дня
|
||||
- **ORCH-44** — надёжность запуска агента (preflight слеп к auth; `--effort` гасит вывод;
|
||||
пустой run-лог → должен быть failed).
|
||||
- **ORCH-46** — «испорченный телефон»: орк не передаёт деву ТЕКСТ замечаний reviewer/tester
|
||||
(только ссылку на файл), противоречивые сигналы tester↔reviewer, нет памяти между кругами.
|
||||
- **ORCH-48 / B6** — staging registry isolation (staging видит прод-проекты вместо sandbox).
|
||||
Reference in New Issue
Block a user