9.7 KiB
02-TRZ — ORCH-086: терминал-скип и dedup на пути F-1 реконсилятора
Техническое задание. Архитектурное решение (КАК именно) — за архитектором (ADR). Здесь — ЧТО должно измениться и инварианты.
1. Задействованные модули src/
src/reconciler.py— основной (и, как ожидается, единственный) изменяемый модуль:Reconciler._reconcile_gate_task(стр.180–228) — путь F-1, где находится баг.Reconciler._note_unblock(стр.444–477) — точка отправки уведомления + dedup-guard.Reconciler._is_terminal_state(стр.327–344) — существующий терминал-детект (сейчас зовётся только из F-2); переиспользуется в F-1.Reconciler._is_blocked_or_needs_input(стр.230–288) — уже делает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, стр.459–463) работал и на пути F-1:
# было:
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, стр.162–168). Любая ошибка терминал-детекта/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()(стр.516–528) и блок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зелёный.