From 6c04e6b9f3236a52477bfad7e80e3cd28f6518b2 Mon Sep 17 00:00:00 2001 From: Stream Date: Wed, 3 Jun 2026 21:10:01 +0300 Subject: [PATCH] auto-sync: 2026-06-03 21:10:01 --- memory/2026-06-03.md | 20 ++++++ tasks/orchestrator/DEV_TASK_PIPELINE_BUGS.md | 65 ++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 tasks/orchestrator/DEV_TASK_PIPELINE_BUGS.md diff --git a/memory/2026-06-03.md b/memory/2026-06-03.md index 87c433d..2625c62 100644 --- a/memory/2026-06-03.md +++ b/memory/2026-06-03.md @@ -75,3 +75,23 @@ approved=a519a341-dada-4a91-8910-7604f82b79c5 rejected=ba958f3c-5db5-461d- - ✅ Проверено: 0 задач в оркестраторе, конвейер не стартанул. Бэклог работает в бою. - Суть фичи (запрос Славы): тап по треку на карте → в popup с инфо кнопка «Скачать» → отдать файл трека (GPX обяз., KML опц.). Источник: пользовательские треки на карте (enduro-trails). Нужен бэкенд-эндпоинт отдачи трека по id + Content-Disposition. - **Создание issue через Plane REST:** токен `ORCH_PLANE_API_TOKEN` (len 60, общий orchestrator-токен), `POST http://localhost:8091/api/v1/workspaces/ag_proj/projects//issues/`, заголовок `X-API-Key`, тело `{name, state, description_html}`. Backlog UUID = `113b24f6-cce8-4be9-9a22-a359b9cf0122`. + +--- + +## Боевой запуск #6 (ET-006) — баги вскрыты, чистка сделана (03.06 ~18:00) + +### Что РАБОТАЕТ (доказано в бою): +- ✅ Триггер по статусу In Progress (после фикса webhook URL) +- ✅ Токены считаются: run 59 analyst → 27 in / 36851 out / $2.13 записаны в agent_runs +- ✅ Per-agent authorship: комменты под именем analyst +- ✅ Webhook URL фикс: `http://172.21.0.1:8500/webhook/plane` (Plane не резолвит DuckDNS — docker-сеть изолирована; старый внешний URL давал 500). UPDATE webhooks SET url в БД Plane сделан. + +### БАГИ (для Dev): +1. **issue.updated без description → QG-0 падает в Blocked.** Plane на смену статуса шлёт только изменённые поля, `description`/`description_stripped` пустые. `start_pipeline` читает описание из webhook payload → QG-0 «Description < 20 символов» → задача в Blocked. ФИКС: тянуть описание через GET Plane API (issue detail) в start_pipeline, не из payload. +2. **Коллизия work_item_id + рассинхрон веток.** M-6 выдал `ET-006` ПОВТОРНО: task 8 (plane 9884fb9c, 22 мая, gpx-upload, done) и task 25 (plane bfb4866c, сегодня, popup-enduro-trails). Worktree резолвится по work_item_id → analyst run 59 писал в ЧУЖОЙ worktree (gpx-upload task 8), а не в свой (popup task 25). Гейт check_analysis_complete искал артефакты в branch task 25 → не нашёл → не перевёл в In Review. ФИКС: get_next_work_item_id должен гарантировать уникальность (не переиспользовать seq); worktree/ветка должны привязываться к task_id, не к work_item_id. + +### Чистка ET-006 (сделана, бэкап БД orchestrator.db.bak-clean-*): +- Удалён дубль task 25 + его runs из БД. +- Удалён мусорный worktree feature_ET-006-gpx-upload. +- Откачен мусорный коммит 6edf97f (analyst run 59 в чужую ветку) → force-push origin/feature/ET-006-gpx-upload на d379e48. +- Тикет #6 (bfb4866c, seq 6) → Backlog, описание на месте. Готов к повторному чистому запуску ПОСЛЕ фикса бага 1. diff --git a/tasks/orchestrator/DEV_TASK_PIPELINE_BUGS.md b/tasks/orchestrator/DEV_TASK_PIPELINE_BUGS.md new file mode 100644 index 0000000..a123b9c --- /dev/null +++ b/tasks/orchestrator/DEV_TASK_PIPELINE_BUGS.md @@ -0,0 +1,65 @@ +# DEV TASK — 2 бага конвейера, вскрытые боевым запуском ET-006 + +**Проект:** orchestrator | **Сервер:** slin@82.22.50.71 | **Репо:** /home/slin/repos/orchestrator | **Контейнер:** orchestrator (8500) +**Ветка:** `fix/pipeline-start-bugs` из свежего main (`git checkout main && git pull && git checkout -b fix/pipeline-start-bugs`). + +⚠️ **ГРАБЛЯ push (ORCH-7):** после push `git log origin/main..origin/fix/pipeline-start-bugs` ДОЛЖЕН показать коммиты ДО отчёта «PR готов». +ℹ️ Токен Gitea для PR: `docker exec orchestrator printenv ORCH_GITEA_TOKEN`. + +## Контекст +Первый боевой запуск задачи по новой статусной модели (триггер In Progress) вскрыл 2 бага. Триггер, токены, авторство — работают. Падает старт конвейера. + +--- + +## БАГ 1 — `issue.updated` без description → QG-0 валит задачу в Blocked + +**Симптом:** перевод задачи в In Progress → `start_pipeline` → QG-0 «Description слишком короткий (нужно >= 20 символов)» → задача в Blocked, хотя описание в Plane ЕСТЬ. + +**Причина (`src/webhooks/plane.py`, ~строка 250 `start_pipeline`):** +```python +description = data.get("description_stripped", data.get("description", "")) +``` +Plane на webhook `issue.updated` (смена статуса) шлёт payload только с ИЗМЕНЁННЫМИ полями — `description`/`description_stripped` пустые/отсутствуют. На `work_item.created` они были, на `updated` — нет. → QG-0 видит пустое описание → fail → `set_issue_blocked`. + +**Фикс:** в `start_pipeline`, если `description` из payload пустой/короткий — дотянуть полное описание из Plane через GET API issue detail (тот же токен/паттерн, что уже используется для PATCH/комментов — см. `src/plane_sync.py`, там есть GET issue). Брать `description_stripped` (или стрипать `description_html`). НЕ создавать свой токен, использовать существующий механизм plane_sync. +- ⚠️ GET issue в plane_sync, скорее всего, уже есть (handle_status_start логировал `GET .../issues// 200`). Используй его, верни description. +- Если и из API пусто — тогда честный QG-0 fail (это валидный кейс пустого тикета). + +## БАГ 2 — коллизия work_item_id + рассинхрон ветки/worktree + +**Симптом:** `ET-006` выдан ПОВТОРНО двум разным тикетам. task 8 (plane `9884fb9c`, 22 мая, branch `feature/ET-006-gpx-upload`, done) и task 25 (plane `bfb4866c`, сегодня, branch `feature/ET-006-popup-enduro-trails`). Analyst run 59 (task 25) писал артефакты в worktree task 8 (`feature_ET-006-gpx-upload`), потому что worktree/путь резолвится по `work_item_id`, а он совпал. Гейт `check_analysis_complete` искал артефакты в ветке task 25 → не нашёл → не перевёл в In Review → задача зависла. + +**Две связанные проблемы:** + +**2a. `get_next_work_item_id` переиспользует seq.** (см. M-6 — derive work_item_id from Plane sequence_id). Plane sequence_id может совпасть для разных проектов/после удаления, ИЛИ маппинг не учитывает уже выданные id. Нужно гарантировать **уникальность work_item_id в рамках репозитория**: если `ET-006` уже есть в БД tasks (любой stage) → не выдавать его снова, взять следующий свободный (ET-007, ET-008...). Глянь текущую логику `get_next_work_item_id` / M-6 derivation и добавь проверку коллизии по таблице tasks. +- ⚠️ M-6 в списке «не ломать» как ЛОГИКА derive, но проверка-на-дубль — это надстройка, не замена. Согласуй: добавь uniqueness-guard ПОВЕРХ M-6, не переписывая derive. + +**2b. worktree/путь привязан к work_item_id, а не к task_id.** Из-за этого два таска с одним work_item_id делят один worktree. Привяжи worktree/ветку к уникальному ключу (task_id или plane_id-производный slug), чтобы коллизия work_item_id физически не могла увести агента в чужой worktree. Глянь `ensure_worktree` / `git_worktree.py` — как формируется путь. +- Если фикс 2a гарантирует уникальность work_item_id — 2b становится защитой в глубину. Сделай ОБА (2a как первичный фикс, 2b как страховка), но если 2b затрагивает много кода — минимум добавь assert/лог при коллизии worktree. + +--- + +## Ограничения +- 🚫 НЕ трогай: nginx, openclaw.json, .env-секреты, deploy-хук, HMAC/verify_plane_signature, очередь (queue_worker/jobs — кроме чтения), webhook URL в БД Plane (уже починен ассистентом на `http://172.21.0.1:8500/webhook/plane`), STAGE_TRANSITIONS логику, per-agent authorship, GET/PATCH общий токен. +- M-6 derive — не переписывать, добавить uniqueness-guard поверх. +- Baseline тестов: см. прошлый прогон (190 passed + 9 pre-existing). Не ломать, conftest-глушилку Telegram (`tests/conftest.py`) НЕ трогать. +- Conventional Commits, отдельные коммиты: `fix(webhook) fetch description from Plane API on status-start`, `fix(work-item) prevent work_item_id collision`, (опц.) `fix(worktree) bind path to task_id`. + +## Тесты (контейнер) +`IMG=$(docker inspect orchestrator --format '{{.Config.Image}}'); docker run --rm -v /home/slin/repos/orchestrator:/code -w /code --entrypoint python3 $IMG -m pytest tests/ -q` +- `test_status_start_fetches_description`: issue.updated с пустым description в payload → start_pipeline тянет описание из Plane API (мок GET) → QG-0 проходит. Мокать httpx. +- `test_work_item_id_uniqueness`: если ET-006 уже в tasks → следующий тикет получает ET-007, не ET-006. +- (если делаешь 2b) `test_worktree_per_task`: два таска с искусственно одинаковым work_item_id не делят worktree. + +## Проверка (ассистент проверит вживую) +| # | Что | Критерий | +|---|-----|----------| +| 1 | description из API | перевод в In Progress тикета с описанием → НЕ Blocked, стартует analyst | +| 2 | уникальность id | новый тикет не получает уже занятый ET-NNN | +| 3 | worktree | агент пишет в свою ветку, гейт находит артефакты, переход в In Review | +| 4 | тесты | baseline не сломан + новые passed | +| 5 | git | PR в main, remote содержит коммиты | + +## Отчёт +- НЕ деплоить, НЕ мержить (мерж — ассистент после живой проверки + повторного боевого запуска #6). +- В отчёте: где именно правил (файл:функция), как тянешь description (какой GET-метод plane_sync переиспользовал), как гарантируешь уникальность work_item_id, вывод релевантных тестов. Если что-то разошлось с кодом/реальностью — отчитайся, не выдумывай. Один исполнитель.