tasks: очередь расставлена от базы к деталям
- порядок беклога и роадмапа назначен слоями: проверки, которым можно верить → долги входа → владелец записи и контракт API → конвейер под тестами → приложение и возможности поверх; у каждого движения записана причина; - заведены восемь задач под пункты «Завершения», которых не закрывала ни одна запись, — цель any-audio-source была без задач вовсе; - у четырёх задач сняты критерии, требовавшие того, что делает задача ниже по очереди; исправлены ссылки на несуществующий repo/sqlite и на отменённую разведку об очереди.
This commit is contained in:
+54
-27
@@ -11,6 +11,25 @@
|
||||
Секция одна — полок домена у проекта нет, и делить очередь на две
|
||||
значило бы держать два порядка вместо одного.
|
||||
|
||||
**Чем очередь упорядочена на этом этапе — от базы к деталям.**
|
||||
Сначала то, на чём стоит остальное: проверки, которым можно верить,
|
||||
владелец записи, единый контракт API, покрытый тестами конвейер, — и
|
||||
только потом экраны и возможности поверх них. Порядок расставлен на
|
||||
груминге 2026-08-12 и держится, пока сервис не собран целиком:
|
||||
задача, взятая раньше своего основания, стоит дважды — сперва её
|
||||
пишут, потом переписывают под появившееся основание.
|
||||
|
||||
Отсюда правило для **новых** записей. Заведённая по ходу работы —
|
||||
интейком, урожаем ревью, разбором находок — задача встаёт в конец
|
||||
очереди машинально, и это **не** её место, а отсутствие места.
|
||||
Слой ей назначает человек на ближайшем груминге: ниже того, чего она
|
||||
требует, и выше того, что требует её. Причина уезжает в запись
|
||||
(`move --after <слаг> --reason`).
|
||||
|
||||
Разведки это касается вдвойне: её исход — новые задачи, и слой они
|
||||
наследуют не от разведки, а от того, что трогают. Разведка о конвейере
|
||||
может принести задачу основания, которой место в голове очереди.
|
||||
|
||||
Тип записи стоит первым полем меты и решает, что у неё может быть:
|
||||
✨ `feature` 🐞 `fix` 🧹 `chore` 🔬 `research`
|
||||
|
||||
@@ -20,55 +39,63 @@
|
||||
|
||||
## Очередь
|
||||
|
||||
- [🐞 Убирать записанный файл, когда приём отказал на середине](items/orphan-file-on-failed-intake.md) — Отказ чтения метаданных и отказ записи на диск оставляют файл в каталоге хранения без задачи и без учёта: сопоставить его не с чем, удалять приходится руками.
|
||||
- [🧹 Задать таймауты обращениям к внешним сервисам](items/external-call-timeouts.md) — Ни у Telegram, ни у Object Storage, ни у SpeechKit нет таймаута: молчащий собеседник держит шаг конвейера до истечения часового захвата.
|
||||
- [🐞 Починить путь миграций в настройке сверки документов](items/fix-migrations-path-in-docs-config.md) — Ключ migrations указывает на каталог migrations/, которого в репозитории нет: шаг гейта зелен при изменённой миграции и нетронутом database.md, а конвенции числят этот род механизированным.
|
||||
- [🧹 Привести коды выхода шагов гейта к общему словарю](items/gate-step-exit-codes.md) — CLAUDE.md объявляет словарь кодов общим для четырёх проверочных шагов и описывает код 3 как «каталог или файл не найден», но все четыре обёртки в Taskfile.yml при отсутствующем скрипте отдают 1 («дрейф»), из-за чего читатель кода идёт искать разъехавшееся там, где просто неполно дерево.
|
||||
- [🧹 Судить ответ в тестах по готовому ответу](items/response-assertions-judge-result.md) — Проверка, читающая изменяемую карту заголовков обработчика, зелена при неработающем коде: класс всплыл трижды, последний раз на уборке куки входа.
|
||||
- [🧹 Ронять гейт на изменённой функции, которую не выполняет ни один тест](items/gate-changed-lines-coverage.md) — Свойство «изменённое место покрыто хоть одним тестом» записано в docs/review.md, но не механизировано: за две задачи подряд непокрытые шаги ловили руками.
|
||||
- [🧹 Ловить уязвимости в зависимостях шагом гейта](items/gate-dependency-vulnerabilities.md) — govulncheck находит две достижимые уязвимости в клиентах Yandex, а ни гейт, ни список «чего в гейте нет» о нём не знают: узнать о третьей будет неоткуда.
|
||||
- [🧹 Поднимать сервис локально без действующего токена бота](items/local-run-without-telegram-token.md) — Адаптер Telegram проверяет токен обращением к Telegram и роняет старт, а боевым токеном запускаться запрещено: проверить поведение живым прогоном не может ни одна задача.
|
||||
- [🐞 Убрать код провайдера из журнала запросов хранилища](items/provider-code-out-of-storage-log.md) — Строка запроса с кодом входа целиком уезжает в таблицу _logs и лежит там пять суток, хотя спека access требует, чтобы код в журнал не попадал.
|
||||
- [🐞 Вести учёт употреблённых состояний входа на сервере](items/server-side-login-state.md) — Одноразовость возврата держится на уборке куки, то есть на браузере: сервер не помнит, какие состояния уже потрачены.
|
||||
- [🧹 Строить адрес входа из настроек коллекции, а не из конфига](items/login-url-from-collection-settings.md) — Первая половина входа собрана руками из конфига и на настройки провайдера не смотрит, вторая берётся из коллекции: обновление библиотеки изменит только вторую половину.
|
||||
- [🔬 Четыре недоказанные гипотезы о поверхности входа](items/login-surface-hypotheses.md) — Ревью назвало четыре пути, которых не смогло ни подтвердить, ни опровергнуть: браузера и живого провайдера в прогоне не было.
|
||||
- [🧹 Назвать в необратимом, что откат кода не откатывает шаг схемы](items/rollback-does-not-undo-schema-step.md) — Откат бинаря оставляет применённый шаг схемы в силе, и на этом строятся решения о выкладке: сегодня об этом не сказано нигде.
|
||||
- [🔬 Адрес объекта в тексте отказа SpeechKit](items/speechkit-error-text-leak.md) — Текст отказа операции приходит от Yandex и уезжает в журнал и в колонку error_text: если он несёт URI объекта, из журнала снова собирается ссылка на чужую запись.
|
||||
- [🧹 Разобрать мелочи http-транспорта](items/http-transport-nits.md) — Маршруты зарегистрированы дважды, и переименование пути в main.go проходит проверки зелёным; обработчик пишет в журнал через стандартный log и дублирует запись, уже сделанную сервисом.
|
||||
- [🧹 Переименовать образец конфига в config.example.toml](items/config-example-toml.md) — Конвенция называет config.dist.toml объявленным расхождением, но тут же пишет это имя как правило — документ противоречит сам себе, а образец расходится с конвенцией.
|
||||
- [✨ Привязать запись к владельцу и отдавать только свои](items/record-ownership.md) — У задачи и файла нет владельца, поэтому знание UUID задачи и есть право её читать.
|
||||
- [✨ Сопоставить пользователя Telegram с учётной записью](items/telegram-account-link.md) — Белый список сверяется с именем пользователя Telegram, которое владелец меняет в любой момент, а записи из бота ни с кем не связаны.
|
||||
- [✨ Свести приём и чтение записей к одному контракту для приложения](items/json-api-for-spa.md) — Сегодняшний API отвечает 404 на любую ошибку чтения и 500 на любую ошибку приёма: строить на нём экраны нечем.
|
||||
- [✨ Сопоставить пользователя Telegram с учётной записью](items/telegram-account-link.md) — Белый список сверяется с именем пользователя Telegram, которое владелец меняет в любой момент, а записи из бота ни с кем не связаны.
|
||||
- [✨ Пускать скрипты в API по личным токенам](items/api-tokens.md) — Вход через OIDC закрывает API целиком, а скрипту браузерная сессия недоступна: автоматизировать загрузку станет нечем.
|
||||
- [🧹 Покрыть тестами шаги конвейера и захват задачи](items/pipeline-step-tests.md) — Тестовых файлов в проекте два, и оба мимо конвейера: потеря ссылки на файл, двойной ответ пользователю и гонка при захвате не поймаются ничем.
|
||||
- [🧹 Покрыть тестами разбор вывода ffprobe](items/metaviewer-adapter-tests.md) — Проверки приёма перестали звать настоящий ffprobe 2026-08-11, а своего теста у адаптера метаданных нет: разбор JSON и отличие «программы нет в PATH» от «обработка отказала» не проверяет ничто.
|
||||
- [🧹 Задать таймауты обращениям к внешним сервисам](items/external-call-timeouts.md) — Ни у Telegram, ни у Object Storage, ни у SpeechKit нет таймаута: молчащий собеседник держит шаг конвейера до истечения часового захвата.
|
||||
- [🧹 Прервать шаг конвейера отменой контекста](items/context-cancel-in-pipeline.md) — Воркер читает ctx только между итерациями: остановка контейнера ждёт конца шага, а на занятом писателе один запрос к хранилищу держится до 9,5 секунды при мягком таймауте в 5.
|
||||
- [🐞 Убирать записанный файл, когда приём отказал на середине](items/orphan-file-on-failed-intake.md) — Отказ чтения метаданных и отказ записи на диск оставляют файл в каталоге хранения без задачи и без учёта: сопоставить его не с чем, удалять приходится руками.
|
||||
- [🧹 Разобрать мелочи слоя хранилища](items/storage-layer-nits.md) — Три мелочи ниже потолка триажа: цикл воркера пишет потерю захвата уровнем ERROR и считает её отказом, тип ошибки заведён там, где конвенция просит sentinel, а FileName несёт два разных смысла.
|
||||
- [✨ Собрать каркас приложения и раздать его из бинарника](items/spa-skeleton.md) — Экранов нет и собирать их нечем: ни сборки фронтенда, ни раздачи статики в проекте не существует.
|
||||
- [✨ Сделать экран загрузки записи и её состояния](items/upload-and-status-screen.md) — Первое, ради чего приложение открывают: отдать файл и увидеть, что с ним происходит.
|
||||
- [✨ Сделать экран списка своих записей и чтения текста](items/records-list-screen.md) — Расшифровка сегодня доходит одним сообщением и теряется в переписке; вернуться к ней через неделю нечем.
|
||||
- [✨ Удалять запись со всеми уровнями текста по требованию владельца](items/delete-record.md) — Ни файлы, ни расшифровки не удаляются вовсе: убрать запись сегодня можно только руками в базе и в каталоге на сервере.
|
||||
- [✨ Проигрывать загруженную запись на экране записи](items/play-recording-in-app.md) — Послушать загруженное приложение не даёт, а самой копии для этого у задачи нет: указатель на файл перезаписывается на каждом шаге конвейера и у готовой задачи ведёт на объект в Object Storage.
|
||||
- [✨ Сделать приложение устанавливаемым на телефон](items/installable-pwa.md) — Приложение, живущее вкладкой браузера, теряется среди прочих: ярлыка на экране у него нет.
|
||||
- [✨ Узнавать уже загруженный файл по хеш-сумме](items/dedup-by-content-hash.md) — Один и тот же файл, отправленный дважды, распознаётся дважды и оплачивается дважды: приём не смотрит на содержимое вовсе.
|
||||
- [✨ Принимать до десяти файлов одной загрузкой](items/multi-file-upload.md) — Приём берёт один файл в запросе, а с телефона выбирают пачку сразу: десять записей значат десять заходов на экран загрузки.
|
||||
- [✨ Показывать ход загрузки записи на экране](items/upload-progress.md) — Гигабайтный файл уходит на сервер молча: до ответа сервера экран не отличает идущую загрузку от зависшей.
|
||||
- [✨ Сделать приложение устанавливаемым на телефон](items/installable-pwa.md) — Приложение, живущее вкладкой браузера, теряется среди прочих: ярлыка на экране у него нет.
|
||||
- [🔬 Загрузка большого файла частями](items/chunked-upload-choice.md) — Гигабайтный файл едет одним запросом, и обрыв на девяноста процентах начинает его заново.
|
||||
- [✨ Удалять запись со всеми уровнями текста по требованию владельца](items/delete-record.md) — Ни файлы, ни расшифровки не удаляются вовсе: убрать запись сегодня можно только руками в базе и в каталоге на сервере.
|
||||
- [✨ Сделать экран настроек и хранить настройки по пользователю](items/settings-screen.md) — Настроек у пользователя нет вовсе: уровни текста и канал уведомлений задаются общим конфигом сервиса.
|
||||
- [✨ Считать заголовок, темы и пересказ внешней моделью](items/llm-insights-adapter.md) — Расшифровка доходит стеной текста: ни заголовка, ни тем, ни пересказа сервис не считает, и клиента языковой модели в нём нет.
|
||||
- [✨ Отдавать вычитанный текст рядом с сырым](items/literary-text-level.md) — Сырая расшифровка идёт без знаков препинания, с повторами и словами-паразитами: читать её подряд тяжело, а другого уровня текста нет.
|
||||
- [✨ Показывать заголовок в списке, отбирать список по темам и считать токены](items/insights-visible-in-list.md) — Заголовок, темы и пересказ считаются, но список по-прежнему показывает первые слова расшифровки и не отбирается ничем, а расход на модель не виден числом.
|
||||
- [✨ Считать только те уровни текста, что включены у владельца записи](items/settings-applied-in-pipeline.md) — Дом настроек есть, а конвейер их не читает: выключенный уровень всё равно уходит платной модели, и настройка ничего не экономит.
|
||||
- [✨ Отправлять готовый текст через apprise и ntfy](items/ntfy-delivery.md) — Пользователь веба узнаёт о готовности только опросом с открытого экрана.
|
||||
- [✨ Слать готовый текст на почту из учётной записи](items/email-notification.md) — Адрес почты приходит вместе с входом через OIDC, но почтового отправителя в сервисе нет.
|
||||
- [✨ Считать объём, минуты и расход по каждому пользователю](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) — Гигабайтный файл едет одним запросом, и обрыв на девяноста процентах начинает его заново.
|
||||
- [🧹 Покрыть тестами разбор вывода ffprobe](items/metaviewer-adapter-tests.md) — Проверки приёма перестали звать настоящий ffprobe 2026-08-11, а своего теста у адаптера метаданных нет: разбор JSON и отличие «программы нет в PATH» от «обработка отказала» не проверяет ничто.
|
||||
- [🧹 Покрыть тестами шаги конвейера и захват задачи](items/pipeline-step-tests.md) — Тестовых файлов в проекте два, и оба мимо конвейера: потеря ссылки на файл, двойной ответ пользователю и гонка при захвате не поймаются ничем.
|
||||
- [🧹 Разобрать мелочи http-транспорта](items/http-transport-nits.md) — Маршруты зарегистрированы дважды, и переименование пути в main.go проходит проверки зелёным; обработчик пишет в журнал через стандартный log и дублирует запись, уже сделанную сервисом.
|
||||
- [🧹 Переименовать образец конфига в config.example.toml](items/config-example-toml.md) — Конвенция называет config.dist.toml объявленным расхождением, но тут же пишет это имя как правило — документ противоречит сам себе, а образец расходится с конвенцией.
|
||||
- [🔬 Перечень форматов, которые конвейер принимает на самом деле](items/audio-format-coverage-measure.md) — Команда ffmpeg проверена на голосовых Telegram, а что она берёт помимо них, не мерил никто: перечень выведен из документации, а не из прогона.
|
||||
- [✨ Принимать видео и брать из него звуковую дорожку](items/video-audio-track-intake.md) — Запись семейного архива приходит видеофайлом, а приём смотрит на аудио: человеку приходится доставать дорожку самому.
|
||||
- [🔬 Стоит ли брать OpenTelemetry вместо голого Prometheus](items/opentelemetry-fit.md) — Метрик одиннадцать штук на пять счётчиков, трассировки нет вовсе: путь одной записи по конвейеру собирается только чтением логов глазами.
|
||||
- [🧹 Ловить уязвимости в зависимостях шагом гейта](items/gate-dependency-vulnerabilities.md) — govulncheck находит две достижимые уязвимости в клиентах Yandex, а ни гейт, ни список «чего в гейте нет» о нём не знают: узнать о третьей будет неоткуда.
|
||||
- [🧹 Считать покрытие изменённых строк шагом гейта](items/gate-changed-lines-coverage.md) — Свойство «изменённое место покрыто хоть одним тестом» записано в docs/review.md, но не механизировано: за две задачи подряд непокрытые шаги ловили руками.
|
||||
- [🧹 Прервать шаг конвейера отменой контекста](items/context-cancel-in-pipeline.md) — Воркер читает ctx только между итерациями: остановка контейнера ждёт конца шага, а на занятом писателе один запрос к хранилищу держится до 9,5 секунды при мягком таймауте в 5.
|
||||
- [🧹 Разобрать мелочи слоя хранилища](items/storage-layer-nits.md) — Три мелочи ниже потолка триажа: цикл воркера пишет потерю захвата уровнем ERROR и считает её отказом, тип ошибки заведён там, где конвенция просит sentinel, а FileName несёт два разных смысла.
|
||||
- [✨ Собирать путь одной записи по конвейеру запросом](items/job-path-by-request.md) — Звенья пути связаны только идентификатором задачи в строках журнала: чтобы понять, где запись провела минуты, владелец читает логи контейнера глазами.
|
||||
- [✨ Считать вызовы, отказы и длительность по каждому внешнему сервису](items/external-service-metrics.md) — Ни у Telegram, ни у Object Storage, ни у SpeechKit нет ни одной метрики: отказ внешнего сервиса виден только строкой в журнале контейнера.
|
||||
- [✨ Показывать метрикой задачу, застрявшую в состоянии](items/stalled-pipeline-metric.md) — Вставший конвейер неотличим от простоя: возраст задачи в состоянии не считается, и очередь без движения выглядит как отсутствие работы.
|
||||
- [✨ Оповещать владельца об отказе, не дожидаясь жалобы](items/owner-alerting.md) — Об отказе владелец узнаёт от пользователя: правил оповещения нет ни на одной метрике, а метрики читают глазами.
|
||||
- [🔬 Уведомление SpeechKit о готовности вместо опроса](items/speechkit-callback-fit.md) — Шаг проверки дёргает операцию раз в 5 секунд всё время распознавания: часовая запись даёт порядка 720 обращений к платному сервису вместо одного ответа.
|
||||
- [🔬 Адрес объекта в тексте отказа SpeechKit](items/speechkit-error-text-leak.md) — Текст отказа операции приходит от Yandex и уезжает в журнал и в колонку error_text: если он несёт URI объекта, из журнала снова собирается ссылка на чужую запись.
|
||||
- [✨ Проигрывать загруженную запись на экране записи](items/play-recording-in-app.md) — Послушать загруженное приложение не даёт, а самой копии для этого у задачи нет: указатель на файл перезаписывается на каждом шаге конвейера и у готовой задачи ведёт на объект в Object Storage.
|
||||
- [✨ Считать объём, минуты и расход по каждому пользователю](items/usage-accounting.md) — Ни объём, ни длительность, ни обращения к платным сервисам никуда не записываются: восстановить расход задним числом не из чего.
|
||||
- [✨ Сделать страницу статистики для владельца](items/admin-stats-screen.md) — Собранный учёт читается только запросом к базе руками: ни страницы, ни признака владельца в приложении нет.
|
||||
- [🧹 Завести недостающие проверки: shellcheck, hadolint и тест скрипта сверки версий](items/gate-extra-linters.md) — Скрипты оболочки и Dockerfile машина не проверяет: shellcheck и hadolint не заведены, lefthook смотрит только gitleaks, а единственный исполняемый скрипт проекта на shell — scripts/check-go-version.sh — разбирали на ревью глазами построчно, потому что машине его показать нечем.
|
||||
- [🧹 Закрепить версию рантайм-базы образа](items/pin-runtime-image-base.md) — Финальный слой Dockerfile собирается на alpine:latest, а task image идёт с --pull, поэтому два образа из одного коммита с разницей в неделю несут разный ffmpeg — регрессия конвертации после такой пересборки выглядит как задачи в failed при пустом диффе репозитория, и откат на прежний коммит её не чинит.
|
||||
- [🧹 Привести коды выхода шагов гейта к общему словарю](items/gate-step-exit-codes.md) — CLAUDE.md объявляет словарь кодов общим для четырёх проверочных шагов и описывает код 3 как «каталог или файл не найден», но все четыре обёртки в Taskfile.yml при отсутствующем скрипте отдают 1 («дрейф»), из-за чего читатель кода идёт искать разъехавшееся там, где просто неполно дерево.
|
||||
- [🧹 Настроить конвейер ревью по итогам прогона go-1-26-upgrade](items/review-config-from-go-upgrade.md) — Прогон вскрыл две прорехи настройки: «Типовые узлы» знают только рантайм и не знают рода «проверочный шаг набора проверок», а «Триггеры метки» не видят оси «изменение трогает канон» — и именно она дала обе блокирующие находки.
|
||||
- [🐞 Починить путь миграций в настройке сверки документов](items/fix-migrations-path-in-docs-config.md) — Ключ migrations указывает на каталог migrations/, которого в репозитории нет: шаг гейта зелен при изменённой миграции и нетронутом database.md, а конвенции числят этот род механизированным.
|
||||
- [🐞 Убрать код провайдера из журнала запросов хранилища](items/provider-code-out-of-storage-log.md) — Строка запроса с кодом входа целиком уезжает в таблицу _logs и лежит там пять суток, хотя спека access требует, чтобы код в журнал не попадал.
|
||||
- [🧹 Поднимать сервис локально без действующего токена бота](items/local-run-without-telegram-token.md) — Адаптер Telegram проверяет токен обращением к Telegram и роняет старт, а боевым токеном запускаться запрещено: проверить поведение живым прогоном не может ни одна задача.
|
||||
- [🧹 Строить адрес входа из настроек коллекции, а не из конфига](items/login-url-from-collection-settings.md) — Первая половина входа собрана руками из конфига и на настройки провайдера не смотрит, вторая берётся из коллекции: обновление библиотеки изменит только вторую половину.
|
||||
- [🧹 Судить ответ в тестах по готовому ответу](items/response-assertions-judge-result.md) — Проверка, читающая изменяемую карту заголовков обработчика, зелена при неработающем коде: класс всплыл трижды, последний раз на уборке куки входа.
|
||||
- [🧹 Назвать в необратимом, что откат кода не откатывает шаг схемы](items/rollback-does-not-undo-schema-step.md) — Откат бинаря оставляет применённый шаг схемы в силе, и на этом строятся решения о выкладке: сегодня об этом не сказано нигде.
|
||||
- [🐞 Вести учёт употреблённых состояний входа на сервере](items/server-side-login-state.md) — Одноразовость возврата держится на уборке куки, то есть на браузере: сервер не помнит, какие состояния уже потрачены.
|
||||
- [🔬 Четыре недоказанные гипотезы о поверхности входа](items/login-surface-hypotheses.md) — Ревью назвало четыре пути, которых не смогло ни подтвердить, ни опровергнуть: браузера и живого провайдера в прогоне не было.
|
||||
- [🔬 Квота по общему размеру загруженного на пользователя](items/per-user-size-quota.md) — Паспорт и security.md запрещают отказы по квоте пользователю, а заметка владельца просит квоту по умолчанию 5 ГБ — открытое противоречие с границей домена, которое владелец решил не разбирать сейчас.
|
||||
|
||||
+3
-3
@@ -25,15 +25,15 @@
|
||||
- [🎯 Сервисом пользуются несколько человек, и записи одного не видны другому](items/multi-user.md) — У задачи нет владельца, а HTTP API открыт наружу без аутентификации: пригласить второго человека сейчас значит открыть ему чужие расшифровки.
|
||||
- [🎯 Записи загружаются и читаются в приложении, которое ставится на телефон](items/web-access.md) — Сегодня записи принимает только бот и голый HTTP API без интерфейса: отдать сервис человеку, у которого нет Telegram, нечем.
|
||||
- [🎯 Загрузка большого файла доходит до сервиса и не повторяется впустую](items/upload-reliability.md) — Приём рассчитан на голосовое в пару мегабайт: обрыв на середине гигабайтного файла начинает загрузку заново, а один и тот же файл распознаётся повторно за наши деньги.
|
||||
- [🎯 Человек убирает свою запись из архива вместе со всеми текстами](items/data-ownership.md) — Хранение бессрочное, а способа убрать запись нет ни одного: ошибочно загруженный файл и разговор, который человек не хочет держать у нас, остаются навсегда.
|
||||
- [🎯 Пользователь настраивает, что сервис делает с его записями](items/user-settings.md) — Уровни текста считает платная модель, а уведомления приходят одним общим способом: отказаться от лишнего и выбрать свой канал пользователю нечем.
|
||||
- [🎯 Приложение показывает, о чём запись, не читая её целиком](items/text-insights.md) — Расшифровка часового разговора — это стена текста: найти в списке нужную запись и вспомнить, о чём она, сегодня нечем.
|
||||
- [🎯 Пользователь узнаёт о готовности текста, не держа приложение открытым](items/ready-notification.md) — Расшифровка занимает минуты, и всё это время человек либо смотрит на экран с опросом статуса, либо забывает вернуться.
|
||||
|
||||
## Направления
|
||||
|
||||
- [🎯 Принимается запись любого формата, включая дорожку из видео](items/any-audio-source.md) — Конвертер вызывается одной командой ffmpeg, проверенной на голосовых Telegram; что он берёт помимо них, никто не мерил.
|
||||
- [🎯 Запись длиной до шести часов доходит до текста](items/long-recordings.md) — Потолок не замерен ни на одном звене: Telegram не отдаёт больше 20 МиБ, границы модели deferred-general неизвестны, а перезапуск на середине начинает распознавание заново.
|
||||
- [🎯 Приложение показывает, о чём запись, не читая её целиком](items/text-insights.md) — Расшифровка часового разговора — это стена текста: найти в списке нужную запись и вспомнить, о чём она, сегодня нечем.
|
||||
- [🎯 Человек убирает свою запись из архива вместе со всеми текстами](items/data-ownership.md) — Хранение бессрочное, а способа убрать запись нет ни одного: ошибочно загруженный файл и разговор, который человек не хочет держать у нас, остаются навсегда.
|
||||
- [🎯 Принимается запись любого формата, включая дорожку из видео](items/any-audio-source.md) — Конвертер вызывается одной командой ffmpeg, проверенной на голосовых Telegram; что он берёт помимо них, никто не мерил.
|
||||
|
||||
## Сопровождение
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Сделать страницу статистики для владельца
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Страница показывает собранный учёт: без учёта показывать нечего.
|
||||
- **Зачем:** Собранный учёт читается только запросом к базе руками: ни страницы, ни признака владельца в приложении нет.
|
||||
- **Теги:** goal:usage-stats
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# 🎯 Принимается запись любого формата, включая дорожку из видео
|
||||
|
||||
- **Тип:** goal
|
||||
- **Секция:** Направления
|
||||
- **Секция:** Направления — Перечень форматов не замерен, и потолок длины у видео тот же, что у долгих записей: тянется следом за ними.
|
||||
- **Зачем:** Конвертер вызывается одной командой ffmpeg, проверенной на голосовых Telegram; что он берёт помимо них, никто не мерил.
|
||||
- **Теги:** decomposed
|
||||
|
||||
Человек отдаёт файл, не думая о том, что внутри: аудио любого распространённого
|
||||
контейнера или видео, из которого нужна только речь. Подготовка на стороне
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Пускать скрипты в API по личным токенам
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Второй способ представиться ставится на готовые владельца и контракт, иначе форма ошибки переписывается дважды.
|
||||
- **Зачем:** Вход через OIDC закрывает API целиком, а скрипту браузерная сессия недоступна: автоматизировать загрузку станет нечем.
|
||||
- **Теги:** goal:multi-user
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
- таблица токенов: владелец, имя, отпечаток, время выпуска и последнего
|
||||
обращения, и её миграция;
|
||||
- эндпоинты выпуска, перечня и отзыва токена;
|
||||
- экран настроек — место, где токен выпускают и отзывают;
|
||||
- `docs/security.md` — второй способ представиться и хранение отпечатка;
|
||||
- `README.md` — пример вызова API скриптом.
|
||||
|
||||
@@ -38,4 +37,6 @@
|
||||
|
||||
Учётные записи по-прежнему заводит Authelia — свою регистрацию не делаем.
|
||||
Сроков жизни и областей действия у токена не заводим: он даёт права владельца
|
||||
целиком. Берётся после `oidc-login`: до неё представляться некому.
|
||||
целиком. Берётся после `oidc-login`: до неё представляться некому. Экрана выпуска здесь
|
||||
нет — приложения ещё не существует, токен выпускается запросом к API; место
|
||||
токена на экране настроек заводит `settings-screen`.
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# 🔬 Перечень форматов, которые конвейер принимает на самом деле
|
||||
|
||||
- **Тип:** research
|
||||
- **Категория:** Очередь — Форматы: сначала замер того, что конвейер берёт на самом деле.
|
||||
- **Зачем:** Команда ffmpeg проверена на голосовых Telegram, а что она берёт помимо них, не мерил никто: перечень выведен из документации, а не из прогона.
|
||||
- **Теги:** goal:any-audio-source
|
||||
|
||||
Двигает пункты 1 и 4 «Завершения» цели: перечень принимаемых форматов замерен и
|
||||
записан, а расхождение `ogg/vorbis` против заявленного SpeechKit `OGG_OPUS`
|
||||
разобрано.
|
||||
|
||||
## Вопрос
|
||||
|
||||
Какие форматы доходят до текста целиком, какие ломаются на конвертации, какие —
|
||||
на распознавании, и чем на самом деле кодирует конвертер: `vorbis` или `opus`.
|
||||
|
||||
## Куда ляжет ответ
|
||||
|
||||
- `docs/research/audio-formats.md` — таблица «формат на входе → исход», с
|
||||
командой замера и версией ffmpeg, на которой он сделан;
|
||||
- расхождение `ogg/vorbis` против `OGG_OPUS`: строка о том, устранено оно или
|
||||
проверенно безвредно, и чем это подтверждено;
|
||||
- форматы, которые принять нельзя, — задачей об отказе на приёме, с провенансом
|
||||
этой разведки.
|
||||
|
||||
## Рамки
|
||||
|
||||
Замер идёт на своих файлах во временном каталоге и на подставном распознавателе
|
||||
`internal/adapter/recognizer/memory.go`; прогон на реальных ключах Yandex
|
||||
запрещён — там, где без настоящего SpeechKit не обойтись, ответ берётся из
|
||||
задачи `speechkit-limits`, а не оплачивается заново. Видеофайлы здесь только
|
||||
измеряются, приём их заводит `video-audio-track-intake`.
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🔬 Загрузка большого файла частями
|
||||
|
||||
- **Тип:** research
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Разведка про докачку идёт после того, как обычная загрузка доведена: иначе сравнивать не с чем.
|
||||
- **Зачем:** Гигабайтный файл едет одним запросом, и обрыв на девяноста процентах начинает его заново.
|
||||
|
||||
Шестичасовая диктофонная запись и видео из семейного архива весят гигабайты, а
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Переименовать образец конфига в config.example.toml
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Поднято наверх: шесть задач ниже правят конфиг и каждая допишет старое имя образца, удлиняя перечень мест переименования.
|
||||
- **Зачем:** Конвенция называет config.dist.toml объявленным расхождением, но тут же пишет это имя как правило — документ противоречит сам себе, а образец расходится с конвенцией.
|
||||
|
||||
Конвенция конфигурации взята из проекта jellybit и **сама называет сегодняшнее
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Прервать шаг конвейера отменой контекста
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Отмена контекста трогает те же шаги, что и таймауты: одна окрестность кода, два захода дороже одного.
|
||||
- **Зачем:** Воркер читает ctx только между итерациями: остановка контейнера ждёт конца шага, а на занятом писателе один запрос к хранилищу держится до 9,5 секунды при мягком таймауте в 5.
|
||||
|
||||
Долг назван в `docs/architecture.md` — раздел про capability `pipeline`, где
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🎯 Человек убирает свою запись из архива вместе со всеми текстами
|
||||
|
||||
- **Тип:** goal
|
||||
- **Секция:** Направления
|
||||
- **Секция:** Запланировано — Очередь у цели появилась: удаление записи стоит 32-й строкой и трогает конвейер, файлы и колонку дедупликации, которые к тому месту готовы.
|
||||
- **Зачем:** Хранение бессрочное, а способа убрать запись нет ни одного: ошибочно загруженный файл и разговор, который человек не хочет держать у нас, остаются навсегда.
|
||||
- **Теги:** decomposed
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Узнавать уже загруженный файл по хеш-сумме
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Дедупликация ищет совпадение в пределах пользователя — то есть после владельца записи, и экономит деньги с первого дня приложения.
|
||||
- **Зачем:** Один и тот же файл, отправленный дважды, распознаётся дважды и оплачивается дважды: приём не смотрит на содержимое вовсе.
|
||||
- **Теги:** goal:upload-reliability
|
||||
|
||||
@@ -38,4 +38,6 @@
|
||||
Владелец записи приходит из `record-ownership` — до неё дедупликация опирается
|
||||
на того владельца, который уже есть. Хеш-сумма считается на сервере: подсчёт на
|
||||
стороне приложения относится к `chunked-upload-choice`. Колонка добавляется во
|
||||
всех четырёх местах репозитория SQLite (инвариант `CLAUDE.md`).
|
||||
всех четырёх местах пакета хранилища `internal/adapter/repo/pocketbase` —
|
||||
`applyToRecord`, `recordToJob`, `acquireColumns`, `acquiredRow` с её `toJob`, —
|
||||
плюс шаг схемы (инвариант `CLAUDE.md`).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Удалять запись со всеми уровнями текста по требованию владельца
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Удаление трогает конвейер, файлы, объект хранилища и колонку дедупликации — всё это к этому месту уже готово.
|
||||
- **Зачем:** Ни файлы, ни расшифровки не удаляются вовсе: убрать запись сегодня можно только руками в базе и в каталоге на сервере.
|
||||
- **Теги:** goal:data-ownership
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Слать готовый текст на почту из учётной записи
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Второй канал на той же доставке.
|
||||
- **Зачем:** Адрес почты приходит вместе с входом через OIDC, но почтового отправителя в сервисе нет.
|
||||
- **Теги:** goal:ready-notification
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Задать таймауты обращениям к внешним сервисам
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Молчащий внешний сервис держит шаг до истечения часового захвата — правится под свежими тестами конвейера.
|
||||
- **Зачем:** Ни у Telegram, ни у Object Storage, ни у SpeechKit нет таймаута: молчащий собеседник держит шаг конвейера до истечения часового захвата.
|
||||
|
||||
Все обращения наружу идут с `context.Background()` либо через `http.Get` без
|
||||
@@ -33,9 +33,6 @@
|
||||
- У каждого обращения наружу есть таймаут, и его значение задаётся конфигом.
|
||||
Оракул — тест на подставном сервере, который не отвечает: вызов
|
||||
возвращается с ошибкой за назначенное время, а не висит.
|
||||
- Отмена контекста при остановке приложения прерывает шаг конвейера. Оракул —
|
||||
тест: отменённый контекст возвращает управление из шага, задача остаётся в
|
||||
прежнем состоянии.
|
||||
- Прерванная по таймауту задача достаётся повторно и доходит до текста. Оракул
|
||||
— тест на повторный прогон шага после отказа по таймауту.
|
||||
- Числа таймаутов записаны в `docs/database.md`. Оракул — `task gate`.
|
||||
@@ -43,4 +40,7 @@
|
||||
## Рамки
|
||||
|
||||
Повторов с нарастающей паузой не заводим — это отдельная работа; здесь только
|
||||
таймаут и отмена.
|
||||
таймаут. Прерывание шага отменённым контекстом сюда не входит: методы записи
|
||||
хранилища контекста не принимают вовсе, и этим занимается следующая задача
|
||||
`context-cancel-in-pipeline`. Контекст первым аргументом интерфейсов эта задача
|
||||
всё же протаскивает — без него таймаут некуда передать.
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# ✨ Считать вызовы, отказы и длительность по каждому внешнему сервису
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь — Метрики внешних сервисов пишутся в выбранном словаре, а не переписываются потом.
|
||||
- **Зачем:** Ни у Telegram, ни у Object Storage, ни у SpeechKit нет ни одной метрики: отказ внешнего сервиса виден только строкой в журнале контейнера.
|
||||
- **Теги:** goal:service-observability
|
||||
|
||||
Двигает пункты 2 и 5 «Завершения» цели: у каждого внешнего сервиса появляются
|
||||
вызовы, отказы и длительность, а расход на платные сервисы виден числом.
|
||||
|
||||
Внешних сервисов сегодня четыре — Telegram, Object Storage, SpeechKit и
|
||||
`ffmpeg`/`ffprobe` как внешний процесс; пятым станет языковая модель. Метрика
|
||||
заводится одной формой на все, чтобы шестой сервис не приносил шестого способа
|
||||
считать.
|
||||
|
||||
## Затрагивает
|
||||
|
||||
- `internal/metrics` — форма метрики внешнего вызова: имя сервиса, операция,
|
||||
исход, гистограмма длительности;
|
||||
- адаптеры `internal/adapter/*` — место, где вызов оборачивается замером;
|
||||
- `docs/architecture.md`, раздел эксплуатации — перечень метрик;
|
||||
- `docs/conventions/` — правило: новый внешний вызов приходит со своей меткой,
|
||||
а не со своей метрикой.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- У каждого из четырёх внешних сервисов есть счётчик вызовов, счётчик отказов и
|
||||
гистограмма длительности. Оракул — тест: прогнать по одному вызову каждого
|
||||
адаптера с подставным собеседником и снять `/metrics`.
|
||||
- Отказ внешнего сервиса виден отдельно от успеха и не теряется в общем счётчике.
|
||||
Оракул — тест: подставной собеседник отвечает отказом, метка исхода в метрике
|
||||
отличается.
|
||||
- Минуты распознавания и объём заливки видны числом. Оракул — тест: после шага
|
||||
распознавания счётчик минут вырос на длительность записи, а после заливки в
|
||||
Object Storage счётчик объёма — на размер файла.
|
||||
|
||||
## Рамки
|
||||
|
||||
Трассировку здесь не заводим — чем развивать наблюдаемость, решает разведка
|
||||
`opentelemetry-fit`; эта задача остаётся в том словаре, который она выберет.
|
||||
Учёт по пользователям — задача `usage-accounting`, здесь метрики сервисные.
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🐞 Починить путь миграций в настройке сверки документов
|
||||
|
||||
- **Тип:** fix
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Шаг гейта зелен при изменённой миграции и нетронутом database.md: на этом шаге стоит приёмка всякой следующей задачи, трогающей схему.
|
||||
- **Зачем:** Ключ migrations указывает на каталог migrations/, которого в репозитории нет: шаг гейта зелен при изменённой миграции и нетронутом database.md, а конвенции числят этот род механизированным.
|
||||
|
||||
Найдено ревью задачи `oidc-login` 2026-08-12, отчёт триажа —
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
# 🧹 Считать покрытие изменённых строк шагом гейта
|
||||
# 🧹 Ронять гейт на изменённой функции, которую не выполняет ни один тест
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Непокрытую изменённую функцию дважды ловил проход ревью, а не машина; порог решён 2026-08-12, брать можно.
|
||||
- **Зачем:** Свойство «изменённое место покрыто хоть одним тестом» записано в docs/review.md, но не механизировано: за две задачи подряд непокрытые шаги ловили руками.
|
||||
- **Теги:** question
|
||||
|
||||
`CLAUDE.md`, раздел «Гейт», объявляет прямо: «покрытие изменённых строк не
|
||||
считается ничем». Цена этого измерена дважды. В задаче
|
||||
@@ -11,6 +10,13 @@
|
||||
`pocketbase-storage` два из трёх шагов конвейера переписали целиком и не
|
||||
выполнили ни одним тестом — нашёл это проход ревью, а не машина.
|
||||
|
||||
**Единица счёта — функция, а не строка** (решение владельца 2026-08-12). Шаг
|
||||
краснеет на новой или изменённой функции, которую не выполняет ни один тест;
|
||||
доля покрытых строк внутри неё не считается и порогом не ограничивается. Довод:
|
||||
процент изменённых строк роняет гейт на всякой ветке отказа, которую нечем
|
||||
изобразить в тесте, а порог ниже ста пришлось бы брать из ниоткуда. Именно
|
||||
непокрытая целиком функция — то, что дважды ловили руками.
|
||||
|
||||
## Затрагивает
|
||||
|
||||
- набор шагов `task gate` в `Taskfile.yml` и переменная `BASE` как база диффа;
|
||||
@@ -25,16 +31,11 @@
|
||||
адресом.
|
||||
- Изменение, не трогающее код, шаг не гоняет. Оракул — `task gate` на дереве с
|
||||
правкой одной только документации: шаг сообщает о пропуске с причиной.
|
||||
- Порог назван числом в `CLAUDE.md` и совпадает с тем, что проверяет шаг. Оракул
|
||||
— `task gate`, шаг `docs.py check`.
|
||||
|
||||
## Вопросы
|
||||
|
||||
- **Какой порог и что считать изменённой строкой.** Варианты: сто процентов
|
||||
изменённых строк (роняет на всякой ветке отказа, которую нечем изобразить),
|
||||
порог ниже ста (число берётся из ниоткуда), только новые функции целиком.
|
||||
Цена решения — частота ложных отказов гейта; пока решения нет, шаг заводить
|
||||
нельзя.
|
||||
- Единица счёта названа в `CLAUDE.md` и совпадает с тем, что проверяет шаг.
|
||||
Оракул — `task gate`, шаг `docs.py check`.
|
||||
- Функция, тронутая правкой на одну строку, шаг не роняет, если её вызывает хоть
|
||||
один тест. Оракул — прогон на дереве с однострочной правкой внутри покрытой
|
||||
функции: шаг зелёный.
|
||||
|
||||
## Рамки
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Ловить уязвимости в зависимостях шагом гейта
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — govulncheck находит две достижимые уязвимости, а гейт о нём не знает: о третьей узнать будет неоткуда.
|
||||
- **Зачем:** govulncheck находит две достижимые уязвимости в клиентах Yandex, а ни гейт, ни список «чего в гейте нет» о нём не знают: узнать о третьей будет неоткуда.
|
||||
|
||||
Обе найдены ревью задачи `pocketbase-storage` 2026-08-12 и достижимы из кода
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Завести недостающие проверки: shellcheck, hadolint и тест скрипта сверки версий
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Инструмент: shellcheck и hadolint не ловят ничего из перечисленного выше и потому ждут.
|
||||
- **Зачем:** Скрипты оболочки и Dockerfile машина не проверяет: shellcheck и hadolint не заведены, lefthook смотрит только gitleaks, а единственный исполняемый скрипт проекта на shell — scripts/check-go-version.sh — разбирали на ревью глазами построчно, потому что машине его показать нечем.
|
||||
|
||||
Собрана из двух источников: заметки владельца от 2026-08-12 про lefthook и
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Привести коды выхода шагов гейта к общему словарю
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Словарь кодов уже разъехался с обёртками: три новых шага гейта ниже напишутся по неверному образцу и попадут под переписывание.
|
||||
- **Зачем:** CLAUDE.md объявляет словарь кодов общим для четырёх проверочных шагов и описывает код 3 как «каталог или файл не найден», но все четыре обёртки в Taskfile.yml при отсутствующем скрипте отдают 1 («дрейф»), из-за чего читатель кода идёт искать разъехавшееся там, где просто неполно дерево.
|
||||
|
||||
Расхождение старое и повторено всеми четырьмя обёртками одинаково — дефектом
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Разобрать мелочи http-транспорта
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Поднято до контракта API: маршруты объявлены дважды, и переименование пути проходит тесты зелёным — переписывать контроллер под слепыми проверками нельзя.
|
||||
- **Зачем:** Маршруты зарегистрированы дважды, и переименование пути в main.go проходит проверки зелёным; обработчик пишет в журнал через стандартный log и дублирует запись, уже сделанную сервисом.
|
||||
- **Теги:** review-2026-08-11
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# ✨ Показывать заголовок в списке, отбирать список по темам и считать токены
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь — Три пункта «Завершения» цели не закрывала ни одна задача: показывать и отбирать можно, когда заголовки и темы уже считаются.
|
||||
- **Зачем:** Заголовок, темы и пересказ считаются, но список по-прежнему показывает первые слова расшифровки и не отбирается ничем, а расход на модель не виден числом.
|
||||
- **Теги:** goal:text-insights
|
||||
|
||||
Двигает пункты 1, 4 и 6 «Завершения» цели — те три, где выводы из текста
|
||||
становятся видны человеку и владельцу: заголовок в списке (1), отбор по темам
|
||||
(4), стоимость числом (6). Сами уровни считает `llm-insights-adapter`, показать
|
||||
их некому: экран списка написан раньше и знает только первые слова расшифровки.
|
||||
|
||||
Берётся после `llm-insights-adapter`: пока заголовков и тем нет, показывать и
|
||||
отбирать нечего.
|
||||
|
||||
## Затрагивает
|
||||
|
||||
- контракт списка записей: заголовок в элементе и отбор по теме параметром
|
||||
запроса;
|
||||
- экран списка — вместо первых слов расшифровки заголовок, и отбор по теме;
|
||||
- индекс по темам в хранилище, если отбор без него читает всю выборку;
|
||||
- `internal/metrics` — число обработанных записей и число токенов языковой
|
||||
модели;
|
||||
- `docs/architecture.md`, раздел эксплуатации — что означает счётчик токенов.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- В списке у записи с заголовком показан заголовок, а не первые слова
|
||||
расшифровки; у записи без заголовка — прежнее поведение. Оракул — тест экрана
|
||||
на подставном API с обеими записями.
|
||||
- Список отбирается по теме и отдаёт только записи с ней. Оракул — тест API:
|
||||
выборка по теме из трёх записей возвращает одну.
|
||||
- Расход языковой модели виден числом: сколько записей обработано и сколько
|
||||
потрачено токенов. Оракул — тест шага на подставной модели: оба счётчика
|
||||
выросли на значения из ответа.
|
||||
|
||||
## Рамки
|
||||
|
||||
Поиска по тексту расшифровки не делаем — отбор идёт только по темам. Разбивку
|
||||
расхода по пользователям считает `usage-accounting`, здесь метрика общая.
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Сделать приложение устанавливаемым на телефон
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Ставить на телефон есть смысл, когда есть что ставить.
|
||||
- **Зачем:** Приложение, живущее вкладкой браузера, теряется среди прочих: ярлыка на экране у него нет.
|
||||
- **Теги:** goal:web-access
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🔬 Потолки приёма, конвертации и заливки по длине записи
|
||||
|
||||
- **Тип:** research
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Второй замер — остальные четыре звена.
|
||||
- **Зачем:** Из пяти звеньев задача speechkit-limits замерила только модель распознавания: где отваливается шестичасовая запись до неё, неизвестно.
|
||||
- **Теги:** goal:long-recordings
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# ✨ Собирать путь одной записи по конвейеру запросом
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь — Пункт 3 «Завершения» цели не закрывала ни одна задача; применяет словарь, который выберет разведка строкой выше.
|
||||
- **Зачем:** Звенья пути связаны только идентификатором задачи в строках журнала: чтобы понять, где запись провела минуты, владелец читает логи контейнера глазами.
|
||||
- **Теги:** goal:service-observability
|
||||
|
||||
Двигает пункт 3 «Завершения» цели: путь одной записи по конвейеру собирается
|
||||
запросом, а не чтением логов глазами.
|
||||
|
||||
Путь длиной в минуты идёт через четыре внешних сервиса и три воркера. Сегодня
|
||||
его звенья связывает `job_id` в строках журнала, и собирает их человек.
|
||||
|
||||
**Форма ответа зависит от исхода разведки** `opentelemetry-fit`: с OpenTelemetry
|
||||
это трасса со спанами на шаг, без него — отметки времени переходов у самой
|
||||
задачи и запрос по ним. Разведка выбирает словарь, эта задача его применяет.
|
||||
|
||||
## Затрагивает
|
||||
|
||||
- таблица задач: отметки времени перехода между состояниями и их шаг схемы —
|
||||
либо экспорт спанов, если разведка выберет трассировку;
|
||||
- шаги конвейера — место, где отмечается начало и конец звена;
|
||||
- способ прочитать собранное: запрос к хранилищу либо внешний приёмник трасс;
|
||||
- `docs/architecture.md`, раздел эксплуатации — как читается путь записи;
|
||||
- `docs/security.md` — путь записи не несёт ни текста, ни имени файла.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- Путь записи от приёма до готового текста читается одним обращением, без чтения
|
||||
журнала. Оракул — тест: прогнать задачу через все шаги на подставных адаптерах
|
||||
и получить длительность каждого звена.
|
||||
- Путь отказавшей записи показывает звено, на котором она встала. Оракул — тест
|
||||
с отказом на конвертации: звено названо, дальнейших звеньев нет.
|
||||
- Собранное не несёт содержимого записи. Оракул — тест: в ответе только
|
||||
идентификаторы, состояния, длительности и расширение.
|
||||
|
||||
## Рамки
|
||||
|
||||
Берётся после `opentelemetry-fit`: пока словарь не выбран, работа рискует быть
|
||||
переписанной целиком. Хранение путей дольше срока жизни задачи за задачей не
|
||||
стоит.
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Свести приём и чтение записей к одному контракту для приложения
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Единая точка трансляции доменной ошибки — база и для экранов, и для токенов; список своих записей заводится после владельца, а не до.
|
||||
- **Зачем:** Сегодняшний API отвечает 404 на любую ошибку чтения и 500 на любую ошибку приёма: строить на нём экраны нечем.
|
||||
- **Теги:** goal:web-access
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- Сбой базы при чтении задачи даёт `500`, а не `404`. Оракул — тест с
|
||||
репозиторием, возвращающим ошибку драйвера.
|
||||
- Негодный файл даёт `400` с человекочитаемым текстом, а не `500`. Оракул —
|
||||
тест: файл, который отвергает разбор метаданных.
|
||||
- Код ответа отвечает причине отказа, а не месту, где он случился: сбой базы при
|
||||
чтении даёт `500`, а не `404`, негодный файл — `400` с человекочитаемым
|
||||
текстом, а не `500`. Оракул — два теста: репозиторий, возвращающий ошибку
|
||||
драйвера, и файл, который отвергает разбор метаданных.
|
||||
- Тело ошибки одной формы на всех эндпоинтах, не содержит сырого `err.Error()` и
|
||||
опечатки `transcibe`. Оракул — тест на четырёх ветвях отказа (форма совпадает,
|
||||
текста внутренней ошибки в теле нет) плюс пустой `grep -rn 'transcibe'
|
||||
@@ -37,12 +37,19 @@
|
||||
тест на выборке больше страницы.
|
||||
- Отображение ошибки живёт в одной функции, и она названа в
|
||||
`docs/architecture.md`. Оракул — `task gate`, шаг `docs.py check`.
|
||||
- Ответ приёма отдаёт список заведённых записей и место под признак повторного
|
||||
файла, даже когда файл в запросе один. Оракул — тест приёма: тело ответа —
|
||||
список из одного элемента, у элемента есть поле признака повтора.
|
||||
|
||||
## Рамки
|
||||
|
||||
Аутентификацию и владельца не заводим — это `oidc-login` и `record-ownership`;
|
||||
задача про форму контракта. Публичный контракт после мерджа обратной правкой не
|
||||
откатывается — поэтому сюда же приехала опечатка `transcibe` в тексте ошибки
|
||||
приёма (нашёл проход `review-specs` на ревью change
|
||||
откатывается, и по решению владельца от 2026-08-12 форма ответа приёма
|
||||
согласуется здесь один раз — сразу списком и с местом под признак повтора, —
|
||||
чтобы `dedup-by-content-hash`, `multi-file-upload` и `reject-oversized-recording`
|
||||
её не переписывали, а экран загрузки не переделывался под вторую форму. Приём
|
||||
по-прежнему берёт из запроса один файл: меняется форма ответа, не число файлов.
|
||||
Сюда же приехала опечатка `transcibe` в тексте ошибки приёма (нашёл проход `review-specs` на ревью change
|
||||
`2026-08-11-fix-http-handler-tests`): в одиночку текст ошибки менять нельзя,
|
||||
а здесь контракт переписывается целиком.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Отдавать вычитанный текст рядом с сырым
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Вычитанный текст считается тем же адаптером.
|
||||
- **Зачем:** Сырая расшифровка идёт без знаков препинания, с повторами и словами-паразитами: читать её подряд тяжело, а другого уровня текста нет.
|
||||
- **Теги:** goal:text-insights
|
||||
|
||||
@@ -26,8 +26,6 @@
|
||||
сырое совпадает с ответом распознавания.
|
||||
- Отказ модели на вычитке не роняет задачу и не портит сырой текст. Оракул —
|
||||
тест с моделью, возвращающей ошибку: задача в `done`, сырой текст на месте.
|
||||
- Выключенный в настройках уровень не запрашивается и остаётся пустым. Оракул —
|
||||
тест с выключенной вычиткой: обращений за ней нет.
|
||||
- Экран показывает оба уровня с переключением между ними. Оракул — тест экрана
|
||||
на подставном API.
|
||||
|
||||
@@ -35,4 +33,5 @@
|
||||
|
||||
Ручной правки текста человеком не делаем — это граница паспорта: сервис отдаёт
|
||||
машинную вычитку, а редактором не становится. Берётся после
|
||||
`llm-insights-adapter`: клиент модели заводит она.
|
||||
`llm-insights-adapter`: клиент модели заводит она. Выключение уровня настройкой
|
||||
пользователя сюда не входит — это `settings-applied-in-pipeline`.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Считать заголовок, темы и пересказ внешней моделью
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Уровни текста: сюда приходит пятая внешняя зависимость, и конвейер к этому моменту покрыт тестами.
|
||||
- **Зачем:** Расшифровка доходит стеной текста: ни заголовка, ни тем, ни пересказа сервис не считает, и клиента языковой модели в нём нет.
|
||||
- **Теги:** goal:text-insights
|
||||
|
||||
@@ -32,16 +32,14 @@ OpenAI-совместимым интерфейсом за шлюзом bifrost,
|
||||
- Отказ и молчание модели не роняют задачу: текст доходит до человека без
|
||||
выводов. Оракул — тест с моделью, возвращающей ошибку и таймаут: задача в
|
||||
`done`, текст на месте, в журнале одна запись уровня `WARN`.
|
||||
- Уровень, выключенный в настройках пользователя, у модели не запрашивается, а
|
||||
настройка берётся та, что действовала на приёме записи. Оракул — тест с
|
||||
выключенными темами и тест с настройкой, изменённой после приёма: вызовов за
|
||||
темами нет в обоих случаях.
|
||||
- Ключ и адрес шлюза не попадают ни в журнал, ни в ответ. Оракул — тест шага с
|
||||
перехваченным журналом.
|
||||
|
||||
## Рамки
|
||||
|
||||
Литературный текст сюда не входит — это `literary-text-level`. Своей модели не
|
||||
Литературный текст сюда не входит — это `literary-text-level`. Выборочность по
|
||||
настройкам пользователя тоже: здесь считаются все уровни, а выключать их учит
|
||||
`settings-applied-in-pipeline`. Своей модели не
|
||||
держим и промптов в код не зашиваем сверх необходимого. Прогон на боевом ключе
|
||||
ради проверки запрещён: подставной адаптер. Обращение к модели платное, и
|
||||
объём расхода учитывает `usage-accounting`.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Поднимать сервис локально без действующего токена бота
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Поднято к долгам входа: живой прогон нужен именно им, а сегодня его нет ни у одной задачи.
|
||||
- **Зачем:** Адаптер Telegram проверяет токен обращением к Telegram и роняет старт, а боевым токеном запускаться запрещено: проверить поведение живым прогоном не может ни одна задача.
|
||||
|
||||
Замечено при попытке проверить вход вживую в задаче `oidc-login` 2026-08-12;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🔬 Четыре недоказанные гипотезы о поверхности входа
|
||||
|
||||
- **Тип:** research
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Разведка закрывает тему входа последней: остальные три задачи меняют то, что она проверяет.
|
||||
- **Зачем:** Ревью назвало четыре пути, которых не смогло ни подтвердить, ни опровергнуть: браузера и живого провайдера в прогоне не было.
|
||||
|
||||
Провенанс — отчёт триажа ревью задачи `oidc-login` 2026-08-12,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Строить адрес входа из настроек коллекции, а не из конфига
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Замыкает тройку правок обработчиков входа.
|
||||
- **Зачем:** Первая половина входа собрана руками из конфига и на настройки провайдера не смотрит, вторая берётся из коллекции: обновление библиотеки изменит только вторую половину.
|
||||
|
||||
Найдено ревью задачи `oidc-login` 2026-08-12, отчёт триажа —
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Резать длинную запись на фрагменты и продолжать с места остановки
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Резка на фрагменты — самая глубокая переделка конвейера, и она идёт по замеренным числам.
|
||||
- **Зачем:** Шаг конвейера повторяется целиком: перезапуск на пятом часу шестичасовой записи начинает распознавание заново и оплачивает его второй раз.
|
||||
- **Теги:** goal:long-recordings
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🎯 Запись длиной до шести часов доходит до текста
|
||||
|
||||
- **Тип:** goal
|
||||
- **Секция:** Направления
|
||||
- **Секция:** Направления — Цель начинается с двух замеров и кончается резкой на фрагменты — самой глубокой переделкой конвейера: очереди внутри нет, пока числа не получены.
|
||||
- **Зачем:** Потолок не замерен ни на одном звене: Telegram не отдаёт больше 20 МиБ, границы модели deferred-general неизвестны, а перезапуск на середине начинает распознавание заново.
|
||||
- **Теги:** decomposed
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Отдавать текст в сотни килобайт файлом, а не сотней сообщений
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Сотни килобайт текста появляются только после долгих записей.
|
||||
- **Зачем:** Отправитель Telegram режет текст по 4000 знаков: расшифровка шестичасовой записи придёт сотней сообщений подряд.
|
||||
- **Теги:** goal:long-recordings
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Покрыть тестами разбор вывода ffprobe
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Разбор вывода ffprobe не проверяет ничто, а приём видео его расширяет.
|
||||
- **Зачем:** Проверки приёма перестали звать настоящий ffprobe 2026-08-11, а своего теста у адаптера метаданных нет: разбор JSON и отличие «программы нет в PATH» от «обработка отказала» не проверяет ничто.
|
||||
- **Теги:** review-2026-08-11
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Принимать до десяти файлов одной загрузкой
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Пачка файлов заводится на готовом экране загрузки и готовой дедупликации.
|
||||
- **Зачем:** Приём берёт один файл в запросе, а с телефона выбирают пачку сразу: десять записей значат десять заходов на экран загрузки.
|
||||
- **Теги:** goal:upload-reliability
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🎯 Сервисом пользуются несколько человек, и записи одного не видны другому
|
||||
|
||||
- **Тип:** goal
|
||||
- **Секция:** Запланировано
|
||||
- **Секция:** Запланировано — Владелец записи — фундамент, на котором стоят список своих записей, дедупликация, удаление, учёт расхода и квота: пока его нет, остальные цели строятся на песке.
|
||||
- **Зачем:** У задачи нет владельца, а HTTP API открыт наружу без аутентификации: пригласить второго человека сейчас значит открыть ему чужие расшифровки.
|
||||
- **Теги:** decomposed
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Отправлять готовый текст через apprise и ntfy
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Канал уведомлений выбирается в настройках, которые уже есть.
|
||||
- **Зачем:** Пользователь веба узнаёт о готовности только опросом с открытого экрана.
|
||||
- **Теги:** goal:ready-notification
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🔬 Стоит ли брать OpenTelemetry вместо голого Prometheus
|
||||
|
||||
- **Тип:** research
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Сопровождение: словарь метрик выбирается до того, как метрик станет втрое больше.
|
||||
- **Зачем:** Метрик одиннадцать штук на пять счётчиков, трассировки нет вовсе: путь одной записи по конвейеру собирается только чтением логов глазами.
|
||||
- **Теги:** goal:service-observability
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🐞 Убирать записанный файл, когда приём отказал на середине
|
||||
|
||||
- **Тип:** fix
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Осиротевший файл — тот же отказ на середине, и чинится тем же местом приёма.
|
||||
- **Зачем:** Отказ чтения метаданных и отказ записи на диск оставляют файл в каталоге хранения без задачи и без учёта: сопоставить его не с чем, удалять приходится руками.
|
||||
- **Теги:** review-2026-08-11, goal:upload-reliability
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# ✨ Оповещать владельца об отказе, не дожидаясь жалобы
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь — Правило оповещения ставится на метрику, которой до этого нет.
|
||||
- **Зачем:** Об отказе владелец узнаёт от пользователя: правил оповещения нет ни на одной метрике, а метрики читают глазами.
|
||||
- **Теги:** goal:service-observability
|
||||
|
||||
Двигает пункт 4 «Завершения» цели: владелец узнаёт об отказе сам.
|
||||
|
||||
Берётся после `external-service-metrics` и `stalled-pipeline-metric`: правило
|
||||
оповещения ставится на метрику, а метрик, на которых его ставить, сегодня нет.
|
||||
|
||||
Доставку берём ту же, что и пользовательскую, — apprise с ntfy: второй канал
|
||||
ради того же сообщения не заводим.
|
||||
|
||||
## Затрагивает
|
||||
|
||||
- правила оповещения: где они живут — в сервисе или в выкладке
|
||||
`pet-project-server`, выбор входит в задачу;
|
||||
- секция конфигурации канала оповещения владельца;
|
||||
- `docs/architecture.md`, раздел эксплуатации — кто и о чём оповещает;
|
||||
- `CLAUDE.md` — запрет на выкладку остаётся: правила выкладывает человек.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- Задача, ушедшая в `failed`, доходит до владельца сообщением. Оракул — тест с
|
||||
подставным каналом: после перевода задачи в `failed` канал получил сообщение с
|
||||
идентификатором задачи.
|
||||
- Сообщение не несёт ни текста расшифровки, ни имени файла пользователя. Оракул
|
||||
— тест: в отправленном теле только идентификаторы, длина и причина.
|
||||
- Недоступность канала оповещения не роняет конвейер. Оракул — тест: канал
|
||||
отвечает отказом, задача остаётся в прежнем состоянии, в журнале одна строка.
|
||||
|
||||
## Рамки
|
||||
|
||||
Выкладку правил запускает человек — `inv pl -- transcriber` из
|
||||
`pet-project-server`. Пользовательские уведомления о готовности здесь не
|
||||
трогаем: это цель `ready-notification`.
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🔬 Квота по общему размеру загруженного на пользователя
|
||||
|
||||
- **Тип:** research
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Сырьё: спор с паспортом не решается, пока объём по пользователям не считается ничем.
|
||||
- **Зачем:** Паспорт и security.md запрещают отказы по квоте пользователю, а заметка владельца просит квоту по умолчанию 5 ГБ — открытое противоречие с границей домена, которое владелец решил не разбирать сейчас.
|
||||
|
||||
Заметка звучит так: «Квота по размеру загруженных файлов на пользователя. В
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Закрепить версию рантайм-базы образа
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Закрепление базы образа окупается при частых пересборках, а они начнутся с приложением.
|
||||
- **Зачем:** Финальный слой Dockerfile собирается на alpine:latest, а task image идёт с --pull, поэтому два образа из одного коммита с разницей в неделю несут разный ffmpeg — регрессия конвертации после такой пересборки выглядит как задачи в failed при пустом диффе репозитория, и откат на прежний коммит её не чинит.
|
||||
|
||||
Класс тот же, ради которого задача `go-1-26-upgrade` завела шаг сверки версий:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Покрыть тестами шаги конвейера и захват задачи
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Шаги конвейера не покрыты ничем, а удаление, уровни текста и резка длинных записей переписывают именно их: дальше без тестов идти вслепую.
|
||||
- **Зачем:** Тестовых файлов в проекте два, и оба мимо конвейера: потеря ссылки на файл, двойной ответ пользователю и гонка при захвате не поймаются ничем.
|
||||
- **Теги:** review-2026-08-11
|
||||
|
||||
@@ -38,8 +38,7 @@ Telegram. Шаги конвейера — `FindAndRunConversionJob`, `FindAndRun
|
||||
|
||||
## Рамки
|
||||
|
||||
Задачу лучше брать **после** разведки
|
||||
[job-queue-choice](job-queue-choice.md): она может сменить модель очереди
|
||||
целиком, и тесты, написанные под сегодняшний захват, придётся выбросить вместе с
|
||||
ним.
|
||||
Модель очереди решена 2026-08-11 —
|
||||
[очередь коллекцией PocketBase](../../docs/adr/ADR-2026-08-11-queue-as-pocketbase-collection.md),
|
||||
— и разведки о ней больше нет. Тесты пишутся под сегодняшний захват.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Проигрывать загруженную запись на экране записи
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Проигрывание живёт на экране записи, которого до списка нет.
|
||||
- **Зачем:** Послушать загруженное приложение не даёт, а самой копии для этого у задачи нет: указатель на файл перезаписывается на каждом шаге конвейера и у готовой задачи ведёт на объект в Object Storage.
|
||||
- **Теги:** goal:web-access
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🐞 Убрать код провайдера из журнала запросов хранилища
|
||||
|
||||
- **Тип:** fix
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Три задачи входа собраны подряд: все правят обработчики входа и одно требование спеки access — три захода в один файл дороже одного.
|
||||
- **Зачем:** Строка запроса с кодом входа целиком уезжает в таблицу _logs и лежит там пять суток, хотя спека access требует, чтобы код в журнал не попадал.
|
||||
|
||||
Найдено ревью задачи `oidc-login` 2026-08-12, отчёт триажа —
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🎯 Пользователь узнаёт о готовности текста, не держа приложение открытым
|
||||
|
||||
- **Тип:** goal
|
||||
- **Секция:** Запланировано
|
||||
- **Секция:** Запланировано — Канал уведомлений выбирается в настройках, которые к этому месту уже есть.
|
||||
- **Зачем:** Расшифровка занимает минуты, и всё это время человек либо смотрит на экран с опросом статуса, либо забывает вернуться.
|
||||
- **Теги:** decomposed
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Привязать запись к владельцу и отдавать только свои
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Фундамент домена: без владельца знание UUID и есть право читать чужое, и на владельце стоят список, дедуп, удаление, учёт и квота.
|
||||
- **Зачем:** У задачи и файла нет владельца, поэтому знание UUID задачи и есть право её читать.
|
||||
- **Теги:** goal:multi-user
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
|
||||
- таблица задач и таблица файлов — колонка владельца и её миграция;
|
||||
- `internal/contract`, интерфейсы репозиториев: чтение сужается владельцем;
|
||||
- `internal/adapter/repo/sqlite` — все четыре запроса задач;
|
||||
- `internal/adapter/repo/pocketbase` — все четыре места, где правятся колонки
|
||||
очереди: `applyToRecord`, `recordToJob`, `acquireColumns` и `acquiredRow`;
|
||||
- `internal/service`, оба метода заведения задачи;
|
||||
- `GET /api/status/:id` — ответ на чужую запись;
|
||||
- `docs/database.md` — схема и правило выборки.
|
||||
@@ -24,9 +25,8 @@
|
||||
- Запрос чужой записи по её идентификатору возвращает «не найдено», а не
|
||||
содержимое и не «доступ запрещён». Оракул — тест: две сессии, задача первой
|
||||
запрашивается второй, ответ 404 и пустое тело.
|
||||
- Запись, заведённая из веба, принадлежит вошедшему; заведённая ботом —
|
||||
пользователю, за которым закреплён чат. Оракул — тест на оба входа со сверкой
|
||||
колонки владельца.
|
||||
- Запись, заведённая из веба, принадлежит вошедшему. Оракул — тест приёма по
|
||||
HTTP со сверкой колонки владельца.
|
||||
- Выборка воркера владельцем **не** сужается: конвейер обрабатывает записи всех.
|
||||
Оракул — тест: задачи двух владельцев проходят конвейер одним воркером.
|
||||
- База заводится с чистого листа, колонка владельца обязательна и без умолчания.
|
||||
@@ -35,4 +35,6 @@
|
||||
## Рамки
|
||||
|
||||
Совместного доступа, ролей и передачи записи другому не делаем: владелец один и
|
||||
неизменяем. Данные не переносим — чистый лист.
|
||||
неизменяем. Данные не переносим — чистый лист. Владельца записи, пришедшей
|
||||
ботом, здесь не назначаем: связи чата с учётной записью ещё нет, её заводит
|
||||
`telegram-account-link` следующей задачей.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Сделать экран списка своих записей и чтения текста
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Список и чтение текста берутся после экрана загрузки — так записано в самой задаче.
|
||||
- **Зачем:** Расшифровка сегодня доходит одним сообщением и теряется в переписке; вернуться к ней через неделю нечем.
|
||||
- **Теги:** goal:web-access
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Отклонять на приёме запись сверх потолка
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Отклонять сверх потолка можно, когда потолок замерен.
|
||||
- **Зачем:** Запись сверх потолка принимается молча и висит в конвейере до истечения часового захвата, а человек всё это время ждёт текста.
|
||||
- **Теги:** goal:long-recordings
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Судить ответ в тестах по готовому ответу
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Тест, читающий живую карту заголовков, зелен при неработающем коде: класс всплывал трижды и трижды стоил ложной приёмки.
|
||||
- **Зачем:** Проверка, читающая изменяемую карту заголовков обработчика, зелена при неработающем коде: класс всплыл трижды, последний раз на уборке куки входа.
|
||||
|
||||
Найдено ревью задачи `oidc-login` 2026-08-12, отчёт триажа —
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Настроить конвейер ревью по итогам прогона go-1-26-upgrade
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Настройка конвейера ревью по итогам прогона — работа над процессом, не над продуктом.
|
||||
- **Зачем:** Прогон вскрыл две прорехи настройки: «Типовые узлы» знают только рантайм и не знают рода «проверочный шаг набора проверок», а «Триггеры метки» не видят оси «изменение трогает канон» — и именно она дала обе блокирующие находки.
|
||||
|
||||
Обе прорехи одного рода — настройка конвейера, живущая в `docs/review.md`, — и
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Назвать в необратимом, что откат кода не откатывает шаг схемы
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Записать необратимость шага схемы надо до того, как схему тронут задачи фундамента.
|
||||
- **Зачем:** Откат бинаря оставляет применённый шаг схемы в силе, и на этом строятся решения о выкладке: сегодня об этом не сказано нигде.
|
||||
|
||||
Найдено ревью задачи `oidc-login` 2026-08-12, отчёт триажа —
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🐞 Вести учёт употреблённых состояний входа на сервере
|
||||
|
||||
- **Тип:** fix
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Тот же файл и та же спека, что у строки выше.
|
||||
- **Зачем:** Одноразовость возврата держится на уборке куки, то есть на браузере: сервер не помнит, какие состояния уже потрачены.
|
||||
|
||||
Найдено ревью задачи `oidc-login` 2026-08-12, отчёт триажа —
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🎯 Состояние сервиса видно без чтения логов
|
||||
|
||||
- **Тип:** goal
|
||||
- **Секция:** Сопровождение
|
||||
- **Секция:** Сопровождение — Словарь метрик выбирается разведкой до того, как метрик станет втрое больше; наблюдаем мы, а не пользователь сервиса.
|
||||
- **Зачем:** Отказ замечает пользователь, а не владелец: оповещения нет, а путь записи по конвейеру собирается глазами по логам контейнера.
|
||||
- **Теги:** decomposed
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# ✨ Считать только те уровни текста, что включены у владельца записи
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь — Выключать нечего, пока уровней нет; настройка ставится сразу за ними, чтобы платная модель не считала лишнее.
|
||||
- **Зачем:** Дом настроек есть, а конвейер их не читает: выключенный уровень всё равно уходит платной модели, и настройка ничего не экономит.
|
||||
- **Теги:** goal:user-settings
|
||||
|
||||
Двигает пункты 1 и 4 «Завершения» цели: выключенный уровень не запрашивается у
|
||||
языковой модели вовсе, а запись обрабатывается по той настройке, которая
|
||||
действовала на приёме.
|
||||
|
||||
Берётся после `settings-screen` (настройки негде хранить) и после
|
||||
`llm-insights-adapter` (нечего выключать).
|
||||
|
||||
Настройка снимается **на приёме** и едет с задачей: правка настроек посреди
|
||||
обработки не меняет того, что уже считается, и не оставляет запись наполовину
|
||||
обработанной.
|
||||
|
||||
## Затрагивает
|
||||
|
||||
- колонка снимка настроек у задачи и её шаг схемы;
|
||||
- заведение задачи обоими входами: бот и приём по HTTP;
|
||||
- шаг конвейера, обращающийся к языковой модели, — выбор уровней перед вызовом;
|
||||
- `internal/contract` — интерфейс расчёта выводов: состав запрошенных уровней;
|
||||
- `docs/database.md` — колонка снимка и её значение по умолчанию.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- Выключенный уровень не запрашивается у модели ни одним обращением. Оракул —
|
||||
тест с подставным адаптером: у пользователя выключены пересказ и темы, адаптер
|
||||
получает запрос без них и не вызывается второй раз.
|
||||
- Запись обрабатывается по настройке, действовавшей на приёме. Оракул — тест:
|
||||
завести задачу, сменить настройки владельца, прогнать шаг — считается прежний
|
||||
состав уровней.
|
||||
- У пользователя без настроек берутся значения по умолчанию, а задача не падает.
|
||||
Оракул — тест на приёме от пользователя, у которого строки настроек нет.
|
||||
|
||||
## Рамки
|
||||
|
||||
Экран настроек и таблицу заводит `settings-screen` — здесь только чтение
|
||||
настройки конвейером. Уже обработанные записи задним числом не пересчитываются.
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Сделать экран настроек и хранить настройки по пользователю
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Дом настроек нужен раньше уровней текста и раньше выбора канала уведомлений.
|
||||
- **Зачем:** Настроек у пользователя нет вовсе: уровни текста и канал уведомлений задаются общим конфигом сервиса.
|
||||
- **Теги:** goal:user-settings
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
- таблица настроек пользователя: переключатели уровней текста, канал и адрес
|
||||
уведомлений, и её миграция;
|
||||
- эндпоинты чтения и записи своих настроек;
|
||||
- новый экран приложения;
|
||||
- новый экран приложения, и на нём же место выпуска и отзыва личного токена —
|
||||
сам токен заводит `api-tokens`, экрана у него нет;
|
||||
- значения по умолчанию для пользователя, у которого настроек ещё нет;
|
||||
- `docs/database.md` — представление данных и значения по умолчанию.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Собрать каркас приложения и раздать его из бинарника
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Каркас приложения: экранов нет и собирать их нечем, а на экране стоят настройки, удаление, уровни текста и статистика.
|
||||
- **Зачем:** Экранов нет и собирать их нечем: ни сборки фронтенда, ни раздачи статики в проекте не существует.
|
||||
- **Теги:** goal:web-access
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🔬 Уведомление SpeechKit о готовности вместо опроса
|
||||
|
||||
- **Тип:** research
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Разведка про уведомление вместо опроса меняет тот же шаг проверки и видна метриками, заведёнными выше.
|
||||
- **Зачем:** Шаг проверки дёргает операцию раз в 5 секунд всё время распознавания: часовая запись даёт порядка 720 обращений к платному сервису вместо одного ответа.
|
||||
|
||||
Заметка владельца от 2026-08-12. Сегодня готовность узнаётся только опросом:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🔬 Адрес объекта в тексте отказа SpeechKit
|
||||
|
||||
- **Тип:** research
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Тот же род утечки в журнал, но вне темы входа — идёт следом за ней.
|
||||
- **Зачем:** Текст отказа операции приходит от Yandex и уезжает в журнал и в колонку error_text: если он несёт URI объекта, из журнала снова собирается ссылка на чужую запись.
|
||||
|
||||
Гипотеза ревью задачи `pocketbase-storage` 2026-08-12, оставшаяся без оракула:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🔬 Потолки SpeechKit по длине записи и по формату
|
||||
|
||||
- **Тип:** research
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Долгие записи начинаются с замера: без потолков ни отказ на приёме, ни резка не проектируются.
|
||||
- **Зачем:** Потолок длины записи и перечень принимаемых форматов неизвестны, а цель про долгие записи без них не начинается.
|
||||
- **Теги:** goal:long-recordings
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# ✨ Показывать метрикой задачу, застрявшую в состоянии
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь — Признак вставшего конвейера — вторая половина той же работы.
|
||||
- **Зачем:** Вставший конвейер неотличим от простоя: возраст задачи в состоянии не считается, и очередь без движения выглядит как отсутствие работы.
|
||||
- **Теги:** goal:service-observability
|
||||
|
||||
Двигает пункт 1 «Завершения» цели: по метрикам видно, что конвейер встал, и это
|
||||
отличимо от «работы нет».
|
||||
|
||||
Сегодня метрики считают события — принято, распознано, отказано, — а состояние
|
||||
очереди не считает ничто. Пустая очередь и очередь, где десять задач висят третий
|
||||
час, дают одинаковый нулевой прирост счётчиков.
|
||||
|
||||
## Затрагивает
|
||||
|
||||
- `internal/metrics` — метрика числа задач по состояниям и возраста самой старой
|
||||
из них;
|
||||
- таблица задач: колонка состояния, по которой считаются числа, и отметка
|
||||
времени перехода, по которой считается возраст самой старой;
|
||||
- `docs/architecture.md`, раздел эксплуатации — что означает каждое число;
|
||||
- `docs/database.md` — частота съёма чисел.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- Число задач в каждом состоянии видно на `/metrics`. Оракул — тест: завести три
|
||||
задачи в разных состояниях и снять метрики.
|
||||
- Возраст самой старой задачи в состоянии виден числом и растёт, пока задача не
|
||||
сменила состояние. Оракул — тест на задаче с заведомо старой отметкой времени.
|
||||
- Съём чисел не мешает работе воркеров. Оракул — чтение кода: запрос идёт по
|
||||
индексу состояния и не берёт захват.
|
||||
|
||||
## Рамки
|
||||
|
||||
Правило оповещения на этой метрике заводит задача `owner-alerting` — здесь
|
||||
только число. Порог «сколько считается застреванием» здесь не решается: он
|
||||
зависит от замеров цели `long-recordings`.
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🧹 Разобрать мелочи слоя хранилища
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Мелочи слоя хранилища разбираются, пока слой в руках.
|
||||
- **Зачем:** Три мелочи ниже потолка триажа: цикл воркера пишет потерю захвата уровнем ERROR и считает её отказом, тип ошибки заведён там, где конвенция просит sentinel, а FileName несёт два разных смысла.
|
||||
|
||||
Все три пришли ревью задачи `pocketbase-storage` 2026-08-12 и остались ниже
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Сопоставить пользователя Telegram с учётной записью
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Связывать чат с учётной записью не с чем, пока у записи нет владельца.
|
||||
- **Зачем:** Белый список сверяется с именем пользователя Telegram, которое владелец меняет в любой момент, а записи из бота ни с кем не связаны.
|
||||
- **Теги:** goal:multi-user
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🎯 Приложение показывает, о чём запись, не читая её целиком
|
||||
|
||||
- **Тип:** goal
|
||||
- **Секция:** Направления
|
||||
- **Секция:** Запланировано — Очередь у цели появилась: три задачи подряд после настроек, и прибавка видна даже в боте.
|
||||
- **Зачем:** Расшифровка часового разговора — это стена текста: найти в списке нужную запись и вспомнить, о чём она, сегодня нечем.
|
||||
- **Теги:** decomposed
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Сделать экран загрузки записи и её состояния
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Первое, ради чего приложение открывают; требует каркаса и контракта, оба выше.
|
||||
- **Зачем:** Первое, ради чего приложение открывают: отдать файл и увидеть, что с ним происходит.
|
||||
- **Теги:** goal:web-access
|
||||
|
||||
@@ -28,9 +28,10 @@
|
||||
тест: после ответа `done` новых запросов к API нет.
|
||||
- Отказ задачи показывается человекочитаемым текстом, а не кодом и не сырой
|
||||
ошибкой. Оракул — тест на ответе с состоянием `failed`.
|
||||
- Файл больше предела отклоняется на экране до отправки, с названным числом.
|
||||
Оракул — тест на файле сверх предела: запроса к API нет, на экране предел
|
||||
числом.
|
||||
- Файл больше предела отклоняется на экране до отправки, с названным числом, и
|
||||
предел экран берёт у сервера, а не держит своей константой. Оракул — тест на
|
||||
файле сверх предела: запроса на загрузку нет, на экране предел числом, и
|
||||
смена предела на стороне сервера меняет это число.
|
||||
|
||||
## Рамки
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Показывать ход загрузки записи на экране
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Ход загрузки — доработка того же экрана.
|
||||
- **Зачем:** Гигабайтный файл уходит на сервер молча: до ответа сервера экран не отличает идущую загрузку от зависшей.
|
||||
- **Теги:** goal:upload-reliability
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🎯 Загрузка большого файла доходит до сервиса и не повторяется впустую
|
||||
|
||||
- **Тип:** goal
|
||||
- **Секция:** Запланировано
|
||||
- **Секция:** Запланировано — Дедупликация и пачка файлов доводятся на готовом экране загрузки и экономят деньги с первого дня приложения.
|
||||
- **Зачем:** Приём рассчитан на голосовое в пару мегабайт: обрыв на середине гигабайтного файла начинает загрузку заново, а один и тот же файл распознаётся повторно за наши деньги.
|
||||
- **Теги:** decomposed
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# ✨ Считать объём, минуты и расход по каждому пользователю
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь
|
||||
- **Категория:** Очередь — Учёт по пользователям требует владельца записи и метрик расхода — оба выше.
|
||||
- **Зачем:** Ни объём, ни длительность, ни обращения к платным сервисам никуда не записываются: восстановить расход задним числом не из чего.
|
||||
- **Теги:** goal:usage-stats
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🎯 Владелец видит, кто сколько загрузил и во что это обошлось
|
||||
|
||||
- **Тип:** goal
|
||||
- **Секция:** Сопровождение
|
||||
- **Секция:** Сопровождение — Учёт по пользователям требует владельца записи и метрик расхода — оба появляются раньше.
|
||||
- **Зачем:** Распознавание и языковая модель оплачиваются по факту, а счёт приходит одной суммой: кто её набрал, из сервиса не выясняется.
|
||||
- **Теги:** decomposed
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🎯 Пользователь настраивает, что сервис делает с его записями
|
||||
|
||||
- **Тип:** goal
|
||||
- **Секция:** Запланировано
|
||||
- **Секция:** Запланировано — Дом настроек нужен раньше уровней текста и раньше выбора канала уведомлений.
|
||||
- **Зачем:** Уровни текста считает платная модель, а уведомления приходят одним общим способом: отказаться от лишнего и выбрать свой канал пользователю нечем.
|
||||
- **Теги:** decomposed
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# ✨ Принимать видео и брать из него звуковую дорожку
|
||||
|
||||
- **Тип:** feature
|
||||
- **Категория:** Очередь — Приём видео и отказ неизвестному формату ставятся на замеренный перечень.
|
||||
- **Зачем:** Запись семейного архива приходит видеофайлом, а приём смотрит на аудио: человеку приходится доставать дорожку самому.
|
||||
- **Теги:** goal:any-audio-source
|
||||
|
||||
Двигает пункты 2 и 3 «Завершения» цели: видеофайл принимается и из него берётся
|
||||
звуковая дорожка, а формат, который принять нельзя, отклоняется на приёме
|
||||
понятным текстом, а не отказом на конвертации через минуту.
|
||||
|
||||
Берётся после `audio-format-coverage-measure`: чем отклонять неизвестное, пока
|
||||
неизвестно, что конвейер берёт.
|
||||
|
||||
## Затрагивает
|
||||
|
||||
- приём обоими входами: перечень допустимых типов содержимого и расширений;
|
||||
- адаптер метаданных — распознавание дорожек в контейнере;
|
||||
- команда конвертера: выбор звуковой дорожки из видео;
|
||||
- метка формата в метрике — перечень известных форматов расширяется
|
||||
([ADR](../../docs/adr/ADR-2026-08-11-known-format-label.md));
|
||||
- `docs/passport.md` — граница «что принимаем» сдвигается на видео;
|
||||
- `openspec/specs/intake/` — требование о принимаемых форматах.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- Видеофайл с одной звуковой дорожкой доходит до текста. Оракул — тест приёма и
|
||||
конвертации на своём коротком видео из временного каталога.
|
||||
- Видео без звуковой дорожки отклоняется на приёме понятным текстом и задачи не
|
||||
заводит. Оракул — тест: файл без звука, ответ с причиной, в очереди пусто.
|
||||
- Формат вне замеренного перечня отклоняется на приёме, а не висит в конвейере.
|
||||
Оракул — тест на файле неизвестного типа: отказ приходит сразу.
|
||||
- Расширение в журнале и метка в метрике остаются в перечне известных форматов.
|
||||
Оракул — тест: на видео с новым контейнером в журнал уходит расширение из
|
||||
перечня, а не сырое расширение файла, и метка метрики берётся из того же
|
||||
перечня.
|
||||
|
||||
## Рамки
|
||||
|
||||
Видео с несколькими звуковыми дорожками берём первую, выбор дорожки человеком за
|
||||
задачей не стоит. Потолок длины остаётся тем, что назначит цель
|
||||
`long-recordings`.
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🎯 Записи загружаются и читаются в приложении, которое ставится на телефон
|
||||
|
||||
- **Тип:** goal
|
||||
- **Секция:** Запланировано
|
||||
- **Секция:** Запланировано — Экраны приложения нужны раньше настроек, удаления, уровней текста и статистики: всем им негде показаться.
|
||||
- **Зачем:** Сегодня записи принимает только бот и голый HTTP API без интерфейса: отдать сервис человеку, у которого нет Telegram, нечем.
|
||||
- **Теги:** decomposed
|
||||
|
||||
|
||||
Reference in New Issue
Block a user