- убраны клиент бота, транспорт обновлений, отправитель сообщений, сборка
входа при старте, секция настроек и зависимость go-telegram-bot-api; из
конвейера ушла доставка ответа отправителю — исход виден опросом готовности.
Колонки адресата и значение источника остались в схеме: применённые шаги не
переписываются
- шаг 202608140003 запрещает пустого владельца у аудиозаписи и у файла;
существующие строки он не проверяет, и это принято сознательно — искать их
надо запросом до выкладки
- ревью нашло два пред-существующих дефекта, оба закрыты: пустой второй ответ
распознавателя стирал сохранённую расшифровку, а пустая расшифровка перестала
быть заметной вместе с убранной доставкой. Попутно поднят golang.org/x/image
до v0.45.0 — красный шаг vulns, воспроизводился и на чистом master
- audiorecords вместо transcribe_jobs: приложения (texts, structures,
recognitions, record_events, topics) живут своими коллекциями, ссылки на
исходник и на приведённую копию перестали переставляться
- рубеж называет достигнутое, отказ стал признаком остановки с причиной, а
сторожей стало двое: число отказов и время в рубеже
- воркеры потеряли специализацию, их число задаётся [pipeline] workers, шаг
выбирается по рубежу, а захват отдаёт идентификатор и признак захвата
- колонка `owner` связью с `users` в обеих коллекциях новым шагом схемы
`202608140001`; чтение задачи сужено владельцем, и чужая, ничья и
несуществующая дают один ответ; правило просмотра файлов сужено им же
- приём по HTTP берёт владельца из сессии, а предъявителя без учётной записи
пользователя отвергает до чтения тела: позже пришлось бы убирать уложенный
файл, а уборки файлов сервис не умеет. Выборка воркера владельцем не сужается
- удаление учётной записи с записями отвергается стражем, и вешает его сама
сборка хранилища: сборка, забывшая его позвать, теряла защиту молча
- Клиент бота собирается один раз и достаётся отправителю и транспорту;
разрез прошёл по «ответил ли Telegram»: ответ «такого бота нет» роняет
старт, недоступность даёт подъём без Telegram (ADR-2026-08-13). Ожидание
при сборке ограничено сроком — иначе молчащий Telegram вешал подъём.
- Недоставленный ответ не роняет шаг: пишется с job_id и считается метрикой,
уровень по причине — WARN для неподнятого входа, ERROR для неназванного
адресата. Заведены transcriber_intake_up и transcriber_undelivered_reply_count.
- Закрыта утечка токена в журнал: отказ разбора адреса рождается раньше
обращения к клиенту, то есть мимо чистки на его границе.
- контекст проложен от воркера и обоих входов до внешних вызовов: ffmpeg и
ffprobe заводятся через exec.CommandContext, SpeechKit и Object Storage
принимают ctx вместо context.Background, скачивание записи идёт запросом с
контекстом. Прежде остановка сервиса не доходила до чужой работы вовсе
- прерванный шаг приговора не выносит: убитый по контексту ffmpeg отдаёт
«signal: killed», от настоящего отказа неотличимо ни типом, ни errors.Is, и
различает их только ctx.Err(). Задача остаётся на повтор, попытку не тратит и
отправителю о несуществующем сбое не сообщает; воркер не считает остановку
отказом, а задача не забирается вовсе, если нас уже остановили
- клиента Bot API заводит единая точка internal/adapter/telegram: токен стоит в
пути каждого обращения, а http.Client кладёт адрес в *url.Error целиком.
Чистка на месте употребления закрывала один вызов из пяти — теперь свой Do
чистит отказ, подменённый логгер вычищает токен из строк самой библиотеки, а
транспорт бота токена не получает вовсе
- принятие операции распознавания защищено от отмены своим пределом: SpeechKit
мог её принять и начать считать деньги, а потерянный идентификатор заставил
бы повтор оплатить ту же запись второй раз
- приём по HTTP доводит запись до задачи независимо от отправителя: на
контексте запроса один обрыв соединения терял полностью загруженную запись
- ответ Telegram с не-2xx кодом больше не становится записью: прежде тело
отказа доезжало до хранилища и умирало на ffprobe, уводя диагностику
- заведён internal/clock: Now даёт метку в UTC, Start — начало измерения
длительности с монотонными часами; девять мест рабочего кода и запрос захвата
переведены на него, долг «время time.Now() по месту» закрыт
- клиент SpeechKit узнавал конец потока сравнением err.Error() == "EOF": отказ
с тем же текстом вернул бы усечённую расшифровку как готовую, теперь
errors.Is(err, io.EOF)
- починены находки новых линтеров: две опечатки, два slog.DiscardHandler,
четыре неэкранированные подстановки в docker/entrypoint.sh
- записи, метаданные и файлы съехались под один каталог данных; появилась
панель владельца, а gin, goqu, goose и требование CGO ушли
- захват задачи стал одним запросом с RETURNING; заведены число попыток,
состояние dead и нарастающая пауза вместо признака is_error
- имя файла в хранилище задаёт сервис и в журнал не идёт: вместе с
идентификатором записи оно собирало бы ссылку на скачивание
- признаки «работы нет» и «задача не найдена» узнаются по смыслу, а не
приведением типа: обёртка `%w` на пути больше не превращает пустой прогон
воркера в отказ раз в секунду
- отказ закрытия соединения с распознавателем доходит до вызывающего
(`errors.Join`) либо до журнала; у `errcheck` включён `check-blank`, иначе
критерий принимал реализацию, выбрасывающую отказ в пустоту
- заведены первые тесты пакета worker и capability `pipeline`; долг из четырёх
замечаний линтера закрыт, гейт зелёный целиком
- расширение приводится к перечню известных форматов прежде метки метрики:
страница метрик открыта, и хвост имени уезжал на неё дословно
- проверки приёма перехватывают все три потока журнала и читают реестр метрик,
каждая падает при снятии того, что сторожит