Files
transcriber/openspec/changes/archive/2026-08-13-start-without-telegram-token/tasks.md
T
av b733a84d6a telegram: сервис поднимается без бота и работает одним входом
- Клиент бота собирается один раз и достаётся отправителю и транспорту;
  разрез прошёл по «ответил ли Telegram»: ответ «такого бота нет» роняет
  старт, недоступность даёт подъём без Telegram (ADR-2026-08-13). Ожидание
  при сборке ограничено сроком — иначе молчащий Telegram вешал подъём.
- Недоставленный ответ не роняет шаг: пишется с job_id и считается метрикой,
  уровень по причине — WARN для неподнятого входа, ERROR для неназванного
  адресата. Заведены transcriber_intake_up и transcriber_undelivered_reply_count.
- Закрыта утечка токена в журнал: отказ разбора адреса рождается раньше
  обращения к клиенту, то есть мимо чистки на его границе.
2026-08-13 19:10:08 +03:00

11 KiB
Raw Blame History

1. Отправитель, который не отправляет

  • 1.1 Завести среди контрактов значение отказа «канал доставки не поднят» — рядом с «работы нет» и «захват потерян», узнаваемое тем же способом
  • 1.2 Завести в пакете отправителя Telegram заглушку, реализующую контракт отправки: она ничего не отправляет и на всякий ответ возвращает это значение
  • 1.3 Проверить тестом, что заглушка возвращает именно его и ничего не пишет сама

2. Ответ отправителю в конвейере

  • 2.1 Научить ответ отправителю узнавать это значение: пишется запись уровня WARN с идентификатором задачи и причиной, шаг завершается без отказа
  • 2.2 Свести к тому же исходу вторую причину недоставки — задачу источника Telegram без названного чата: сегодня она даёт отказ шага на уже завершённой работе, то есть ложный сбой в счётчике воркера и перезапись служебных полей
  • 2.3 Проверить, что в записи нет ни текста расшифровки, ни сообщения отправителя
  • 2.4 Тест конвейера: задача источника Telegram доходит до ответа через заглушку — шаг без отказа, состояние задачи не откатывается, в повтор она не уходит и в failed не переводится
  • 2.5 Тест: задача источника Telegram без чата даёт тот же исход
  • 2.6 Тест: задача, принятая по HTTP, до заглушки не доходит и записи о недоставке не порождает

3. Сборка при старте

  • 3.1 Свести сборку клиента бота к одной: отправитель ответов принимает готового клиента вместо токена, транспорт получает того же
  • 3.2 Поставить разрез в этом единственном месте: пустой токен даёт заглушку и одну запись уровня WARN о неподнятом боте, любой другой отказ сборки роняет старт
  • 3.3 Тест на непустой токен, с которым бот не заводится: старт роняется. Живой Telegram не нужен — адрес подставляется, как в имеющемся тесте клиента
  • 3.4 Проверить, что ни запись о неподнятом боте, ни текст отказа старта не несут значения токена
  • 3.5 Проверить остановку: сигнал остановки на конфиге с пустым токеном завершает процесс тем же кодом и в тот же срок, что и с токеном
  • 3.6 Удалить неупотребляемый тип отказа «токен пуст» в транспорте бота — третье представление того же факта

4. Настройки и их образец

  • 4.1 Описать в образце конфига, что пустой токен означает подъём без Telegram и что при этом перестаёт работать
  • 4.2 Назвать там же остальные секции, без которых сервис не поднимется: настройки входа и Yandex требуют непустых значений, при локальном прогоне годятся выдуманные, наружу при старте не ходит ни одна

5. Проверки

  • 5.1 Тест на сборку отправителя с непустым токеном: прежний путь сохранён
  • 5.2 Живой прогон: конфиг с пустым токеном и заполненными по 4.2 секциями, GET /health отвечает 200, в выводе есть запись о неподнятом боте
  • 5.3 task gate зелёный

