6.8 KiB
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) обязан пропускать (не трогать) задачи, которые:
- исчерпали лимит developer-ретраев (
_developer_retry_count >= MAX_DEVELOPER_RETRIES), и/или - находятся в явном «человеческом»/терминальном 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: <wi> ... разблокирована, 0 повторных запусков агентов,
0 Telegram-нотификаций разблокировки; задача спокойно ждёт человека в
development/Blocked. При этом штатные «честно застрявшие» задачи
(retry < лимита, не Blocked) reconciler по-прежнему доигрывает.