Files
wiki/tasks/orchestrator/PROGRESS_2026-06-02.md
2026-06-03 00:20:01 +03:00

147 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Orchestrator — Журнал прогресса 2026-06-02
Большой день. Инцидент → root-fix → надёжная очередь. Хронология и итоги.
---
## 0. Контекст дня
Создала в Plane задачи ORCH-1..7. Plane-webhook поймал каждую и авто-запустил конвейер — но без фильтра по проекту, всё ушло в неправильный репо `enduro-trails`, наплодив мусор ET-010..016. Подход: **стоп → чистка → защита → root-fix → надёжность**.
---
## 1. ИНЦИДЕНТ: webhook авто-запуск (19:00)
### Что случилось
- Plane-webhook (id `93f0c342-a614-4248-9d0f-c107276f5620`) срабатывал на ЛЮБОЕ issue в workspace, без фильтра по проекту
- `plane.py:91` hardcode `repo=settings.default_repo` → всё лилось в `enduro-trails`
- Наплодило ветки/работу ET-010..016 в чужом репо
- ⚠️ **Позитив:** автономность реально работает (analyst exit=0, auto-commit, worktree ORCH-2 сработал) — баг был в маршрутизации, не в движке
### Меры (предохранитель)
- 🛡️ **Plane-webhook ДЕАКТИВИРОВАН** в Plane postgres: `UPDATE webhooks SET is_active=false` → проверено `is_active=f`
- 🧹 Вычищено: ветки ET-010..016 (git local+remote), worktree `_wt/enduro-trails/*` (root-owned → sudo rm), тестовые iso-A/iso-B, tasks≥19 + agent_runs в БД орка
- Plane чист: orchestrator=7, enduro=5 (родные)
- Заметка: `docs/INCIDENT_2026-06-02_webhook_autorun.txt`
### Грабли (postgres-пароль)
- `\x27`-экранирование кавычек через ssh+docker ЛОМАЕТСЯ → писать SQL в файл + `psql -f`, пароль из env контейнера
- Plane postgres: контейнер `plane-app-plane-db-1`, db/user `plane`
---
## 2. ROOT FIX = ORCH-6 (Multi-repo) — ✅ ЗАМЕРЖЕН (PR #2)
### ТЗ
`DEV_TASK_ORCH6_MULTIREPO.md` — реестр проектов (Plane id→repo+prefix) + фильтр webhook по проекту + resolve repo + plane_sync в правильный проект + prefix per project.
### Реализация (Dev, Opus 4.8)
- `src/projects.py``ProjectConfig` + резолверы `get_project_by_plane_id`/`get_project_by_repo`/`known_plane_project_ids`
- Реестр: enduro→`enduro-trails`/ET, orchestrator→`orchestrator`/ORCH, unknown→игнор
- Затронуты: config.py, plane.py, db.py (`get_next_work_item_id` per project), plane_sync.py, gitea.py
- ⚠️ Первый прогон упал по таймауту LLM на ~75% — дослала добивку (НЕ начинала заново, рабочее не теряем)
### Проверено мной вживую + замержено
- ✅ PR #2 смержен (merge commit `b021ff7`), main пересобран, health ok
- ✅ 57 passed (20 новых), 9 fail — pre-existing baseline цел
-**Plane-webhook ВКЛЮЧЁН обратно** (`is_active=t`)
- ✅ Боевой тест защиты на проде:
1. HMAC-подпись: без валидной → `401 Invalid signature`
2. Фильтр проекта: валидная подпись + unknown проект → `200 {"status":"ignored","reason":"unknown project"}`, лог `known: 2`
3. Задача НЕ создалась (11→11) — **инцидент технически невозможен**
---
## 3. ORCH-1 (F-2b) — Персистентная очередь задач — ✅ БАЗА ГОТОВА (PR #3)
### Проблема (in-process)
- `launcher.launch()` синхронно: `Popen(claude)` + 2 daemon-thread (watchdog 1800с + monitor: ждёт/коммитит/advance)
- 8 точек: `plane.py:189,234,308,389` + `gitea.py:126,203,275,300`
- Беды: рестарт = агенты-сироты + потеря работы; нет лимита параллелизма; нет ретраев; webhook блокируется
### Реализация (Dev, Opus 4.8) — ТЗ `DEV_TASK_ORCH1_QUEUE.md`
- Таблица `jobs` (queued/running/done/failed) + atomic `claim_next_job` + хелперы (enqueue/mark/count/requeue/get/status_counts/recent)
- `src/queue_worker.py` — drain-loop + `max_concurrency` + graceful stop
- `launcher.launch_job()` + `_finalize_job` (done/requeue/failed+Telegram)
- `main.py` lifespan: queue-recovery (requeue running на старте) после M-1
- Webhook-хэндлеры (8 точек) → `enqueue_job`
- `GET /queue` эндпоинт
- config: `max_concurrency=1`, `queue_poll_interval=2.0`
- Доки на сервере: `docs/ARCHITECTURE.md`, `docs/ORCH-1_JOB_QUEUE.md`
### Проверено мной вживую
- ✅ 76 passed (19 новых, вкл. атомарность claim на 8 потоках/20 jobs), 9 pre-existing fail
-`/queue` живой: counts, max_concurrency, recent
- ✅ PR #3 open, mergeable:True
- ✅ Фиксы B-1/B-2/M-1/ORCH-2/ORCH-6 не тронуты
### Метрики прогона
- Dev `orch1_queue`: done, 12m26s, 337k токенов (in 294k / out 43k)
### ⚠️ Урок: resilience в базовый PR Dev НЕ встроил
Моё уточнение (preflight/429/backoff/breaker) пришло когда Dev уже ушёл в финал → в коде его нет. **Правило:** при дослыле уточнения в активную сессию — проверять, что оно реально вошло в КОД, а не только в отчёт «done».
---
## 4. ORCH-1b (Resilience-слой) — ✅ ГОТОВ (в PR #3, проверен на проде)
### Итог (Dev `orch1b_resilience`, Opus 4.8) — проверено мной вживую
- 7 коммитов поверх базы (4ef87a3..c23f000), запушено (local==remote), PR #3 mergeable
- `preflight.py`, `error_classifier.py`, db `_ensure_column` (PRAGMA-safe), `compute_backoff`+Retry-After, `CircuitBreaker`
-**110 passed** (26 новых resilience, всего 34 в test_resilience), 9 fail — pre-existing 401
-`/queue` отдаёт `resilience.breaker` (closed) + `preflight_ok:true`
- ✅ preflight reason `2.1.142 (Claude Code)`**токены НЕ потрачены**
- ✅ классификатор: 429/overloaded→transient, traceback→permanent
- ⚠️ Dev «остановился из осторожности» (решил что параллельная сессия пишет те же файлы — mtime менялись). Параллельной сессии НЕ БЫЛО — это его собственная активность. По факту всё довёл/запушил; пересборку сделала я.
### Идея Славы (отличный вопрос про надёжность claude CLI)
Два РАЗНЫХ зверя, лечить раздельно:
**A. CLI недоступен** (бинарь/сеть) → дешёвый preflight:
- `os.path.exists(CLAUDE_BIN)` + `claude --version` (timeout 5с — токены НЕ жжёт) + опц. TCP до endpoint
- Кэш ~45с, FAIL → воркер не claim'ит job (остаётся queued), не падает
- 🚫 НИКАКОГО prompt-ping (ping→pong) перед каждым job — это трата лимита
**B. Rate limit 429** → предсказать НЕЛЬЗЯ, ловить на ВЫХОДЕ:
- Парсить log/stderr на `429`/`rate limit`/`overloaded`/`Retry-After`/`quota`
- Классификатор transient vs permanent → разные ветки ретраев
**C. Backoff** для transient:
- Колонки `available_at` + `transient_attempts` в `jobs`
- claim: `WHERE status='queued' AND (available_at IS NULL OR available_at<=now)`
- Exp backoff `min(2^n*10, 600)с` + уважать `Retry-After`
**D. Circuit breaker:**
- 3 transient подряд → open: пауза 5 мин (CLI не дёргаем, лимит не выжигаем) + Telegram-алерт
- half-open → 1 проба → ожил=closed, иначе снова open
- отражать в `/queue`
### Config (добавляется)
`preflight_cache_ttl=45`, `backoff_base_seconds=10`, `backoff_max_seconds=600`, `transient_max_attempts=5`, `breaker_threshold=3`, `breaker_pause_seconds=300`
### ⚠️ Урок: тест-алерты дёргают Славу
Dev гонял retry-тест с синтетическим `repo r-retry`/job 3 → fail-алерт долетел в Telegram, Слава испугался («Ау»). Проверка: `/queue` по нулям, в прод-БД нет `r-retry` — жил только в эфемерной pytest-БД. **TODO ORCH-1b:** подавить Telegram-нотификации в тестовых прогонах (нотификации только прод).
### Требование к миграции БД
Безопасная: PRAGMA table_info-проверка перед ALTER (prod-база `jobs` уже живая).
---
## 5. Итоги дня
| Что | Статус |
|-----|--------|
| Инцидент webhook | 🟢 локализован, вычищен |
| ORCH-6 multi-repo (root fix) | 🟢 замержен PR #2, проверен на проде |
| Plane-webhook | 🟢 включён обратно, защита боевая |
| ORCH-1 очередь | 🟢 база готова PR #3, проверена |
| ORCH-1b resilience | 🟢 готов в PR #3 (110 passed), проверен на проде |
## 6. Следующие шаги
- PR #3 (base + resilience) готов, проверен — **ждёт ОК Славы на мерж**
- TODO: подавить Telegram-нотификации в pytest-прогонах (тест-алерты дёргают Славу)
- Бэклог: ORCH-3 (S-2/S-3 rollback), ORCH-4 (M-3 stage-engine), ORCH-5 (M-7 idempotency/webhook dedup)
## 7. Висящие вопросы Славе
- PR #19 (enduro-trails) — мержить?
- PR #1 (orchestrator worktree) — мержить?
- PR #2 (ORCH-6) — ✅ уже смержен