206 lines
14 KiB
Markdown
206 lines
14 KiB
Markdown
# ТЗ — 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-наблюдение карточки).
|