Files
orchestrator/docs/work-items/ORCH-086/02-trz.md

69 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 02-TRZ — ORCH-086: терминал-скип и dedup на пути F-1 реконсилятора
> Техническое задание. Архитектурное решение (КАК именно) — за архитектором (ADR). Здесь — ЧТО должно измениться и инварианты.
## 1. Задействованные модули `src/`
- **`src/reconciler.py`** — основной (и, как ожидается, единственный) изменяемый модуль:
- `Reconciler._reconcile_gate_task` (стр.180228) — путь F-1, где находится баг.
- `Reconciler._note_unblock` (стр.444477) — точка отправки уведомления + dedup-guard.
- `Reconciler._is_terminal_state` (стр.327344) — существующий терминал-детект (сейчас зовётся только из F-2); переиспользуется в F-1.
- `Reconciler._is_blocked_or_needs_input` (стр.230288) — уже делает `fetch_issue_state`; желательно переиспользовать его результат, чтобы не удваивать сетевой вызов.
- **Возможно затрагиваемые (read-only переиспользование, без изменения контракта):** `src/plane_sync.py` (`fetch_issue_state`, `get_project_states`, `get_project_state_groups`), `src/projects.py` (`get_project_by_repo`). Изменять их не требуется.
- **НЕ затрагиваются:** `src/stages.py` (`STAGE_TRANSITIONS`), `src/qg/checks.py` (`QG_CHECKS`), `src/stage_engine.py` (`advance_stage`/`advance_if_gate_passed`), `src/db.py` (схема), `src/config.py` (новые флаги не вводятся).
## 2. Требуемые изменения (функциональные)
### TR-1 (G2): терминал-скип на пути F-1
В `_reconcile_gate_task` ДО вызова `_note_unblock` (а лучше — до/вместо доведения терминальной задачи до `advance_if_gate_passed`) добавить проверку: **является ли задача терминальной**.
- Терминальность определяется тем же способом, что и в F-2 (`_is_terminal_state`): первичный дискриминатор — **группа статуса Plane** issue ∈ `{completed, cancelled}`; fallback (группа недоступна) — логические ключи `done`/`cancelled` проекта. Это покрывает грабли R1 (enduro vs orchestrator).
- Дополнительно: терминальной считается и задача, чья **стадия в БД оркестратора**`{done, cancelled}` (на случай дрейфа Plane↔БД; `get_active_tasks_for_reconcile` уже отсекает `done`, но `cancelled` — нет).
- Терминальная задача → **return без advance и без `_note_unblock`**; инкремент `self.skipped_terminal_total` (единая семантика с F-2, стр.363).
- Скип **безусловный** (как терминал-скип F-2 — без отдельного kill-switch). Это НЕ маскирует легитимный replay: реально застрявшая задача терминальной в Plane не бывает.
> **Где именно** ставить проверку (до `advance_if_gate_passed` или внутри/перед `_note_unblock`) — решает архитектор. Рекомендация: ставить как ранний guard в `_reconcile_gate_task` рядом с Guard 1/Guard 2 (чтобы терминальная задача даже не запускала `advance_if_gate_passed`/гейт). Если терминал-детект требует Plane-статус, он логично переиспользует fetch из Guard 2.
### TR-2 (G3): проброс `state_uuid` в `_note_unblock` из F-1
Вызов на стр.228 должен передавать `state_uuid` (текущий Plane-state issue), чтобы in-memory dedup-guard (`_unblock_dedup`, стр.459463) работал и на пути F-1:
```python
# было:
self._note_unblock(task.get("work_item_id") or str(task_id), stage)
# должно (концептуально):
self._note_unblock(task.get("work_item_id") or str(task_id), stage, state_uuid)
```
- `state_uuid` — текущий uuid статуса issue в Plane (тот же, что используется для терминал-детекта TR-1).
- Если Plane недоступен и `state_uuid` достоверно получить нельзя → допустимо передать `None` (dedup деградирует в no-op, как сегодня), НО приоритетно сначала отрабатывает терминал-скип TR-1; never-raise сохраняется.
- **Сигнатуру `_note_unblock` не менять** (3-й параметр `state_uuid` уже опциональный, стр.445).
### TR-3: переиспользование сетевого вызова (R4, нефункц., желательно)
F-1 не должен делать > 1 обращения к Plane API на задачу за тик ради статуса. `_is_blocked_or_needs_input` уже вызывает `fetch_issue_state`. Архитектор решает форму переиспользования (например, вынести резолв `(project_states, groups, current_state_uuid)` в один helper, питающий Guard 2 + терминал-скип TR-1 + dedup TR-2). Допустимо и без рефакторинга, если число вызовов на тик не растёт значимо.
## 3. Контракты и инварианты (НЕ нарушать)
- **never-raise:** каждая единица работы F-1 изолирована (`_reconcile_gate_task` уже под `try/except` в `reconcile_gate_once`, стр.162168). Любая ошибка терминал-детекта/fetch → не падает тик; консервативное поведение (R3): при невозможности достоверно определить терминальность — НЕ слать ложно, но и не глушить легитимный (см. AC-4: легитимный unblock — это реальная смена стадии не-терминальной задачи; терминал-неопределённость к нему не относится).
- **silence-when-in-sync:** терминальная (= полностью синхронизированная) задача → тишина (инвариант ORCH-068 AC-1/AC-2, теперь и для F-1).
- **Легитимный unblock сохраняется:** не-терминальная реально застрявшая задача с зелёным гейтом по-прежнему `advance` + уведомление (AC-4).
- **Наблюдаемость ORCH-068:** `skipped_terminal_total` инкрементируется при терминал-скипе F-1; `deduped_total` — при подавлении повтора dedup'ом; `unblocked_total`/`last_unblocked` — только при реальной отправке. Снимок `status()` (стр.516528) и блок `reconcile` в `GET /queue` — без структурных изменений.
- **Условность мультипроекта:** терминал-детект работает и для enduro, и для orchestrator (по группе статуса + fallback). Пайплайн/статусы enduro не трогаются.
## 4. Изменения API
Нет. HTTP-эндпоинты не меняются. `GET /queue` блок `reconcile` сохраняет форму (значения счётчиков — наблюдаемое поведение).
## 5. Изменения схемы БД
Нет. Миграции нет. (Терминальность Plane резолвится онлайн, как в ORCH-068 / Guard 2 — Вариант A без колонки статуса в `tasks`.)
## 6. Новые/изменённые QG checks
Нет. Реестр `QG_CHECKS` и `STAGE_TRANSITIONS` не меняются.
## 7. Конфигурация
Новые флаги НЕ вводятся. Терминал-скип безусловен (как у F-2). Существующие `reconcile_enabled`, `reconcile_notify_unblock`, `reconcile_skip_blocked_enabled`, `reconcile_plane_enabled` — без изменений семантики. (`reconcile_skip_blocked_enabled` гейтит ТОЛЬКО Guard 2; терминал-скип TR-1 ему НЕ подчиняется.)
## 8. Артефакты pipeline, подлежащие обновлению (документация = golden source)
- `docs/architecture/README.md` — раздел «Reconciler … ORCH-068»: дописать, что терминал-исключение и dedup теперь покрывают и F-1 (gate-side), не только F-2.
- `CHANGELOG.md` — запись `fix:` про ORCH-086.
- `docs/work-items/ORCH-086/06-adr/ADR-NNN-*.md` — ADR (создаёт архитектор).
- (Опционально) краткая ссылка в ADR ORCH-068, что F-1-пробел закрыт ORCH-086.
## 9. Готовность к development (Definition of Ready)
- G1 подтверждён по prod-логам/БД (точная стадия ET-002 и путь срабатывания задокументированы в ADR/12-review).
- Тест-план `04-test-plan.yaml` реализован в `tests/test_reconciler.py`.
- `pytest tests/ -q` зелёный.