analyst(ET): auto-commit from analyst run_id=361
This commit is contained in:
158
docs/work-items/ORCH-067/01-brd.md
Normal file
158
docs/work-items/ORCH-067/01-brd.md
Normal file
@@ -0,0 +1,158 @@
|
||||
# BRD — ORCH-067: Telegram tracker (bump + статусы Plane + кликабельный номер задачи)
|
||||
|
||||
Work Item: **ORCH-067**
|
||||
Тип: **Багфикс + enhancement**
|
||||
Приоритет: высокий
|
||||
Компонент: Telegram live-tracker и уведомления оркестратора (`src/notifications.py`)
|
||||
Расширяет: открытый баг seq=55 («bump не сработал, регресс ORCH-042»)
|
||||
|
||||
---
|
||||
|
||||
## 1. Бизнес-контекст и проблема
|
||||
|
||||
Оркестратор ведёт по одной «живой карточке» (live-tracker) на каждую задачу в Telegram
|
||||
(`src/notifications.py`). Карточка тихо обновляется на каждом переходе стадии, а отдельными
|
||||
пингами шлются только события, требующие внимания владельца (approve-gate, деплой-фейл,
|
||||
падение агента, ошибка задачи).
|
||||
|
||||
Сейчас есть четыре боли:
|
||||
|
||||
1. **bump не работает в проде.** Диагностика оператора: код режима `bump` в
|
||||
`update_task_tracker` корректен (delete старого → sendMessage вниз → repoint
|
||||
`tracker_message_id`), НО в проде `tracker_mode="edit"` (дефолт `src/config.py:408`),
|
||||
а `ORCH_TRACKER_MODE=bump` не выставлен. Карточка обновляется edit-in-place и остаётся
|
||||
«вверху» ленты, тонет под новыми сообщениями — наблюдатель не видит актуального
|
||||
состояния без скролла.
|
||||
|
||||
2. **Карточка показывает внутренние названия стадий, а не Plane-статусы.** После ввода
|
||||
осмысленной статусной модели Plane (ORCH-066) карточка по-прежнему рендерит внутренние
|
||||
ярлыки стадий (Анализ/Архитектура/…), а текущий статус задачи в терминах, понятных
|
||||
наблюдателю в Plane (To Analyse → Analysis → In Review → … → Done), в шапке карточки
|
||||
не отражён. Особенно теряется состояние **ожидания согласования BRD** = Plane-статус
|
||||
`In Review`: сейчас это лишь строка «✅/⏸️ Подтверждение BRD … ⏳», не выраженная как
|
||||
полноценный статус.
|
||||
|
||||
3. **Номер задачи в карточке некликабелен.** `ORCH-066` в карточке — обычный текст;
|
||||
чтобы открыть задачу в Plane, наблюдателю приходится искать её вручную.
|
||||
|
||||
4. **Номер задачи некликабелен и во всех остальных уведомлениях орка** (approve-requested,
|
||||
QG-fail, deploy SUCCESS/FAIL, Needs Input, прод-деплой и т. п.) — везде, где упоминается
|
||||
`work_item_id`, это просто текст.
|
||||
|
||||
## 2. Цель
|
||||
|
||||
Сделать live-tracker и уведомления орка наблюдаемыми «из коробки»:
|
||||
- bump работает по умолчанию (карточка падает вниз свежим сообщением при каждом обновлении,
|
||||
ровно одна карточка на задачу, без спама и дублей);
|
||||
- карточка явно показывает текущий Plane-статус по модели ORCH-066, включая человеческие
|
||||
гейты (`⏸️ In Review` — согласование BRD, `⏸️ Awaiting Deploy` — ожидание Confirm Deploy,
|
||||
`❓ Needs Input` — нужны уточнения);
|
||||
- номер задачи кликабелен в карточке и во всех Telegram-уведомлениях орка и ведёт на
|
||||
страницу задачи в Plane.
|
||||
|
||||
## 3. Заинтересованные стороны
|
||||
|
||||
- **Owner (Слава)** — основной потребитель карточки и уведомлений; источник 4 требований.
|
||||
- **Агенты конвейера** — косвенно (карточка отражает их прогресс; поведение агентов не меняется).
|
||||
- **Другие проекты (enduro-trails)** — общий инстанс/БД; изменения не должны вызывать регресс.
|
||||
|
||||
## 4. Объём работ (scope)
|
||||
|
||||
### 4.1. Требование 1 — bump по умолчанию
|
||||
- Режим `bump` должен быть поведением по умолчанию: при каждом обновлении карточка
|
||||
удаляется и пересоздаётся внизу ленты, одна карточка на задачу, тихо
|
||||
(`disable_notification`), без дублей.
|
||||
- Инвариант «одна карточка на задачу» сохраняется в обоих режимах (`edit` остаётся как
|
||||
опция через env).
|
||||
- Транзиентный фейл `send` не должен обнулять `tracker_message_id` и плодить дубли
|
||||
(инвариант уже заложен в коде — сохранить).
|
||||
|
||||
### 4.2. Требование 2 — статусы карточки как в Plane (модель ORCH-066)
|
||||
- В шапке/верхней части карточки явно отображается **текущий Plane-статус** задачи по
|
||||
модели ORCH-066.
|
||||
- Полный маппинг состояний (имена — финальные из модели ORCH-066):
|
||||
```
|
||||
To Analyse → Analysis → In Review (⏸️ ожидание согласования BRD) → Architecture →
|
||||
Development → Code-Review → Testing → Awaiting Deploy (⏸️ ожидание Confirm Deploy) →
|
||||
Deploying → Monitoring after Deploy → Done
|
||||
```
|
||||
Ветки: `Needs Input` (аналитик задал вопросы), `Blocked`, `Rejected`, `Cancelled`.
|
||||
- Человеческие гейты отражаются как ПОЛНОЦЕННЫЕ статусы с паузой:
|
||||
- согласование BRD → «⏸️ In Review — ожидание согласования BRD»;
|
||||
- ожидание прод-деплоя → «⏸️ Awaiting Deploy — ожидание Confirm Deploy»;
|
||||
- вопросы аналитика → «❓ Needs Input — нужны уточнения».
|
||||
- Существующая семантика строки «Подтверждение BRD» сохраняется (время ожидания/«твоё
|
||||
время»), но статус карточки при этом явно показывает In Review (approve-pending).
|
||||
|
||||
### 4.3. Требование 3 — кликабельный номер задачи в карточке
|
||||
- `work_item_id` (напр. `ORCH-066`) в карточке — гиперссылка на страницу задачи Plane:
|
||||
`https://<PLANE_WEB_BASE>/<workspace_slug>/projects/<project_id>/issues/<issue_id>/`.
|
||||
- Источники частей URL:
|
||||
- `PLANE_WEB_BASE` — из конфигурации (env, поле `plane_web_url` / `ORCH_PLANE_WEB_URL`;
|
||||
значение прод — `plane.mva154.duckdns.org`); fail-safe: не задан → номер без ссылки;
|
||||
- `workspace_slug` — `plane_workspace_slug` (уже есть в settings, прод — `ag_proj`);
|
||||
- `project_id` — резолвится per-task по репозиторию задачи (ORCH / Sandbox);
|
||||
- `issue_id` (UUID) — из БД: колонка `tasks.plane_issue_id`.
|
||||
- Рендер через `<a href="...">ORCH-NNN</a>` (`parse_mode=HTML` уже включён);
|
||||
HTML в title/тексте экранируется, чтобы не сломать разметку.
|
||||
|
||||
### 4.4. Требование 4 — кликабельный номер во ВСЕХ уведомлениях орка
|
||||
- Единый хелпер (напр. `plane_issue_link(work_item_id, plane_issue_id, project_id) -> html`)
|
||||
строит кликабельный номер с fail-safe; применяется во всех точках `send_telegram`/
|
||||
`notify_*`, где упоминается `work_item_id` (approve-requested, QG-fail, deploy
|
||||
SUCCESS/FAIL, Needs Input, прод-деплой, alert'ы launcher/merge_gate/job_reaper/
|
||||
security_gate/reconciler/main).
|
||||
|
||||
## 5. Вне объёма (out of scope)
|
||||
|
||||
- Транспорт `send_telegram` / `edit_telegram` / `delete_telegram` (parse_mode HTML уже есть) — не трогать.
|
||||
- Инвариант «одна карточка на задачу» — не нарушать (не плодить дубли).
|
||||
- Логика `disable_notification` (карточка тихая; пингуют только alert-хелперы) — не трогать.
|
||||
- `STAGE_TRANSITIONS`, Quality Gates, схема БД — НЕ менять.
|
||||
- Изменение поведения агентов/конвейера.
|
||||
|
||||
## 6. Зависимости
|
||||
|
||||
- Маппинг статусов (требование 2) опирается на статусную модель ORCH-066. ORCH-066 уже в
|
||||
конвейере на стадии deploy. Эту задачу делать ПОСЛЕ прода ORCH-066, чтобы имена статусов
|
||||
совпали. Если ORCH-066 ещё не в проде на момент разработки — использовать согласованные
|
||||
финальные имена из модели: To Analyse, Analysis, Code-Review, Awaiting Deploy, Deploying,
|
||||
Monitoring after Deploy, In Review, Needs Input, Blocked, Cancelled, Done.
|
||||
- Конфигурация `plane_web_url` / `plane_workspace_slug` уже существует в `src/config.py`
|
||||
(ORCH-017); реестр проектов `src/projects.py` (`get_project_by_repo().plane_project_id`)
|
||||
уже даёт per-task project_id.
|
||||
|
||||
## 7. Fail-safe (обязательно)
|
||||
|
||||
- Нет `PLANE_WEB_BASE` / нет `plane_issue_id` / нет `project_id` / loopback-база →
|
||||
показывать номер БЕЗ ссылки, **не падать**.
|
||||
- HTML-экранирование пользовательского текста (title и пр.) во всех сообщениях с
|
||||
`parse_mode=HTML`.
|
||||
- Bump: транзиентный фейл `send` не обнуляет `tracker_message_id` и не плодит дубли.
|
||||
- Любая ошибка построения статуса/ссылки никогда не должна ронять рендер карточки или
|
||||
отправку уведомления (degrade gracefully).
|
||||
|
||||
## 8. Критерии успеха (Definition of Done)
|
||||
|
||||
- Bump работает из коробки: карточка падает вниз при обновлении, одна на задачу.
|
||||
- Карточка показывает Plane-статус новой модели, включая `⏸️ In Review` (согласование BRD),
|
||||
`⏸️ Awaiting Deploy`, `❓ Needs Input`.
|
||||
- Номер задачи кликабелен в карточке И во всех уведомлениях орка (ведёт на страницу Plane).
|
||||
- Fail-safe покрыт тестами (нет URL/plane_id/project → без ссылки, не падает;
|
||||
HTML-экранирование).
|
||||
- `pytest tests/ -q` зелёный.
|
||||
- Документация обновлена в том же PR: `CLAUDE.md` (раздел нотификаций/tracker),
|
||||
`CHANGELOG.md`, ADR per-work-item.
|
||||
|
||||
## 9. Риски
|
||||
|
||||
- **Регресс enduro-trails.** Смена дефолта `tracker_mode` на bump меняет поведение для всех
|
||||
проектов. Митигация: bump уже реализован и протестирован концептуально; инвариант «одна
|
||||
карточка» сохранён; env-переключатель `edit` остаётся.
|
||||
- **Поломка HTML-разметки** при неэкранированном title → сообщение не доставится. Митигация:
|
||||
обязательное `html.escape` + тесты.
|
||||
- **Источник «истинного» Plane-статуса** для веток, не выводимых из `tasks.stage`
|
||||
(Needs Input/Blocked/Rejected/Cancelled, Deploying/Monitoring), при запрете на изменение
|
||||
схемы БД — архитектурное решение (ADR), с обязательным fail-safe (без сети не падать).
|
||||
- **Self-hosting.** Орк правит сам себя; обязательна страховка через staging (8501) перед
|
||||
прод-деплоем; прод-контейнер не ронять в рамках задачи.
|
||||
205
docs/work-items/ORCH-067/02-trz.md
Normal file
205
docs/work-items/ORCH-067/02-trz.md
Normal file
@@ -0,0 +1,205 @@
|
||||
# ТЗ — ORCH-067: Telegram tracker (bump + статусы Plane + кликабельный номер задачи)
|
||||
|
||||
Work Item: **ORCH-067**
|
||||
Документ описывает КОНКРЕТНЫЕ изменения кода/конфигурации/тестов и документации.
|
||||
Архитектурные развилки помечены `[ARCH]` — решение принимает архитектор (ADR), здесь
|
||||
зафиксированы только требования и ограничения к ним.
|
||||
|
||||
---
|
||||
|
||||
## 0. Задействованные модули `src/`
|
||||
|
||||
| Модуль | Роль в задаче |
|
||||
|---|---|
|
||||
| `src/config.py` | Дефолт `tracker_mode`; поле `plane_web_url`/`plane_workspace_slug` (уже есть). |
|
||||
| `src/notifications.py` | Основные изменения: bump-дефолт, статус-строка карточки, хелпер ссылки, применение хелпера в `notify_*`. |
|
||||
| `src/plane_sync.py` | Источник имён статусов/маппинга ORCH-066 (`_PLANE_NAME_TO_KEY`, `_STAGE_TO_STATE_KEY`); при необходимости reverse-map UUID→имя `[ARCH]`. |
|
||||
| `src/projects.py` | `get_project_by_repo(repo).plane_project_id` — per-task project_id для ссылки. |
|
||||
| `src/db.py` | Чтение `tasks.plane_issue_id`, `tasks.repo` (без изменений схемы). |
|
||||
| `src/stage_engine.py`, `src/agents/launcher.py`, `src/merge_gate.py`, `src/job_reaper.py`, `src/security_gate.py`, `src/reconciler.py`, `src/main.py` | Точки `send_telegram`, где есть `work_item_id` — применить хелпер ссылки (требование 4). |
|
||||
|
||||
Изменения API (HTTP endpoints) — **нет**. Изменения схемы БД — **нет**. Новые QG checks — **нет**.
|
||||
|
||||
---
|
||||
|
||||
## 1. Требование 1 — bump по умолчанию
|
||||
|
||||
### 1.1. Изменение
|
||||
- `src/config.py` (~стр. 408): сменить дефолт
|
||||
`tracker_mode: str = "edit"` → `tracker_mode: str = "bump"`.
|
||||
- Обновить docstring-комментарий рядом (ORCH-042): отметить, что **дефолт теперь `bump`**,
|
||||
`edit` остаётся доступен через `ORCH_TRACKER_MODE=edit`.
|
||||
|
||||
### 1.2. Без изменений (сохранить инвариант)
|
||||
- Логика `update_task_tracker` (`src/notifications.py`, ветка `if mode == "bump"`):
|
||||
`delete_telegram(old)` best-effort → `send_telegram(text, disable_notification=True)` →
|
||||
`set_tracker_message_id` ТОЛЬКО при `new_mid is not None`. Не менять.
|
||||
- `send_telegram`/`edit_telegram`/`delete_telegram` — не трогать.
|
||||
|
||||
### 1.3. Прод-аспект
|
||||
- Для прод-инстанса орка можно дополнительно выставить `ORCH_TRACKER_MODE=bump` в `.env`
|
||||
на хосте (как страховку), но код должен работать «из коробки» и без env. Канон env —
|
||||
`.env.example` (обновить, если там фигурирует tracker_mode).
|
||||
|
||||
---
|
||||
|
||||
## 2. Требование 2 — статус-строка карточки по модели ORCH-066
|
||||
|
||||
### 2.1. Новый чистый хелпер маппинга
|
||||
Добавить в `src/notifications.py` функцию, возвращающую отображаемый Plane-статус для
|
||||
карточки на основе доступных данных задачи. Сигнатура (ориентир):
|
||||
```python
|
||||
def plane_status_label(task_row) -> str:
|
||||
"""Вернуть строку текущего Plane-статуса для шапки карточки (с emoji).
|
||||
Никогда не падает: на неизвестном входе -> разумный дефолт по stage."""
|
||||
```
|
||||
Хелпер обязан быть чистым/детерминированным от входных данных и **никогда не бросать**
|
||||
исключения (любая ошибка → дефолт по `stage`, рендер карточки не ломается).
|
||||
|
||||
### 2.2. Маппинг внутреннее состояние → Plane-статус (обязательные строки)
|
||||
Имена статусов — финальные из модели ORCH-066 (см. `_PLANE_NAME_TO_KEY` в `plane_sync.py`).
|
||||
|
||||
| Источник (данные задачи в БД) | Plane-статус (отображение в карточке) |
|
||||
|---|---|
|
||||
| `stage == "created"` | `To Analyse` |
|
||||
| `stage == "analysis"`, BRD-clock не запущен | `Analysis` |
|
||||
| `stage == "analysis"`, `brd_review_started_at` есть, `brd_review_ended_at` пуст | `⏸️ In Review — ожидание согласования BRD` |
|
||||
| `stage == "architecture"` | `Architecture` |
|
||||
| `stage == "development"` | `Development` |
|
||||
| `stage == "review"` | `Code-Review` |
|
||||
| `stage == "testing"` | `Testing` |
|
||||
| `stage == "deploy"` (ожидание Confirm Deploy) | `⏸️ Awaiting Deploy — ожидание Confirm Deploy` |
|
||||
| `stage == "done"` | `Done` |
|
||||
|
||||
Ветки (Needs Input / Blocked / Rejected / Cancelled / Deploying / Monitoring after Deploy):
|
||||
- `❓ Needs Input — нужны уточнения` — состояние «аналитик задал вопросы»;
|
||||
- `Blocked`, `Rejected`, `Cancelled`, `Deploying`, `Monitoring after Deploy`.
|
||||
|
||||
`[ARCH]` **Источник сигнала для веток, не выводимых из `tasks.stage`** (Needs Input,
|
||||
Blocked, Rejected, Cancelled, Deploying, Monitoring after Deploy):
|
||||
- запрещено менять схему БД (нельзя добавлять колонку-флаг);
|
||||
- варианты для архитектора: (а) best-effort чтение живого Plane-статуса
|
||||
(`fetch_issue_state` + reverse-map UUID→имя через `get_project_states`/
|
||||
`_PLANE_NAME_TO_KEY`) с обязательным fail-safe (нет сети/ответа → деградация на
|
||||
stage-маппинг, без задержки, блокирующей конвейер); (б) только stage-выводимые статусы,
|
||||
а ветки — по уже имеющимся сигналам (например, In Review через brd-clock).
|
||||
- ОБЯЗАТЕЛЬНО к покрытию (DoD): `⏸️ In Review`, `⏸️ Awaiting Deploy`, `❓ Needs Input`.
|
||||
In Review полностью выводится из brd-clock (см. таблицу) и должен работать без сети.
|
||||
|
||||
### 2.3. Встраивание в `render_task_tracker`
|
||||
- В `render_task_tracker` (`src/notifications.py`) добавить в шапку/верх карточки отдельную
|
||||
СТРОКУ статуса (под заголовком `🛠️ ORCH-NNN · <title>` / над разделителем `bar`),
|
||||
напр.: `📍 <status_label>`.
|
||||
- Существующие строки по стадиям (`✅ done` / `🔄 active`), строка «Подтверждение BRD»,
|
||||
тоталы токенов/стоимости, done-строка с PR/⏱️ — СОХРАНИТЬ (семантику не ломать).
|
||||
- Семантика строки «Подтверждение BRD» (⏸️+⏳ при ожидании, ✅ при пройденном гейте)
|
||||
сохраняется; новая статус-строка дублирует её смысл в терминах Plane-статуса.
|
||||
|
||||
---
|
||||
|
||||
## 3. Требование 3 + 4 — кликабельный номер задачи
|
||||
|
||||
### 3.1. Единый хелпер
|
||||
Добавить в `src/notifications.py`:
|
||||
```python
|
||||
def plane_issue_link(work_item_id, plane_issue_id=None, project_id=None, repo=None) -> str:
|
||||
"""Вернуть HTML с кликабельным номером задачи (<a href=...>ORCH-NNN</a>),
|
||||
либо просто html.escape(work_item_id), если ссылку построить нельзя.
|
||||
Никогда не падает."""
|
||||
```
|
||||
Поведение:
|
||||
- База URL: `settings.plane_web_url` → fallback `settings.plane_api_url`; loopback-база
|
||||
(`localhost`/`127.0.0.1`/…) трактуется как «нет web URL» (переиспользовать
|
||||
`_is_loopback_base`).
|
||||
- `workspace_slug`: `settings.plane_workspace_slug`.
|
||||
- `project_id`: явный аргумент → иначе резолв по `repo` через
|
||||
`get_project_by_repo(repo).plane_project_id`.
|
||||
- `issue_id`: `plane_issue_id` (UUID из `tasks.plane_issue_id`).
|
||||
- URL-шаблон: `{web_base}/{workspace}/projects/{project_id}/issues/{issue_id}/`.
|
||||
- Текст ссылки = `html.escape(work_item_id)`; `href` = `html.escape(url, quote=True)`.
|
||||
- **Fail-safe:** если не хватает любого из (`web_base` валидный/не loopback, `workspace`,
|
||||
`project_id`, `plane_issue_id`) → вернуть `html.escape(work_item_id)` (номер без ссылки).
|
||||
- Логика построения URL уже существует в `_build_plane_issue_link` (ORCH-017) — допустимо
|
||||
переиспользовать/обобщить её, разнеся «текст-ссылки = номер» и «текст-ссылки = `✅ Задача
|
||||
в Plane`», чтобы не дублировать резолв проекта и loopback-guard.
|
||||
|
||||
### 3.2. Применение в карточке (требование 3)
|
||||
- В `render_task_tracker` заголовок строится из `work_item_id`. Заменить
|
||||
`html.escape(work_item_id)` в обоих вариантах заголовка (done / not-done) на
|
||||
`plane_issue_link(work_item_id, plane_issue_id, repo=repo)` — номер становится
|
||||
кликабельным.
|
||||
- Для этого `render_task_tracker` должен дополнительно выбрать из БД `repo` и
|
||||
`plane_issue_id` (расширить существующий `SELECT` по `tasks`). Схему НЕ менять — колонки
|
||||
уже есть.
|
||||
- `title` уже экранируется (`html.escape(title)`) — сохранить.
|
||||
|
||||
### 3.3. Применение во всех уведомлениях (требование 4)
|
||||
Во всех точках `send_telegram`/`notify_*`, где в тексте есть `work_item_id`, заменить
|
||||
«сырой» номер на `plane_issue_link(...)`. Перечень точек (из `src`):
|
||||
- `src/notifications.py`: `notify_approve_requested`, `notify_error`
|
||||
(и любые будущие notify_* с work_item_id);
|
||||
- `src/stage_engine.py`: все `send_telegram(...)` с `work_item_id`
|
||||
(≈ строки 613, 672, 719, 776, 820, 916, 971, 1057, 1134, 1192, 1228, 1257, 1355, 1367,
|
||||
1425, 1447, 1601 — проверить каждую: применять ТОЛЬКО где упоминается номер задачи);
|
||||
- `src/agents/launcher.py`: deploy-failed alert (≈685–686), agent-failed alert (≈698–699),
|
||||
alert ≈821–822;
|
||||
- `src/merge_gate.py` (≈431–432);
|
||||
- `src/job_reaper.py` (≈395–396);
|
||||
- `src/security_gate.py` (≈673–674);
|
||||
- `src/reconciler.py` (≈449);
|
||||
- `src/main.py` (≈45–47).
|
||||
|
||||
`[ARCH]` Способ доступа к `plane_issue_id`/`project_id` в каждой точке (часто там уже есть
|
||||
`work_item_id`, но не обязательно `plane_issue_id`): хелпер должен уметь резолвить
|
||||
недостающее по `repo`/БД, оставаясь fail-safe. Допустимо добавить тонкую обёртку, которая по
|
||||
`work_item_id`/`task_id` достаёт `repo`+`plane_issue_id` из БД и зовёт `plane_issue_link`
|
||||
(аналогично существующему `_get_task_link_fields`). Везде, где данных нет — деградация на
|
||||
просто номер, без падения.
|
||||
|
||||
### 3.4. HTML-экранирование
|
||||
- `parse_mode=HTML` уже стоит в `send_telegram`/`edit_telegram`. Любой пользовательский
|
||||
текст (title, описания, причины QG-fail, сообщения об ошибках), попадающий в сообщение с
|
||||
ссылками, должен экранироваться `html.escape`, чтобы не сломать `<a>`-разметку.
|
||||
|
||||
---
|
||||
|
||||
## 4. Конфигурация
|
||||
|
||||
- `plane_web_url` (env `ORCH_PLANE_WEB_URL`) — уже существует (`src/config.py`), значение
|
||||
прод — `plane.mva154.duckdns.org` (схему `https://` учесть при сборке URL).
|
||||
Дополнительных полей конфигурации не требуется.
|
||||
- `tracker_mode` — сменить дефолт на `bump` (раздел 1).
|
||||
- Обновить `.env.example`, если в нём фигурируют `ORCH_TRACKER_MODE` / `ORCH_PLANE_WEB_URL`
|
||||
(канон секретов/настроек — `.env.example`, не коммитить реальные секреты).
|
||||
|
||||
---
|
||||
|
||||
## 5. Артефакты pipeline, которые должны быть созданы/обновлены
|
||||
|
||||
- `docs/work-items/ORCH-067/06-adr/ADR-NNN-*.md` — архитектурное решение (минимум: источник
|
||||
«истинного» Plane-статуса для веток при запрете изменения схемы БД; дефолт bump; единый
|
||||
хелпер ссылки).
|
||||
- `CLAUDE.md` — раздел про нотификации/tracker (дефолт bump; статус-строка карточки;
|
||||
кликабельный номер в карточке и уведомлениях).
|
||||
- `CHANGELOG.md` — запись ORCH-067.
|
||||
- `docs/architecture/README.md` — при необходимости синхронизировать описание tracker'а.
|
||||
|
||||
---
|
||||
|
||||
## 6. Ограничения (что НЕ трогать)
|
||||
|
||||
- Транспорт `send_telegram`/`edit_telegram`/`delete_telegram`.
|
||||
- Инвариант «одна карточка на задачу».
|
||||
- Логику `disable_notification` (карточка тихая; пингуют только alert-хелперы).
|
||||
- `STAGE_TRANSITIONS`, Quality Gates, схему БД.
|
||||
- Поведение агентов/конвейера.
|
||||
|
||||
---
|
||||
|
||||
## 7. Замечания по самохостингу
|
||||
|
||||
Орк правит сам себя в проде (общий инстанс/БД с enduro-trails):
|
||||
- НЕ перезапускать прод-контейнер `orchestrator` в рамках задачи.
|
||||
- Обязательная страховка через `deploy-staging` (8501) до прод-деплоя.
|
||||
- Смена дефолта `tracker_mode` затрагивает ВСЕ проекты — проверить отсутствие регресса для
|
||||
enduro-trails (тесты + staging-наблюдение карточки).
|
||||
129
docs/work-items/ORCH-067/03-acceptance-criteria.md
Normal file
129
docs/work-items/ORCH-067/03-acceptance-criteria.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Acceptance Criteria — ORCH-067
|
||||
|
||||
Work Item: **ORCH-067**
|
||||
Каждый критерий формулирует чёткое условие PASS/FAIL. Привязка к тестам — в `04-test-plan.yaml`.
|
||||
|
||||
---
|
||||
|
||||
## Группа A — Bump по умолчанию (Требование 1)
|
||||
|
||||
### AC-1 — дефолт tracker_mode = bump
|
||||
- **PASS:** `Settings().tracker_mode == "bump"` без выставленного env `ORCH_TRACKER_MODE`.
|
||||
- **FAIL:** дефолт остался `"edit"` или иное.
|
||||
|
||||
### AC-2 — bump-поведение: одна карточка падает вниз
|
||||
- **PASS:** при втором (и последующем) вызове `update_task_tracker` для задачи с уже
|
||||
сохранённым `tracker_message_id` вызывается `delete_telegram(old_id)` (best-effort),
|
||||
затем `send_telegram(...)` с `disable_notification=True`, затем `set_tracker_message_id`
|
||||
на новый id. В чате остаётся ровно одна карточка на задачу.
|
||||
- **FAIL:** карточка редактируется на месте при дефолте; либо появляются дубли; либо новая
|
||||
карточка отправляется со звуком (`disable_notification` не True).
|
||||
|
||||
### AC-3 — bump fail-safe: транзиентный фейл send не обнуляет указатель
|
||||
- **PASS:** если `send_telegram` вернул `None` (нет креды/транзиентный фейл),
|
||||
`tracker_message_id` НЕ перезаписывается в `None` и дубликат в рамках вызова не создаётся.
|
||||
- **FAIL:** указатель обнулён или создан второй card-месседж в одном вызове.
|
||||
|
||||
### AC-4 — режим edit остаётся доступен через env
|
||||
- **PASS:** при `ORCH_TRACKER_MODE=edit` поведение прежнее (editMessageText, fallback на
|
||||
новый месседж только при EDIT_GONE).
|
||||
- **FAIL:** edit-режим сломан/недоступен.
|
||||
|
||||
---
|
||||
|
||||
## Группа B — Статус-строка карточки по модели ORCH-066 (Требование 2)
|
||||
|
||||
### AC-5 — статус-строка присутствует в карточке
|
||||
- **PASS:** `render_task_tracker(task_id)` содержит явную строку текущего Plane-статуса
|
||||
(напр. `📍 <status>`) в шапке/верхней части карточки.
|
||||
- **FAIL:** статус-строки нет.
|
||||
|
||||
### AC-6 — корректный маппинг stage → Plane-статус
|
||||
- **PASS:** для всех stage-выводимых состояний строка статуса соответствует таблице ТЗ §2.2:
|
||||
`created→To Analyse`, `analysis→Analysis`, `architecture→Architecture`,
|
||||
`development→Development`, `review→Code-Review`, `testing→Testing`,
|
||||
`deploy→Awaiting Deploy`, `done→Done`.
|
||||
- **FAIL:** хотя бы один stage маппится на неверное имя/внутренний ярлык.
|
||||
|
||||
### AC-7 — In Review (ожидание согласования BRD) как полноценный статус
|
||||
- **PASS:** при `stage == "analysis"`, `brd_review_started_at` задан и
|
||||
`brd_review_ended_at` пуст — статус-строка явно отражает `⏸️ In Review` с пометкой
|
||||
«ожидание согласования BRD»; при этом существующая строка «Подтверждение BRD …» с ⏸️/⏳
|
||||
сохранена. Работает без сетевых вызовов.
|
||||
- **FAIL:** In Review теряется/не показан как статус, либо строка «Подтверждение BRD» исчезла.
|
||||
|
||||
### AC-8 — Awaiting Deploy и Needs Input отражены
|
||||
- **PASS:** состояние ожидания Confirm Deploy показывается как
|
||||
`⏸️ Awaiting Deploy — ожидание Confirm Deploy`; состояние вопросов аналитика — как
|
||||
`❓ Needs Input — нужны уточнения`.
|
||||
- **FAIL:** любое из этих состояний не отражено в статус-строке.
|
||||
|
||||
### AC-9 — рендер карточки никогда не падает
|
||||
- **PASS:** при любой ошибке построения статуса (битые данные, недоступный источник)
|
||||
`render_task_tracker` возвращает корректную карточку (деградация на stage-маппинг или
|
||||
fallback-строку), исключение наружу не выходит.
|
||||
- **FAIL:** `render_task_tracker` бросает исключение.
|
||||
|
||||
---
|
||||
|
||||
## Группа C — Кликабельный номер в карточке (Требование 3)
|
||||
|
||||
### AC-10 — номер задачи в карточке — гиперссылка
|
||||
- **PASS:** при наличии `plane_web_url` (не loopback), `plane_workspace_slug`, `project_id`
|
||||
(резолв по repo) и `plane_issue_id` карточка содержит
|
||||
`<a href="https://<base>/<ws>/projects/<pid>/issues/<issue_id>/">ORCH-NNN</a>`.
|
||||
- **FAIL:** номер выводится сырым текстом при наличии всех данных, либо URL собран неверно.
|
||||
|
||||
### AC-11 — fail-safe ссылки в карточке
|
||||
- **PASS:** при отсутствии любого из (web_base/не-loopback, workspace, project_id,
|
||||
plane_issue_id) карточка показывает номер БЕЗ ссылки (`html.escape(work_item_id)`) и не
|
||||
падает.
|
||||
- **FAIL:** падение, пустая ссылка `<a href="">`, либо битый `<a>` тег.
|
||||
|
||||
---
|
||||
|
||||
## Группа D — Кликабельный номер во всех уведомлениях (Требование 4)
|
||||
|
||||
### AC-12 — единый хелпер ссылки
|
||||
- **PASS:** существует `plane_issue_link(...)`, возвращающий HTML-ссылку при достаточных
|
||||
данных и `html.escape(work_item_id)` при недостаточных; никогда не бросает.
|
||||
- **FAIL:** хелпера нет, либо он падает на неполных данных.
|
||||
|
||||
### AC-13 — хелпер применён во всех уведомлениях с work_item_id
|
||||
- **PASS:** во всех точках `send_telegram`/`notify_*` из ТЗ §3.3, где упоминается
|
||||
`work_item_id` (`notify_approve_requested`, `notify_error`, alert'ы stage_engine,
|
||||
launcher, merge_gate, job_reaper, security_gate, reconciler, main), номер задачи
|
||||
кликабелен (при наличии данных) и ведёт на ту же страницу Plane.
|
||||
- **FAIL:** хотя бы одна такая точка выводит номер сырым текстом при наличии данных.
|
||||
|
||||
### AC-14 — HTML-экранирование пользовательского текста
|
||||
- **PASS:** title/причины/сообщения с потенциальным HTML (`<`, `>`, `&`) экранируются
|
||||
`html.escape`; разметка `<a>` остаётся валидной; сообщение проходит `parse_mode=HTML`.
|
||||
- **FAIL:** неэкранированный текст ломает разметку (тест с title, содержащим `<b>`/`&`,
|
||||
обнаруживает поломку).
|
||||
|
||||
---
|
||||
|
||||
## Группа E — Нерегресс и качество
|
||||
|
||||
### AC-15 — инварианты транспорта/нотификаций сохранены
|
||||
- **PASS:** `send_telegram`/`edit_telegram`/`delete_telegram` не изменены по сигнатуре/
|
||||
семантике; карточка тихая (`disable_notification=True`); инвариант «одна карточка на
|
||||
задачу» соблюдён; `STAGE_TRANSITIONS`/QG/схема БД не тронуты.
|
||||
- **FAIL:** изменён транспорт, карточка пингует, появились дубли, тронута схема БД/QG.
|
||||
|
||||
### AC-16 — нет регресса для enduro-trails
|
||||
- **PASS:** существующие тесты нотификаций (`test_notify_approve_links.py`,
|
||||
`test_notify_done_regression.py` и др.) проходят; поведение карточки для не-ORCH проектов
|
||||
без новых Plane-статусов деградирует корректно (alias-fallback, без ссылки при нехватке
|
||||
данных).
|
||||
- **FAIL:** падение существующих тестов или сломанная карточка для enduro.
|
||||
|
||||
### AC-17 — весь набор тестов зелёный
|
||||
- **PASS:** `pytest tests/ -q` зелёный.
|
||||
- **FAIL:** любой упавший тест.
|
||||
|
||||
### AC-18 — документация обновлена в том же PR
|
||||
- **PASS:** обновлены `CLAUDE.md` (раздел нотификаций/tracker), `CHANGELOG.md`,
|
||||
создан ADR per-work-item.
|
||||
- **FAIL:** функционал изменён, документация — нет (reviewer → REQUEST_CHANGES).
|
||||
181
docs/work-items/ORCH-067/04-test-plan.yaml
Normal file
181
docs/work-items/ORCH-067/04-test-plan.yaml
Normal file
@@ -0,0 +1,181 @@
|
||||
work_item: ORCH-067
|
||||
description: >
|
||||
План тестов для ORCH-067 (Telegram tracker: bump по умолчанию, статус-строка
|
||||
карточки по модели Plane ORCH-066, кликабельный номер задачи в карточке и во
|
||||
всех уведомлениях орка). Сеть изолируется: send_telegram/edit_telegram/
|
||||
delete_telegram подменяются рекордерами (как в tests/conftest.py и
|
||||
tests/test_notify_approve_links.py); БД — временный SQLite, сидируемый фикстурой.
|
||||
|
||||
tests:
|
||||
# --- Группа A: bump по умолчанию (AC-1..AC-4) ---
|
||||
- id: TC-01
|
||||
type: unit
|
||||
description: "Дефолт Settings().tracker_mode == 'bump' без env ORCH_TRACKER_MODE"
|
||||
module: tests/test_tracker_bump_default.py
|
||||
asserts: "AC-1"
|
||||
expected: PASS
|
||||
|
||||
- id: TC-02
|
||||
type: unit
|
||||
description: >
|
||||
bump-поведение: при повторном update_task_tracker с сохранённым
|
||||
tracker_message_id вызывается delete_telegram(old) -> send_telegram(...,
|
||||
disable_notification=True) -> set_tracker_message_id(new). Одна карточка.
|
||||
module: tests/test_tracker_bump_default.py
|
||||
asserts: "AC-2"
|
||||
expected: PASS
|
||||
|
||||
- id: TC-03
|
||||
type: unit
|
||||
description: >
|
||||
bump fail-safe: send_telegram вернул None (нет креды/транзиент) ->
|
||||
tracker_message_id не обнуляется, дубликат в вызове не создаётся.
|
||||
module: tests/test_tracker_bump_default.py
|
||||
asserts: "AC-3"
|
||||
expected: PASS
|
||||
|
||||
- id: TC-04
|
||||
type: unit
|
||||
description: "ORCH_TRACKER_MODE=edit -> прежнее edit-поведение (editMessageText)"
|
||||
module: tests/test_tracker_bump_default.py
|
||||
asserts: "AC-4"
|
||||
expected: PASS
|
||||
|
||||
# --- Группа B: статус-строка карточки (AC-5..AC-9) ---
|
||||
- id: TC-05
|
||||
type: unit
|
||||
description: "render_task_tracker содержит явную строку текущего Plane-статуса"
|
||||
module: tests/test_tracker_status_line.py
|
||||
asserts: "AC-5"
|
||||
expected: PASS
|
||||
|
||||
- id: TC-06
|
||||
type: unit
|
||||
description: >
|
||||
Маппинг stage -> Plane-статус по таблице ТЗ §2.2: created->To Analyse,
|
||||
analysis->Analysis, architecture->Architecture, development->Development,
|
||||
review->Code-Review, testing->Testing, deploy->Awaiting Deploy, done->Done
|
||||
(параметризованный тест по всем stage).
|
||||
module: tests/test_tracker_status_line.py
|
||||
asserts: "AC-6"
|
||||
expected: PASS
|
||||
|
||||
- id: TC-07
|
||||
type: unit
|
||||
description: >
|
||||
analysis + brd_review_started_at задан + brd_review_ended_at пуст ->
|
||||
статус '⏸️ In Review' (ожидание согласования BRD); строка 'Подтверждение
|
||||
BRD' с ⏸️/⏳ сохранена; без сетевых вызовов.
|
||||
module: tests/test_tracker_status_line.py
|
||||
asserts: "AC-7"
|
||||
expected: PASS
|
||||
|
||||
- id: TC-08
|
||||
type: unit
|
||||
description: >
|
||||
Awaiting Deploy ('ожидание Confirm Deploy') и Needs Input ('нужны
|
||||
уточнения') корректно отражаются в статус-строке.
|
||||
module: tests/test_tracker_status_line.py
|
||||
asserts: "AC-8"
|
||||
expected: PASS
|
||||
|
||||
- id: TC-09
|
||||
type: unit
|
||||
description: >
|
||||
render_task_tracker не падает при битых/недоступных данных статуса
|
||||
(деградация на stage-маппинг/fallback, исключение не наружу).
|
||||
module: tests/test_tracker_status_line.py
|
||||
asserts: "AC-9, AC-16"
|
||||
expected: PASS
|
||||
|
||||
# --- Группа C: кликабельный номер в карточке (AC-10..AC-11) ---
|
||||
- id: TC-10
|
||||
type: unit
|
||||
description: >
|
||||
При полных данных (plane_web_url не loopback, workspace, project_id по repo,
|
||||
plane_issue_id) карточка содержит <a href=".../issues/<id>/">ORCH-NNN</a>
|
||||
с корректным URL.
|
||||
module: tests/test_tracker_issue_link.py
|
||||
asserts: "AC-10"
|
||||
expected: PASS
|
||||
|
||||
- id: TC-11
|
||||
type: unit
|
||||
description: >
|
||||
Fail-safe ссылки в карточке: при отсутствии любого из (web_base/не-loopback,
|
||||
workspace, project_id, plane_issue_id) номер выводится html.escape без <a>,
|
||||
рендер не падает. Параметризовать по каждому отсутствующему полю.
|
||||
module: tests/test_tracker_issue_link.py
|
||||
asserts: "AC-11"
|
||||
expected: PASS
|
||||
|
||||
# --- Группа D: единый хелпер и уведомления (AC-12..AC-14) ---
|
||||
- id: TC-12
|
||||
type: unit
|
||||
description: >
|
||||
plane_issue_link(...) возвращает HTML-ссылку при достаточных данных и
|
||||
html.escape(work_item_id) при недостаточных; никогда не бросает (в т.ч. на
|
||||
None-аргументах и loopback-базе).
|
||||
module: tests/test_plane_issue_link.py
|
||||
asserts: "AC-12"
|
||||
expected: PASS
|
||||
|
||||
- id: TC-13
|
||||
type: unit
|
||||
description: >
|
||||
notify_approve_requested: номер задачи кликабелен (ведёт на страницу Plane),
|
||||
сохранён call-to-action 'Approved', ровно одно notifying-сообщение.
|
||||
module: tests/test_notify_issue_links.py
|
||||
asserts: "AC-13"
|
||||
expected: PASS
|
||||
|
||||
- id: TC-14
|
||||
type: unit
|
||||
description: >
|
||||
notify_error: номер задачи кликабелен при наличии данных, деградирует на
|
||||
сырой номер без падения при их отсутствии.
|
||||
module: tests/test_notify_issue_links.py
|
||||
asserts: "AC-13, AC-12"
|
||||
expected: PASS
|
||||
|
||||
- id: TC-15
|
||||
type: integration
|
||||
description: >
|
||||
Точки send_telegram в stage_engine/launcher/merge_gate/job_reaper/
|
||||
security_gate/reconciler/main, где есть work_item_id, используют
|
||||
plane_issue_link (или эквивалент) — номер кликабелен. Проверка рекордером
|
||||
send_telegram на представительных alert-путях (deploy fail, agent fail,
|
||||
QG fail, прод-деплой).
|
||||
module: tests/test_notify_issue_links.py
|
||||
asserts: "AC-13"
|
||||
expected: PASS
|
||||
|
||||
- id: TC-16
|
||||
type: unit
|
||||
description: >
|
||||
HTML-экранирование: title с '<b>'/'&'/'>' экранируется, <a>-разметка
|
||||
остаётся валидной, сообщение не ломается под parse_mode=HTML (карточка и
|
||||
уведомления).
|
||||
module: tests/test_tracker_issue_link.py
|
||||
asserts: "AC-14"
|
||||
expected: PASS
|
||||
|
||||
# --- Группа E: нерегресс (AC-15..AC-18) ---
|
||||
- id: TC-17
|
||||
type: integration
|
||||
description: >
|
||||
Инварианты: карточка отправляется с disable_notification=True; одна карточка
|
||||
на задачу; транспорт send/edit/delete не изменён по семантике.
|
||||
module: tests/test_tracker_bump_default.py
|
||||
asserts: "AC-15"
|
||||
expected: PASS
|
||||
|
||||
- id: TC-18
|
||||
type: integration
|
||||
description: >
|
||||
Нерегресс существующих тестов нотификаций (test_notify_approve_links.py,
|
||||
test_notify_done_regression.py) и корректная деградация карточки для
|
||||
enduro-trails без новых Plane-статусов.
|
||||
module: tests/test_notify_done_regression.py
|
||||
asserts: "AC-16, AC-17"
|
||||
expected: PASS
|
||||
Reference in New Issue
Block a user