7. Развилки ревью кода — решения владельца 2026-08-13

  • 7.1 Недоступность Telegram на старт не влияет: разрез перенесён на «ответил ли Telegram». Ответ «такого бота нет» роняет старт, всё прочее даёт подъём без Telegram с записью WARN
  • 7.2 Ограничить ожидание при сборке клиента сроком — без него недоступность неотличима от подъёма; длинный опрос сроком не ограничен
  • 7.3 Признак поднятости входов метрикой и счётчик недоставленных ответов с причиной меткой
  • 7.4 Развести уровни недоставки: неподнятый вход — WARN, неназванный адресат — ERROR (симптом порчи записи)
  • 7.5 Проверки на все четыре ветки сборки и на оба уровня недоставки

6. Документы

  • 6.1 CLAUDE.md, раздел «Запреты»: рядом с запретом на боевой токен встаёт способ подняться без него
  • 6.2 docs/review.md, подраздел «Недоступно проверке»: строка о живом прогоне сужается с остатком — подъём и осмотр стали доступны, прогон с пустыми ключами Yandex по-прежнему нет
  • 6.3 docs/architecture.md: перечень capability отражает, что нормируют intake и pipeline после этого изменения
  • 6.4 docs/architecture.md, таблица отказов внешних зависимостей: строка про Telegram сегодня обещает дежурному «бот не стартует, приложение продолжает работу без него» — привести к новому разрезу ссылкой на требование, не перенося поведение в обзор

Критерии приёмки

Первые три — дословно из записи задачи local-run-without-telegram-token. Четвёртый переписан решением владельца на чекпоинте 2026-08-13: в прежней редакции он требовал, чтобы задача осталась пригодной к повтору либо перешла в failed, а дизайн отверг оба исхода с ценой, и норма pipeline требует прямо обратного. Прежняя редакция сделала бы приёмку зелёной на поведении, которое это же изменение запрещает. Запись задачи поправлена тем же решением.

  • Сервис поднимается с пустым токеном бота: HTTP отвечает, воркеры идут, бот не создан. Оракул — запуск с конфигом без токена и запрос GET /health: код 200.
  • Отсутствие бота названо в журнале один раз при старте, а не молчанием. Оракул — тот же запуск: в выводе есть строка о том, что бот не поднят и почему.
  • Поведение с настоящим токеном не изменилось. Оракул — тест на создание отправителя с непустым токеном: прежний путь сохранён.
  • Задача из Telegram, дошедшая до ответа при отсутствующем боте, не роняет процесс и не теряется молча: она остаётся в достигнутом состоянии, в повтор не уходит и в failed не переводится, а недоставка видна записью журнала с идентификатором задачи. Оракул — тест конвейера с задачей источника Telegram и заглушкой вместо отправителя.
  • Задача источника Telegram без названного чата даёт тот же исход, а не отказ шага. Оракул — тест конвейера на такой задаче: воркеру сбой не засчитан, служебные поля завершённой задачи не переписаны.

Сверх записи задачи — из ревью дизайна:

  • Непустой токен, с которым бот не заводится, роняет старт. Оракул — тест с подставным адресом Bot API.
  • Записей о неподнятом боте ровно одна. Оракул — живой прогон с пустым токеном: отбор по журналу даёт одну строку, а не две.
  • Клиент бота собирается в одном месте. Оракул — отправитель ответов принимает клиента, а не токен, и NewBot зовётся из сборки при старте однажды.

Сверх ревью кода — решения владельца по трём развилкам:

  • Недоступность Telegram подъёму не мешает, ответ «такого бота нет» роняет старт. Оракул — проверки на четыре ветки сборки.
  • Поднятость входов видна метрикой. Оракул — живой прогон с пустым токеном: признак входа Telegram равен нулю, признак HTTP — единице.
  • Неназванный адресат пишется уровнем ERROR, неподнятый вход — WARN. Оракул — проверки конвейера на обе причины.