Files
orchestrator/docs/BUGFIXES_2026-06-03.md
Dev Agent ca81f38330 docs: document multi-repo registry + ORCH-6 bugfix and incident
ORCH-6: ARCHITECTURE.md gets a project-registry section; README explains
how to add a project via ORCH_PROJECTS_JSON; BUGFIXES_2026-06-03.md
records the fix and links the 2026-06-02 webhook autorun incident.
2026-06-02 22:30:51 +03:00

5.4 KiB
Raw Permalink Blame History

BUGFIXES / CHANGES — 2026-06-03

ORCH-6 — Multi-repo: фильтр проекта + маппинг repo per project

Тип: root-fix инцидента + новая возможность (multi-repo) Ветка: feature/ORCH-6-multirepo Plane: ORCH-6 (project 8da6aa25-a60e-44d6-a1e2-d8ae59aa7d6a) Связанный инцидент: INCIDENT_2026-06-02_webhook_autorun.txt

Контекст инцидента

При создании задач ORCH-1..7 в Plane (проект orchestrator) Plane-webhook (id 93f0c342-a614-4248-9d0f-c107276f5620) сработал на каждую задачу и запустил конвейер — но всё ушло в репо enduro-trails, потому что plane.py:91 хардкодил repo = settings.default_repo. Webhook слушал весь workspace без фильтра по проекту, наплодив мусорные ET-010..016.

Митигация на время фикса: Plane-webhook деактивирован (is_active=false).

Root cause

  1. Нет фильтра по Plane-проекту — любая issue из любого проекта попадала в конвейер.
  2. repo хардкожен на единственный default_repo (enduro-trails).
  3. work_item_prefix всегда ET (db.py).
  4. plane_sync ходил в единственный хардкоженный PROJECT_ID (enduro).

Что сделано

Файл Изменение
src/projects.py (новый) Реестр проектов: ProjectConfig + дефолт-список (enduro-trails + orchestrator) + резолверы get_project_by_plane_id / get_project_by_repo / known_plane_project_ids. Источник переопределения — ORCH_PROJECTS_JSON; устойчивый парсинг (битый JSON / битые записи → fallback на дефолт).
src/config.py Добавлен projects_json: str = "" (env ORCH_PROJECTS_JSON).
src/webhooks/plane.py Фильтр по проекту: data.project не в реестре → {"status":"ignored","reason":"unknown project"}. Резолв repo/prefix/Plane-проекта из реестра. Plane-sync для задачи идёт в её собственный проект.
src/db.py get_next_work_item_id(repo, prefix="ET") — нумерация per (repo, prefix); ORCH-001 независимо от ET-010. Дефолт ET сохранён для обратной совместимости.
src/plane_sync.py _resolve_project_id + параметризация project_id (дефолт на enduro → обратная совместимость существующих вызовов).
src/webhooks/gitea.py Неизвестный repo (get_project_by_repo → None) → ignored в 3 хэндлерах.

Тесты

  • tests/test_projects.py (16 тестов): резолверы (by plane_id, by repo, unknown→None, known_plane_project_ids), парсинг ORCH_PROJECTS_JSON (валидный / битый JSON / не массив / битые записи → skip / all-bad → fallback), reload с кастомным JSON.
  • tests/test_plane_webhook.py (4 теста, FastAPI TestClient, launcher.launch замокан): unknown project → ignored + нет task/branch/agent; orchestrator-проект → repo=orchestrator, ORCH-*; enduro-проект → repo=enduro-trails, ET-*; независимые префиксы (ORCH-001/ORCH-002 параллельно с ET-001).

Прогон (в контейнере, образ orchestrator-orchestrator): 57 passed. 9 падений в tests/test_webhooks.pypre-existing (webhook signature 401 / TypeError, не связаны с ORCH-6, не трогались).

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

Проверка резолва (offline, в работающем контейнере)

docker exec orchestrator python3 -c "
from src.projects import get_project_by_plane_id, known_plane_project_ids
o = get_project_by_plane_id('8da6aa25-a60e-44d6-a1e2-d8ae59aa7d6a')
e = get_project_by_plane_id('7a79f0a9-5278-49cd-9007-9a338f238f9c')
assert o.repo=='orchestrator' and o.work_item_prefix=='ORCH'
assert e.repo=='enduro-trails' and e.work_item_prefix=='ET'
assert get_project_by_plane_id('00000000-0000-0000-0000-000000000000') is None
print('RESOLVE OK:', o.repo, e.repo, '| known:', len(known_plane_project_ids()))
"

⚠️ Важно

  • Plane-webhook остаётся выключенным (is_active=false). Включение — отдельный шаг Стрим после ревью PR.
  • ORCH_PROJECTS_JSON (если задан) полностью заменяет дефолт — перечислять все нужные проекты.
  • Обратная совместимость plane_sync сохранена (дефолт project_id = enduro), ET-задачи не сломаны.

Re-enable webhook (после ревью, делает Стрим)

UPDATE webhooks SET is_active=true WHERE id='93f0c342-a614-4248-9d0f-c107276f5620';