Files
wiki/tasks/orchestrator/DEV_TASK_PIPELINE_BUGS.md
2026-06-03 21:10:01 +03:00

8.3 KiB
Raw Permalink Blame History

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):

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/<id>/ 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, вывод релевантных тестов. Если что-то разошлось с кодом/реальностью — отчитайся, не выдумывай. Один исполнитель.