analyst(ET): auto-commit from analyst run_id=420
All checks were successful
CI / test (push) Successful in 27s
All checks were successful
CI / test (push) Successful in 27s
This commit is contained in:
113
docs/work-items/ORCH-087/01-brd.md
Normal file
113
docs/work-items/ORCH-087/01-brd.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# 01-BRD — ORCH-087
|
||||
|
||||
**Заголовок:** Трекер-карточка застревает на старом статусе («To Analyse») + осиротевшие карточки при `bump`; + эффорт в строке стадии.
|
||||
|
||||
**Тип:** Багфикс (UX live-трекера) + малое расширение карточки.
|
||||
**Приоритет:** MEDIUM.
|
||||
**Зона:** `src/notifications.py` (`update_task_tracker` bump-режим, `render_task_tracker`), `src/db.py` (хранение message_id'ов задачи + колонка `agent_runs.effort`), `src/agents/launcher.py` (стамп фактического эффорта).
|
||||
**Связь:** ORCH-042 (введён bump), ORCH-067 (формат карточки, статус-строка, ссылки), ORCH-052h/ORCH-081 (эффорт реально работает), ORCH-080 (link-preview).
|
||||
|
||||
---
|
||||
|
||||
## 1. Контекст и проблема
|
||||
|
||||
Live-трекер ведёт **одну карточку на задачу** (`update_task_tracker`). Дефолтный режим — `bump`
|
||||
(ORCH-067): на каждом обновлении `delete_telegram(old_mid)` (best-effort, НЕ блокирует send) →
|
||||
`send_telegram(new)` (тихо, вниз чата) → `set_tracker_message_id(new_mid)` — но указатель
|
||||
`tasks.tracker_message_id` хранит **только последний** message_id.
|
||||
|
||||
**Симптом (Слава, скриншот 08.06, задача ORCH-082):**
|
||||
1. В Telegram висит карточка с заголовком «📍 To Analyse», хотя конвейер прошёл весь путь и все
|
||||
стадии ✅ вплоть до «Внедрение».
|
||||
2. Статусы деплоя не отражены (нет строки «Awaiting Deploy / Confirm Deploy»), хотя задача реально
|
||||
на стадии `deploy`.
|
||||
|
||||
**Код-аудит 08.06 (предварительная гипотеза, подлежит ПОДТВЕРЖДЕНИЮ — см. G0):**
|
||||
`render_task_tracker` СЕЙЧАС рендерит корректно (заголовок текущей стадии, deploy виден). Значит
|
||||
карточка со скриншота — **осиротевшая** старая (msg 18204), застрявшая на первом рендере
|
||||
(`_DEFAULT_STATUS_LABEL = "To Analyse"`). `bump` не удалил её: при рассинхроне `tracker_message_id`
|
||||
(сбой send → `new_mid=None` → указатель не перезаписан; рестарт орка между delete и send;
|
||||
пересоздание во время ручного фикса/CLI) часть карточек осиротевает и навсегда замирает на старом
|
||||
статусе. Проверено: бот МОЖЕТ удалять (`deleteMessage → ok:true` для 18204 и 18227) — дело не в
|
||||
правах, а в потере ссылки на старые message_id.
|
||||
|
||||
Это **диагноз-кандидат**, его нельзя принимать на веру (см. требование G0 ниже — расследование
|
||||
должно установить точную механику, потому что предыдущие диагнозы уже путали причины).
|
||||
|
||||
## 2. Цели (бизнес-уровень)
|
||||
|
||||
- **G0 (расследование ПЕРВЫМ, до фикса):** установить ТОЧНУЮ механику бага по данным, не вслепую.
|
||||
- **G1:** Не оставлять осиротевших карточек: при `bump` старая карточка удаляется ВСЕГДА, либо
|
||||
хранится список ВСЕХ созданных message_id задачи и незакрытые подчищаются.
|
||||
- **G2:** Заголовок живой карточки отражает ТЕКУЩУЮ стадию (не застывает на «To Analyse»).
|
||||
- **G3:** Статусы деплой-цикла (Awaiting Deploy / Confirm Deploy / Deploying / Monitoring / Done)
|
||||
видны на карточке на соответствующих стадиях.
|
||||
- **G4 (расширение):** В строку каждой стадии карточки добавить уровень эффорта рядом с моделью.
|
||||
|
||||
## 3. G0 — обязательное расследование (вход в ADR)
|
||||
|
||||
Вывод G0 оформляется архитектором в `06-adr/` ДО фикса. Расследование обязано ответить на:
|
||||
|
||||
- **R-1. Сколько РЕАЛЬНО карточек одной задачи висело в чате** к моменту бага — собрать `message_id`
|
||||
из логов/Telegram (сирот могло быть >1, а не ровно 2).
|
||||
- **R-2. В какие МОМЕНТЫ `tracker_message_id` рассинхронизируется** с реальными сообщениями:
|
||||
(a) `send` вернул `None` (нет креды/transient) → mid не перезаписан;
|
||||
(b) рестарт орка между `delete` и `send`;
|
||||
(c) пересоздание карточки во время CLI-фикса/ручных операций;
|
||||
(d) гонка двух `update_task_tracker` подряд (быстрые стадии);
|
||||
(e) `delete` упал (rate-limit/48ч), но `send` прошёл.
|
||||
По каждому пункту — подтвердить/опровергнуть как реальный источник сирот.
|
||||
- **R-3. Почему ИМЕННО заголовок застывает на «To Analyse»:** это старый рендер (до смены stage) или
|
||||
баг плана-лейбла? Воспроизвести на staging: прогнать задачу, на КАЖДОЙ стадии зафиксировать
|
||||
факт в Telegram (заголовок + тело) против БД (`tasks.stage`).
|
||||
- **R-4. `bump` vs `edit` — что реально надёжнее против сирот.** Замерить, не предполагать.
|
||||
`edit` правит ОДНО сообщение in-place (нет сирот, но карточка не уезжает вниз чата);
|
||||
`bump` держит карточку внизу (фича-просьба ORCH-042), но плодит сирот при рассинхроне. Дать
|
||||
обоснованную рекомендацию С ДАННЫМИ (оставить `bump` с гарантированной зачисткой ИЛИ перейти на
|
||||
`edit`).
|
||||
|
||||
## 4. G4 — эффорт в строке стадии (расширение)
|
||||
|
||||
Сейчас строка стадии показывает модель (`opus-4-8`), но не эффорт. После ORCH-052h/ORCH-081 эффорт
|
||||
реально применяется (developer=`xhigh`, tester/deployer=`medium`, прочие=`high`). Нужно показывать
|
||||
фактически применённый уровень рядом с моделью.
|
||||
|
||||
- **Формат** (компактно, на усмотрение архитектора): `… · opus-4-8 · xhigh` ИЛИ `… · opus-4-8/xhigh`.
|
||||
Пример полной строки: `✅ Разработка 7м · 4.9M↓/32k↑ · $3.97 · opus-4-8 · xhigh`.
|
||||
- **Источник эффорта (предпочтительно):** сохранять ФАКТИЧЕСКИ применённый эффорт в `agent_runs`
|
||||
(то, что реально ушло в CLI), а не пересчитывать `resolve_agent_effort(agent)` на рендере —
|
||||
новая колонка `agent_runs.effort` (как уже есть `agent_runs.model`). Это надёжнее: не зависит от
|
||||
изменения конфига между запуском и рендером.
|
||||
- developer-строка показывает `xhigh`; механические (tester/deployer) — `medium`.
|
||||
- Архитектор вправе вынести G4 отдельной мелкой задачей; **по умолчанию — в этом же PR** (одна зона:
|
||||
`notifications.py` + `agent_runs`).
|
||||
|
||||
## 5. Не-цели
|
||||
|
||||
- Не плодить дубликаты — инвариант «одна карточка на задачу» сохранить.
|
||||
- Не пинговать — `disable_notification` остаётся (карточка тихая).
|
||||
- Не ломать ссылки ORCH-067 (`plane_issue_link`) и подавление link-preview ORCH-080.
|
||||
- Не менять `STAGE_TRANSITIONS`, `QG_CHECKS`, статусную модель ORCH-066.
|
||||
- Не трогать поведение для не-self проектов сверх необходимого (общая БД/очередь — enduro-trails).
|
||||
|
||||
## 6. Ограничения / грабли
|
||||
|
||||
- **Telegram 48ч:** сообщения старше 48ч удалить нельзя (`deleteMessage` вернёт «message can't be
|
||||
deleted»). Для совсем старых сирот зачистка может не сработать — документировать как ограничение
|
||||
(поведение `delete_telegram`: такой исход трактуется как «уже не наша проблема», `_DELETE_GONE_MARKERS`).
|
||||
- **Rate-limit / 429** на массовом удалении при накопившихся сиротах.
|
||||
- **Общая БД/очередь** с enduro-trails — любое изменение схемы строго аддитивно и идемпотентно
|
||||
(`_ensure_column`), нулевая регрессия для других проектов.
|
||||
- **never-raise:** компонент нотификаций никогда не валит конвейер (CLAUDE.md, ORCH-067).
|
||||
|
||||
## 7. Заинтересованные лица
|
||||
- **Owner / наблюдатель (Слава)** — видит карточку, инициатор бага.
|
||||
- **Self-hosting прод** — карточки всех проектов из одного инстанса.
|
||||
|
||||
## 8. Критерии успеха (бизнес)
|
||||
См. `03-acceptance-criteria.md`. Кратко: завершённая задача не оставляет карточек с устаревшим
|
||||
заголовком; единственная актуальная карточка показывает текущий статус включая deploy-цикл; сироты
|
||||
не возникают/подчищаются при сбое send/рестарте; эффорт виден в каждой строке стадии; `pytest`
|
||||
зелёный, never-raise.
|
||||
</content>
|
||||
</invoke>
|
||||
130
docs/work-items/ORCH-087/02-trz.md
Normal file
130
docs/work-items/ORCH-087/02-trz.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# 02-ТЗ — ORCH-087
|
||||
|
||||
Техническое задание. Конкретные изменения кода/БД. Архитектурное решение (включая выбор
|
||||
`bump`-с-зачисткой vs `edit`, схему хранения message_id'ов, вывод G0) принимает архитектор и
|
||||
фиксирует в `06-adr/`. Ниже — границы и обязательные требования к реализации.
|
||||
|
||||
> ⚠️ Порядок работ жёсткий: **G0 (расследование → ADR) ПЕРВЫМ**, фикс G1–G3 и расширение G4 — только
|
||||
> после. Фикс вслепую без подтверждённой механики запрещён (см. BRD §3).
|
||||
|
||||
## 0. Задействованные модули `src/`
|
||||
- `src/notifications.py` — `update_task_tracker` (ветка `mode == "bump"`), `render_task_tracker`
|
||||
(`_stage_line` — строка стадии), низкоуровневые `send_telegram`/`delete_telegram`.
|
||||
- `src/db.py` — хранение message_id'ов задачи (G1) + миграция `agent_runs.effort` (G4),
|
||||
геттеры/сеттеры.
|
||||
- `src/agents/launcher.py` — `_spawn`: стамп фактически применённого `effort` в `agent_runs` (G4).
|
||||
- `src/config.py` — при необходимости новый kill-switch (например `tracker_orphan_sweep_enabled`).
|
||||
- `tests/` — новые/расширенные тесты (см. `04-test-plan.yaml`).
|
||||
|
||||
## 1. G0 — расследование (артефакт: `06-adr/`)
|
||||
Не код. Архитектор по данным логов/Telegram/staging-прогона отвечает на R-1…R-4 (BRD §3) и выносит
|
||||
решение: **остаётся `bump` с гарантированной зачисткой ИЛИ переход на `edit` по умолчанию**. Всё
|
||||
последующее ТЗ ниже описывает требования, инвариантные к этому выбору, и явно помечает пункты,
|
||||
зависящие от него.
|
||||
|
||||
## 2. G1 — не оставлять сирот (anti-orphan)
|
||||
**Требование:** при `bump`-обновлении в чате не должно оставаться более одной живой карточки задачи;
|
||||
старые message_id, не подтверждённые удалёнными, должны подчищаться.
|
||||
|
||||
Допустимые подходы (архитектор выбирает один; рекомендация — Б как наиболее устойчивый):
|
||||
|
||||
- **Подход А (только указатель + строгий swap):** `set_tracker_message_id(new_mid)` ТОЛЬКО при
|
||||
успешном `delete` старого ИЛИ при подтверждённом «уже нет» — но это не покрывает рестарт между
|
||||
delete и send. Недостаточно сам по себе.
|
||||
- **Подход Б (реестр всех message_id — рекомендуется):** хранить ВСЕ созданные message_id задачи и
|
||||
их состояние. Перед/после успешного `send` нового — подчистить все ранее не удалённые. Реализация:
|
||||
- Новая аддитивная таблица, например
|
||||
`tracker_messages(task_id INTEGER, message_id INTEGER, created_at TEXT, deleted INTEGER DEFAULT 0,
|
||||
PRIMARY KEY(task_id, message_id))` через `CREATE TABLE IF NOT EXISTS` (идемпотентно, не трогает
|
||||
`tasks`/`agent_runs`/enduro-данные).
|
||||
- При `send` нового — записать его в реестр; при удачном `delete` старого — пометить `deleted=1`.
|
||||
- **Sweep незакрытых:** перед отправкой свежей карточки (или фоновым проходом) пытаться удалить все
|
||||
`deleted=0` записи задачи, кроме актуального `tracker_message_id`; успешный/«уже-нет» delete →
|
||||
`deleted=1`. 48ч-«can't be deleted» (`_DELETE_GONE_MARKERS`) → тоже пометить `deleted=1` (наша
|
||||
проблема исчерпана) + лог.
|
||||
- `set_tracker_message_id` обновляется ТОЛЬКО при `new_mid is not None` (как сейчас — не обнулять
|
||||
указатель при transient send-failure).
|
||||
- **Подход В (переход на `edit`):** сменить дефолт `tracker_mode` на `edit` (правка одного сообщения
|
||||
in-place) — структурно исключает сирот, но теряет «карточка внизу чата» (фича ORCH-042). Допустим
|
||||
ТОЛЬКО если G0 докажет, что выгода против сирот перевешивает (решение — ADR).
|
||||
|
||||
**Инвариант (любой подход):** `update_task_tracker` остаётся `never-raise`; ни один сетевой сбой
|
||||
delete/send не валит конвейер; карточка всегда `disable_notification=True`.
|
||||
|
||||
## 3. G2 — заголовок отражает текущую стадию
|
||||
- При живой (не осиротевшей) карточке заголовок/статус-строка строятся `render_task_tracker` →
|
||||
`_card_status_label` → `plane_status_label(task_row)` из `tasks.stage` на момент рендера. Эта часть
|
||||
уже корректна (код-аудит). Реальная причина «застывания» — сирота (G1). Требование G2 удовлетворяется
|
||||
устранением сирот (G1): единственная живая карточка по определению отрисована на текущей стадии.
|
||||
- Доп. требование: убедиться (тест R-3), что при ПЕРЕрисовке (bump) свежая карточка всегда несёт
|
||||
актуальный `plane_status_label` — регрессионный тест «stage меняется → статус-строка меняется».
|
||||
|
||||
## 4. G3 — статусы deploy-цикла видимы
|
||||
- Offline-ядро уже даёт для `stage="deploy"` → «⏸️ Awaiting Deploy — ожидание Confirm Deploy»
|
||||
(`_STAGE_STATUS_LABEL`), для `done` → «Done».
|
||||
- Ветки `Deploying` / `Monitoring after Deploy` / `Confirm Deploy` рисуются live-overlay
|
||||
(`_live_plane_branch_override`, kill-switch `tracker_live_status`). Требование: проверить тестами,
|
||||
что весь deploy-цикл покрыт (offline-label для `deploy`/`done`; overlay-маппинг для
|
||||
`deploying`/`monitoring`). Если обнаружится дыра покрытия — закрыть в `_STAGE_STATUS_LABEL` /
|
||||
`_LIVE_BRANCH_LABELS` без изменения статусной модели ORCH-066.
|
||||
- Изменения `STAGE_TRANSITIONS` / `plane_sync` ключей НЕ требуются.
|
||||
|
||||
## 5. G4 — эффорт в строке стадии
|
||||
### 5.1 Схема БД (`src/db.py`)
|
||||
- Добавить колонку `agent_runs.effort TEXT` идемпотентной миграцией:
|
||||
`_ensure_column(conn, "agent_runs", "effort", "TEXT")` (рядом с `..."model"...`). NULL для старых
|
||||
строк — рендер деградирует (см. 5.3).
|
||||
|
||||
### 5.2 Стамп фактического эффорта (`src/agents/launcher._spawn`)
|
||||
- В `_spawn` эффорт уже резолвится: `effort = resolve_agent_effort(agent, project_id)` (стр. ~475).
|
||||
Записать ФАКТИЧЕСКИ применённое значение в `agent_runs` строкой run'а. Варианты:
|
||||
- расширить начальный `INSERT INTO agent_runs (task_id, agent)` → `(task_id, agent, effort)` с
|
||||
`effort` (то, что ушло в `--effort`; пустая строка `""` при omit → хранить NULL или `""`); ИЛИ
|
||||
- отдельный `UPDATE agent_runs SET effort=? WHERE id=?` сразу после получения `run_id`.
|
||||
- Источник — РОВНО та строка `effort`, что формирует `effort_flag` (фактически в CLI), НЕ повторный
|
||||
`resolve_agent_effort` на рендере. Если `effort==""` (флаг опущен) — хранить пустое/NULL.
|
||||
- Никогда не валить `_spawn` из-за записи эффорта (контракт launcher).
|
||||
|
||||
### 5.3 Рендер (`src/notifications.render_task_tracker._stage_line`)
|
||||
- В `SELECT ... FROM agent_runs` (стр. ~322) добавить `effort` в выборку.
|
||||
- В `_stage_line` после `model` добавить эффорт. Формат — компактный, рядом с моделью; рекомендуемый:
|
||||
`… · {model} · {effort}` при наличии обоих; при пустом `effort` — НЕ добавлять суффикс (как уже
|
||||
делает `model_suffix` для пустой модели). Точный разделитель (`·` vs `/`) — на усмотрение
|
||||
архитектора, но единообразно.
|
||||
- Деградация: `effort is None/""` → строка как сейчас (без эффорт-суффикса), без падения.
|
||||
|
||||
## 6. Изменения API
|
||||
Нет внешних HTTP API. Внутренние сигнатуры:
|
||||
- Возможен новый helper в `db.py` для реестра message_id (G1, подход Б): например
|
||||
`record_tracker_message(task_id, message_id)`, `mark_tracker_message_deleted(task_id, message_id)`,
|
||||
`list_live_tracker_messages(task_id) -> list[int]`. Сигнатуры — на усмотрение архитектора.
|
||||
- `get_tracker_message_id` / `set_tracker_message_id` — сохраняются (обратная совместимость).
|
||||
|
||||
## 7. Изменения схемы БД (сводно, всё аддитивно/идемпотентно)
|
||||
- `agent_runs.effort TEXT` (G4) — `_ensure_column`.
|
||||
- (Подход Б) `tracker_messages(...)` — `CREATE TABLE IF NOT EXISTS`.
|
||||
- Никаких изменений существующих колонок; нулевая регрессия для enduro-trails в общей БД.
|
||||
|
||||
## 8. Требования к новым QG checks
|
||||
Нет. Изменения только в слое нотификаций/БД; `QG_CHECKS`, гейты, машина стадий не трогаются.
|
||||
|
||||
## 9. Конфигурация (`src/config.py`)
|
||||
- При подходе Б — опциональный kill-switch `tracker_orphan_sweep_enabled: bool = True` (env
|
||||
`ORCH_TRACKER_ORPHAN_SWEEP_ENABLED`) для безопасного раската/отката зачистки.
|
||||
- При подходе В — менять дефолт `tracker_mode` (сейчас `"bump"`). Решение — ADR.
|
||||
|
||||
## 10. Артефакты pipeline (обновить в ТОМ ЖЕ PR)
|
||||
- `CLAUDE.md` — секция «Нотификации / Telegram live-tracker»: описать anti-orphan-зачистку и
|
||||
эффорт-в-строке-стадии.
|
||||
- `docs/architecture/README.md` — компонент «Notifications / Live-tracker»: то же.
|
||||
- `CHANGELOG.md` — `## [Unreleased]` (union-merge, ORCH-073).
|
||||
- `06-adr/ADR-NNN-*.md` — вывод G0 + принятое решение (bump-зачистка vs edit) + схема хранения.
|
||||
- При необходимости `08-data-requirements.md` (схема `tracker_messages` / `agent_runs.effort`).
|
||||
|
||||
## 11. Инварианты (не нарушать)
|
||||
- never-raise во всём слое нотификаций; конвейер не блокируется сетевыми сбоями Telegram.
|
||||
- «Одна карточка на задачу» — после устранения сирот строго одна живая карточка.
|
||||
- Карточка тихая (`disable_notification=True`); пингуют только alert-хелперы.
|
||||
- `disable_web_page_preview=True` (ORCH-080) и кликабельный `plane_issue_link` (ORCH-067) сохранены.
|
||||
- Миграции идемпотентны и безопасны на живой общей прод-БД.
|
||||
</content>
|
||||
93
docs/work-items/ORCH-087/03-acceptance-criteria.md
Normal file
93
docs/work-items/ORCH-087/03-acceptance-criteria.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# 03-Критерии приёмки — ORCH-087
|
||||
|
||||
Каждый критерий имеет явное условие PASS/FAIL. Проверяется тестами (`04-test-plan.yaml`) и/или
|
||||
ручной верификацией на staging.
|
||||
|
||||
## G0 — расследование
|
||||
|
||||
### AC-0.1 — механика бага установлена по данным
|
||||
- **PASS:** в `06-adr/` зафиксированы ответы на R-1…R-4 (BRD §3): реальное число висевших карточек
|
||||
(с message_id), подтверждённые/опровергнутые источники рассинхрона (a–e), точная причина застывания
|
||||
заголовка (старый рендер vs баг лейбла) с воспроизведением на staging, и обоснованная рекомендация
|
||||
`bump`-с-зачисткой vs `edit` С ДАННЫМИ.
|
||||
- **FAIL:** фикс начат без ADR; вывод опирается на предположения, а не на собранные message_id /
|
||||
staging-прогон.
|
||||
|
||||
### AC-0.2 — фикс следует за расследованием
|
||||
- **PASS:** изменения кода G1–G4 соответствуют решению ADR (выбранный режим/схема хранения).
|
||||
- **FAIL:** реализация противоречит выводу G0 или сделана до него.
|
||||
|
||||
## G1–G3 — сироты, заголовок, deploy-цикл
|
||||
|
||||
### AC-1 — нет устаревшего заголовка на завершённой задаче
|
||||
- **PASS:** после прохождения всех стадий в чате НЕ остаётся карточек с заголовком/статус-строкой
|
||||
«To Analyse» (или иным устаревшим). Тест: прогон полного жизненного цикла → единственная карточка
|
||||
показывает финальный статус.
|
||||
- **FAIL:** в чате остаётся хотя бы одна карточка с устаревшим статусом.
|
||||
|
||||
### AC-2 — единственная актуальная карточка показывает текущий статус, включая deploy-цикл
|
||||
- **PASS:** для `stage="deploy"` статус-строка = «⏸️ Awaiting Deploy — ожидание Confirm Deploy»;
|
||||
для `done` = «Done»; ветки `Deploying` / `Confirm Deploy` / `Monitoring after Deploy` отображаются
|
||||
через live-overlay при соответствующем Plane-статусе. Весь deploy-цикл покрыт.
|
||||
- **FAIL:** на стадии deploy/done карточка показывает не-deploy статус, или ветка deploy-цикла не
|
||||
покрыта рендером.
|
||||
|
||||
### AC-3 — нет сирот при сбое send / рестарте орка
|
||||
- **PASS:** при `send`, вернувшем `None` (нет креды/transient), указатель `tracker_message_id` не
|
||||
обнуляется; ранее созданные карточки либо удалены, либо учтены в реестре и подчищаются при следующем
|
||||
обновлении / sweep. После симуляции «рестарт между delete и send» сироты не накапливаются.
|
||||
- **FAIL:** transient send обнуляет указатель; осиротевшие карточки накапливаются и не подчищаются.
|
||||
|
||||
### AC-4 — инвариант «одна карточка на задачу»
|
||||
- **PASS:** в любой момент после успешного обновления у задачи живёт строго одна карточка; дубликаты в
|
||||
пределах одного `update_task_tracker` не создаются (≤1 send за вызов).
|
||||
- **FAIL:** наблюдается >1 живой карточки или дублирующий send в одном вызове.
|
||||
|
||||
### AC-5 — never-raise и тихая карточка
|
||||
- **PASS:** любой сбой Telegram (delete/send/edit: network/timeout/5xx/429/48ч) не поднимает
|
||||
исключение из `update_task_tracker` и не блокирует конвейер; карточка отправляется с
|
||||
`disable_notification=True`; `disable_web_page_preview=True`; `plane_issue_link` кликабелен.
|
||||
- **FAIL:** исключение пробивается наружу, карточка пингует, link-preview разворачивается, или ссылка
|
||||
сломана.
|
||||
|
||||
### AC-6 — ограничение 48ч документировано
|
||||
- **PASS:** в коде/ADR/CLAUDE.md зафиксировано: сироты старше 48ч удалить нельзя (Telegram); такой
|
||||
delete трактуется как «уже не наша проблема» (`_DELETE_GONE_MARKERS`) и помечается обработанным без
|
||||
падения.
|
||||
- **FAIL:** ограничение не документировано или 48ч-ошибка ломает зачистку.
|
||||
|
||||
## G4 — эффорт в строке стадии
|
||||
|
||||
### AC-7 — эффорт виден в каждой строке стадии
|
||||
- **PASS:** строка завершённой стадии показывает уровень эффорта рядом с моделью, формат компактный
|
||||
(например `… · opus-4-8 · xhigh` или `… · opus-4-8/xhigh`).
|
||||
- **FAIL:** эффорт не отображается, либо ломает формат/выравнивание существующей строки.
|
||||
|
||||
### AC-8 — корректные уровни по ролям
|
||||
- **PASS:** developer-строка показывает `xhigh`; механические (tester/deployer) — `medium`; прочие
|
||||
(analyst/architect/reviewer) — `high` (при дефолтном конфиге). Значение берётся из фактически
|
||||
применённого эффорта (`agent_runs.effort`), а не пересчётом на рендере.
|
||||
- **FAIL:** уровни не соответствуют фактически применённым / берутся пересчётом и расходятся с CLI.
|
||||
|
||||
### AC-9 — деградация при отсутствии эффорта
|
||||
- **PASS:** для старых `agent_runs` строк (`effort` NULL/пусто) рендер опускает эффорт-суффикс без
|
||||
падения (как для пустой модели).
|
||||
- **FAIL:** NULL-эффорт ломает рендер или печатает мусор (`None`).
|
||||
|
||||
### AC-10 — миграция безопасна и аддитивна
|
||||
- **PASS:** `agent_runs.effort` (и при подходе Б — `tracker_messages`) добавляются идемпотентно
|
||||
(`_ensure_column` / `CREATE TABLE IF NOT EXISTS`); повторный `init_db` — no-op; существующие колонки
|
||||
и данные enduro-trails не затронуты.
|
||||
- **FAIL:** миграция не идемпотентна, меняет существующие колонки или ломает общую прод-БД.
|
||||
|
||||
## Сквозное
|
||||
|
||||
### AC-11 — документация обновлена в том же PR
|
||||
- **PASS:** обновлены `CLAUDE.md` (секция трекера), `docs/architecture/README.md` (компонент
|
||||
Notifications), `CHANGELOG.md` (`## [Unreleased]`), создан `06-adr/ADR-NNN-*.md`.
|
||||
- **FAIL:** функционал изменён, документация — нет (reviewer → REQUEST_CHANGES).
|
||||
|
||||
### AC-12 — pytest зелёный
|
||||
- **PASS:** `pytest tests/ -q` зелёный; новые тесты из `04-test-plan.yaml` присутствуют и проходят.
|
||||
- **FAIL:** красные/отсутствующие тесты.
|
||||
</content>
|
||||
115
docs/work-items/ORCH-087/04-test-plan.yaml
Normal file
115
docs/work-items/ORCH-087/04-test-plan.yaml
Normal file
@@ -0,0 +1,115 @@
|
||||
work_item: ORCH-087
|
||||
description: >
|
||||
Тест-план для anti-orphan bump-зачистки live-трекера, корректности заголовка/deploy-статусов
|
||||
и эффорта в строке стадии. Все тесты автономны (Telegram-примитивы мокаются monkeypatch'ем,
|
||||
как в существующих tests/test_tracker_bump*.py). Контракт never-raise проверяется явно.
|
||||
tests:
|
||||
|
||||
# --- G1: anti-orphan bump ------------------------------------------------
|
||||
- id: TC-01
|
||||
type: unit
|
||||
description: >
|
||||
bump: успешный send нового сообщения помечает старые незакрытые карточки на удаление и
|
||||
delete_telegram вызывается для каждого ранее созданного, не удалённого message_id (sweep).
|
||||
module: tests/test_tracker_orphan_sweep.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-02
|
||||
type: unit
|
||||
description: >
|
||||
bump: send вернул None (нет креды/transient) -> tracker_message_id НЕ обнуляется,
|
||||
реестр прежних message_id не теряется, дубликата не создано (<=1 send за вызов).
|
||||
module: tests/test_tracker_orphan_sweep.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-03
|
||||
type: unit
|
||||
description: >
|
||||
Симуляция рассинхрона "рестарт между delete и send": в реестре остаётся незакрытый старый
|
||||
mid; следующий update_task_tracker подчищает его (delete вызван) -> сироты не накапливаются.
|
||||
module: tests/test_tracker_orphan_sweep.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-04
|
||||
type: unit
|
||||
description: >
|
||||
delete старше 48ч (Telegram "message can't be deleted", _DELETE_GONE_MARKERS) -> запись
|
||||
помечается обработанной (deleted=1), без падения, без бесконечных повторов.
|
||||
module: tests/test_tracker_orphan_sweep.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-05
|
||||
type: unit
|
||||
description: >
|
||||
never-raise: delete и send оба бросают/возвращают ошибку -> update_task_tracker не поднимает
|
||||
исключение; карточка отправляется с disable_notification=True и disable_web_page_preview=True.
|
||||
module: tests/test_tracker_orphan_sweep.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-06
|
||||
type: unit
|
||||
description: >
|
||||
Инвариант "одна карточка": за один update_task_tracker отправляется не более одного нового
|
||||
сообщения; после успешного цикла в реестре ровно один live (deleted=0) message_id.
|
||||
module: tests/test_tracker_orphan_sweep.py
|
||||
expected: PASS
|
||||
|
||||
# --- G2/G3: заголовок и deploy-статусы -----------------------------------
|
||||
- id: TC-07
|
||||
type: unit
|
||||
description: >
|
||||
Регресс: при смене tasks.stage статус-строка карточки меняется (created->To Analyse,
|
||||
analysis->Analysis, deploy->Awaiting Deploy, done->Done); НЕ застывает на To Analyse.
|
||||
module: tests/test_tracker_status_line.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-08
|
||||
type: unit
|
||||
description: >
|
||||
plane_status_label покрывает весь deploy-цикл offline: stage=deploy -> "Awaiting Deploy",
|
||||
stage=done -> "Done"; live-overlay маппит deploying/monitoring/confirm на свои лейблы.
|
||||
module: tests/test_tracker_status_line.py
|
||||
expected: PASS
|
||||
|
||||
# --- G4: эффорт в строке стадии ------------------------------------------
|
||||
- id: TC-09
|
||||
type: unit
|
||||
description: >
|
||||
Миграция agent_runs.effort идемпотентна: повторный init_db не падает, колонка присутствует,
|
||||
существующие колонки/данные не затронуты.
|
||||
module: tests/test_db_effort_column.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-10
|
||||
type: unit
|
||||
description: >
|
||||
_spawn стампит фактически применённый effort в agent_runs (то, что ушло в --effort), без
|
||||
повторного resolve на рендере; пустой effort (omit) -> NULL/"" без падения.
|
||||
module: tests/test_launcher.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-11
|
||||
type: unit
|
||||
description: >
|
||||
render_task_tracker: строка стадии показывает эффорт рядом с моделью в компактном формате;
|
||||
developer-строка -> xhigh, tester/deployer -> medium (по данным agent_runs.effort).
|
||||
module: tests/test_tracker_effort_line.py
|
||||
expected: PASS
|
||||
|
||||
- id: TC-12
|
||||
type: unit
|
||||
description: >
|
||||
Деградация: agent_runs.effort NULL/пусто -> строка рендерится без эффорт-суффикса (как при
|
||||
пустой модели), без вывода "None" и без падения.
|
||||
module: tests/test_tracker_effort_line.py
|
||||
expected: PASS
|
||||
|
||||
# --- Сквозное -------------------------------------------------------------
|
||||
- id: TC-13
|
||||
type: integration
|
||||
description: >
|
||||
Полный жизненный цикл задачи (created->done) через update_task_tracker на каждой стадии:
|
||||
в конце ровно одна live-карточка с финальным статусом "Done", сирот нет, never-raise.
|
||||
module: tests/test_tracker_lifecycle.py
|
||||
expected: PASS
|
||||
</content>
|
||||
Reference in New Issue
Block a user