From c326ef0ac46149a55faa6458ae05a90204ae5a57 Mon Sep 17 00:00:00 2001 From: Dev Agent Date: Fri, 22 May 2026 13:45:40 +0300 Subject: [PATCH] =?UTF-8?q?docs:=20lessons=20learned=20ET-006=20=E2=80=94?= =?UTF-8?q?=20problems=20and=20solutions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/LESSONS_ET006.md | 190 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 docs/LESSONS_ET006.md diff --git a/docs/LESSONS_ET006.md b/docs/LESSONS_ET006.md new file mode 100644 index 0000000..7718bd6 --- /dev/null +++ b/docs/LESSONS_ET006.md @@ -0,0 +1,190 @@ +# 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