- intake-limits-measure меряет четыре звена, которых не берёт speechkit-limits: приём из Telegram и по HTTP, конвертацию и заливку в Object Storage; - reject-oversized-recording отклоняет запись сверх потолка на приёме, long-text-delivery отдаёт текст в сотни килобайт файлом вместо сотни сообщений Telegram; - все шесть пунктов «Завершения» цели теперь закрыты задачами.
14 KiB
14 KiB
Беклог
Что можно взять. Одна задача = один файл items/<slug>.md
- строка здесь. Целей тут нет — они в ROADMAP.md: беклог — то, что берут,
роадмап — то, подо что берут. Порядок строк значим:
это очередь, и первая строка — то, что делают следующим. Порядок
назначает человек на груминге, машина его не выводит. Одно исключение
производно от типа — сырьё (
researchбез раздела «Вопрос») стоит в конце: его не берут. Ведётся скилломtasks.
Секция одна — полок домена у проекта нет, и делить очередь на две значило бы держать два порядка вместо одного.
Тип записи стоит первым полем меты и решает, что у неё может быть:
✨ feature 🐞 fix 🧹 chore 🔬 research
Секции «блокеры» здесь нет и не заводится: блокер — это состояние (работа не может продолжаться ни одной задачей), оно живёт до ответа человека, а его следы — вопросами в файлах задач.
Очередь
- 🐞 Не писать имя файла пользователя в журнал — Приём кладёт имя файла, данное пользователем, в журнал контейнера на каждой принятой записи — инвариант приватности объявляет это критическим и необратимым.
- 🐞 Убирать записанный файл, когда приём отказал на середине — Отказ чтения метаданных и отказ записи на диск оставляют файл в каталоге хранения без задачи и без учёта: сопоставить его не с чем, удалять приходится руками.
- 🧹 Задать таймауты обращениям к внешним сервисам — Ни у Telegram, ни у Object Storage, ни у SpeechKit нет таймаута: молчащий собеседник держит шаг конвейера до истечения часового захвата.
- 🔬 Админка PocketBase: данные, пользователи и файлы — Переход на PocketBase решён ради его панели администратора, а что она даёт по данным, пользователям и файлам на диске — не проверено.
- 🔬 Очередь задач: своя таблица или готовая библиотека — Очередь написана вручную: захват двумя запросами без транзакции, протухание временем, опрос раз в секунду вхолостую тремя воркерами.
- 🧹 Перевести хранилище на встроенный PocketBase — Из трёх доводов за перевод остался один: учётные записи ушли к Authelia, страницу статистики рисует само приложение, а панель администратора не проверена.
- ✨ Пускать в приложение только после входа через OIDC — HTTP API открыт наружу без аутентификации: любой из интернета заводит задачи за наши деньги и читает чужие расшифровки по идентификатору.
- ✨ Привязать запись к владельцу и отдавать только свои — У задачи и файла нет владельца, поэтому знание UUID задачи и есть право её читать.
- ✨ Сопоставить пользователя Telegram с учётной записью — Белый список сверяется с именем пользователя Telegram, которое владелец меняет в любой момент, а записи из бота ни с кем не связаны.
- ✨ Свести приём и чтение записей к одному контракту для приложения — Сегодняшний API отвечает 404 на любую ошибку чтения и 500 на любую ошибку приёма: строить на нём экраны нечем.
- ✨ Пускать скрипты в API по личным токенам — Вход через OIDC закрывает API целиком, а скрипту браузерная сессия недоступна: автоматизировать загрузку станет нечем.
- 🔬 Выбор фреймворка для приложения — Конвенция веб-UI написана под htmx, а решено делать SPA: до выбора фреймворка не заводится ни сборка, ни первый экран.
- ✨ Собрать каркас приложения и раздать его из бинарника — Экранов нет и собирать их нечем: ни сборки фронтенда, ни раздачи статики в проекте не существует.
- ✨ Сделать экран загрузки записи и её состояния — Первое, ради чего приложение открывают: отдать файл и увидеть, что с ним происходит.
- ✨ Сделать экран списка своих записей и чтения текста — Расшифровка сегодня доходит одним сообщением и теряется в переписке; вернуться к ней через неделю нечем.
- ✨ Узнавать уже загруженный файл по хеш-сумме — Один и тот же файл, отправленный дважды, распознаётся дважды и оплачивается дважды: приём не смотрит на содержимое вовсе.
- ✨ Принимать до десяти файлов одной загрузкой — Приём берёт один файл в запросе, а с телефона выбирают пачку сразу: десять записей значат десять заходов на экран загрузки.
- ✨ Показывать ход загрузки записи на экране — Гигабайтный файл уходит на сервер молча: до ответа сервера экран не отличает идущую загрузку от зависшей.
- ✨ Сделать приложение устанавливаемым на телефон — Приложение, живущее вкладкой браузера, теряется среди прочих: ярлыка на экране у него нет.
- ✨ Сделать экран настроек и хранить настройки по пользователю — Настроек у пользователя нет вовсе: уровни текста и канал уведомлений задаются общим конфигом сервиса.
- ✨ Считать заголовок, темы и пересказ внешней моделью — Расшифровка доходит стеной текста: ни заголовка, ни тем, ни пересказа сервис не считает, и клиента языковой модели в нём нет.
- ✨ Отдавать вычитанный текст рядом с сырым — Сырая расшифровка идёт без знаков препинания, с повторами и словами-паразитами: читать её подряд тяжело, а другого уровня текста нет.
- ✨ Отправлять готовый текст через apprise и ntfy — Пользователь веба узнаёт о готовности только опросом с открытого экрана.
- ✨ Слать готовый текст на почту из учётной записи — Адрес почты приходит вместе с входом через OIDC, но почтового отправителя в сервисе нет.
- ✨ Считать объём, минуты и расход по каждому пользователю — Ни объём, ни длительность, ни обращения к платным сервисам никуда не записываются: восстановить расход задним числом не из чего.
- ✨ Сделать страницу статистики для владельца — Собранный учёт читается только запросом к базе руками: ни страницы, ни признака владельца в приложении нет.
- 🔬 Потолки SpeechKit по длине записи и по формату — Потолок длины записи и перечень принимаемых форматов неизвестны, а цель про долгие записи без них не начинается.
- 🔬 Потолки приёма, конвертации и заливки по длине записи — Из пяти звеньев задача speechkit-limits замерила только модель распознавания: где отваливается шестичасовая запись до неё, неизвестно.
- ✨ Отклонять на приёме запись сверх потолка — Запись сверх потолка принимается молча и висит в конвейере до истечения часового захвата, а человек всё это время ждёт текста.
- ✨ Резать длинную запись на фрагменты и продолжать с места остановки — Шаг конвейера повторяется целиком: перезапуск на пятом часу шестичасовой записи начинает распознавание заново и оплачивает его второй раз.
- ✨ Отдавать текст в сотни килобайт файлом, а не сотней сообщений — Отправитель Telegram режет текст по 4000 знаков: расшифровка шестичасовой записи придёт сотней сообщений подряд.
- 🔬 Загрузка большого файла частями — Гигабайтный файл едет одним запросом, и обрыв на девяноста процентах начинает его заново.
- 🧹 Сравнивать доменные ошибки через errors.As — NoopJobError и JobNotFoundError проверяются приведением типа: первая же обёртка %w между слоями сломает проверку молча.
- 🧹 Покрыть тестами разбор вывода ffprobe — Проверки приёма перестали звать настоящий ffprobe 2026-08-11, а своего теста у адаптера метаданных нет: разбор JSON и отличие «программы нет в PATH» от «обработка отказала» не проверяет ничто.
- 🧹 Покрыть тестами шаги конвейера и захват задачи — Тестовых файлов в проекте два, и оба мимо конвейера: потеря ссылки на файл, двойной ответ пользователю и гонка при захвате не поймаются ничем.
- 🧹 Разобрать мелочи http-транспорта — Маршруты зарегистрированы дважды, и переименование пути в main.go проходит проверки зелёным; обработчик пишет в журнал через стандартный log и дублирует запись, уже сделанную сервисом.
- 🧹 Переименовать образец конфига в config.example.toml — Конвенция называет config.dist.toml объявленным расхождением, но тут же пишет это имя как правило — документ противоречит сам себе, а образец расходится с конвенцией.
- 🔬 Стоит ли брать OpenTelemetry вместо голого Prometheus — Метрик одиннадцать штук на пять счётчиков, трассировки нет вовсе: путь одной записи по конвейеру собирается только чтением логов глазами.