tasks: упразднены цели и роадмап, объявлена стадия build
- 11 записей типа goal закрыты с причиной, называющей задачи-наследники; ROADMAP.md удалён, индекс остался один — BACKLOG.md; - 33 записи переписаны: ссылка «Двигает пункты N «Завершения» цели» уступила место прямому утверждению — без целей номера пунктов вели в никуда; - шапка BACKLOG.md размечена парой <!-- стадия -->, порядок строк теперь объявлен зависимостью, а не важностью.
This commit is contained in:
+2
-1
@@ -1,7 +1,7 @@
|
|||||||
# Раскладка av-dev в этом проекте: версия и настройки проверок.
|
# Раскладка av-dev в этом проекте: версия и настройки проверок.
|
||||||
# Файл ведут скиллы плагина, править руками можно — комментарии свои.
|
# Файл ведут скиллы плагина, править руками можно — комментарии свои.
|
||||||
|
|
||||||
version = 1 # версия раскладки; обратной совместимости нет, есть «приведён» и «нет»
|
version = 3 # версия раскладки; обратной совместимости нет, есть «приведён» и «нет»
|
||||||
|
|
||||||
[docs]
|
[docs]
|
||||||
# каталог миграций: по нему docs.py сверяет схему с database.md
|
# каталог миграций: по нему docs.py сверяет схему с database.md
|
||||||
@@ -10,3 +10,4 @@ migrations = "internal/adapter/repo/pocketbase/migrations"
|
|||||||
[tasks]
|
[tasks]
|
||||||
# каталог задач от корня репозитория; имена частей — умолчания скрипта
|
# каталог задач от корня репозитория; имена частей — умолчания скрипта
|
||||||
dir = "tasks"
|
dir = "tasks"
|
||||||
|
stage = "build"
|
||||||
|
|||||||
+20
-16
@@ -1,23 +1,27 @@
|
|||||||
# Беклог
|
# Беклог
|
||||||
|
|
||||||
Что **можно взять**. Одна задача = один файл `items/<slug>.md`
|
Что **можно взять**. Одна задача = один файл `items/<slug>.md`
|
||||||
+ строка здесь. Целей тут нет — они в [ROADMAP.md](ROADMAP.md): беклог — то, что берут,
|
+ строка здесь. Ведётся скиллом `av-dev:task-track`.
|
||||||
роадмап — то, подо что берут. **Порядок строк значим:**
|
|
||||||
это очередь, и первая строка — то, что делают следующим. Порядок
|
|
||||||
назначает человек на груминге, машина его не выводит. Одно исключение
|
|
||||||
производно от типа — сырьё (`research` без раздела «Вопрос»)
|
|
||||||
стоит в конце: его не берут. Ведётся скиллом `av-dev:task-track`.
|
|
||||||
|
|
||||||
Секция одна — полок домена у проекта нет, и делить очередь на две
|
<!-- стадия -->
|
||||||
значило бы держать два порядка вместо одного.
|
Стадия проекта — **стройка** (`[tasks] stage = "build"`).
|
||||||
|
**Порядок строк — зависимость:** это план стройки от базы к деталям,
|
||||||
|
и строка выше сделана раньше не потому, что важнее, а потому, что
|
||||||
|
иначе нельзя. Секция здесь **одна**: разложенный по полкам план
|
||||||
|
перестаёт быть планом. Список пишется вперёд целиком — это не
|
||||||
|
гниение беклога, а замысел. Пустой беклог значит, что стройка
|
||||||
|
окончена: дальше `tasks.py stage support`.
|
||||||
|
<!-- /стадия -->
|
||||||
|
|
||||||
**Чем очередь упорядочена на этом этапе — от базы к деталям.**
|
**Чем основание отличается от детали в этом проекте.** Сначала идёт то,
|
||||||
Сначала то, на чём стоит остальное: проверки, которым можно верить,
|
на чём стоит остальное: проверки, которым можно верить, владелец записи,
|
||||||
владелец записи, единый контракт API, покрытый тестами конвейер, — и
|
единый контракт API, покрытый тестами конвейер, — и только потом экраны
|
||||||
только потом экраны и возможности поверх них. Порядок расставлен на
|
и возможности поверх них. Этот порядок расставили 2026-08-12.
|
||||||
груминге 2026-08-12 и держится, пока сервис не собран целиком:
|
Задача, взятая раньше своего основания, стоит дважды: сперва её пишут,
|
||||||
задача, взятая раньше своего основания, стоит дважды — сперва её
|
потом переписывают под появившееся основание.
|
||||||
пишут, потом переписывают под появившееся основание.
|
|
||||||
|
Одно место в очереди назначено не человеком, а типом: сырьё
|
||||||
|
(`research` без раздела «Вопрос») стоит в конце секции — его не берут.
|
||||||
|
|
||||||
Отсюда правило для **новых** записей. Заведённая по ходу работы —
|
Отсюда правило для **новых** записей. Заведённая по ходу работы —
|
||||||
интейком, урожаем ревью, разбором находок — задача встаёт в конец
|
интейком, урожаем ревью, разбором находок — задача встаёт в конец
|
||||||
@@ -76,7 +80,7 @@
|
|||||||
- [✨ Считать только те уровни текста, что включены у владельца записи](items/settings-applied-in-pipeline.md) — Дом настроек есть, а конвейер их не читает: выключенный уровень всё равно уходит платной модели, и настройка ничего не экономит.
|
- [✨ Считать только те уровни текста, что включены у владельца записи](items/settings-applied-in-pipeline.md) — Дом настроек есть, а конвейер их не читает: выключенный уровень всё равно уходит платной модели, и настройка ничего не экономит.
|
||||||
- [✨ Отправлять готовый текст через apprise и ntfy](items/ntfy-delivery.md) — Пользователь веба узнаёт о готовности только опросом с открытого экрана.
|
- [✨ Отправлять готовый текст через apprise и ntfy](items/ntfy-delivery.md) — Пользователь веба узнаёт о готовности только опросом с открытого экрана.
|
||||||
- [✨ Слать готовый текст на почту из учётной записи](items/email-notification.md) — Адрес почты приходит вместе с входом через OIDC, но почтового отправителя в сервисе нет.
|
- [✨ Слать готовый текст на почту из учётной записи](items/email-notification.md) — Адрес почты приходит вместе с входом через OIDC, но почтового отправителя в сервисе нет.
|
||||||
- [🔬 Потолки SpeechKit по длине записи и по формату](items/speechkit-limits.md) — Потолок длины записи и перечень принимаемых форматов неизвестны, а цель про долгие записи без них не начинается.
|
- [🔬 Потолки SpeechKit по длине записи и по формату](items/speechkit-limits.md) — Потолок длины записи и перечень принимаемых форматов неизвестны, а работа над долгими записями без них не начинается.
|
||||||
- [🔬 Потолки приёма, конвертации и заливки по длине записи](items/intake-limits-measure.md) — Из пяти звеньев задача speechkit-limits замерила только модель распознавания: где отваливается шестичасовая запись до неё, неизвестно.
|
- [🔬 Потолки приёма, конвертации и заливки по длине записи](items/intake-limits-measure.md) — Из пяти звеньев задача speechkit-limits замерила только модель распознавания: где отваливается шестичасовая запись до неё, неизвестно.
|
||||||
- [✨ Отклонять на приёме запись сверх потолка](items/reject-oversized-recording.md) — Запись сверх потолка принимается молча и висит в конвейере до истечения часового захвата, а человек всё это время ждёт текста.
|
- [✨ Отклонять на приёме запись сверх потолка](items/reject-oversized-recording.md) — Запись сверх потолка принимается молча и висит в конвейере до истечения часового захвата, а человек всё это время ждёт текста.
|
||||||
- [✨ Резать длинную запись на фрагменты и продолжать с места остановки](items/long-audio-chunking.md) — Шаг конвейера повторяется целиком: перезапуск на пятом часу шестичасовой записи начинает распознавание заново и оплачивает его второй раз.
|
- [✨ Резать длинную запись на фрагменты и продолжать с места остановки](items/long-audio-chunking.md) — Шаг конвейера повторяется целиком: перезапуск на пятом часу шестичасовой записи начинает распознавание заново и оплачивает его второй раз.
|
||||||
|
|||||||
@@ -6,3 +6,14 @@
|
|||||||
|
|
||||||
<!-- - ГГГГ-ММ-ДД `slug` — Заголовок. Причина: … Была секция: … -->
|
<!-- - ГГГГ-ММ-ДД `slug` — Заголовок. Причина: … Была секция: … -->
|
||||||
- 2026-08-12 `gate-go-version-sync` — 🧹 Сверять версию Go в образе с директивой go.mod. Причина: слита в go-1-26-upgrade 2026-08-12: сверка версии и само обновление правят одни и те же строки go.mod и Dockerfile, и порознь заводят расхождение заново. Была секция: Очередь.
|
- 2026-08-12 `gate-go-version-sync` — 🧹 Сверять версию Go в образе с директивой go.mod. Причина: слита в go-1-26-upgrade 2026-08-12: сверка версии и само обновление правят одни и те же строки go.mod и Dockerfile, и порознь заводят расхождение заново. Была секция: Очередь.
|
||||||
|
- 2026-08-13 `any-audio-source` — 🎯 Принимается запись любого формата, включая дорожку из видео. Причина: Зонтик над разобранной работой: перечень форматов меряет audio-format-coverage-measure, дорожку из видео берёт video-audio-track-intake. Тип goal упразднён раскладкой av-dev 3. Была секция: Направления.
|
||||||
|
- 2026-08-13 `data-ownership` — 🎯 Человек убирает свою запись из архива вместе со всеми текстами. Причина: Зонтик над разобранной работой: удаление записи со всеми уровнями текста делает delete-record. Тип goal упразднён раскладкой av-dev 3. Была секция: Запланировано.
|
||||||
|
- 2026-08-13 `long-recordings` — 🎯 Запись длиной до шести часов доходит до текста. Причина: Зонтик над разобранной работой: потолки меряют speechkit-limits и intake-limits-measure, дальше идут reject-oversized-recording, long-audio-chunking и long-text-delivery. Тип goal упразднён раскладкой av-dev 3. Была секция: Направления.
|
||||||
|
- 2026-08-13 `multi-user` — 🎯 Сервисом пользуются несколько человек, и записи одного не видны другому. Причина: Зонтик над разобранной работой: вход сделан задачей oidc-login, дальше идут record-ownership, telegram-account-link и api-tokens. Тип goal упразднён раскладкой av-dev 3. Была секция: Запланировано.
|
||||||
|
- 2026-08-13 `ready-notification` — 🎯 Пользователь узнаёт о готовности текста, не держа приложение открытым. Причина: Зонтик над разобранной работой: доставку делают ntfy-delivery и email-notification. Тип goal упразднён раскладкой av-dev 3. Была секция: Запланировано.
|
||||||
|
- 2026-08-13 `service-observability` — 🎯 Состояние сервиса видно без чтения логов. Причина: Зонтик над разобранной работой: словарь метрик выбирает opentelemetry-fit, дальше идут stalled-pipeline-metric, external-service-metrics, job-path-by-request и owner-alerting. Тип goal упразднён раскладкой av-dev 3. Была секция: Сопровождение.
|
||||||
|
- 2026-08-13 `text-insights` — 🎯 Приложение показывает, о чём запись, не читая её целиком. Причина: Зонтик над разобранной работой: уровни текста считает llm-insights-adapter, вычитку даёт literary-text-level, показывает их insights-visible-in-list. Тип goal упразднён раскладкой av-dev 3. Была секция: Запланировано.
|
||||||
|
- 2026-08-13 `upload-reliability` — 🎯 Загрузка большого файла доходит до сервиса и не повторяется впустую. Причина: Зонтик над разобранной работой: дедупликацию делает dedup-by-content-hash, пачку файлов multi-file-upload, ход загрузки upload-progress, уборку за обрывом orphan-file-on-failed-intake. Тип goal упразднён раскладкой av-dev 3. Была секция: Запланировано.
|
||||||
|
- 2026-08-13 `usage-stats` — 🎯 Владелец видит, кто сколько загрузил и во что это обошлось. Причина: Зонтик над разобранной работой: учёт ведёт usage-accounting, показывает его admin-stats-screen. Тип goal упразднён раскладкой av-dev 3. Была секция: Сопровождение.
|
||||||
|
- 2026-08-13 `user-settings` — 🎯 Пользователь настраивает, что сервис делает с его записями. Причина: Зонтик над разобранной работой: дом настроек заводит settings-screen, читает их в конвейере settings-applied-in-pipeline. Тип goal упразднён раскладкой av-dev 3. Была секция: Запланировано.
|
||||||
|
- 2026-08-13 `web-access` — 🎯 Записи загружаются и читаются в приложении, которое ставится на телефон. Причина: Зонтик над разобранной работой: каркас даёт spa-skeleton, контракт json-api-for-spa, экраны upload-and-status-screen, records-list-screen, play-recording-in-app, установку на телефон installable-pwa. Тип goal упразднён раскладкой av-dev 3. Была секция: Запланировано.
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
# Роадмап
|
|
||||||
|
|
||||||
Состояние проекта: что приложение **уже умеет** и чего ещё не умеет.
|
|
||||||
Цель — возможность приложения: файл типа `goal` (🎯) в
|
|
||||||
`items/`. Её задачи здесь **не перечисляются** — перечень даёт
|
|
||||||
`tasks.py list --goal <слаг>`.
|
|
||||||
|
|
||||||
- **Запланировано** — очередь значима и обосновывается прозой;
|
|
||||||
- **Направления** — очереди нет, тянутся долго;
|
|
||||||
- **Сопровождение** — чем держат проект: инструмент,
|
|
||||||
процесс, эксплуатация. Не возможности приложения, и отдельно —
|
|
||||||
чтобы не читаться как обещание продукта;
|
|
||||||
- **Готово** — достигнутое: строку пишет
|
|
||||||
`tasks.py close <цель> --implemented`, ссылки на файл в ней нет —
|
|
||||||
файл удаляется, поведение живёт в спеках. Стоит последней: копится.
|
|
||||||
|
|
||||||
Секции **канонические** и переименованию проектом не подлежат:
|
|
||||||
у каждой свой смысл, и в достигнутое пишет сам `close`. Порядок
|
|
||||||
тоже канонический. Английский
|
|
||||||
вариант — Planned | Directions | Operations | Done, один язык на весь
|
|
||||||
индекс.
|
|
||||||
|
|
||||||
## Запланировано
|
|
||||||
|
|
||||||
- [🎯 Сервисом пользуются несколько человек, и записи одного не видны другому](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/long-recordings.md) — Потолок не замерен ни на одном звене: Telegram не отдаёт больше 20 МиБ, границы модели deferred-general неизвестны, а перезапуск на середине начинает распознавание заново.
|
|
||||||
- [🎯 Принимается запись любого формата, включая дорожку из видео](items/any-audio-source.md) — Конвертер вызывается одной командой ffmpeg, проверенной на голосовых Telegram; что он берёт помимо них, никто не мерил.
|
|
||||||
|
|
||||||
## Сопровождение
|
|
||||||
|
|
||||||
- [🎯 Состояние сервиса видно без чтения логов](items/service-observability.md) — Отказ замечает пользователь, а не владелец: оповещения нет, а путь записи по конвейеру собирается глазами по логам контейнера.
|
|
||||||
- [🎯 Владелец видит, кто сколько загрузил и во что это обошлось](items/usage-stats.md) — Распознавание и языковая модель оплачиваются по факту, а счёт приходит одной суммой: кто её набрал, из сервиса не выясняется.
|
|
||||||
|
|
||||||
## Готово
|
|
||||||
|
|
||||||
- 2025-08-14 `telegram-transcription` — Голосовое сообщение из Telegram возвращается текстом. Первый вход сервиса: бот принимает голосовое, аудиофайл и документ с аудио и отвечает расшифровкой.
|
|
||||||
- 2025-08-08 `api-transcription` — Запись, отданная по HTTP, возвращается текстом. Программный вход: файл отдаётся формой, готовность и текст забираются опросом статуса задачи.
|
|
||||||
@@ -3,10 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Страница показывает собранный учёт: без учёта показывать нечего.
|
- **Категория:** Очередь — Страница показывает собранный учёт: без учёта показывать нечего.
|
||||||
- **Зачем:** Собранный учёт читается только запросом к базе руками: ни страницы, ни признака владельца в приложении нет.
|
- **Зачем:** Собранный учёт читается только запросом к базе руками: ни страницы, ни признака владельца в приложении нет.
|
||||||
- **Теги:** goal:usage-stats
|
|
||||||
|
|
||||||
Двигает пункты 1, 2 и 3 «Завершения» цели: расход по каждому пользователю виден
|
Расход по каждому пользователю виден на странице, и открывается она только
|
||||||
на странице, и открывается она только владельцу сервиса.
|
владельцу сервиса.
|
||||||
|
|
||||||
## Затрагивает
|
## Затрагивает
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
# 🎯 Принимается запись любого формата, включая дорожку из видео
|
|
||||||
|
|
||||||
- **Тип:** goal
|
|
||||||
- **Секция:** Направления — Перечень форматов не замерен, и потолок длины у видео тот же, что у долгих записей: тянется следом за ними.
|
|
||||||
- **Зачем:** Конвертер вызывается одной командой ffmpeg, проверенной на голосовых Telegram; что он берёт помимо них, никто не мерил.
|
|
||||||
- **Теги:** decomposed
|
|
||||||
|
|
||||||
Человек отдаёт файл, не думая о том, что внутри: аудио любого распространённого
|
|
||||||
контейнера или видео, из которого нужна только речь. Подготовка на стороне
|
|
||||||
пользователя не требуется.
|
|
||||||
|
|
||||||
## Завершение
|
|
||||||
|
|
||||||
1. Перечень принимаемых форматов замерен и записан в `research/`, а не выведен
|
|
||||||
из документации ffmpeg.
|
|
||||||
2. Видеофайл принимается, и из него берётся звуковая дорожка.
|
|
||||||
3. Формат, который принять нельзя, отклоняется на приёме — с текстом, из
|
|
||||||
которого понятно почему, а не отказом на конвертации через минуту.
|
|
||||||
4. Расхождение ogg/vorbis против заявленного SpeechKit `OGG_OPUS` разобрано:
|
|
||||||
либо устранено, либо записано как проверенно безвредное.
|
|
||||||
@@ -3,11 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Второй способ представиться ставится на готовые владельца и контракт, иначе форма ошибки переписывается дважды.
|
- **Категория:** Очередь — Второй способ представиться ставится на готовые владельца и контракт, иначе форма ошибки переписывается дважды.
|
||||||
- **Зачем:** Вход через OIDC закрывает API целиком, а скрипту браузерная сессия недоступна: автоматизировать загрузку станет нечем.
|
- **Зачем:** Вход через OIDC закрывает API целиком, а скрипту браузерная сессия недоступна: автоматизировать загрузку станет нечем.
|
||||||
- **Теги:** goal:multi-user
|
|
||||||
|
|
||||||
Двигает пункты 1 и 6 «Завершения» цели: запрос без токена не проходит (пункт 1),
|
Запрос без токена не проходит, а скрипт ходит в API по токену, выпущенному
|
||||||
а скрипт ходит в API по токену, выпущенному пользователем, и видит ровно его
|
пользователем, и видит ровно его записи.
|
||||||
записи (пункт 6).
|
|
||||||
|
|
||||||
Токен принадлежит учётной записи и даёт ровно её права: записи, заведённые по
|
Токен принадлежит учётной записи и даёт ровно её права: записи, заведённые по
|
||||||
токену, видны владельцу в приложении, и наоборот.
|
токену, видны владельцу в приложении, и наоборот.
|
||||||
|
|||||||
@@ -3,11 +3,9 @@
|
|||||||
- **Тип:** research
|
- **Тип:** research
|
||||||
- **Категория:** Очередь — Форматы: сначала замер того, что конвейер берёт на самом деле.
|
- **Категория:** Очередь — Форматы: сначала замер того, что конвейер берёт на самом деле.
|
||||||
- **Зачем:** Команда ffmpeg проверена на голосовых Telegram, а что она берёт помимо них, не мерил никто: перечень выведен из документации, а не из прогона.
|
- **Зачем:** Команда ffmpeg проверена на голосовых Telegram, а что она берёт помимо них, не мерил никто: перечень выведен из документации, а не из прогона.
|
||||||
- **Теги:** goal:any-audio-source
|
|
||||||
|
|
||||||
Двигает пункты 1 и 4 «Завершения» цели: перечень принимаемых форматов замерен и
|
Замер даёт перечень принимаемых форматов и разбирает расхождение `ogg/vorbis`
|
||||||
записан, а расхождение `ogg/vorbis` против заявленного SpeechKit `OGG_OPUS`
|
против заявленного SpeechKit `OGG_OPUS`.
|
||||||
разобрано.
|
|
||||||
|
|
||||||
## Вопрос
|
## Вопрос
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
# 🎯 Человек убирает свою запись из архива вместе со всеми текстами
|
|
||||||
|
|
||||||
- **Тип:** goal
|
|
||||||
- **Секция:** Запланировано — Очередь у цели появилась: удаление записи стоит 32-й строкой и трогает конвейер, файлы и колонку дедупликации, которые к тому месту готовы.
|
|
||||||
- **Зачем:** Хранение бессрочное, а способа убрать запись нет ни одного: ошибочно загруженный файл и разговор, который человек не хочет держать у нас, остаются навсегда.
|
|
||||||
- **Теги:** decomposed
|
|
||||||
|
|
||||||
Сервис объявлен архивом 2026-08-11, и с тем же решением у человека появляется
|
|
||||||
обратное право: сказать «убери это» и убедиться, что убрано. Речь в записи
|
|
||||||
принадлежит тем, кто говорил, а не хранилищу.
|
|
||||||
|
|
||||||
Стирается всё, что породила запись: сам файл, его фрагменты, объект в Object
|
|
||||||
Storage и все уровни текста. **Учёт расхода при этом остаётся** — деньги уже
|
|
||||||
потрачены, и сводка владельца задним числом не переписывается; строки
|
|
||||||
потребления несут идентификаторы и числа, не текст.
|
|
||||||
|
|
||||||
## Завершение
|
|
||||||
|
|
||||||
1. Своя запись убирается одним действием, и после него не остаётся ни файла, ни
|
|
||||||
объекта в хранилище, ни одного из уровней текста.
|
|
||||||
2. Убранное не возвращается: восстановления нет, и человек предупреждён об этом
|
|
||||||
до подтверждения.
|
|
||||||
3. Чужую запись убрать нельзя — ни по идентификатору, ни по токену.
|
|
||||||
4. Сводка расхода после удаления не меняется: потраченное остаётся видно
|
|
||||||
владельцу сервиса.
|
|
||||||
5. Тот же файл, загруженный снова, обрабатывается как новая запись, а не
|
|
||||||
узнаётся дедупликацией удалённой.
|
|
||||||
@@ -3,10 +3,8 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Дедупликация ищет совпадение в пределах пользователя — то есть после владельца записи, и экономит деньги с первого дня приложения.
|
- **Категория:** Очередь — Дедупликация ищет совпадение в пределах пользователя — то есть после владельца записи, и экономит деньги с первого дня приложения.
|
||||||
- **Зачем:** Один и тот же файл, отправленный дважды, распознаётся дважды и оплачивается дважды: приём не смотрит на содержимое вовсе.
|
- **Зачем:** Один и тот же файл, отправленный дважды, распознаётся дважды и оплачивается дважды: приём не смотрит на содержимое вовсе.
|
||||||
- **Теги:** goal:upload-reliability
|
|
||||||
|
|
||||||
Двигает пункт 1 «Завершения» цели: повторная отправка того же файла возвращает
|
Повторная отправка того же файла возвращает прежнюю запись вместо второй задачи.
|
||||||
прежнюю запись вместо второй задачи.
|
|
||||||
|
|
||||||
Совпадение ищется **в пределах одного пользователя**: чужая расшифровка по
|
Совпадение ищется **в пределах одного пользователя**: чужая расшифровка по
|
||||||
совпадению хеш-суммы не отдаётся и о её существовании отправитель не узнаёт.
|
совпадению хеш-суммы не отдаётся и о её существовании отправитель не узнаёт.
|
||||||
|
|||||||
@@ -3,11 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Удаление трогает конвейер, файлы, объект хранилища и колонку дедупликации — всё это к этому месту уже готово.
|
- **Категория:** Очередь — Удаление трогает конвейер, файлы, объект хранилища и колонку дедупликации — всё это к этому месту уже готово.
|
||||||
- **Зачем:** Ни файлы, ни расшифровки не удаляются вовсе: убрать запись сегодня можно только руками в базе и в каталоге на сервере.
|
- **Зачем:** Ни файлы, ни расшифровки не удаляются вовсе: убрать запись сегодня можно только руками в базе и в каталоге на сервере.
|
||||||
- **Теги:** goal:data-ownership
|
|
||||||
|
|
||||||
Двигает все пять пунктов «Завершения» цели: запись убирается одним действием
|
Запись убирается одним действием вместе с файлом, объектом в хранилище и всеми
|
||||||
вместе с файлом, объектом в хранилище и всеми уровнями текста. Чужую запись
|
уровнями текста. Чужую запись убрать нельзя. Учёт расхода остаётся.
|
||||||
убрать нельзя. Учёт расхода остаётся.
|
|
||||||
|
|
||||||
Удаление необратимо и потому спрашивает подтверждения. Задача, которая ещё в
|
Удаление необратимо и потому спрашивает подтверждения. Задача, которая ещё в
|
||||||
работе, тоже убирается: конвейер обязан заметить исчезнувшую запись и не
|
работе, тоже убирается: конвейер обязан заметить исчезнувшую запись и не
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Второй канал на той же доставке.
|
- **Категория:** Очередь — Второй канал на той же доставке.
|
||||||
- **Зачем:** Адрес почты приходит вместе с входом через OIDC, но почтового отправителя в сервисе нет.
|
- **Зачем:** Адрес почты приходит вместе с входом через OIDC, но почтового отправителя в сервисе нет.
|
||||||
- **Теги:** goal:ready-notification
|
|
||||||
|
|
||||||
Двигает пункты 1, 2 и 3 «Завершения» цели: готовый текст и отказ доходят
|
Готовый текст и отказ доходят письмом, а адрес берётся у учётной записи, а не из
|
||||||
письмом, а адрес берётся у учётной записи, а не из общего конфига.
|
общего конфига.
|
||||||
|
|
||||||
Почта — второй канал рядом с тем, что заводит `ntfy-delivery`; выбор канала
|
Почта — второй канал рядом с тем, что заводит `ntfy-delivery`; выбор канала
|
||||||
остаётся в той же единой точке, что и сейчас.
|
остаётся в той же единой точке, что и сейчас.
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Метрики внешних сервисов пишутся в выбранном словаре, а не переписываются потом.
|
- **Категория:** Очередь — Метрики внешних сервисов пишутся в выбранном словаре, а не переписываются потом.
|
||||||
- **Зачем:** Ни у Telegram, ни у Object Storage, ни у SpeechKit нет ни одной метрики: отказ внешнего сервиса виден только строкой в журнале контейнера.
|
- **Зачем:** Ни у Telegram, ни у Object Storage, ни у SpeechKit нет ни одной метрики: отказ внешнего сервиса виден только строкой в журнале контейнера.
|
||||||
- **Теги:** goal:service-observability
|
|
||||||
|
|
||||||
Двигает пункты 2 и 5 «Завершения» цели: у каждого внешнего сервиса появляются
|
У каждого внешнего сервиса появляются вызовы, отказы и длительность, а расход на
|
||||||
вызовы, отказы и длительность, а расход на платные сервисы виден числом.
|
платные сервисы становится виден числом.
|
||||||
|
|
||||||
Внешних сервисов сегодня четыре — Telegram, Object Storage, SpeechKit и
|
Внешних сервисов сегодня четыре — Telegram, Object Storage, SpeechKit и
|
||||||
`ffmpeg`/`ffprobe` как внешний процесс; пятым станет языковая модель. Метрика
|
`ffmpeg`/`ffprobe` как внешний процесс; пятым станет языковая модель. Метрика
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
# ✨ Показывать заголовок в списке, отбирать список по темам и считать токены
|
# ✨ Показывать заголовок в списке, отбирать список по темам и считать токены
|
||||||
|
|
||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Три пункта «Завершения» цели не закрывала ни одна задача: показывать и отбирать можно, когда заголовки и темы уже считаются.
|
- **Категория:** Очередь — Показывать и отбирать можно, когда заголовки и темы уже считаются.
|
||||||
- **Зачем:** Заголовок, темы и пересказ считаются, но список по-прежнему показывает первые слова расшифровки и не отбирается ничем, а расход на модель не виден числом.
|
- **Зачем:** Заголовок, темы и пересказ считаются, но список по-прежнему показывает первые слова расшифровки и не отбирается ничем, а расход на модель не виден числом.
|
||||||
- **Теги:** goal:text-insights
|
|
||||||
|
|
||||||
Двигает пункты 1, 4 и 6 «Завершения» цели — те три, где выводы из текста
|
Выводы из текста становятся видны человеку и владельцу: заголовок в списке,
|
||||||
становятся видны человеку и владельцу: заголовок в списке (1), отбор по темам
|
отбор по темам, стоимость числом. Сами уровни считает `llm-insights-adapter`,
|
||||||
(4), стоимость числом (6). Сами уровни считает `llm-insights-adapter`, показать
|
показать их некому: экран списка написан раньше и знает только первые слова
|
||||||
их некому: экран списка написан раньше и знает только первые слова расшифровки.
|
расшифровки.
|
||||||
|
|
||||||
Берётся после `llm-insights-adapter`: пока заголовков и тем нет, показывать и
|
Берётся после `llm-insights-adapter`: пока заголовков и тем нет, показывать и
|
||||||
отбирать нечего.
|
отбирать нечего.
|
||||||
|
|||||||
@@ -3,11 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Ставить на телефон есть смысл, когда есть что ставить.
|
- **Категория:** Очередь — Ставить на телефон есть смысл, когда есть что ставить.
|
||||||
- **Зачем:** Приложение, живущее вкладкой браузера, теряется среди прочих: ярлыка на экране у него нет.
|
- **Зачем:** Приложение, живущее вкладкой браузера, теряется среди прочих: ярлыка на экране у него нет.
|
||||||
- **Теги:** goal:web-access
|
|
||||||
|
|
||||||
Двигает пункты 5 и 6 «Завершения» цели: приложение ставится с телефона и
|
Приложение ставится с телефона и запускается с ярлыка без адресной строки, а
|
||||||
запускается с ярлыка без адресной строки, а открытое без сети показывает это
|
открытое без сети показывает это состоянием.
|
||||||
состоянием.
|
|
||||||
|
|
||||||
Берётся после того, как есть что ставить, — то есть после
|
Берётся после того, как есть что ставить, — то есть после
|
||||||
`records-list-screen`.
|
`records-list-screen`.
|
||||||
@@ -41,5 +39,5 @@
|
|||||||
## Рамки
|
## Рамки
|
||||||
|
|
||||||
Офлайн-чтения готовых расшифровок и очереди отправки без сети **не делаем** —
|
Офлайн-чтения готовых расшифровок и очереди отправки без сети **не делаем** —
|
||||||
это за границей цели. Web Push не делаем: уведомления идут через apprise и ntfy,
|
это за границей из паспорта. Web Push не делаем: уведомления идут через apprise
|
||||||
цель `ready-notification`.
|
и ntfy — задача `ntfy-delivery`.
|
||||||
|
|||||||
@@ -3,11 +3,10 @@
|
|||||||
- **Тип:** research
|
- **Тип:** research
|
||||||
- **Категория:** Очередь — Второй замер — остальные четыре звена.
|
- **Категория:** Очередь — Второй замер — остальные четыре звена.
|
||||||
- **Зачем:** Из пяти звеньев задача speechkit-limits замерила только модель распознавания: где отваливается шестичасовая запись до неё, неизвестно.
|
- **Зачем:** Из пяти звеньев задача speechkit-limits замерила только модель распознавания: где отваливается шестичасовая запись до неё, неизвестно.
|
||||||
- **Теги:** goal:long-recordings
|
|
||||||
|
|
||||||
Пункт 1 «Завершения» цели требует замера пяти звеньев, а разведка
|
Звеньев пять, а разведка `speechkit-limits` меряет одно — модель
|
||||||
`speechkit-limits` меряет одно — модель `deferred-general`. Остальные четыре
|
`deferred-general`. Остальные четыре дешевле: они не требуют боевых ключей и
|
||||||
дешевле: они не требуют боевых ключей и считаются локально, кроме заливки.
|
считаются локально, кроме заливки.
|
||||||
|
|
||||||
Числа нужны раньше кода: они назначают потолок, который проверяет приём, и длину
|
Числа нужны раньше кода: они назначают потолок, который проверяет приём, и длину
|
||||||
фрагмента, на которые режет `long-audio-chunking`.
|
фрагмента, на которые режет `long-audio-chunking`.
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
# ✨ Собирать путь одной записи по конвейеру запросом
|
# ✨ Собирать путь одной записи по конвейеру запросом
|
||||||
|
|
||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Пункт 3 «Завершения» цели не закрывала ни одна задача; применяет словарь, который выберет разведка строкой выше.
|
- **Категория:** Очередь — Применяет словарь метрик, который выберет разведка строкой выше.
|
||||||
- **Зачем:** Звенья пути связаны только идентификатором задачи в строках журнала: чтобы понять, где запись провела минуты, владелец читает логи контейнера глазами.
|
- **Зачем:** Звенья пути связаны только идентификатором задачи в строках журнала: чтобы понять, где запись провела минуты, владелец читает логи контейнера глазами.
|
||||||
- **Теги:** goal:service-observability
|
|
||||||
|
|
||||||
Двигает пункт 3 «Завершения» цели: путь одной записи по конвейеру собирается
|
Путь одной записи по конвейеру собирается запросом, а не чтением логов глазами.
|
||||||
запросом, а не чтением логов глазами.
|
|
||||||
|
|
||||||
Путь длиной в минуты идёт через четыре внешних сервиса и три воркера. Сегодня
|
Путь длиной в минуты идёт через четыре внешних сервиса и три воркера. Сегодня
|
||||||
его звенья связывает `job_id` в строках журнала, и собирает их человек.
|
его звенья связывает `job_id` в строках журнала, и собирает их человек.
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Единая точка трансляции доменной ошибки — база и для экранов, и для токенов; список своих записей заводится после владельца, а не до.
|
- **Категория:** Очередь — Единая точка трансляции доменной ошибки — база и для экранов, и для токенов; список своих записей заводится после владельца, а не до.
|
||||||
- **Зачем:** Сегодняшний API отвечает 404 на любую ошибку чтения и 500 на любую ошибку приёма: строить на нём экраны нечем.
|
- **Зачем:** Сегодняшний API отвечает 404 на любую ошибку чтения и 500 на любую ошибку приёма: строить на нём экраны нечем.
|
||||||
- **Теги:** goal:web-access
|
|
||||||
|
|
||||||
Двигает пункты 1, 2 и 4 «Завершения» цели: экраны заводят задачу, видят её
|
Экраны заводят задачу, видят её состояние и листают список — всё через один
|
||||||
состояние и листают список — всё через один контракт.
|
контракт.
|
||||||
|
|
||||||
Обработчик `GET /api/status/:id` сегодня отвечает `404` на **любую** ошибку
|
Обработчик `GET /api/status/:id` сегодня отвечает `404` на **любую** ошибку
|
||||||
чтения, включая сбой базы, а `POST /api/audio` — `500` на любую ошибку заведения,
|
чтения, включая сбой базы, а `POST /api/audio` — `500` на любую ошибку заведения,
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Вычитанный текст считается тем же адаптером.
|
- **Категория:** Очередь — Вычитанный текст считается тем же адаптером.
|
||||||
- **Зачем:** Сырая расшифровка идёт без знаков препинания, с повторами и словами-паразитами: читать её подряд тяжело, а другого уровня текста нет.
|
- **Зачем:** Сырая расшифровка идёт без знаков препинания, с повторами и словами-паразитами: читать её подряд тяжело, а другого уровня текста нет.
|
||||||
- **Теги:** goal:text-insights
|
|
||||||
|
|
||||||
Двигает пункт «Завершения» цели про литературный текст: у записи появляется
|
У записи появляется второй уровень текста — тот же разговор, вычитанный до
|
||||||
второй уровень — тот же разговор, вычитанный до читаемого вида.
|
читаемого вида.
|
||||||
|
|
||||||
Вычитку считает та же внешняя модель, что заголовок и темы. Сырой текст
|
Вычитку считает та же внешняя модель, что заголовок и темы. Сырой текст
|
||||||
остаётся и не переписывается: уровни лежат рядом, а не поверх друг друга.
|
остаётся и не переписывается: уровни лежат рядом, а не поверх друг друга.
|
||||||
|
|||||||
@@ -3,11 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Уровни текста: сюда приходит пятая внешняя зависимость, и конвейер к этому моменту покрыт тестами.
|
- **Категория:** Очередь — Уровни текста: сюда приходит пятая внешняя зависимость, и конвейер к этому моменту покрыт тестами.
|
||||||
- **Зачем:** Расшифровка доходит стеной текста: ни заголовка, ни тем, ни пересказа сервис не считает, и клиента языковой модели в нём нет.
|
- **Зачем:** Расшифровка доходит стеной текста: ни заголовка, ни тем, ни пересказа сервис не считает, и клиента языковой модели в нём нет.
|
||||||
- **Теги:** goal:text-insights
|
|
||||||
|
|
||||||
Двигает пункты 1, 3, 4 и 5 «Завершения» цели: у готовой записи появляются
|
У готовой записи появляются заголовок, пересказ и темы, а отказ и молчание
|
||||||
заголовок (1), пересказ (3) и темы (4), а отказ и молчание модели не роняют
|
модели не роняют задачу.
|
||||||
задачу (5).
|
|
||||||
|
|
||||||
Здесь появляется пятая внешняя зависимость — языковая модель с
|
Здесь появляется пятая внешняя зависимость — языковая модель с
|
||||||
OpenAI-совместимым интерфейсом за шлюзом bifrost, — и текст расшифровки уходит
|
OpenAI-совместимым интерфейсом за шлюзом bifrost, — и текст расшифровки уходит
|
||||||
|
|||||||
@@ -3,11 +3,10 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Резка на фрагменты — самая глубокая переделка конвейера, и она идёт по замеренным числам.
|
- **Категория:** Очередь — Резка на фрагменты — самая глубокая переделка конвейера, и она идёт по замеренным числам.
|
||||||
- **Зачем:** Шаг конвейера повторяется целиком: перезапуск на пятом часу шестичасовой записи начинает распознавание заново и оплачивает его второй раз.
|
- **Зачем:** Шаг конвейера повторяется целиком: перезапуск на пятом часу шестичасовой записи начинает распознавание заново и оплачивает его второй раз.
|
||||||
- **Теги:** goal:long-recordings
|
|
||||||
|
|
||||||
Двигает пункты 3, 5 и 6 «Завершения» цели: запись в пределах потолка доходит до
|
Запись в пределах потолка доходит до текста целиком, долгая задача не занимает
|
||||||
текста целиком, долгая задача не занимает воркер на часы, а перезапуск на
|
воркер на часы, а перезапуск на середине продолжает работу с первого
|
||||||
середине продолжает работу с первого неотмеченного фрагмента.
|
неотмеченного фрагмента.
|
||||||
|
|
||||||
Запись делится на фрагменты, каждый распознаётся отдельно, готовый фрагмент
|
Запись делится на фрагменты, каждый распознаётся отдельно, готовый фрагмент
|
||||||
отмечается в базе. После перезапуска работа продолжается с первого неотмеченного, а
|
отмечается в базе. После перезапуска работа продолжается с первого неотмеченного, а
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
# 🎯 Запись длиной до шести часов доходит до текста
|
|
||||||
|
|
||||||
- **Тип:** goal
|
|
||||||
- **Секция:** Направления — Цель начинается с двух замеров и кончается резкой на фрагменты — самой глубокой переделкой конвейера: очереди внутри нет, пока числа не получены.
|
|
||||||
- **Зачем:** Потолок не замерен ни на одном звене: Telegram не отдаёт больше 20 МиБ, границы модели deferred-general неизвестны, а перезапуск на середине начинает распознавание заново.
|
|
||||||
- **Теги:** decomposed
|
|
||||||
|
|
||||||
Лекция, созвон, интервью и диктофонная запись из семейного архива целиком
|
|
||||||
превращаются в текст. Сегодня неизвестно даже, на каком звене такая запись
|
|
||||||
отваливается, — цель начинается с замера, а не с переделки.
|
|
||||||
|
|
||||||
Расчётный потолок — **шесть часов**: он взят с запасом под диктофонные записи и
|
|
||||||
дорожки из видео, и замер проверяет, каким звеном он ограничен на самом деле.
|
|
||||||
|
|
||||||
## Завершение
|
|
||||||
|
|
||||||
1. Потолки каждого звена замерены и записаны в `research/` с командой замера:
|
|
||||||
приём из Telegram, приём по HTTP, конвертация, заливка в Object Storage,
|
|
||||||
модель `deferred-general`.
|
|
||||||
2. Запись, превышающая потолок, отклоняется на приёме понятным текстом, а не
|
|
||||||
висит в конвейере до истечения захвата.
|
|
||||||
3. Запись в пределах потолка доходит до текста и не теряет его хвост.
|
|
||||||
4. Текст в несколько сотен килобайт доходит до получателя: и в браузере, и в
|
|
||||||
Telegram, где предел сообщения — 4000 символов.
|
|
||||||
5. Долгая задача не блокирует короткие: запись на три часа не останавливает
|
|
||||||
конвейер для голосового на десять секунд.
|
|
||||||
6. Перезапуск сервиса на середине долгой расшифровки не начинает её заново:
|
|
||||||
работа продолжается с места остановки.
|
|
||||||
@@ -3,10 +3,8 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Сотни килобайт текста появляются только после долгих записей.
|
- **Категория:** Очередь — Сотни килобайт текста появляются только после долгих записей.
|
||||||
- **Зачем:** Отправитель Telegram режет текст по 4000 знаков: расшифровка шестичасовой записи придёт сотней сообщений подряд.
|
- **Зачем:** Отправитель Telegram режет текст по 4000 знаков: расшифровка шестичасовой записи придёт сотней сообщений подряд.
|
||||||
- **Теги:** goal:long-recordings
|
|
||||||
|
|
||||||
Двигает пункт 4 «Завершения» цели: текст в несколько сотен килобайт доходит и в
|
Текст в несколько сотен килобайт доходит и в Telegram, и в браузере.
|
||||||
Telegram, и в браузере.
|
|
||||||
|
|
||||||
Деление по словам (`internal/adapter/telegram/split.go`) остаётся для обычной
|
Деление по словам (`internal/adapter/telegram/split.go`) остаётся для обычной
|
||||||
расшифровки; сверх названного числа частей вместо потока сообщений уходит один
|
расшифровки; сверх названного числа частей вместо потока сообщений уходит один
|
||||||
|
|||||||
@@ -3,10 +3,8 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Пачка файлов заводится на готовом экране загрузки и готовой дедупликации.
|
- **Категория:** Очередь — Пачка файлов заводится на готовом экране загрузки и готовой дедупликации.
|
||||||
- **Зачем:** Приём берёт один файл в запросе, а с телефона выбирают пачку сразу: десять записей значат десять заходов на экран загрузки.
|
- **Зачем:** Приём берёт один файл в запросе, а с телефона выбирают пачку сразу: десять записей значат десять заходов на экран загрузки.
|
||||||
- **Теги:** goal:upload-reliability
|
|
||||||
|
|
||||||
Двигает пункт 2 «Завершения» цели: пачка файлов уходит одной загрузкой, и отказ
|
Пачка файлов уходит одной загрузкой, и отказ одного не отменяет остальные.
|
||||||
одного не отменяет остальные.
|
|
||||||
|
|
||||||
## Затрагивает
|
## Затрагивает
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
# 🎯 Сервисом пользуются несколько человек, и записи одного не видны другому
|
|
||||||
|
|
||||||
- **Тип:** goal
|
|
||||||
- **Секция:** Запланировано — Владелец записи — фундамент, на котором стоят список своих записей, дедупликация, удаление, учёт расхода и квота: пока его нет, остальные цели строятся на песке.
|
|
||||||
- **Зачем:** У задачи нет владельца, а HTTP API открыт наружу без аутентификации: пригласить второго человека сейчас значит открыть ему чужие расшифровки.
|
|
||||||
- **Теги:** decomposed
|
|
||||||
|
|
||||||
Приложение узнаёт, кто к нему пришёл, и показывает каждому только его записи.
|
|
||||||
Учётные записи заводит и проверяет внешний провайдер — Authelia по OIDC; своей
|
|
||||||
регистрации и своих паролей не делаем, это граница из
|
|
||||||
[паспорта](../../docs/passport.md).
|
|
||||||
|
|
||||||
## Завершение
|
|
||||||
|
|
||||||
1. Неаутентифицированный запрос к записям не проходит: ни к странице, ни к API.
|
|
||||||
2. У задачи и файла есть владелец, и выборка чужой записи по её
|
|
||||||
идентификатору возвращает «не найдено», а не содержимое.
|
|
||||||
3. Вход идёт через OIDC у Authelia; выход из сессии работает.
|
|
||||||
4. Пользователь Telegram сопоставлен с учётной записью, и записи, пришедшие
|
|
||||||
ботом, видны ему же в браузере.
|
|
||||||
5. Белый список Telegram перестаёт быть отдельным механизмом: право писать боту
|
|
||||||
выводится из учётной записи.
|
|
||||||
6. Скрипт ходит в API по токену, выпущенному пользователем, и видит ровно его
|
|
||||||
записи.
|
|
||||||
@@ -3,12 +3,10 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Канал уведомлений выбирается в настройках, которые уже есть.
|
- **Категория:** Очередь — Канал уведомлений выбирается в настройках, которые уже есть.
|
||||||
- **Зачем:** Пользователь веба узнаёт о готовности только опросом с открытого экрана.
|
- **Зачем:** Пользователь веба узнаёт о готовности только опросом с открытого экрана.
|
||||||
- **Теги:** goal:ready-notification
|
|
||||||
|
|
||||||
Двигает пункты 1, 2, 4 и 5 «Завершения» цели: готовый текст и отказ доходят до
|
Готовый текст и отказ доходят до пользователя веба без открытого приложения,
|
||||||
пользователя веба без открытого приложения, отказ канала задачу не роняет, а
|
отказ канала задачу не роняет, а пользователь Telegram получает ответ
|
||||||
пользователь Telegram получает ответ по-прежнему ботом. Выбор канала самим
|
по-прежнему ботом. Выбор канала самим пользователем заводит `settings-screen`.
|
||||||
пользователем (пункт 3) заводит `settings-screen`.
|
|
||||||
|
|
||||||
Сегодня `completeJob` и `failJob` отвечают только источнику `telegram`;
|
Сегодня `completeJob` и `failJob` отвечают только источнику `telegram`;
|
||||||
источник `api` не получает ничего. Здесь появляется второй способ доставки, и
|
источник `api` не получает ничего. Здесь появляется второй способ доставки, и
|
||||||
@@ -46,3 +44,8 @@
|
|||||||
Web Push с VAPID и своим хранением подписок не делаем. Своего сервера ntfy не
|
Web Push с VAPID и своим хранением подписок не делаем. Своего сервера ntfy не
|
||||||
поднимаем — адрес приходит конфигом. Текст расшифровки уходит на внешний сервис,
|
поднимаем — адрес приходит конфигом. Текст расшифровки уходит на внешний сервис,
|
||||||
и это сдвиг периметра: строка в `docs/security.md` обязательна.
|
и это сдвиг периметра: строка в `docs/security.md` обязательна.
|
||||||
|
|
||||||
|
Отказ от Web Push сегодня живёт открытым вопросом `docs/architecture.md`,
|
||||||
|
«Уведомления», и своего ADR не имеет: заводить его не из чего, пока нет
|
||||||
|
`design.md` этой задачи. Решение промоутится из него, когда задача пойдёт в
|
||||||
|
работу.
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
- **Тип:** research
|
- **Тип:** research
|
||||||
- **Категория:** Очередь — Сопровождение: словарь метрик выбирается до того, как метрик станет втрое больше.
|
- **Категория:** Очередь — Сопровождение: словарь метрик выбирается до того, как метрик станет втрое больше.
|
||||||
- **Зачем:** Метрик одиннадцать штук на пять счётчиков, трассировки нет вовсе: путь одной записи по конвейеру собирается только чтением логов глазами.
|
- **Зачем:** Метрик одиннадцать штук на пять счётчиков, трассировки нет вовсе: путь одной записи по конвейеру собирается только чтением логов глазами.
|
||||||
- **Теги:** goal:service-observability
|
|
||||||
|
|
||||||
Эндпоинт `/metrics` остаётся и развивается — это решено. Вопрос в том, чем его
|
Эндпоинт `/metrics` остаётся и развивается — это решено. Вопрос в том, чем его
|
||||||
развивать: дописывать счётчики в `internal/metrics` напрямую через
|
развивать: дописывать счётчики в `internal/metrics` напрямую через
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
- **Тип:** fix
|
- **Тип:** fix
|
||||||
- **Категория:** Очередь — Осиротевший файл — тот же отказ на середине, и чинится тем же местом приёма.
|
- **Категория:** Очередь — Осиротевший файл — тот же отказ на середине, и чинится тем же местом приёма.
|
||||||
- **Зачем:** Отказ чтения метаданных и отказ записи на диск оставляют файл в каталоге хранения без задачи и без учёта: сопоставить его не с чем, удалять приходится руками.
|
- **Зачем:** Отказ чтения метаданных и отказ записи на диск оставляют файл в каталоге хранения без задачи и без учёта: сопоставить его не с чем, удалять приходится руками.
|
||||||
- **Теги:** review-2026-08-11, goal:upload-reliability
|
- **Теги:** review-2026-08-11
|
||||||
|
|
||||||
Приём пишет файл на диск, потом спрашивает у источника метаданных длительность,
|
Приём пишет файл на диск, потом спрашивает у источника метаданных длительность,
|
||||||
потом заводит запись в учёте и задачу. Уборка при отказе есть **только на
|
потом заводит запись в учёте и задачу. Уборка при отказе есть **только на
|
||||||
|
|||||||
@@ -3,9 +3,8 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Правило оповещения ставится на метрику, которой до этого нет.
|
- **Категория:** Очередь — Правило оповещения ставится на метрику, которой до этого нет.
|
||||||
- **Зачем:** Об отказе владелец узнаёт от пользователя: правил оповещения нет ни на одной метрике, а метрики читают глазами.
|
- **Зачем:** Об отказе владелец узнаёт от пользователя: правил оповещения нет ни на одной метрике, а метрики читают глазами.
|
||||||
- **Теги:** goal:service-observability
|
|
||||||
|
|
||||||
Двигает пункт 4 «Завершения» цели: владелец узнаёт об отказе сам.
|
Владелец узнаёт об отказе сам.
|
||||||
|
|
||||||
Берётся после `external-service-metrics` и `stalled-pipeline-metric`: правило
|
Берётся после `external-service-metrics` и `stalled-pipeline-metric`: правило
|
||||||
оповещения ставится на метрику, а метрик, на которых его ставить, сегодня нет.
|
оповещения ставится на метрику, а метрик, на которых его ставить, сегодня нет.
|
||||||
@@ -35,4 +34,4 @@
|
|||||||
|
|
||||||
Выкладку правил запускает человек — `inv pl -- transcriber` из
|
Выкладку правил запускает человек — `inv pl -- transcriber` из
|
||||||
`pet-project-server`. Пользовательские уведомления о готовности здесь не
|
`pet-project-server`. Пользовательские уведомления о готовности здесь не
|
||||||
трогаем: это цель `ready-notification`.
|
трогаем: их доставку заводят `ntfy-delivery` и `email-notification`.
|
||||||
|
|||||||
@@ -3,10 +3,8 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Проигрывание живёт на экране записи, которого до списка нет.
|
- **Категория:** Очередь — Проигрывание живёт на экране записи, которого до списка нет.
|
||||||
- **Зачем:** Послушать загруженное приложение не даёт, а самой копии для этого у задачи нет: указатель на файл перезаписывается на каждом шаге конвейера и у готовой задачи ведёт на объект в Object Storage.
|
- **Зачем:** Послушать загруженное приложение не даёт, а самой копии для этого у задачи нет: указатель на файл перезаписывается на каждом шаге конвейера и у готовой задачи ведёт на объект в Object Storage.
|
||||||
- **Теги:** goal:web-access
|
|
||||||
|
|
||||||
Двигает пункт 7 «Завершения» цели: запись слушается на её экране, а не
|
Запись слушается на её экране, а не скачивается файлом.
|
||||||
скачивается файлом.
|
|
||||||
|
|
||||||
Заметка владельца от 2026-08-12. Копии на диске остаются все три — исходник,
|
Заметка владельца от 2026-08-12. Копии на диске остаются все три — исходник,
|
||||||
ogg после конвертации и запись об объекте, — но `job.FileID` на каждом шаге
|
ogg после конвертации и запись об объекте, — но `job.FileID` на каждом шаге
|
||||||
@@ -47,6 +45,6 @@ ogg после конвертации и запись об объекте, —
|
|||||||
## Рамки
|
## Рамки
|
||||||
|
|
||||||
Скачивания файла, обрезки и синхронизации текста со звуком не делаем. Записи
|
Скачивания файла, обрезки и синхронизации текста со звуком не делаем. Записи
|
||||||
звука в приложении не делаем — это граница цели. Формат хранения и раскладку
|
звука в приложении не делаем — это граница из паспорта. Формат хранения и раскладку
|
||||||
каталога данных не трогаем: они объявлены необратимыми, а шаг схемы, уехавший на
|
каталога данных не трогаем: они объявлены необратимыми, а шаг схемы, уехавший на
|
||||||
сервер, не переписывается — изменение только новым файлом шага.
|
сервер, не переписывается — изменение только новым файлом шага.
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
# 🎯 Пользователь узнаёт о готовности текста, не держа приложение открытым
|
|
||||||
|
|
||||||
- **Тип:** goal
|
|
||||||
- **Секция:** Запланировано — Канал уведомлений выбирается в настройках, которые к этому месту уже есть.
|
|
||||||
- **Зачем:** Расшифровка занимает минуты, и всё это время человек либо смотрит на экран с опросом статуса, либо забывает вернуться.
|
|
||||||
- **Теги:** decomposed
|
|
||||||
|
|
||||||
Задача уходит в работу — человек закрывает приложение и получает сообщение,
|
|
||||||
когда текст готов. Пользователь Telegram это уже имеет: бот отвечает сам.
|
|
||||||
Пользователь веба — нет.
|
|
||||||
|
|
||||||
Каналов два: почта, адрес которой приходит вместе с входом через OIDC, и
|
|
||||||
Telegram для того, кто связал свою учётную запись с ботом. Доставку берём
|
|
||||||
внешнюю: apprise как отправитель, ntfy как один из каналов. Web Push с VAPID и
|
|
||||||
собственным хранением подписок за целью **не стоит** — это отдельная
|
|
||||||
инфраструктура ради того же результата.
|
|
||||||
|
|
||||||
## Завершение
|
|
||||||
|
|
||||||
1. Готовый текст доходит до пользователя веба сообщением, без открытого
|
|
||||||
приложения.
|
|
||||||
2. Отказ задачи доходит тем же путём и тем же человекочитаемым текстом, что
|
|
||||||
видит пользователь Telegram.
|
|
||||||
3. Канал и адрес уведомлений задаёт пользователь, а не общий конфиг: у каждого
|
|
||||||
свой, и от уведомлений можно отказаться.
|
|
||||||
4. Недоступность канала уведомлений не роняет задачу и не мешает ей завершиться:
|
|
||||||
текст остаётся в приложении.
|
|
||||||
5. Пользователь Telegram получает ответ по-прежнему ботом, а не вторым каналом.
|
|
||||||
@@ -3,10 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Фундамент домена: без владельца знание UUID и есть право читать чужое, и на владельце стоят список, дедуп, удаление, учёт и квота.
|
- **Категория:** Очередь — Фундамент домена: без владельца знание UUID и есть право читать чужое, и на владельце стоят список, дедуп, удаление, учёт и квота.
|
||||||
- **Зачем:** У задачи и файла нет владельца, поэтому знание UUID задачи и есть право её читать.
|
- **Зачем:** У задачи и файла нет владельца, поэтому знание UUID задачи и есть право её читать.
|
||||||
- **Теги:** goal:multi-user
|
|
||||||
|
|
||||||
Двигает пункт 2 «Завершения» цели: выборка чужой записи по её идентификатору
|
Выборка чужой записи по её идентификатору возвращает «не найдено», а не
|
||||||
возвращает «не найдено», а не содержимое.
|
содержимое.
|
||||||
|
|
||||||
Берётся после `oidc-login`: до входа неизвестно, кто владелец.
|
Берётся после `oidc-login`: до входа неизвестно, кто владелец.
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Список и чтение текста берутся после экрана загрузки — так записано в самой задаче.
|
- **Категория:** Очередь — Список и чтение текста берутся после экрана загрузки — так записано в самой задаче.
|
||||||
- **Зачем:** Расшифровка сегодня доходит одним сообщением и теряется в переписке; вернуться к ней через неделю нечем.
|
- **Зачем:** Расшифровка сегодня доходит одним сообщением и теряется в переписке; вернуться к ней через неделю нечем.
|
||||||
- **Теги:** goal:web-access
|
|
||||||
|
|
||||||
Двигает пункты 3 и 4 «Завершения» цели: список своих записей открывается и
|
Список своих записей открывается и листается, а готовый текст читается и
|
||||||
листается, а готовый текст читается и копируется целиком.
|
копируется целиком.
|
||||||
|
|
||||||
Берётся после `upload-and-status-screen`.
|
Берётся после `upload-and-status-screen`.
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Отклонять сверх потолка можно, когда потолок замерен.
|
- **Категория:** Очередь — Отклонять сверх потолка можно, когда потолок замерен.
|
||||||
- **Зачем:** Запись сверх потолка принимается молча и висит в конвейере до истечения часового захвата, а человек всё это время ждёт текста.
|
- **Зачем:** Запись сверх потолка принимается молча и висит в конвейере до истечения часового захвата, а человек всё это время ждёт текста.
|
||||||
- **Теги:** goal:long-recordings
|
|
||||||
|
|
||||||
Двигает пункт 2 «Завершения» цели: запись длиннее потолка отклоняется на приёме
|
Запись длиннее потолка отклоняется на приёме понятным текстом, а не отказом
|
||||||
понятным текстом, а не отказом через час.
|
через час.
|
||||||
|
|
||||||
Потолок задаётся конфигурацией, а не зашивается в код: числа приходят из
|
Потолок задаётся конфигурацией, а не зашивается в код: числа приходят из
|
||||||
разведок `intake-limits-measure` и `speechkit-limits`, и меняются они без сборки.
|
разведок `intake-limits-measure` и `speechkit-limits`, и меняются они без сборки.
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
# 🎯 Состояние сервиса видно без чтения логов
|
|
||||||
|
|
||||||
- **Тип:** goal
|
|
||||||
- **Секция:** Сопровождение — Словарь метрик выбирается разведкой до того, как метрик станет втрое больше; наблюдаем мы, а не пользователь сервиса.
|
|
||||||
- **Зачем:** Отказ замечает пользователь, а не владелец: оповещения нет, а путь записи по конвейеру собирается глазами по логам контейнера.
|
|
||||||
- **Теги:** decomposed
|
|
||||||
|
|
||||||
Наблюдаем **мы**, а не пользователь сервиса, — поэтому цель стоит в
|
|
||||||
сопровождении, а не среди возможностей приложения. Пользовательская половина
|
|
||||||
той же темы живёт отдельно: о готовности своей записи человек узнаёт по цели
|
|
||||||
[ready-notification](ready-notification.md).
|
|
||||||
|
|
||||||
Эндпоинт `/metrics` остаётся и развивается. Чем именно развивать — голым
|
|
||||||
`client_golang` или OpenTelemetry с трассировкой — решает разведка
|
|
||||||
`opentelemetry-fit`.
|
|
||||||
|
|
||||||
## Завершение
|
|
||||||
|
|
||||||
1. По метрикам видно, что конвейер встал: задача висит в состоянии дольше
|
|
||||||
обычного, и это отличимо от «работы нет».
|
|
||||||
2. Каждый внешний сервис имеет метрику вызовов, отказов и длительности —
|
|
||||||
сегодня их нет ни у одного.
|
|
||||||
3. Путь одной записи по конвейеру собирается запросом, а не чтением логов
|
|
||||||
глазами.
|
|
||||||
4. Владелец узнаёт об отказе сам, а не от пользователя.
|
|
||||||
5. Стоимость обращений к платным сервисам видна числом.
|
|
||||||
@@ -3,11 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Выключать нечего, пока уровней нет; настройка ставится сразу за ними, чтобы платная модель не считала лишнее.
|
- **Категория:** Очередь — Выключать нечего, пока уровней нет; настройка ставится сразу за ними, чтобы платная модель не считала лишнее.
|
||||||
- **Зачем:** Дом настроек есть, а конвейер их не читает: выключенный уровень всё равно уходит платной модели, и настройка ничего не экономит.
|
- **Зачем:** Дом настроек есть, а конвейер их не читает: выключенный уровень всё равно уходит платной модели, и настройка ничего не экономит.
|
||||||
- **Теги:** goal:user-settings
|
|
||||||
|
|
||||||
Двигает пункты 1 и 4 «Завершения» цели: выключенный уровень не запрашивается у
|
Выключенный уровень не запрашивается у языковой модели вовсе, а запись
|
||||||
языковой модели вовсе, а запись обрабатывается по той настройке, которая
|
обрабатывается по той настройке, которая действовала на приёме.
|
||||||
действовала на приёме.
|
|
||||||
|
|
||||||
Берётся после `settings-screen` (настройки негде хранить) и после
|
Берётся после `settings-screen` (настройки негде хранить) и после
|
||||||
`llm-insights-adapter` (нечего выключать).
|
`llm-insights-adapter` (нечего выключать).
|
||||||
|
|||||||
@@ -3,11 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Дом настроек нужен раньше уровней текста и раньше выбора канала уведомлений.
|
- **Категория:** Очередь — Дом настроек нужен раньше уровней текста и раньше выбора канала уведомлений.
|
||||||
- **Зачем:** Настроек у пользователя нет вовсе: уровни текста и канал уведомлений задаются общим конфигом сервиса.
|
- **Зачем:** Настроек у пользователя нет вовсе: уровни текста и канал уведомлений задаются общим конфигом сервиса.
|
||||||
- **Теги:** goal:user-settings
|
|
||||||
|
|
||||||
Двигает пункты 1, 2 и 3 «Завершения» цели: у каждого пользователя свои
|
У каждого пользователя свои переключатели уровней текста и свой канал
|
||||||
переключатели уровней текста и свой канал уведомлений, и они переживают
|
уведомлений, и они переживают повторный вход.
|
||||||
повторный вход.
|
|
||||||
|
|
||||||
Здесь появляется дом настроек — таблица и эндпоинты; применяют их задачи
|
Здесь появляется дом настроек — таблица и эндпоинты; применяют их задачи
|
||||||
уровней текста и доставки уведомлений.
|
уровней текста и доставки уведомлений.
|
||||||
|
|||||||
@@ -3,12 +3,11 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Каркас приложения: экранов нет и собирать их нечем, а на экране стоят настройки, удаление, уровни текста и статистика.
|
- **Категория:** Очередь — Каркас приложения: экранов нет и собирать их нечем, а на экране стоят настройки, удаление, уровни текста и статистика.
|
||||||
- **Зачем:** Экранов нет и собирать их нечем: ни сборки фронтенда, ни раздачи статики в проекте не существует.
|
- **Зачем:** Экранов нет и собирать их нечем: ни сборки фронтенда, ни раздачи статики в проекте не существует.
|
||||||
- **Теги:** goal:web-access
|
|
||||||
|
|
||||||
Ни одной строки «Завершения» цели каркас не закрывает: он готовит четыре
|
Каркас готовит четыре экрана — загрузку, состояние задачи, чтение текста и
|
||||||
экранные — 1, 2, 3 и 4, — но сам по себе пользователю ничего не даёт. Видимое от
|
список, — но сам по себе пользователю ничего не даёт. Видимое от него одно:
|
||||||
него одно: приложение открывается и достаёт данные с живого сервера, а не отдаёт
|
приложение открывается и достаёт данные с живого сервера, а не отдаёт пустую
|
||||||
пустую страницу.
|
страницу.
|
||||||
|
|
||||||
Фреймворк выбран 2026-08-11 — Vue 3 с роутером пятой версии и сборкой Vite
|
Фреймворк выбран 2026-08-11 — Vue 3 с роутером пятой версии и сборкой Vite
|
||||||
([ADR](../../docs/adr/ADR-2026-08-11-spa-on-vue.md)); правила кода приложения
|
([ADR](../../docs/adr/ADR-2026-08-11-spa-on-vue.md)); правила кода приложения
|
||||||
|
|||||||
@@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
- **Тип:** research
|
- **Тип:** research
|
||||||
- **Категория:** Очередь — Долгие записи начинаются с замера: без потолков ни отказ на приёме, ни резка не проектируются.
|
- **Категория:** Очередь — Долгие записи начинаются с замера: без потолков ни отказ на приёме, ни резка не проектируются.
|
||||||
- **Зачем:** Потолок длины записи и перечень принимаемых форматов неизвестны, а цель про долгие записи без них не начинается.
|
- **Зачем:** Потолок длины записи и перечень принимаемых форматов неизвестны, а работа над долгими записями без них не начинается.
|
||||||
- **Теги:** goal:long-recordings
|
|
||||||
|
|
||||||
Цель «запись длиной в несколько часов доходит до текста» упирается в то, что
|
Запись длиной в несколько часов упирается в то, что неизвестно, на каком звене
|
||||||
неизвестно, на каком звене такая запись отваливается. Начинать с переделки
|
она отваливается. Начинать с переделки
|
||||||
приёма или с деления записи на куски — разные работы, и выбор между ними
|
приёма или с деления записи на куски — разные работы, и выбор между ними
|
||||||
определяет замер, а не рассуждение.
|
определяет замер, а не рассуждение.
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,8 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Признак вставшего конвейера — вторая половина той же работы.
|
- **Категория:** Очередь — Признак вставшего конвейера — вторая половина той же работы.
|
||||||
- **Зачем:** Вставший конвейер неотличим от простоя: возраст задачи в состоянии не считается, и очередь без движения выглядит как отсутствие работы.
|
- **Зачем:** Вставший конвейер неотличим от простоя: возраст задачи в состоянии не считается, и очередь без движения выглядит как отсутствие работы.
|
||||||
- **Теги:** goal:service-observability
|
|
||||||
|
|
||||||
Двигает пункт 1 «Завершения» цели: по метрикам видно, что конвейер встал, и это
|
По метрикам видно, что конвейер встал, и это отличимо от «работы нет».
|
||||||
отличимо от «работы нет».
|
|
||||||
|
|
||||||
Сегодня метрики считают события — принято, распознано, отказано, — а состояние
|
Сегодня метрики считают события — принято, распознано, отказано, — а состояние
|
||||||
очереди не считает ничто. Пустая очередь и очередь, где десять задач висят третий
|
очереди не считает ничто. Пустая очередь и очередь, где десять задач висят третий
|
||||||
@@ -34,4 +32,4 @@
|
|||||||
|
|
||||||
Правило оповещения на этой метрике заводит задача `owner-alerting` — здесь
|
Правило оповещения на этой метрике заводит задача `owner-alerting` — здесь
|
||||||
только число. Порог «сколько считается застреванием» здесь не решается: он
|
только число. Порог «сколько считается застреванием» здесь не решается: он
|
||||||
зависит от замеров цели `long-recordings`.
|
зависит от замеров `speechkit-limits` и `intake-limits-measure`.
|
||||||
|
|||||||
@@ -3,11 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Связывать чат с учётной записью не с чем, пока у записи нет владельца.
|
- **Категория:** Очередь — Связывать чат с учётной записью не с чем, пока у записи нет владельца.
|
||||||
- **Зачем:** Белый список сверяется с именем пользователя Telegram, которое владелец меняет в любой момент, а записи из бота ни с кем не связаны.
|
- **Зачем:** Белый список сверяется с именем пользователя Telegram, которое владелец меняет в любой момент, а записи из бота ни с кем не связаны.
|
||||||
- **Теги:** goal:multi-user
|
|
||||||
|
|
||||||
Двигает пункты 4 и 5 «Завершения» цели: записи из бота видны тому же человеку в
|
Записи из бота видны тому же человеку в приложении, а белый список перестаёт
|
||||||
приложении, а белый список перестаёт быть отдельным механизмом — право писать
|
быть отдельным механизмом — право писать боту выводится из учётной записи.
|
||||||
боту выводится из учётной записи.
|
|
||||||
|
|
||||||
Берётся после `record-ownership`: связывать не с чем, пока у записи нет
|
Берётся после `record-ownership`: связывать не с чем, пока у записи нет
|
||||||
владельца.
|
владельца.
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
# 🎯 Приложение показывает, о чём запись, не читая её целиком
|
|
||||||
|
|
||||||
- **Тип:** goal
|
|
||||||
- **Секция:** Запланировано — Очередь у цели появилась: три задачи подряд после настроек, и прибавка видна даже в боте.
|
|
||||||
- **Зачем:** Расшифровка часового разговора — это стена текста: найти в списке нужную запись и вспомнить, о чём она, сегодня нечем.
|
|
||||||
- **Теги:** decomposed
|
|
||||||
|
|
||||||
Текст записи выдаётся уровнями: сырая расшифровка, вычитанный литературный
|
|
||||||
текст, заголовок в одну строку, темы-теги и пересказ в два-три предложения.
|
|
||||||
Считает их внешний сервис с OpenAI-совместимым интерфейсом за шлюзом bifrost —
|
|
||||||
своей модели не держим, как не держим и модели распознавания.
|
|
||||||
|
|
||||||
Каждый уровень сверх сырого пользователь выключает у себя — это цель
|
|
||||||
[user-settings](user-settings.md).
|
|
||||||
|
|
||||||
**Это сдвиг границы паспорта, и сдвигали её дважды.** До 2026-08-10 «понимание
|
|
||||||
сказанного» было записано как то, чем проект не является: «мы отдаём текст, а не
|
|
||||||
выводы из него». 2026-08-11 к выводам добавился литературный текст — машинная
|
|
||||||
вычитка расшифровки, прежде запрещённая строкой про редактор.
|
|
||||||
|
|
||||||
От обоих запретов остались более узкие: ответов на вопросы по записи и поиска по
|
|
||||||
смыслу не делаем, правку текста руками человеку не даём.
|
|
||||||
|
|
||||||
## Завершение
|
|
||||||
|
|
||||||
1. У готовой записи есть заголовок в одну строку, и он виден в списке вместо
|
|
||||||
первых слов расшифровки.
|
|
||||||
2. Рядом с сырой расшифровкой лежит вычитанный текст: со знаками препинания, без
|
|
||||||
повторов и слов-паразитов.
|
|
||||||
3. У записи есть пересказ в два-три предложения, который читается быстрее самой
|
|
||||||
расшифровки.
|
|
||||||
4. У записи есть темы, и по ним список отбирается.
|
|
||||||
5. Отказ или молчание внешнего сервиса не роняют задачу: расшифровка доходит до
|
|
||||||
человека без выводов из неё.
|
|
||||||
6. Стоимость обращения к сервису видна метрикой: сколько записей обработано и
|
|
||||||
сколько это стоило по числу токенов.
|
|
||||||
@@ -3,10 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Первое, ради чего приложение открывают; требует каркаса и контракта, оба выше.
|
- **Категория:** Очередь — Первое, ради чего приложение открывают; требует каркаса и контракта, оба выше.
|
||||||
- **Зачем:** Первое, ради чего приложение открывают: отдать файл и увидеть, что с ним происходит.
|
- **Зачем:** Первое, ради чего приложение открывают: отдать файл и увидеть, что с ним происходит.
|
||||||
- **Теги:** goal:web-access
|
|
||||||
|
|
||||||
Двигает пункты 1 и 2 «Завершения» цели: экран принимает файл и заводит задачу, а
|
Экран принимает файл и заводит задачу, а её состояние обновляется само, пока
|
||||||
её состояние обновляется само, пока задача не дошла до `done` или `failed`.
|
задача не дошла до `done` или `failed`.
|
||||||
|
|
||||||
Берётся после `spa-skeleton` и `json-api-for-spa`.
|
Берётся после `spa-skeleton` и `json-api-for-spa`.
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,8 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Ход загрузки — доработка того же экрана.
|
- **Категория:** Очередь — Ход загрузки — доработка того же экрана.
|
||||||
- **Зачем:** Гигабайтный файл уходит на сервер молча: до ответа сервера экран не отличает идущую загрузку от зависшей.
|
- **Зачем:** Гигабайтный файл уходит на сервер молча: до ответа сервера экран не отличает идущую загрузку от зависшей.
|
||||||
- **Теги:** goal:upload-reliability
|
|
||||||
|
|
||||||
Двигает пункт 3 «Завершения» цели: ход загрузки виден числом, а не одним
|
Ход загрузки виден числом, а не одним ожиданием.
|
||||||
ожиданием.
|
|
||||||
|
|
||||||
Загрузка шестичасовой записи по сотовой сети идёт минутами. Пока сервер не
|
Загрузка шестичасовой записи по сотовой сети идёт минутами. Пока сервер не
|
||||||
ответил, экран показывает долю отправленного и позволяет её отменить.
|
ответил, экран показывает долю отправленного и позволяет её отменить.
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
# 🎯 Загрузка большого файла доходит до сервиса и не повторяется впустую
|
|
||||||
|
|
||||||
- **Тип:** goal
|
|
||||||
- **Секция:** Запланировано — Дедупликация и пачка файлов доводятся на готовом экране загрузки и экономят деньги с первого дня приложения.
|
|
||||||
- **Зачем:** Приём рассчитан на голосовое в пару мегабайт: обрыв на середине гигабайтного файла начинает загрузку заново, а один и тот же файл распознаётся повторно за наши деньги.
|
|
||||||
- **Теги:** decomposed
|
|
||||||
|
|
||||||
Человек отдаёт диктофонную запись или видео из семейного архива с телефона, по
|
|
||||||
сотовой сети, и загрузка либо доходит, либо честно говорит, что не дошла.
|
|
||||||
Отданное однажды второй раз не грузится и второй раз не распознаётся.
|
|
||||||
|
|
||||||
Дедупликация ищет совпадение **в пределах одного пользователя**: чужая
|
|
||||||
расшифровка не достаётся по совпадению хеш-суммы, даже когда файл тот же.
|
|
||||||
|
|
||||||
Загрузка частями за целью пока **не стоит**: сначала один запрос, а протокол
|
|
||||||
докачки разбирает разведка.
|
|
||||||
|
|
||||||
## Завершение
|
|
||||||
|
|
||||||
1. Файл, уже загруженный этим пользователем, узнаётся по хеш-сумме: сервис
|
|
||||||
возвращает прежнюю запись и не заводит вторую задачу.
|
|
||||||
2. До десяти файлов уходят одной загрузкой, и отказ одного не отменяет
|
|
||||||
остальные.
|
|
||||||
3. Ход загрузки виден на экране числом, а не одним ожиданием.
|
|
||||||
4. Оборванная загрузка не оставляет ни файла в хранилище, ни задачи в очереди.
|
|
||||||
@@ -3,10 +3,9 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Учёт по пользователям требует владельца записи и метрик расхода — оба выше.
|
- **Категория:** Очередь — Учёт по пользователям требует владельца записи и метрик расхода — оба выше.
|
||||||
- **Зачем:** Ни объём, ни длительность, ни обращения к платным сервисам никуда не записываются: восстановить расход задним числом не из чего.
|
- **Зачем:** Ни объём, ни длительность, ни обращения к платным сервисам никуда не записываются: восстановить расход задним числом не из чего.
|
||||||
- **Теги:** goal:usage-stats
|
|
||||||
|
|
||||||
Двигает пункты 1, 2 и 4 «Завершения» цели: по каждому пользователю копятся
|
По каждому пользователю копятся объём, минуты и расход на внешние сервисы, и
|
||||||
объём, минуты и расход на внешние сервисы, и повтор шага не удваивает счёт.
|
повтор шага не удваивает счёт.
|
||||||
|
|
||||||
Учёт ведётся записями о потреблении, а не счётчиком в строке пользователя:
|
Учёт ведётся записями о потреблении, а не счётчиком в строке пользователя:
|
||||||
счётчик, увеличенный дважды при повторе шага, обратно не отматывается.
|
счётчик, увеличенный дважды при повторе шага, обратно не отматывается.
|
||||||
@@ -38,10 +37,10 @@
|
|||||||
## Рамки
|
## Рамки
|
||||||
|
|
||||||
Снаружи эта задача не видна сама по себе: ни экрана, ни эндпоинта она не
|
Снаружи эта задача не видна сама по себе: ни экрана, ни эндпоинта она не
|
||||||
заводит — их строит `admin-stats-screen`. Тип оставлен `feature` сознательно,
|
заводит — их строит `admin-stats-screen`. Тип оставлен `feature` сознательно:
|
||||||
как шаг цели.
|
это первая половина работы, которую вторая делает видимой.
|
||||||
|
|
||||||
Потолков и отказов по исчерпании квоты не заводим — цель показывает, а не
|
Потолков и отказов по исчерпании квоты не заводим — учёт показывает, а не
|
||||||
ограничивает. Пересчёт расхода в деньги здесь не делается: копятся минуты и
|
ограничивает. Пересчёт расхода в деньги здесь не делается: копятся минуты и
|
||||||
токены, цена прайс-листа живёт вне сервиса. Данные о потреблении содержат
|
токены, цена прайс-листа живёт вне сервиса. Данные о потреблении содержат
|
||||||
идентификаторы, но не текст записи и не имя файла (инвариант приватности).
|
идентификаторы, но не текст записи и не имя файла (инвариант приватности).
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
# 🎯 Владелец видит, кто сколько загрузил и во что это обошлось
|
|
||||||
|
|
||||||
- **Тип:** goal
|
|
||||||
- **Секция:** Сопровождение — Учёт по пользователям требует владельца записи и метрик расхода — оба появляются раньше.
|
|
||||||
- **Зачем:** Распознавание и языковая модель оплачиваются по факту, а счёт приходит одной суммой: кто её набрал, из сервиса не выясняется.
|
|
||||||
- **Теги:** decomposed
|
|
||||||
|
|
||||||
Владелец открывает страницу и видит по каждому пользователю объём загруженного,
|
|
||||||
длительность записей в минутах и расход на внешние сервисы. Приглашая человека,
|
|
||||||
он понимает, во что это обойдётся.
|
|
||||||
|
|
||||||
Цель **показывает, но не ограничивает**: потолков и отказов по исчерпании квоты
|
|
||||||
здесь нет — перебравшего останавливает разговор или отзыв доступа в Authelia.
|
|
||||||
|
|
||||||
Секция — сопровождение, потому что наблюдает владелец сервиса, а не его
|
|
||||||
пользователь.
|
|
||||||
|
|
||||||
## Завершение
|
|
||||||
|
|
||||||
1. По каждому пользователю видны объём загруженных файлов и длительность записей
|
|
||||||
в минутах, накопительно и за период.
|
|
||||||
2. Видно, во что обошлись внешние сервисы: минуты распознавания и число токенов
|
|
||||||
языковой модели.
|
|
||||||
3. Страница открывается только владельцу, обычному пользователю она недоступна.
|
|
||||||
4. Учёт переживает перезапуск и не считает одну запись дважды при повторе шага.
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# 🎯 Пользователь настраивает, что сервис делает с его записями
|
|
||||||
|
|
||||||
- **Тип:** goal
|
|
||||||
- **Секция:** Запланировано — Дом настроек нужен раньше уровней текста и раньше выбора канала уведомлений.
|
|
||||||
- **Зачем:** Уровни текста считает платная модель, а уведомления приходят одним общим способом: отказаться от лишнего и выбрать свой канал пользователю нечем.
|
|
||||||
- **Теги:** decomposed
|
|
||||||
|
|
||||||
У каждого своя мера: одному нужна только сырая расшифровка, другому — все пять
|
|
||||||
уровней текста. Настройки принадлежат человеку, а не общему конфигу сервиса, и
|
|
||||||
переживают выход и повторный вход.
|
|
||||||
|
|
||||||
Выключенный уровень **не считается вовсе**: настройка экономит деньги, а не
|
|
||||||
прячет готовое.
|
|
||||||
|
|
||||||
## Завершение
|
|
||||||
|
|
||||||
1. Каждый уровень текста сверх сырого включается и выключается отдельно, и
|
|
||||||
выключенный не запрашивается у языковой модели.
|
|
||||||
2. Канал уведомлений выбирает сам пользователь, а не общий конфиг.
|
|
||||||
3. Настройки переживают выход и повторный вход, и у каждого пользователя свои.
|
|
||||||
4. Запись, заведённая до правки настроек, обрабатывается по той настройке,
|
|
||||||
которая действовала на приёме.
|
|
||||||
@@ -3,11 +3,10 @@
|
|||||||
- **Тип:** feature
|
- **Тип:** feature
|
||||||
- **Категория:** Очередь — Приём видео и отказ неизвестному формату ставятся на замеренный перечень.
|
- **Категория:** Очередь — Приём видео и отказ неизвестному формату ставятся на замеренный перечень.
|
||||||
- **Зачем:** Запись семейного архива приходит видеофайлом, а приём смотрит на аудио: человеку приходится доставать дорожку самому.
|
- **Зачем:** Запись семейного архива приходит видеофайлом, а приём смотрит на аудио: человеку приходится доставать дорожку самому.
|
||||||
- **Теги:** goal:any-audio-source
|
|
||||||
|
|
||||||
Двигает пункты 2 и 3 «Завершения» цели: видеофайл принимается и из него берётся
|
Видеофайл принимается и из него берётся звуковая дорожка, а формат, который
|
||||||
звуковая дорожка, а формат, который принять нельзя, отклоняется на приёме
|
принять нельзя, отклоняется на приёме понятным текстом, а не отказом на
|
||||||
понятным текстом, а не отказом на конвертации через минуту.
|
конвертации через минуту.
|
||||||
|
|
||||||
Берётся после `audio-format-coverage-measure`: чем отклонять неизвестное, пока
|
Берётся после `audio-format-coverage-measure`: чем отклонять неизвестное, пока
|
||||||
неизвестно, что конвейер берёт.
|
неизвестно, что конвейер берёт.
|
||||||
@@ -38,5 +37,5 @@
|
|||||||
## Рамки
|
## Рамки
|
||||||
|
|
||||||
Видео с несколькими звуковыми дорожками берём первую, выбор дорожки человеком за
|
Видео с несколькими звуковыми дорожками берём первую, выбор дорожки человеком за
|
||||||
задачей не стоит. Потолок длины остаётся тем, что назначит цель
|
задачей не стоит. Потолок длины остаётся тем, что назначат замеры
|
||||||
`long-recordings`.
|
`speechkit-limits` и `intake-limits-measure`.
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
# 🎯 Записи загружаются и читаются в приложении, которое ставится на телефон
|
|
||||||
|
|
||||||
- **Тип:** goal
|
|
||||||
- **Секция:** Запланировано — Экраны приложения нужны раньше настроек, удаления, уровней текста и статистики: всем им негде показаться.
|
|
||||||
- **Зачем:** Сегодня записи принимает только бот и голый HTTP API без интерфейса: отдать сервис человеку, у которого нет Telegram, нечем.
|
|
||||||
- **Теги:** decomposed
|
|
||||||
|
|
||||||
Приложение получает поверхность, на которой запись загружают и забирают текст,
|
|
||||||
не открывая Telegram и не вызывая API руками. Ставится оно на телефон и
|
|
||||||
открывается с ярлыка, как обычное приложение. Конвейер обработки при этом
|
|
||||||
остаётся прежним — меняется вход и способ показать результат.
|
|
||||||
|
|
||||||
**Приложение — основной вход сервиса**, бот остаётся дополнением для голосовых
|
|
||||||
сообщений. Экраны рисуются сначала под телефон, потом под широкий экран.
|
|
||||||
|
|
||||||
Границы взяты уже: запись звука в самом приложении и работа без сети за целью
|
|
||||||
**не стоят** — файл выбирают в системном диалоге, а без сети приложение
|
|
||||||
показывает, что связи нет.
|
|
||||||
|
|
||||||
## Завершение
|
|
||||||
|
|
||||||
1. Экран принимает файл и заводит задачу — ту же, что заводит бот.
|
|
||||||
2. Состояние задачи видно на экране и обновляется само, пока задача не дошла до
|
|
||||||
`done` или `failed`; отказ показывается человекочитаемым текстом.
|
|
||||||
3. Готовый текст читается и копируется с экрана целиком, без деления на части.
|
|
||||||
4. Список своих записей открывается и листается.
|
|
||||||
5. Приложение ставится на телефон из браузера и запускается с ярлыка на
|
|
||||||
отдельном экране, без адресной строки.
|
|
||||||
6. Открытое без сети, приложение показывает это состоянием, а не пустой
|
|
||||||
страницей и не ошибкой браузера.
|
|
||||||
7. Загруженная запись слушается на своём экране, а не скачивается файлом.
|
|
||||||
Reference in New Issue
Block a user