# BRD: Reconciler не должен трогать escalated / max-retries задачи Work Item ID: ORCH-060 Стадия: analysis → architecture Связано: ORCH-053 (reconciler), ORCH-046 (retry-счётчик), ORCH-047 (BLOCKED-вердикт) ## 1. Контекст и проблема ORCH-053 ввёл фоновый reconciler (`src/reconciler.py`) — sweeper, доигрывающий пропущенные webhook-переходы. Слой F-1 (`reconcile_gate_once` → `_reconcile_gate_task`) для каждой не-терминальной задачи (`stage != 'done'`) без активного job и старше grace делает read-only пред-оценку канонического QG; если гейт зелёный → `advance_if_gate_passed` → `advance_stage(..., finished_agent=None)`. **Дефект.** Задача, исчерпавшая лимит developer-ретраев (`_developer_retry_count(task_id) >= MAX_DEVELOPER_RETRIES = 3`), **escalated** — но эскалация в обработчиках Gitea (`src/webhooks/gitea.py:280` для CI-failure, `:371` для review REQUEST_CHANGES) выполняет ТОЛЬКО `notify_error(...)`: - стадия НЕ меняется (остаётся `development`); - терминального маркера в БД нет (нет `blocked`-флага в таблице `tasks`); - активного job нет. Для reconciler такая задача неотличима от «застрявшей из-за потерянного webhook». Если CI к этому моменту зелёный (типичный кейс: разработчик починил CI, но reviewer продолжал слать REQUEST_CHANGES → ушли в лимит), F-1 каждые `reconcile_interval_s` (120 с) видит зелёный `check_ci_green` и **разблокирует** задачу `development → review`. Reviewer снова REQUEST_CHANGES → откат на `development` → снова эскалация (стадия не меняется). Следующий тик — снова разблокировка. Бесконечный цикл. **Реальный инцидент (наблюдение 06–07.06.2026).** ET-013 разблокирована reconciler'ом **10 раз за ночь**, в итоге всё равно escalated — бесполезный поллинг каждые 2 минуты, лишние запуски агентов (токены, деньги), шум в Telegram (`reconcile_notify_unblock`), нагрузка на конвейер общего инстанса (self-hosting: один инстанс обслуживает ORCH + enduro-trails). Симметричный риск: задача, которую человек/агент явно перевёл в Plane-статус **Blocked** или **Needs Input** (ручной гейт), не должна автоматически разблокироваться reconciler'ом до вмешательства человека. ## 2. Бизнес-цель Reconciler (F-1) обязан **пропускать** (не трогать) задачи, которые: 1. исчерпали лимит developer-ретраев (`_developer_retry_count >= MAX_DEVELOPER_RETRIES`), и/или 2. находятся в явном «человеческом»/терминальном Plane-статусе **Blocked** / **Needs Input**. Такие задачи ждут ручного вмешательства; автоматический sweeper их игнорирует. ## 3. Заинтересованные стороны - **Owner проекта** — прекращение «фантомной» активности и шума по escalated-задачам. - **Другие проекты на инстансе (enduro-trails)** — снижение паразитной нагрузки общей очереди. - **Агенты-разработчики оркестратора** — корректная семантика терминального состояния. ## 4. Объём (Scope) ### Входит - Гард в F-1 (`_reconcile_gate_task` / `advance_if_gate_passed`), который ДО оценки гейта и вызова `advance_stage` пропускает escalated-задачи (retry-count >= лимит) — детерминированно, без сети. - Гард, пропускающий задачи в Plane-статусе Blocked / Needs Input. - Тесты (unit) на оба условия + регресс happy-path и отсутствия спама/нотификаций. - Обновление документации: `docs/architecture/README.md` (описание F-1), per-work-item ADR, `CHANGELOG.md`. ### Не входит - Изменение порога `MAX_DEVELOPER_RETRIES` или логики самой эскалации в `gitea.py`. - Изменение F-2 plane-side по существу (F-2 уже реагирует только на in_progress/approved/rejected, то есть Blocked/Needs Input им не доигрываются — достаточно регресс-теста, фиксирующего это поведение). - Реестры `STAGE_TRANSITIONS` / `QG_CHECKS`, схема прочих стадий. ## 5. Допущения и ограничения - **Инвариант reconciler (ORCH-053):** схема БД и реестры не меняются. Решение должно либо обойтись без миграции, либо архитектор обязан явно обосновать необходимость нового столбца как терминального маркера. - **Never-raise:** гард не должен ломать тик; любая ошибка вычисления условия → безопасный фоллбэк (не трогать задачу — консервативно). - **self-hosting:** нельзя ронять/рестартить прод-контейнер; изменение — чисто логика sweeper'а, деплой через staging (8501) по канону. - Источник истины по retry — `agent_runs` (как у `_developer_retry_count`). ## 6. Критерий успеха (бизнес) После выката на конкретной escalated-задаче (как ET-013): за ночь — **0** строк `reconciler: ... разблокирована`, **0** повторных запусков агентов, **0** Telegram-нотификаций разблокировки; задача спокойно ждёт человека в `development`/Blocked. При этом штатные «честно застрявшие» задачи (retry < лимита, не Blocked) reconciler по-прежнему доигрывает.