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.
This commit is contained in:
Dev Agent
2026-06-02 22:30:51 +03:00
parent c1f35a2047
commit ca81f38330
4 changed files with 160 additions and 2 deletions

View File

@@ -9,9 +9,39 @@ Orchestrator — event-driven FastAPI сервис, который управл
### 1. Webhook Receivers
#### Plane Webhook (`src/webhooks/plane.py`)
- Принимает `work_item.created` — создаёт задачу в DB, запускает analyst
- **Фильтр по проекту (ORCH-6):** извлекает `data.project` (Plane project uuid) и игнорирует событие, если проект не в реестре (`known_plane_project_ids()`) → ответ `{"status":"ignored","reason":"unknown project"}`. Это предотвращает инцидент 2026-06-02 (webhook на весь workspace без фильтра).
- Принимает `work_item.created` — резолвит repo/prefix/Plane-проект из реестра по `project`, создаёт задачу в DB, запускает analyst
- Принимает `work_item.updated` — синхронизация статусов
#### Реестр проектов (`src/projects.py`, multi-repo, ORCH-6)
Маппинг **Plane project id → (repo, work_item_prefix, name)**. Позволяет одному
оркестратору обслуживать несколько репозиториев, не путая их.
```python
@dataclass(frozen=True)
class ProjectConfig:
plane_project_id: str # uuid Plane-проекта (ключ реестра)
repo: str # имя gitea-репо (= папка в /repos)
work_item_prefix: str # ET / ORCH
name: str # человекочитаемое
```
Резолверы:
- `get_project_by_plane_id(uuid) -> ProjectConfig | None` — для фильтра/резолва в plane-webhook.
- `get_project_by_repo(repo) -> ProjectConfig | None` — когда известен только repo (gitea-webhook, plane_sync).
- `known_plane_project_ids() -> set[str]` — множество разрешённых проектов (фильтр).
**Источник конфигурации:** env `ORCH_PROJECTS_JSON` (JSON-массив `ProjectConfig`).
Если пусто/битый JSON — используется встроенный дефолт-реестр (enduro-trails + orchestrator),
чтобы система работала из коробки. Парсинг устойчив: битые записи пропускаются,
полностью невалидный JSON → fallback на дефолт.
Следствия multi-repo:
- **repo per project:** `repo = get_project_by_plane_id(project_id).repo` вместо хардкода `default_repo`.
- **prefix per project:** `get_next_work_item_id(repo, prefix)` нумерует независимо — `ORCH-001` vs `ET-010` (`src/db.py`).
- **plane_sync в правильный проект:** state/comment пишутся в Plane-проект самой задачи (резолв по repo через `get_project_by_repo`), а не в единственный хардкоженный `PROJECT_ID` (обратная совместимость сохранена дефолтом на enduro).
- **gitea-webhook:** push в repo вне реестра → `ignored` (не триггерит конвейер).
#### Gitea Webhook (`src/webhooks/gitea.py`)
- **push** — проверяет наличие артефактов (docs/, src/), продвигает стадию
- **pull_request\*** (wildcard) — обрабатывает review approved/rejected, PR merge