Files

14 KiB
Raw Permalink Blame History

ТЗ — 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 → 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 (≈685686), agent-failed alert (≈698699), alert ≈821822;
  • src/merge_gate.py (≈431432);
  • src/job_reaper.py (≈395396);
  • src/security_gate.py (≈673674);
  • src/reconciler.py (≈449);
  • src/main.py (≈4547).

[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-наблюдение карточки).