Files
orchestrator/docs/LESSONS_ET006.md

191 lines
8.6 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.
# Lessons Learned — ET-006 (GPX Upload & Visualization)
## Дата: 2026-05-22
## Задача: ET-006 — Загрузка и визуализация GPX-треков
---
## Что сработало хорошо
### 1. Review bounce — реальный баг найден и исправлен автоматически
Reviewer обнаружил P1: `Math.min.apply(null, array)` падает с RangeError на массивах >100K элементов.
Developer пофиксил за 6 минут (attempt 2), второй review прошёл чисто.
**Вывод:** reviewer в пайплайне оправдывает себя — ловит баги которые unit-тесты пропускают.
### 2. Auto-advance testing → deploy
Новый `_try_advance_stage()` в launcher сработал без ручного вмешательства.
### 3. Качество артефактов агентов
- Analyst предусмотрел REQ-F-13 (persist GPX layers при map style switch) — предотвратил архитектурный bounce-back
- Architect обосновал невозможность Web Worker (DOMParser отсутствует в WorkerGlobalScope)
- Developer: ~1300 строк production + 700 строк тестов, все REQ покрыты
- Tester: полный e2e с Playwright, 48 pass / 0 fail
### 4. Полный цикл с bounce
```
analysis → architecture → development → review (REQUEST_CHANGES)
→ development (fix P1) → review (APPROVED) → testing → deploy → done
```
Время: ~6.5 часов (включая ожидание API и e2e тесты)
---
## Проблемы найденные
### P1. Zombie processes после docker rebuild
**Симптом:** Monitor threads умирают при `docker compose up --build`, agent процессы остаются zombie.
**Влияние:** Ручное вмешательство для commit/push и advance stage.
**Root cause:** Daemon threads в Python не переживают restart контейнера, но child processes (claude.exe) наследуются init (PID 1).
### P2. Stale reviews блокируют merge
**Симптом:** Tester пушит коммит после review approval → approval становится stale → merge отклоняется.
**Влияние:** Ручной re-approve перед каждым merge.
**Root cause:** Branch protection `dismiss_stale_approvals: true`.
### P3. Plane sync 404
**Симптом:** `plane_issue_id` в orchestrator DB не совпадает с реальным UUID issue в Plane API.
**Влияние:** State updates в Plane не работают (comments работают).
**Root cause:** Webhook payload содержит ID объекта webhook event, не issue ID.
### P4. Неполный event routing
**Симптом:** `pull_request_rejected` event type не роутился в `handle_pr`.
**Влияние:** REQUEST_CHANGES от reviewer не откатывал задачу автоматически.
**Root cause:** Gitea использует разные event types: `pull_request`, `pull_request_approved`, `pull_request_rejected`.
### P5. Analyst не запускался автоматически
**Симптом:** После создания задачи через Plane webhook analyst не стартовал.
**Влияние:** Ручной запуск analyst.
**Root cause:** В `handle_work_item_created` не было вызова `launcher.launch("analyst")`.
### P6. Tester долгий (25 мин)
**Симптом:** Playwright e2e тесты с headless Chromium на GPX-фиче заняли 25 минут.
**Влияние:** Долгое ожидание, watchdog timeout (30 мин) почти сработал.
**Root cause:** Рендеринг 700K точек + установка зависимостей (Playwright, shapely) в runtime.
---
## Решения
### P1. Zombie processes → Entrypoint + orphan recovery
**Решение A (быстрое):** Добавить в Dockerfile:
```dockerfile
RUN git config --global --add safe.directory '*'
```
**Решение B (полное):** Startup recovery в `main.py`:
```python
@app.on_event("startup")
async def recover_orphaned_runs():
"""Mark orphaned runs (started but never finished) as failed."""
conn = get_db()
orphans = conn.execute(
"UPDATE agent_runs SET finished_at=datetime('now'), exit_code=-1 "
"WHERE finished_at IS NULL AND started_at < datetime('now', '-35 minutes')"
).rowcount
conn.commit()
if orphans:
logger.warning(f"Recovered {orphans} orphaned agent runs")
# Re-check tasks stuck in intermediate stages
stuck = conn.execute(
"SELECT id, stage, work_item_id, repo, branch FROM tasks "
"WHERE stage NOT IN ('done', 'created')"
).fetchall()
for task in stuck:
# Try to advance if QG passes
...
```
**Решение C (robust):** Использовать `tini` как PID 1 в контейнере для proper zombie reaping:
```dockerfile
RUN apt-get install -y tini
ENTRYPOINT ["tini", "--"]
CMD ["uvicorn", "src.main:app", ...]
```
### P2. Stale reviews → Отключить dismiss или auto-re-approve
**Решение A (простое):** Отключить `dismiss_stale_approvals`:
```bash
curl -X PATCH '.../branch_protections/main' -d '{"dismiss_stale_approvals": false}'
```
**Решение B (лучше):** Auto-re-approve в launcher после tester push:
```python
# В _monitor_agent, после успешного push для tester:
if agent == "tester":
_reapprove_pr(repo, branch)
```
**Рекомендация:** Решение A — проще и безопаснее. В нашем пайплайне reviewer уже проверяет код, stale dismiss не добавляет ценности.
### P3. Plane sync → Исправить маппинг ID
**Решение:** При `work_item.created` webhook сохранять правильный `issue_id`:
```python
# В handle_work_item_created:
plane_issue_id = data.get("id") # Это ID issue, не event
# Проверить через Plane API: GET /issues/{id} — если 404, искать по name
```
**Диагностика:** Сравнить `plane_issue_id` в DB с реальным через:
```bash
curl http://localhost:8091/api/v1/workspaces/ag_proj/projects/.../issues/?search=ET-006
```
### P4. Event routing → Wildcard для pull_request_*
**Решение:**
```python
if event_type == "push":
await handle_push(payload)
elif event_type.startswith("pull_request"):
await handle_pr(payload)
elif event_type == "status":
await handle_ci_status(payload)
```
### P5. Analyst auto-launch → Уже исправлено
Патч применён: `launcher.launch("analyst")` добавлен в `handle_work_item_created`.
### P6. Tester долгий → Pre-bake dependencies
**Решение A:** Добавить Playwright и зависимости в Dockerfile:
```dockerfile
RUN pip install playwright pytest-playwright shapely mapbox-vector-tile && \
playwright install chromium --with-deps
```
**Решение B:** Разделить unit/integration и e2e тесты. Unit/integration — обязательные (быстрые), e2e — опциональные (по флагу в task description).
**Решение C:** Увеличить timeout для tester до 45 минут:
```python
AGENT_CONFIGS = {
"tester": {..., "timeout": 2700}, # 45 min
}
```
---
## Приоритет исправлений
| # | Проблема | Приоритет | Усилие | Решение |
|---|----------|-----------|--------|---------|
| P1 | Zombie processes | HIGH | Medium | tini + startup recovery |
| P2 | Stale reviews | HIGH | Low | Отключить dismiss_stale_approvals |
| P4 | Event routing | HIGH | Low | startswith("pull_request") |
| P5 | Analyst auto-launch | DONE | — | Уже исправлено |
| P6 | Tester timeout | MEDIUM | Medium | Pre-bake deps в Dockerfile |
| P3 | Plane sync 404 | LOW | Medium | Исправить маппинг ID |
---
## Метрики ET-006
- **Общее время:** ~6.5 часов (00:20 → 06:45 UTC)
- **Agent runs:** 7 (analyst, architect, developer×2, reviewer×2, tester)
- **Ручные вмешательства:** 4 (zombie recovery×2, PR approve, event re-trigger)
- **Код написан агентами:** ~2000 строк (1300 production + 700 tests)
- **Баги найдены reviewer:** 1×P1, 3×P2, 6×P3
- **Баги исправлены developer:** все P1 + все P2 + 3×P3