- убраны клиент бота, транспорт обновлений, отправитель сообщений, сборка входа при старте, секция настроек и зависимость go-telegram-bot-api; из конвейера ушла доставка ответа отправителю — исход виден опросом готовности. Колонки адресата и значение источника остались в схеме: применённые шаги не переписываются - шаг 202608140003 запрещает пустого владельца у аудиозаписи и у файла; существующие строки он не проверяет, и это принято сознательно — искать их надо запросом до выкладки - ревью нашло два пред-существующих дефекта, оба закрыты: пустой второй ответ распознавателя стирал сохранённую расшифровку, а пустая расшифровка перестала быть заметной вместе с убранной доставкой. Попутно поднят golang.org/x/image до v0.45.0 — красный шаг vulns, воспроизводился и на чистом master
68 lines
5.9 KiB
Markdown
68 lines
5.9 KiB
Markdown
# Намерение объявляется признаком, а не выводится из ключа доступа
|
||
|
||
- **Дата:** 2026-08-13
|
||
- **Источник:** openspec/changes/archive/2026-08-13-telegram-enabled-flag/design.md
|
||
- **Статус:** устарело — вход Telegram убран решением [ADR-2026-08-15-telegram-intake-removed-temporarily](ADR-2026-08-15-telegram-intake-removed-temporarily.md); довод устоял и понадобится возврату входа
|
||
|
||
## Решение
|
||
|
||
Вход Telegram включается отдельным признаком `telegram.enabled`, а `bot_token`
|
||
означает только доступ. Признак **обязателен**: умолчания у него нет, и файл
|
||
настроек без него негоден — сервис выходит с ошибкой настройки, назвав
|
||
недостающий ключ.
|
||
|
||
## Почему
|
||
|
||
Прежде пустой ключ доступа значил разом две вещи — «вход выключен намеренно» и
|
||
«ключа нет», — и сервис поднимался без бота в обоих случаях. Цена расхождения
|
||
падала на выкладку: файл настроек собирает Ansible, и потерянный при сборке ключ
|
||
выглядел для сервиса как решение владельца.
|
||
|
||
Умолчания у признака нет, и это **намеренный отказ от очевидного подхода** —
|
||
булев ключ обычно заводят с умолчанием. Цитата из источника:
|
||
|
||
> умолчание — это угаданное намерение, а признак заводится ровно затем, чтобы
|
||
> намерение объявляли. Файл, где его забыли, одинаково плохо читается в обе
|
||
> стороны, и любое умолчание делает одну из двух ошибок тихой.
|
||
|
||
Отвергнуты оба умолчания. «Включён» — файл без признака работал бы «как-нибудь»,
|
||
и разница между объявленным и угаданным намерением исчезала бы ровно там, где её
|
||
завели. «Выключен» — первый же подъём после выкладки выключил бы бота молча, то
|
||
есть дал бы исход, против которого написано само требование.
|
||
|
||
Отсутствие ключа судит **разбор**, а не значение: `toml.MetaData.IsDefined`
|
||
отличает «не задан» от «задан ложным», тогда как нулевое значение `bool` у обоих
|
||
одинаковое. Форма поля с указателем отвергнута: указатель пережил бы проверку и
|
||
уехал к потребителям, где `nil` уже невозможен, но выглядит возможным.
|
||
|
||
Тем же решением закрыт разрез текста отказа при разборе файла настроек. Цитата
|
||
из источника:
|
||
|
||
> Пересказывать библиотеку нельзя: она собирает текст отказа из разбираемого
|
||
> куска файла, и оборванная строка секретного ключа уехала бы в журнал вместе со
|
||
> значением.
|
||
|
||
Норму держит инвариант «Секрет не покидает конфиг», а форму записи — конвенция
|
||
настроек. Спеки загрузку настроек не нормируют, и это назначено явно: загрузка
|
||
не принадлежит ни одной заведённой capability.
|
||
|
||
## Последствия
|
||
|
||
- `+` потерянный при сборке файла ключ доступа роняет старт вслух, а не оставляет
|
||
сервис работать в половину силы;
|
||
- `+` выключенный вход перестал быть поводом для предупреждения: решение
|
||
владельца сообщается записью «к сведению», а предупреждение осталось за тем,
|
||
чего владелец не выбирал, — недоступностью Telegram;
|
||
- `+` оборванная строка секретного ключа больше не уносит значение в журнал
|
||
контейнера;
|
||
- `−` **порядок выкладки стал обязательным**: шаблон настроек обязан получить
|
||
признак раньше накатки образа, иначе сервис не поднимется вовсе. Правило живёт
|
||
в [architecture.md](../architecture.md), раздел «Эксплуатация», и задаётся там
|
||
по ключу, а не по файлу целиком;
|
||
- `−` один путь молчаливой потери бота остался: признак, ошибочно собранный
|
||
как «выключен», отличим от решения владельца только записью журнала. Признак
|
||
поднятости входа тут не помощник — он равен нулю и при недоступности Telegram;
|
||
- `−` отказ разбора файла настроек стал беднее на текст библиотеки: место и ключ
|
||
названы, а что именно в строке не так — нет. Плата принята ради инварианта,
|
||
помеченного необратимым.
|