analyst(ET): auto-commit from analyst run_id=288
All checks were successful
CI / test (push) Successful in 17s
All checks were successful
CI / test (push) Successful in 17s
This commit is contained in:
90
docs/work-items/ORCH-060/01-brd.md
Normal file
90
docs/work-items/ORCH-060/01-brd.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# 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: <wi> ... разблокирована`, **0** повторных запусков агентов,
|
||||
**0** Telegram-нотификаций разблокировки; задача спокойно ждёт человека в
|
||||
`development`/Blocked. При этом штатные «честно застрявшие» задачи
|
||||
(retry < лимита, не Blocked) reconciler по-прежнему доигрывает.
|
||||
113
docs/work-items/ORCH-060/02-trz.md
Normal file
113
docs/work-items/ORCH-060/02-trz.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# ТЗ: Reconciler пропускает escalated / max-retries / blocked-needs-input задачи
|
||||
|
||||
Work Item ID: ORCH-060
|
||||
Стадия: analysis → architecture (архитектор фиксирует механику в ADR)
|
||||
|
||||
## 1. Задействованные модули `src/`
|
||||
|
||||
| Модуль | Роль в задаче |
|
||||
|--------|---------------|
|
||||
| `src/reconciler.py` | **Основное изменение.** F-1: `Reconciler._reconcile_gate_task` — добавить пред-проверки (escalated / blocked / needs-input) ДО `advance_if_gate_passed`. |
|
||||
| `src/stage_engine.py` | Источник `MAX_DEVELOPER_RETRIES` (=3) и `_developer_retry_count(task_id)`. Кандидат на промоут приватного хелпера в переиспользуемый (решает архитектор). |
|
||||
| `src/db.py` | Чтение состояния задачи (`get_active_tasks_for_reconcile` уже отдаёт строки `tasks`); возможный новый read-helper для retry-count, если решено не импортировать приватный из stage_engine. |
|
||||
| `src/plane_sync.py` | Маппинг Plane-статусов (`PLANE_STATES`, `get_project_states`): `blocked`, `needs_input`. Источник для проверки «человеческого» статуса, если архитектор выберет проверку через Plane API. |
|
||||
| `src/webhooks/gitea.py` | НЕ меняется (только справочно: точки эскалации `:280`, `:371`). |
|
||||
|
||||
## 2. Требуемое поведение (контракт F-1)
|
||||
|
||||
`Reconciler._reconcile_gate_task(task)` ДО вызова `advance_if_gate_passed(...)`
|
||||
обязан вернуться (пропустить задачу, ничего не делая, не инкрементируя
|
||||
`unblocked_total`, не слать нотификации), если выполнено ЛЮБОЕ из условий:
|
||||
|
||||
1. **Escalated по ретраям (обязательно, детерминированно, без сети):**
|
||||
`developer_retry_count(task_id) >= MAX_DEVELOPER_RETRIES`.
|
||||
- `MAX_DEVELOPER_RETRIES` импортируется из `stage_engine` (НЕ хардкодить число).
|
||||
- Источник счётчика — тот же запрос, что в `_developer_retry_count`:
|
||||
`SELECT COUNT(*) FROM agent_runs WHERE task_id=? AND agent='developer'`.
|
||||
|
||||
2. **Явный человеческий/терминальный Plane-статус:** issue в состоянии
|
||||
**Blocked** или **Needs Input**.
|
||||
|
||||
Порядок: проверки добавляются в `_reconcile_gate_task` ПОСЛЕ существующих гардов
|
||||
(`stage=='analysis'` carve-out, `get_qg_for_stage is None`, `has_active_job_for_task`,
|
||||
grace) и ДО `advance_if_gate_passed`. Условие (1) — дешёвое (локальный SQL) —
|
||||
проверять раньше условия (2), если (2) требует сети.
|
||||
|
||||
## 3. Механика проверки blocked/needs-input (выбор — за архитектором, ADR)
|
||||
|
||||
В таблице `tasks` НЕТ столбца статуса (`stage` всегда `development` у escalated).
|
||||
Архитектор выбирает и обосновывает один из вариантов; требования к каждому:
|
||||
|
||||
- **Вариант A — проверка через Plane API (без миграции, предпочтительно по
|
||||
инварианту ORCH-053 «схема не меняется»):** для кандидата F-1 запросить текущее
|
||||
состояние issue (per-project `get_project_states` → сверка с `blocked`/`needs_input`).
|
||||
Допустимо, т.к. F-1 уже делает сетевой вызов в гейте (`check_ci_green`), а
|
||||
кандидатов после grace+no-active-job немного. Обязателен never-raise: ошибка
|
||||
запроса → консервативно НЕ трогать задачу (skip), либо явно обоснованный фоллбэк.
|
||||
- **Вариант B — локальный терминальный маркер в БД:** идемпотентная миграция
|
||||
(`tasks.blocked`/`tasks.reconcile_skip`), выставляется в точках `set_issue_blocked`/
|
||||
`set_issue_needs_input` и в точках эскалации `gitea.py`. Требует обоснования
|
||||
нарушения инварианта «схема reconciler не меняется» и затрагивает больше точек.
|
||||
|
||||
> Рекомендация аналитика: условие (1) полностью закрывает зафиксированный инцидент
|
||||
> (ET-013 = escalated = max retries) детерминированно и без сети — оно
|
||||
> обязательно к реализации. Условие (2) — защита от автоперекрытия ручного гейта;
|
||||
> минимально-инвазивный путь — Вариант A. Архитектор вправе ограничить (2)
|
||||
> Вариантом A либо обосновать B.
|
||||
|
||||
## 4. Изменения API
|
||||
|
||||
Нет. Эндпоинты не добавляются и не меняются. Снимок `GET /queue` (блок `reconcile`)
|
||||
по содержимому не меняется; опционально архитектор может добавить best-effort
|
||||
счётчик `skipped_escalated` (необязательно, вне scope AC).
|
||||
|
||||
## 5. Изменения схемы БД
|
||||
|
||||
По умолчанию — **нет** (Вариант A). При выборе Варианта B — идемпотентная
|
||||
ALTER-миграция через `_ensure_column` (как остальные в `db.init_db`),
|
||||
restart-safe, безопасная на живой прод-БД; обязательна явная мотивация в ADR.
|
||||
|
||||
## 6. Требования к QG checks
|
||||
|
||||
Нет новых QG. Реестр `QG_CHECKS` и `STAGE_TRANSITIONS` не меняются. Гард —
|
||||
ВНЕ гейта: он решает, ЗАПУСКАТЬ ли пред-оценку гейта вообще, а не меняет вердикт
|
||||
гейта.
|
||||
|
||||
## 7. Инварианты, которые нельзя нарушить
|
||||
|
||||
- **Never-raise** на единицу работы (per-task `try/except` в `reconcile_gate_once`
|
||||
сохраняется; новая логика не должна бросать наружу).
|
||||
- **Тишина при пропуске:** пропущенная задача не инкрементирует `unblocked_total`,
|
||||
не пишет лог `разблокирована`, не шлёт Telegram.
|
||||
- **Регресс F-1 happy-path:** задача с retry < лимита и не-Blocked/Needs-Input при
|
||||
зелёном гейте по-прежнему доигрывается (`advance_stage` вызывается).
|
||||
- **F-2** по существу не меняется: Blocked/Needs Input не входят в
|
||||
{in_progress, approved, rejected} → не доигрываются (зафиксировать регресс-тестом).
|
||||
- `analysis` carve-out F-1 сохраняется.
|
||||
- Kill-switch'и (`reconcile_enabled`, `reconcile_plane_enabled`) работают как прежде.
|
||||
|
||||
## 8. Артефакты pipeline, которые должны быть созданы/обновлены
|
||||
|
||||
- `docs/work-items/ORCH-060/06-adr/ADR-001-*.md` — решение по механике (2) (A vs B).
|
||||
- `docs/architecture/README.md` — дополнить описание F-1 («skip escalated /
|
||||
blocked / needs-input»).
|
||||
- `CHANGELOG.md` — запись `fix(reconciler): ...`.
|
||||
- Тесты — `tests/test_reconciler.py` (расширение).
|
||||
- Обновить footer `docs/architecture/README.md` (статус ORCH-060).
|
||||
|
||||
## 9. Точки изменения кода (конкретно)
|
||||
|
||||
1. `src/reconciler.py`, `_reconcile_gate_task`: после grace-проверки и до
|
||||
`advance_if_gate_passed` вставить:
|
||||
```python
|
||||
# ORCH-060: escalated tasks (max developer retries reached) are terminal —
|
||||
# they wait for a human, not the sweeper. Skip deterministically (no network).
|
||||
if developer_retry_count(task_id) >= MAX_DEVELOPER_RETRIES:
|
||||
return
|
||||
# ORCH-060: respect an explicit human gate (Blocked / Needs Input).
|
||||
if self._is_blocked_or_needs_input(task): # mechanism per ADR (Variant A/B)
|
||||
return
|
||||
```
|
||||
2. `src/reconciler.py`: импорт `MAX_DEVELOPER_RETRIES` (и retry-count хелпера) из
|
||||
`stage_engine` (или новый read-helper в `db.py`).
|
||||
3. Хелпер проверки Plane-статуса (`_is_blocked_or_needs_input`) — never-raise.
|
||||
124
docs/work-items/ORCH-060/03-acceptance-criteria.md
Normal file
124
docs/work-items/ORCH-060/03-acceptance-criteria.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# Критерии приёмки: ORCH-060
|
||||
|
||||
Work Item ID: ORCH-060
|
||||
|
||||
Формат: каждый критерий — Дано / Когда / Тогда, с однозначным PASS/FAIL.
|
||||
|
||||
---
|
||||
|
||||
## AC-1 — Escalated-задача (retry == лимит) не разблокируется (главный кейс ET-013)
|
||||
|
||||
- **Дано:** задача на `stage='development'`, без активного job, `age >= grace`,
|
||||
`check_ci_green` зелёный; в `agent_runs` ровно `MAX_DEVELOPER_RETRIES` (=3)
|
||||
записей `agent='developer'`.
|
||||
- **Когда:** выполняется `Reconciler.reconcile_gate_once()`.
|
||||
- **Тогда:** стадия остаётся `development`; `advance_stage`/`advance_if_gate_passed`
|
||||
не приводит к смене стадии; `unblocked_total == 0`; новый developer/reviewer job
|
||||
не создаётся.
|
||||
- **PASS:** стадия не изменилась И `unblocked_total == 0` И нет новых job.
|
||||
- **FAIL:** стадия стала `review` / появился новый job / `unblocked_total > 0`.
|
||||
|
||||
## AC-2 — Граница: retry > лимита тоже пропускается
|
||||
|
||||
- **Дано:** то же, но developer-записей `> MAX_DEVELOPER_RETRIES` (например 4–5).
|
||||
- **Когда:** `reconcile_gate_once()`.
|
||||
- **Тогда:** задача пропущена (как AC-1).
|
||||
- **PASS / FAIL:** как AC-1.
|
||||
|
||||
## AC-3 — Регресс happy-path: retry < лимита по-прежнему доигрывается
|
||||
|
||||
- **Дано:** `development`, без активного job, `age >= grace`, `check_ci_green`
|
||||
зелёный; developer-записей `< MAX_DEVELOPER_RETRIES` (например 0, 1 или 2).
|
||||
- **Когда:** `reconcile_gate_once()`.
|
||||
- **Тогда:** задача доигрывается `development → review`; `unblocked_total == 1`;
|
||||
enqueue следующего агента происходит как раньше.
|
||||
- **PASS:** стадия стала `review` И `unblocked_total == 1`.
|
||||
- **FAIL:** задача пропущена / стадия не изменилась.
|
||||
|
||||
## AC-4 — Граница ровно на лимите (==3) → skip, на (лимит−1) → advance
|
||||
|
||||
- **Дано:** две задачи-близнеца, идентичные кроме числа developer-записей:
|
||||
одна с `MAX_DEVELOPER_RETRIES`, другая с `MAX_DEVELOPER_RETRIES − 1`.
|
||||
- **Когда:** `reconcile_gate_once()`.
|
||||
- **Тогда:** первая пропущена (skip), вторая доиграна (advance).
|
||||
- **PASS:** ровно одна из двух доиграна (та, что `−1`).
|
||||
- **FAIL:** обе доиграны / обе пропущены / доиграна задача на лимите.
|
||||
|
||||
## AC-5 — Plane-статус Blocked → пропуск
|
||||
|
||||
- **Дано:** задача-кандидат F-1 (stage не-терминальный, без активного job,
|
||||
`age >= grace`, гейт зелёный), у которой текущий Plane-статус issue = **Blocked**;
|
||||
retry < лимита (чтобы изолировать именно этот гард).
|
||||
- **Когда:** `reconcile_gate_once()`.
|
||||
- **Тогда:** задача пропущена; стадия не меняется; `unblocked_total == 0`.
|
||||
- **PASS:** стадия не изменилась И `unblocked_total == 0`.
|
||||
- **FAIL:** задача доиграна.
|
||||
|
||||
## AC-6 — Plane-статус Needs Input → пропуск
|
||||
|
||||
- **Дано:** как AC-5, но Plane-статус = **Needs Input**.
|
||||
- **Когда:** `reconcile_gate_once()`.
|
||||
- **Тогда:** задача пропущена (как AC-5).
|
||||
- **PASS / FAIL:** как AC-5.
|
||||
|
||||
## AC-7 — Тишина при пропуске (no spam)
|
||||
|
||||
- **Дано:** escalated-задача (как AC-1).
|
||||
- **Когда:** `reconcile_gate_once()` (один или несколько тиков).
|
||||
- **Тогда:** НЕ вызывается `_note_unblock`; нет лог-строки `... разблокирована`;
|
||||
нет `send_telegram`; нет `notify_qg_failure` (пропуск — раньше оценки гейта).
|
||||
- **PASS:** ни одна из перечисленных нотификаций не вызвана.
|
||||
- **FAIL:** вызвана любая нотификация.
|
||||
|
||||
## AC-8 — Никакого сетевого вызова гейта на escalated-задаче
|
||||
|
||||
- **Дано:** escalated-задача (как AC-1) с замоканным `check_ci_green`.
|
||||
- **Когда:** `reconcile_gate_once()`.
|
||||
- **Тогда:** `check_ci_green` (через `advance_if_gate_passed`/`_run_qg`) НЕ
|
||||
вызывается для этой задачи — пропуск происходит раньше.
|
||||
- **PASS:** мок гейта не вызван.
|
||||
- **FAIL:** мок гейта вызван.
|
||||
|
||||
## AC-9 — F-2 не доигрывает Blocked/Needs Input (регресс)
|
||||
|
||||
- **Дано:** issue в Plane-статусе Blocked или Needs Input (не входит в
|
||||
{in_progress, approved, rejected}).
|
||||
- **Когда:** `reconcile_plane_once()`.
|
||||
- **Тогда:** ни `handle_status_start`, ни `handle_verdict` не вызываются для
|
||||
этого issue; `unblocked_total == 0`.
|
||||
- **PASS:** обработчики не вызваны.
|
||||
- **FAIL:** вызван любой обработчик.
|
||||
|
||||
## AC-10 — Never-raise: ошибка проверки статуса не ломает тик
|
||||
|
||||
- **Дано:** проверка blocked/needs-input (Plane API в Варианте A) бросает
|
||||
исключение для одной задачи; в выборке есть ещё одна валидная задача.
|
||||
- **Когда:** `reconcile_gate_once()`.
|
||||
- **Тогда:** тик не падает; сбойная задача консервативно НЕ трогается (skip);
|
||||
остальные обрабатываются.
|
||||
- **PASS:** исключение изолировано, остальные задачи обработаны.
|
||||
- **FAIL:** исключение всплыло из `reconcile_gate_once`.
|
||||
|
||||
## AC-11 — Лимит не хардкодится
|
||||
|
||||
- **Дано:** код F-1-гарда.
|
||||
- **Тогда:** используется `stage_engine.MAX_DEVELOPER_RETRIES`, а не литерал `3`.
|
||||
- **PASS:** граница берётся из константы.
|
||||
- **FAIL:** в reconciler.py появился магический `3`.
|
||||
|
||||
## AC-12 — Документация обновлена (golden source)
|
||||
|
||||
- **Дано:** PR задачи.
|
||||
- **Тогда:** обновлены `docs/architecture/README.md` (описание F-1 с новым skip),
|
||||
`CHANGELOG.md`, создан `06-adr/ADR-001-*.md`.
|
||||
- **PASS:** все три артефакта обновлены/созданы в этом же PR.
|
||||
- **FAIL:** любой отсутствует (reviewer → REQUEST_CHANGES).
|
||||
|
||||
## AC-13 — Регресс существующих тестов reconciler
|
||||
|
||||
- **Дано:** существующий `tests/test_reconciler.py` (ORCH-053).
|
||||
- **Когда:** `pytest tests/test_reconciler.py -q`.
|
||||
- **Тогда:** все прежние тесты зелёные (поведение happy-path/analysis/kill-switch
|
||||
не сломано).
|
||||
- **PASS:** 0 регрессий.
|
||||
- **FAIL:** любой ранее зелёный тест упал.
|
||||
82
docs/work-items/ORCH-060/04-test-plan.yaml
Normal file
82
docs/work-items/ORCH-060/04-test-plan.yaml
Normal file
@@ -0,0 +1,82 @@
|
||||
work_item: ORCH-060
|
||||
description: >
|
||||
Reconciler F-1 пропускает escalated (retry >= MAX_DEVELOPER_RETRIES) и
|
||||
явно-blocked / needs-input задачи; happy-path и no-spam сохранены.
|
||||
Конвенции test-фикстур — как в существующем tests/test_reconciler.py
|
||||
(изолированная sqlite-БД, моки Plane/Telegram/gate). Хелпер _make_task
|
||||
вставляет задачу; developer-ретраи моделируются вставкой N строк в agent_runs
|
||||
(agent='developer'); зелёный CI — через _green_ci(monkeypatch).
|
||||
|
||||
tests:
|
||||
- id: TC-01
|
||||
type: unit
|
||||
description: "AC-1: escalated dev-задача (ровно MAX_DEVELOPER_RETRIES developer-ранов) при зелёном CI НЕ разблокируется — стадия остаётся development, unblocked_total==0, новых job нет"
|
||||
module: tests/test_reconciler.py
|
||||
setup: "_make_task('development', age_s=grace+60); insert MAX_DEVELOPER_RETRIES rows agent_runs(agent='developer'); _green_ci()"
|
||||
expected: PASS
|
||||
|
||||
- id: TC-02
|
||||
type: unit
|
||||
description: "AC-2: developer-ранов > MAX_DEVELOPER_RETRIES (4–5) → также skip"
|
||||
module: tests/test_reconciler.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-03
|
||||
type: unit
|
||||
description: "AC-3 (регресс happy-path): developer-ранов < MAX (0/1/2) при зелёном CI → задача доигрывается development->review, unblocked_total==1"
|
||||
module: tests/test_reconciler.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-04
|
||||
type: unit
|
||||
description: "AC-4: граница — задача с ровно MAX пропущена, задача с MAX-1 доиграна (ровно одна advance)"
|
||||
module: tests/test_reconciler.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-05
|
||||
type: unit
|
||||
description: "AC-5: задача в Plane-статусе Blocked (retry<лимита) пропущена — стадия не меняется, unblocked_total==0 (мок проверки статуса возвращает Blocked)"
|
||||
module: tests/test_reconciler.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-06
|
||||
type: unit
|
||||
description: "AC-6: задача в Plane-статусе Needs Input (retry<лимита) пропущена"
|
||||
module: tests/test_reconciler.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-07
|
||||
type: unit
|
||||
description: "AC-7 (no spam): на escalated-задаче не вызваны _note_unblock / send_telegram / notify_qg_failure; нет лог-строки 'разблокирована'"
|
||||
module: tests/test_reconciler.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-08
|
||||
type: unit
|
||||
description: "AC-8: на escalated-задаче мок check_ci_green НЕ вызван (skip раньше пред-оценки гейта)"
|
||||
module: tests/test_reconciler.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-09
|
||||
type: unit
|
||||
description: "AC-9 (регресс F-2): issue в Blocked/Needs Input не передаётся ни в handle_status_start, ни в handle_verdict при reconcile_plane_once; unblocked_total==0"
|
||||
module: tests/test_reconciler.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-10
|
||||
type: unit
|
||||
description: "AC-10 (never-raise): проверка blocked/needs-input бросает исключение на одной задаче → тик не падает, сбойная skip, валидная соседняя обработана"
|
||||
module: tests/test_reconciler.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-11
|
||||
type: unit
|
||||
description: "AC-11: граница берётся из stage_engine.MAX_DEVELOPER_RETRIES — тест с monkeypatch значения константы меняет точку отсечения (нет хардкода 3)"
|
||||
module: tests/test_reconciler.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-12
|
||||
type: integration
|
||||
description: "AC-13 (регресс): полный прогон tests/test_reconciler.py (ORCH-053 кейсы) — все прежние тесты зелёные"
|
||||
module: tests/test_reconciler.py
|
||||
expected: PASS
|
||||
Reference in New Issue
Block a user