tasks: заведены три задачи под незакрытые пункты цели о долгих записях

- intake-limits-measure меряет четыре звена, которых не берёт speechkit-limits:
  приём из Telegram и по HTTP, конвертацию и заливку в Object Storage;
- reject-oversized-recording отклоняет запись сверх потолка на приёме,
  long-text-delivery отдаёт текст в сотни килобайт файлом вместо сотни
  сообщений Telegram;
- все шесть пунктов «Завершения» цели теперь закрыты задачами.
This commit is contained in:
av
2026-08-11 10:28:56 +03:00
parent d2c85af80a
commit 9a54afba60
4 changed files with 117 additions and 0 deletions
+3
View File
@@ -47,7 +47,10 @@
- [✨ Считать объём, минуты и расход по каждому пользователю](items/usage-accounting.md) — Ни объём, ни длительность, ни обращения к платным сервисам никуда не записываются: восстановить расход задним числом не из чего.
- [✨ Сделать страницу статистики для владельца](items/admin-stats-screen.md) — Собранный учёт читается только запросом к базе руками: ни страницы, ни признака владельца в приложении нет.
- [🔬 Потолки SpeechKit по длине записи и по формату](items/speechkit-limits.md) — Потолок длины записи и перечень принимаемых форматов неизвестны, а цель про долгие записи без них не начинается.
- [🔬 Потолки приёма, конвертации и заливки по длине записи](items/intake-limits-measure.md) — Из пяти звеньев задача speechkit-limits замерила только модель распознавания: где отваливается шестичасовая запись до неё, неизвестно.
- [✨ Отклонять на приёме запись сверх потолка](items/reject-oversized-recording.md) — Запись сверх потолка принимается молча и висит в конвейере до истечения часового захвата, а человек всё это время ждёт текста.
- [✨ Резать длинную запись на фрагменты и продолжать с места остановки](items/long-audio-chunking.md) — Шаг конвейера повторяется целиком: перезапуск на пятом часу шестичасовой записи начинает распознавание заново и оплачивает его второй раз.
- [✨ Отдавать текст в сотни килобайт файлом, а не сотней сообщений](items/long-text-delivery.md) — Отправитель Telegram режет текст по 4000 знаков: расшифровка шестичасовой записи придёт сотней сообщений подряд.
- [🔬 Загрузка большого файла частями](items/chunked-upload-choice.md) — Гигабайтный файл едет одним запросом, и обрыв на девяноста процентах начинает его заново.
- [🧹 Сравнивать доменные ошибки через errors.As](items/errors-as-instead-of-typecast.md) — NoopJobError и JobNotFoundError проверяются приведением типа: первая же обёртка %w между слоями сломает проверку молча.
- [🧹 Покрыть тестами разбор вывода ffprobe](items/metaviewer-adapter-tests.md) — Проверки приёма перестали звать настоящий ffprobe 2026-08-11, а своего теста у адаптера метаданных нет: разбор JSON и отличие «программы нет в PATH» от «обработка отказала» не проверяет ничто.
+32
View File
@@ -0,0 +1,32 @@
# 🔬 Потолки приёма, конвертации и заливки по длине записи
- **Тип:** research
- **Категория:** Очередь
- **Зачем:** Из пяти звеньев задача speechkit-limits замерила только модель распознавания: где отваливается шестичасовая запись до неё, неизвестно.
- **Теги:** goal:long-recordings
Пункт 1 «Завершения» цели требует замера пяти звеньев, а разведка
`speechkit-limits` меряет одно — модель `deferred-general`. Остальные четыре
дешевле: они не требуют боевых ключей и считаются локально, кроме заливки.
Числа нужны раньше кода: они назначают потолок, который проверяет приём, и длину
фрагмента, на которые режет `long-audio-chunking`.
## Вопрос
На какой длине и на каком весе записи отказывает каждое звено до распознавания —
приём из Telegram, приём по HTTP вместе с обратным прокси, конвертация `ffmpeg`
и заливка в Object Storage, — и сколько времени и места на диске занимает
шестичасовая запись на каждом из них.
## Куда ляжет ответ
`docs/research/intake-limits.md` — числами и с командой замера по каждому звену.
Оттуда их берут задачи `reject-oversized-recording` и `long-audio-chunking`.
## Рамки
Заливка в Object Storage оплачивается по факту — число прогонов назначает
человек, пробные файлы после замера удаляются. Прочие звенья меряются локально,
на файлах, созданных `ffmpeg`, а не на записях пользователей. Потолок самой
модели меряет `speechkit-limits`, здесь он не трогается.
+41
View File
@@ -0,0 +1,41 @@
# ✨ Отдавать текст в сотни килобайт файлом, а не сотней сообщений
- **Тип:** feature
- **Категория:** Очередь
- **Зачем:** Отправитель Telegram режет текст по 4000 знаков: расшифровка шестичасовой записи придёт сотней сообщений подряд.
- **Теги:** goal:long-recordings
Двигает пункт 4 «Завершения» цели: текст в несколько сотен килобайт доходит и в
Telegram, и в браузере.
Деление по словам (`internal/adapter/telegram/split.go`) остаётся для обычной
расшифровки; сверх названного числа частей вместо потока сообщений уходит один
файл.
## Затрагивает
- `internal/adapter/telegram` — отправка документа помимо текстовых сообщений;
- порог, за которым текст уходит файлом, и его место в конфигурации;
- формат отправляемого файла и его имя, которое не раскрывает имени, данного
пользователем;
- экран чтения записи: показ текста в сотни килобайт и его выгрузка файлом;
- `docs/database.md` — порог числом.
## Критерии приёмки
- Текст, разбиваемый более чем на названное число частей, уходит в Telegram одним
файлом, а не потоком сообщений. Оракул — тест отправителя на тексте в 300 КиБ:
вызов отправки документа один, вызовов отправки сообщения нет.
- Обычная расшифровка по-прежнему приходит сообщениями, разбитыми по словам.
Оракул — существующий тест деления плюс тест на тексте в две части.
- Имя отправляемого файла не содержит имени, данного пользователем, и текста
записи. Оракул — тест: имя собрано из идентификатора задачи.
- Экран чтения открывает текст в сотни килобайт целиком и даёт выгрузить его
файлом. Оракул — тест экрана на подставном API с текстом в 300 КиБ.
## Рамки
Порог считается в частях, а не в байтах: он производен от предела сообщения
Telegram, который задаёт не наш сервис. Формат файла — простой текст, разметки
не добавляем. Инвариант приватности: имя файла пользователя в журнал и в имя
отправляемого документа не попадает.
+41
View File
@@ -0,0 +1,41 @@
# ✨ Отклонять на приёме запись сверх потолка
- **Тип:** feature
- **Категория:** Очередь
- **Зачем:** Запись сверх потолка принимается молча и висит в конвейере до истечения часового захвата, а человек всё это время ждёт текста.
- **Теги:** goal:long-recordings
Двигает пункт 2 «Завершения» цели: запись длиннее потолка отклоняется на приёме
понятным текстом, а не отказом через час.
Потолок задаётся конфигурацией, а не зашивается в код: числа приходят из
разведок `intake-limits-measure` и `speechkit-limits`, и меняются они без сборки.
## Затрагивает
- `TranscribeService.createTranscribeJob` — проверка длительности и веса до
записи файла на диск;
- контракт `POST /api/audio`: код и тело ответа на запись сверх потолка;
- ответ бота на слишком длинную запись;
- секция конфигурации: потолок длительности и потолок веса;
- `docs/database.md` — настройки с числовым значением.
## Критерии приёмки
- Запись длиннее потолка отклоняется на приёме: задача не заводится, файл на
диске не остаётся. Оракул — тест приёма на записи сверх потолка: задач ноль,
каталог хранения пуст.
- Отправитель видит человекочитаемый текст с названным пределом, а не код
ошибки. Оракул — тест: в теле ответа предел числом, текста внутренней ошибки
нет.
- Запись в пределах потолка принимается по-прежнему. Оракул — существующие
тесты приёма по HTTP.
- Потолок меняется конфигурацией без пересборки. Оракул — тест разбора
конфигурации с двумя разными значениями.
## Рамки
Берётся после `intake-limits-measure`: до неё потолок брать неоткуда. Длительность
известна из `ffprobe` — значит, проверка идёт после чтения метаданных, но до
записи в базу. Предел веса на стороне сервера уже есть
(`router.MaxMultipartMemory`), и он не отменяется, а дополняется.