docs: ссылки на упразднённый роадмап переадресованы, у восьми фактов назван дом
- ссылки на tasks/ROADMAP.md переведены на BACKLOG.md и на openspec/specs, упоминания целей — на задачи, которые эту работу делают; - судьи документации нашли восемь расхождений: Purpose спеки pipeline объявлял неописанным то, что уже нормирован пятью требованиями, вид времени в конвенции спорил со схемой, а квоты, шесть часов и отказ от Web Push жили сразу в двух документах без ссылки друг на друга; - два числа получили провенанс: 259 200 запросов в сутки и потолок в шесть часов теперь ведут к записке разведки, а не читаются как замер.
This commit is contained in:
+10
-6
@@ -5,7 +5,7 @@
|
||||
помечены маркером долга и переезжают туда первой же задачей, которая их трогает.
|
||||
|
||||
Документ описывает **сегодняшнее** устройство. Куда проект идёт — в
|
||||
[passport.md](passport.md) и в [tasks/ROADMAP.md](../tasks/ROADMAP.md); что из
|
||||
[passport.md](passport.md) и в [tasks/BACKLOG.md](../tasks/BACKLOG.md); что из
|
||||
этого ещё не решено — в разделе «Открытые вопросы».
|
||||
|
||||
Заведены пять capability. Четыре первые нормируют **поведение сервиса** для его
|
||||
@@ -81,7 +81,7 @@
|
||||
| Отправитель Telegram | `internal/adapter/telegram` | Отправка текста, деление длинного по словам |
|
||||
| Репозитории | `internal/adapter/repo/pocketbase` | Задачи и файлы коллекциями хранилища; захват — сырым запросом |
|
||||
| Шаги схемы | `internal/adapter/repo/pocketbase/migrations` | Файл на шаг, имя файла — имя шага; там же имена коллекций |
|
||||
| Панель владельца | `internal/adapter/repo/pocketbase`, `panel.go` | Правка задачи в панели проходит те же правила перехода, что и правка из кода |
|
||||
| Панель владельца | `internal/adapter/repo/pocketbase`, `panel.go` | Панель хранилища; правила правки задачи нормирует [storage](../openspec/specs/storage/spec.md), «Владелец видит записи в панели» |
|
||||
|
||||
<!-- канон: поведение → openspec/specs/pipeline; ещё НЕ переехало: цепочка переходов состояний -->
|
||||
|
||||
@@ -192,8 +192,11 @@
|
||||
текст расшифровки начинает уходить на сторону — сдвиг периметра
|
||||
[security.md](security.md).
|
||||
- **Долгие записи.** Потолок сегодня неизвестен и не замерялся: 20 МиБ на приём
|
||||
из Telegram — точно, ограничения `deferred-general` по длине — нет. Расчётный
|
||||
потолок проекта — шесть часов, и он взят с запасом, а не замером.
|
||||
из Telegram — точно, ограничения `deferred-general` по длине — нет. Расчётные
|
||||
шесть часов нормирует [storage](../openspec/specs/storage/spec.md), «Файл
|
||||
записи живёт в хранилище»; откуда взято число —
|
||||
[research/pocketbase-defaults.md](research/pocketbase-defaults.md), «Чего эта
|
||||
записка не узнала».
|
||||
- **Приём большого файла.** Форма читается целиком, предел памяти под multipart
|
||||
задан числом в [database.md](database.md), «Настройки с числовым значением»;
|
||||
обрыв начинает загрузку заново.
|
||||
@@ -218,8 +221,9 @@
|
||||
- **Очередь.** Модель очереди сделана задачей `pocketbase-storage` 2026-08-12
|
||||
([ADR](adr/ADR-2026-08-11-queue-as-pocketbase-collection.md)) и нормирована
|
||||
спекой `pipeline`. Не решено, отказываться ли от холостого опроса: три воркера
|
||||
дают 259 200 запросов в сутки при нагрузке в единицы записей в день, и во что
|
||||
это обходится, никто не мерил.
|
||||
дают 259 200 запросов к базе в сутки — расчёт из паузы воркера, а не замер
|
||||
([research/job-queue.md](research/job-queue.md), «Как снималось»), — при
|
||||
нагрузке в единицы записей в день, и во что это обходится, никто не мерил.
|
||||
- **Наблюдаемость.** `/metrics` остаётся и развивается. Чем — дописывать
|
||||
счётчики через `client_golang` или перейти на OpenTelemetry с трассировкой —
|
||||
решает разведка `opentelemetry-fit`. Коллектор был бы процессом, которого в
|
||||
|
||||
@@ -58,6 +58,11 @@
|
||||
лексикографическую сортировку TEXT, то есть хронологию (`ORDER BY created_at`).
|
||||
Единая точка генерации — приложение, а не умолчание в схеме: так забытая
|
||||
вставка падает громко. Измерение длительности — не метка времени.
|
||||
*Расхождение:* вид времени задаёт хранилище — `2006-01-02 15:04:05.000Z`,
|
||||
пробел вместо `T` и доли секунды ([../database.md](../database.md), «Время»).
|
||||
Правило RFC 3339 действует на то, что пишем мы сами мимо хранилища; вид
|
||||
хранилища не меняем — сравнение строк в сыром запросе побайтово, и
|
||||
разошедшийся вид молча обращает условие срока захвата в константу.
|
||||
- Миграции — шаги PocketBase на Go
|
||||
(`internal/adapter/repo/pocketbase/migrations`, файл на шаг): коллекции и их
|
||||
поля заводятся кодом. При изменении структуры обновляем схему
|
||||
|
||||
@@ -33,11 +33,13 @@
|
||||
- **Шрифты и скрипты — со своего хоста**, без внешних. Внешних ресурсов времени
|
||||
выполнения нет.
|
||||
- **Офлайн-чтения расшифровок и очереди отправки без сети не делаем** — граница
|
||||
цели [web-access](../../tasks/items/web-access.md). Без сети приложение
|
||||
показывает состояние, а не пустой экран.
|
||||
- **Web Push не делаем**: уведомления идут через apprise и ntfy, цель
|
||||
[ready-notification](../../tasks/items/ready-notification.md).
|
||||
- **Записи звука в приложении не делаем** — файл выбирают системным диалогом.
|
||||
из [паспорта](../passport.md). Без сети приложение показывает состояние, а не
|
||||
пустой экран.
|
||||
- **Web Push не делаем**: уведомления идут через apprise и ntfy — решение живёт
|
||||
в [architecture.md](../architecture.md), «Уведомления», делает его
|
||||
[ntfy-delivery](../../tasks/items/ntfy-delivery.md).
|
||||
- **Записи звука в приложении не делаем** — граница из
|
||||
[паспорта](../passport.md), «Диктофон»; файл выбирают системным диалогом.
|
||||
|
||||
## Фреймворк и сборка
|
||||
|
||||
@@ -55,9 +57,12 @@
|
||||
|
||||
## Маршруты
|
||||
|
||||
- **Четыре экрана, одна таблица маршрутов** через `createRouter`. Маршруты по
|
||||
файлам не включаем: сборочная надстройка роутера пятой версии стоит 34 пакета
|
||||
в установке и на четырёх маршрутах не окупается.
|
||||
- **Одна таблица маршрутов** через `createRouter`. Маршруты по файлам не
|
||||
включаем: сборочная надстройка роутера пятой версии стоит 34 пакета в
|
||||
установке и на нашем числе маршрутов не окупается
|
||||
([research/spa-framework.md](../research/spa-framework.md), «Vue»). Сколько
|
||||
экранов и какие — не здесь: состав нормирует спека приложения, а до неё его
|
||||
держит [spa-skeleton](../../tasks/items/spa-skeleton.md).
|
||||
- **Адреса обычные, а не после решётки** (`createWebHistory`). Отсюда требование
|
||||
к серверу: неизвестный путь **вне** `/api/` отдаёт `index.html`, а не `404`;
|
||||
пути внутри `/api/` в приложение не проваливаются никогда.
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ CGO сборке не нужен.
|
||||
|
||||
Каталог у шагов свой, а не файл внутри пакета репозитория, и причина внешняя:
|
||||
шаг гейта сверяет изменённые шаги схемы с правкой этого документа по **префиксу
|
||||
пути** (`.av-dev.toml`, ключ `migrations` секции `[docs]`), а префикс наводится только на
|
||||
каталог. Имена коллекций живут там же, рядом с шагом, который их заводит; пакет
|
||||
пути**, а префикс наводится только на каталог. Где этот префикс задан —
|
||||
[conventions/go-linters.md](conventions/go-linters.md), «Механизировано». Имена коллекций живут там же, рядом с шагом, который их заводит; пакет
|
||||
репозитория берёт их оттуда.
|
||||
|
||||
**Идентификаторы** записей выдаёт хранилище — 15 знаков собственного алфавита.
|
||||
|
||||
+6
-6
@@ -1,7 +1,7 @@
|
||||
# Паспорт проекта
|
||||
|
||||
Зачем это и для кого. [architecture.md](architecture.md) отвечает «как
|
||||
устроено», [tasks/ROADMAP.md](../tasks/ROADMAP.md) — «в каком порядке», паспорт —
|
||||
устроено», [tasks/BACKLOG.md](../tasks/BACKLOG.md) — «в каком порядке», паспорт —
|
||||
«зачем и для кого».
|
||||
|
||||
## Цель
|
||||
@@ -32,8 +32,8 @@
|
||||
|
||||
- запись любого распространённого формата принимается без предварительной
|
||||
подготовки, включая дорожку из видео;
|
||||
- запись длиной до шести часов доходит до текста, а не прерывается ошибкой при
|
||||
достижении предела;
|
||||
- запись расчётного потолка — шести часов — доходит до текста, а не прерывается
|
||||
ошибкой при достижении предела (норма — `openspec/specs/storage`);
|
||||
- сервисом пользуются несколько человек, и записи одного не видны другому;
|
||||
- текст доступен там же, где загружали, — в приложении и в Telegram. Человек
|
||||
узнаёт о его готовности, не держа приложение открытым;
|
||||
@@ -54,7 +54,8 @@
|
||||
- **Разговор о записи.** Ответы на вопросы по содержанию и поиск по смыслу — за
|
||||
границей. Заголовок, пересказ и темы **внутри** границы: она сдвинута
|
||||
2026-08-10, и до того запись читалась «мы отдаём текст, а не выводы из него».
|
||||
Направление — цель [text-insights](../tasks/items/text-insights.md).
|
||||
Считать уровни текста берётся задача
|
||||
[llm-insights-adapter](../tasks/items/llm-insights-adapter.md).
|
||||
- **Собственные модели.** Не обучаем и не держим у себя ни модель распознавания,
|
||||
ни языковую модель: и речь, и выводы из текста считает внешний сервис.
|
||||
- **Управление учётными записями.** Пользователей заводит и проверяет внешний
|
||||
@@ -65,8 +66,7 @@
|
||||
- **Живая расшифровка.** Работаем с готовой записью, поток в реальном времени не
|
||||
обрабатываем.
|
||||
- **Диктофон.** Запись звука делает телефон, а приложение принимает готовый
|
||||
файл. Своей записи и работы без сети не делаем — граница цели
|
||||
[web-access](../tasks/items/web-access.md).
|
||||
файл. Своей записи и работы без сети не делаем.
|
||||
- **Файловое хранилище общего назначения.** Храним аудио и видео, отданные ради
|
||||
речи в них. Складом произвольных файлов и папками сервис не становится. Общего
|
||||
доступа к чужим записям целью тоже нет — но **сегодня он есть**: владельца у
|
||||
|
||||
@@ -25,7 +25,8 @@ Nuxt, Next — не рассматривали: конвенция
|
||||
правил, и в сборке он у всех троих совпал до байта (883 Б), что и подтверждает
|
||||
одинаковость экрана.
|
||||
- **Четыре маршрута** — тот же экран плюс три заглушки и переходы между ними:
|
||||
столько экранов у цели [web-access](../../tasks/items/web-access.md). Роутеры
|
||||
столько экранов заводит задача
|
||||
[spa-skeleton](../../tasks/items/spa-skeleton.md). Роутеры
|
||||
`svelte-spa-router` 5.1.1, `vue-router` 5.2.0 и 4.6.4, `react-router` 8.3.0.
|
||||
- **Размеры** — `stat -c%s` и `gzip -9c | wc -c` по файлам `dist/`. Числа Vite в
|
||||
своём выводе печатает по другому уровню сжатия, поэтому в таблицах ниже стоят
|
||||
|
||||
+8
-4
@@ -151,8 +151,10 @@
|
||||
ещё — самый большой.
|
||||
- `conventions`: новая колонка правится во всех четырёх местах репозитория
|
||||
(CLAUDE.md, «Инварианты»).
|
||||
- `autotests`: покрыт ли изменённый шаг конвейера хоть одним тестом — сегодня
|
||||
тестов два файла, и оба мимо конвейера.
|
||||
- `autotests`: покрыт ли изменённый шаг конвейера хоть одним **проходящим**
|
||||
тестом. Что уже закрыто проверками, видно по журналу дефектов ниже и по
|
||||
[conventions/go-linters.md](conventions/go-linters.md), «Механизировано»;
|
||||
числа файлов здесь не называем — оно протухает с каждой задачей.
|
||||
- `autotests`: судит ли проверка ответа по готовому ответу, а не по изменяемому
|
||||
состоянию обработчика — **только там, где ответ идёт мимо recorder**, через
|
||||
свой `http.ResponseWriter`. Обращение к живой карте recorder'а с
|
||||
@@ -188,8 +190,10 @@
|
||||
|
||||
- вход через OIDC и разграничение доступа: как связаны пользователь Telegram и
|
||||
пользователь приложения, до начала работы назвать нельзя;
|
||||
- всё, что делается на выбранном фреймворке впервые: форма решения нащупывается
|
||||
по ходу, пока конвенция веб-UI пуста;
|
||||
- всё, что делается на выбранном фреймворке впервые: правила
|
||||
[conventions/web-ui.md](conventions/web-ui.md) выведены из выбора и из замера
|
||||
на пробном экране, а не из написанного кода, и первая же задача проверяет их
|
||||
собой — форма решения нащупывается по ходу;
|
||||
- установка на телефон: service worker перехватывает запросы, и что он кэширует,
|
||||
до работы назвать нельзя;
|
||||
- работа с записями в несколько часов: потолки внешних сервисов не замерены,
|
||||
|
||||
+5
-4
@@ -298,10 +298,11 @@ Telegram отправителю.
|
||||
не замер: распределения длин у сервиса нет, а самая длинная проверенная запись
|
||||
— 9,6 МБ ([research/pocketbase-defaults.md](research/pocketbase-defaults.md)).
|
||||
Потолок длины стоит открытым вопросом `architecture.md`, «Долгие записи». Квот
|
||||
нет и не будет: решено считать расход и показывать его владельцу, а не
|
||||
отказывать (цель `usage-stats`). Перебравшего останавливает разговор или отзыв доступа в
|
||||
Authelia. Рост каталога данных при этом ничем не наблюдается —
|
||||
открытый вопрос `architecture.md`.
|
||||
нет — это граница домена, [passport.md](passport.md), «Учёт денег»; расход
|
||||
считают `usage-accounting` и `admin-stats-screen`. Для модели угроз отсюда
|
||||
следует одно: ни числом запросов, ни размером записи вошедший не ограничен, и
|
||||
защищаться от исчерпания диска мы не пытаемся. Рост каталога данных при этом
|
||||
ничем не наблюдается — открытый вопрос `architecture.md`.
|
||||
- **Перерасход денег на внешних сервисах.** Распознавание и языковая модель
|
||||
оплачиваются по факту; потолка на пользователя нет по тому же решению.
|
||||
- **Стойкость `ffmpeg` к вредоносному входу.** Разбор чужого формата отдан
|
||||
|
||||
@@ -32,7 +32,8 @@ context: |
|
||||
- docs/architecture.md — устройство; docs/security.md — периметр;
|
||||
docs/database.md — схема и настройки с числами; docs/adr/ — почему решено
|
||||
так; docs/research/ — что уже измерено;
|
||||
- tasks/ROADMAP.md — что приложение уже умеет и чего ещё не умеет.
|
||||
- openspec/specs/ — что приложение уже умеет; tasks/BACKLOG.md — что осталось
|
||||
и в каком порядке это берут.
|
||||
Пересказа этих документов здесь нет намеренно: второй дом факта расходится с
|
||||
первым молча, и заметно это становится в предложении, которое уже написано.
|
||||
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
Конвейер расшифровки: как задача движется по состояниям, что делает воркер,
|
||||
когда работы нет, и что считается отказом шага.
|
||||
|
||||
Описан пока **только пустой прогон воркера** — тот, что нормируют проверки
|
||||
пакета `internal/controller/worker` и перевод признака в `internal/service`.
|
||||
Сознательно не описаны переходы состояний и цепочка `created → converted →
|
||||
transcribe → done | failed`, захват задачи и срок его протухания, отмена
|
||||
контекста посреди шага, освобождение ресурсов внешних клиентов. Это не значит,
|
||||
что такого поведения нет: оно живёт в коде, а требования на него не написаны,
|
||||
потому что требование без проверки — предположение, а не норма. Первая задача,
|
||||
которая трогает любое из перечисленного, дописывает его сюда.
|
||||
Описаны пустой прогон воркера, неделимость захвата и срок его протухания, число
|
||||
попыток и состояние «мертва», нарастающая пауза перед повтором и условие записи
|
||||
результата держателем захвата. Сознательно не описаны: цепочка переходов
|
||||
`created → converted → transcribe → done | failed`, отмена контекста посреди
|
||||
шага и освобождение ресурсов внешних клиентов. Это не значит, что такого
|
||||
поведения нет: оно живёт в коде, а требования на него не написаны, потому что
|
||||
требование без проверки — предположение, а не норма. Первая задача, которая
|
||||
трогает любое из перечисленного, дописывает его сюда.
|
||||
## Requirements
|
||||
### Requirement: Пустой прогон воркера — не отказ
|
||||
|
||||
|
||||
Reference in New Issue
Block a user