136 lines
11 KiB
Markdown
136 lines
11 KiB
Markdown
# 01-BRD — Управление зависимостями задач (B ждёт A) в очереди
|
||
|
||
**Work Item:** ORCH-026
|
||
**Repo:** orchestrator (self-hosting)
|
||
**Branch:** feature/ORCH-026-b-a
|
||
**Стадия:** analysis
|
||
**Источник:** предложение Стрим, одобрено Славой (2026-06-04); дополнение Слава+Стрим 2026-06-08 (инцидент эрозии `main`)
|
||
|
||
---
|
||
|
||
## 1. Контекст и проблема
|
||
|
||
### 1.1 Первопричина (мотивация СЕЙЧАС — инцидент 08.06)
|
||
Эрозия `main` 08.06 (потеря кода ORCH-067/069, фантом-merge) родилась НЕ из логических
|
||
зависимостей, а из **некоординированного параллелизма**: несколько self-hosting задач
|
||
(ORCH-067/069/071) одновременно срезали ветки от `main` и правили общие файлы
|
||
(`CHANGELOG.md`, `notifications.py`, `config.py`). Последствия:
|
||
|
||
- CHANGELOG-конфликты на `auto_rebase` → откаты `deploy-staging → development` (дорого:
|
||
ORCH-069 = 3 попытки = $3.98);
|
||
- тихое затирание кода соседа при merge ветки, срезанной от устаревшего `main` (фантом).
|
||
|
||
**ORCH-073** закрыл ПОСЛЕДСТВИЯ (3 рубежа: CHANGELOG `merge=union` + SHA-in-main verify +
|
||
регресс-гард маркеров). ORCH-026 должен закрыть **ПЕРВОПРИЧИНУ**: задачи одного репо не
|
||
должны мешать друг другу в `main`.
|
||
|
||
### 1.2 Исходный скоуп (плоская очередь ORCH-1)
|
||
Очередь (`src/queue_worker.py`, ORCH-1) — плоская: `jobs` упорядочены по `id` (FIFO),
|
||
гейтятся только `available_at` и `max_concurrency`. Нельзя выразить «задача B не стартует,
|
||
пока не готова A». Декомпозиция эпиков (ORCH-025) порождает заведомо зависимые подзадачи.
|
||
|
||
### 1.3 Что уже есть (опора, НЕ переписывать)
|
||
- **ORCH-1** — персистентная очередь (`jobs`), atomic claim, `available_at`-defer, restart-safe.
|
||
- **ORCH-065** — `merge-lease` (`src/merge_gate.py`): per-repo файловый лиз
|
||
`.merge-lease-<repo>.json`, неблокирующий acquire, holder-aware release, проактивный
|
||
реклейм мёртвого/устаревшего держателя. **Сейчас лиз держится только на ребре
|
||
`deploy-staging → deploy`** (от merge-gate до фактического merge).
|
||
- **ORCH-043** — merge-gate: `branch_is_behind_main`, `auto_rebase_onto_main` (rebase
|
||
**только когда ветка отстаёт или при конфликте**), `retest_branch`.
|
||
- **ORCH-073** — merge-verify: `verify_merged_to_main` (SHA-in-main), `check_main_regression`.
|
||
- **Plane-статусы** `Blocked` / `Needs Input` + `set_issue_blocked` (`src/plane_sync.py`).
|
||
- **Telegram live-tracker** (`src/notifications.py`) — одна карточка на задачу, уже умеет
|
||
показывать статус `Blocked`.
|
||
|
||
---
|
||
|
||
## 2. Цель (бизнес-результат)
|
||
|
||
Задачи одного репозитория перестают повреждать `main` друг друга, а очередь умеет
|
||
выражать логические зависимости между задачами — БЕЗ потери параллелизма между разными
|
||
репозиториями и без риска для self-hosting прода.
|
||
|
||
---
|
||
|
||
## 3. Два уровня требований (объединить в одной задаче; приоритет — Уровень A)
|
||
|
||
### Уровень A — Сериализация merge/деплоя внутри ОДНОГО репо (КРИТИЧНО, корень эрозии)
|
||
Закрывает первопричину инцидента 08.06.
|
||
|
||
- **A-1.** В рамках ОДНОГО репо merge-в-`main` + деплой должны быть **сериализованы**: пока
|
||
задача A не слита в `main` (и для self-hosting — не задеплоена), задача B того же репо НЕ
|
||
доходит до своего merge/деплоя от устаревшего `main`.
|
||
- **A-2.** B перед своим merge-gate **обязана ребейзнуться на СВЕЖИЙ `main`** (где уже есть
|
||
A) — **proactive pre-merge rebase**, а не только при текстовом конфликте (как сейчас в
|
||
ORCH-043). Цель: B всегда несёт актуальный код предшественников → структурный анти-фантом
|
||
на уровне планировщика (дополняет рубежи ORCH-073, не заменяет).
|
||
- **A-3.** Сериализация — **только внутри одного репо**. Задачи РАЗНЫХ репо (orchestrator vs
|
||
enduro-trails) параллелятся свободно (общая БД/очередь — пропускная способность не падает).
|
||
- **A-4.** Механизм — минимально-инвазивный и **restart-safe** (как ORCH-1/065): переживает
|
||
рестарт прод-контейнера, не оставляет навсегда захваченных ресурсов (опора на проактивный
|
||
реклейм ORCH-065).
|
||
- **A-5.** **Совместимость с self-hosting safety:** не ронять/не рестартить прод-контейнер
|
||
вне штатного deploy; гейт `Confirm Deploy` (ORCH-059) сохранён; никаких push/force-push в
|
||
`main`.
|
||
- **A-6.** Защита от взаимоблокировки: B при занятой сериализации **defer** (повторная
|
||
постановка с задержкой через `available_at`), а НЕ откат на `development` и НЕ вечное
|
||
ожидание; bounded defer-бюджет (анти-livelock, как `merge_defer_max_attempts`).
|
||
|
||
### Уровень B — Декларативные зависимости (исходный скоуп ORCH-26)
|
||
- **B-1.** Задача может объявить связь `blocked-by` / `blocks` (depends-on).
|
||
- **B-2.** Планировщик очереди (ORCH-1) **не запускает** заблокированную задачу, пока все её
|
||
depends-on не достигли терминального состояния (`done`).
|
||
- **B-3.** **Защита от дедлоков:** циклические зависимости детектируются; задача в цикле не
|
||
«пропадает молча» — выставляется `Blocked` + alert (Telegram/Plane).
|
||
- **B-4.** **Видимость:** заблокированная задача видна — Plane-статус `Blocked` и/или
|
||
ожидание в Telegram-карточке (что и кого ждёт).
|
||
|
||
---
|
||
|
||
## 4. Открытые вопросы для архитектора (НЕ решаются на этапе анализа)
|
||
|
||
> Аналитик фиксирует требования; выбор механизма — за архитектором (ADR в `06-adr/`).
|
||
|
||
1. **Где хранить связи (Уровень B):** Plane relations (родное, видимо в UI, но требует
|
||
сетевого запроса и зависит от Plane) vs таблица в БД (`job_deps`/поля `tasks`, надёжно и
|
||
offline, но дубль источника) vs **гибрид** (Plane — источник декларации, БД — кэш для
|
||
планировщика). Рекомендация анализа: гибрид с offline-fallback (см. §6).
|
||
2. **Механизм сериализации (Уровень A):** глобальный per-repo merge-lock vs FIFO merge-queue
|
||
vs **обязательный pre-merge rebase + расширение окна merge-lease** (от «момента merge» до
|
||
«main-updated»). Выбрать минимально-инвазивный, restart-safe, переиспользующий ORCH-065/043.
|
||
3. **Граница окна сериализации для self-hosting:** для не-self репо «merged в main» = конец
|
||
окна; для self (orchestrator) деплой асинхронный (Phase B/C, ORCH-036/071) — нужно решить,
|
||
до какого события держать лиз (до `merged_to_main: true` / до `done`).
|
||
4. **Совместимость B и A:** depends-on (B) на уровне постановки в очередь vs merge-сериализация
|
||
(A) на уровне merge-gate — разные точки конвейера; убедиться, что не конфликтуют.
|
||
|
||
---
|
||
|
||
## 5. Вне скоупа (Non-goals)
|
||
- Изменение машины стадий `STAGE_TRANSITIONS` (сериализация/зависимости — врезки/гейты, не
|
||
новые стадии — паттерн ORCH-043/058/071).
|
||
- Приоритизация/перепланирование задач по весам (только зависимости и сериализация).
|
||
- Кросс-репо зависимости (A-3 явно запрещает кросс-репо сериализацию; кросс-репо логические
|
||
зависимости — возможный follow-up, не v1).
|
||
- Отмена/замена рубежей ORCH-073 — ORCH-026 их **дополняет** на уровне планировщика.
|
||
|
||
---
|
||
|
||
## 6. Заинтересованные стороны
|
||
- **Owner (Слава)** — одобряет BRD; держатель self-hosting прод-риска.
|
||
- **Стрим** — автор предложения.
|
||
- **Конвейер агентов** — потребитель: developer/deployer работают с веткой, которую затрагивает
|
||
сериализация; reviewer проверяет обновление доки.
|
||
|
||
---
|
||
|
||
## 7. Критерии успеха (бизнес-уровень)
|
||
- Две зелёные задачи одного репо больше не способны затереть код друг друга в `main` на уровне
|
||
планировщика (без участия рубежей-последствий ORCH-073).
|
||
- Задача может объявить зависимость; заблокированная задача не стартует раньше времени и видна
|
||
наблюдателю.
|
||
- Пропускная способность разных репо не деградирует.
|
||
- Прод-контейнер orchestrator не падает и не рестартится вне штатного `Confirm Deploy`.
|
||
|
||
Точные PASS/FAIL — `03-acceptance-criteria.md`.
|