14 KiB
ТЗ — 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-статус для
карточки на основе доступных данных задачи. Сигнатура (ориентир):
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:
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→ fallbacksettings.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(envORCH_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-наблюдение карточки).