внутренняя модель перестроена вокруг аудиозаписи
- audiorecords вместо transcribe_jobs: приложения (texts, structures, recognitions, record_events, topics) живут своими коллекциями, ссылки на исходник и на приведённую копию перестали переставляться - рубеж называет достигнутое, отказ стал признаком остановки с причиной, а сторожей стало двое: число отказов и время в рубеже - воркеры потеряли специализацию, их число задаётся [pipeline] workers, шаг выбирается по рубежу, а захват отдаёт идентификатор и признак захвата
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
## Why
|
||||
|
||||
Сервис объявлен архивом: записи и расшифровки лежат бессрочно, к ним
|
||||
возвращаются через месяцы, а поверх них строятся список, темы, уровни текста и
|
||||
учёт расхода. Держать всё это негде — центральной сущности «аудиозапись» в
|
||||
сервисе нет вовсе: есть задача конвейера, у которой поля захвата лежат в одной
|
||||
строке с расшифровкой, указатель на файл переставляет каждый шаг, а отказ стирает
|
||||
достигнутый рубеж и делает продолжение с места остановки невозможным.
|
||||
|
||||
Всякая задача, взятая раньше этой, будет переписана вместе с моделью — потому
|
||||
владелец 2026-08-14 поставил её первой в план стройки.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Центральная сущность — аудиозапись.** Домен записи (владелец, заголовок,
|
||||
краткое описание, рубеж, ссылки на приложения) отделяется от того, что нужно
|
||||
только конвейеру, и от того, что принадлежит провайдеру распознавания.
|
||||
- **Приложения к записи живут отдельными строками.** Файлы, тексты, структура
|
||||
реплик, темы, журнал событий и попытка распознавания перестают быть колонками
|
||||
одной строки и адресуются ссылками с записи.
|
||||
- **Ссылки на файлы перестают переставляться.** У записи две отдельные ссылки —
|
||||
на исходник и на приведённую копию, — и обе живут до конца. Сегодня их одна, и
|
||||
прошедшая конвейер запись ведёт на объект во внешнем хранилище: послушать
|
||||
загруженное нечем.
|
||||
- **Копия во внешнем хранилище перестаёт быть файлом записи.** Она существует
|
||||
только потому, что распознаватель читает аудио по адресу, и переезжает в
|
||||
строку о попытке распознавания вместе с идентификатором операции.
|
||||
- **Сырой ответ распознавателя сохраняется целиком** — вложением, а не колонкой.
|
||||
Результат операции у провайдера не переспрашивается, а связь реплики с
|
||||
говорящим сервис строить пока не умеет: когда научится, архив пересчитается из
|
||||
сохранённого без единого рубля.
|
||||
- **Состояние называет достигнутое, а не предстоящее.** Цепочка рубежей:
|
||||
`uploaded → normalized → submitted → transcribed → done`. **BREAKING**: перечень
|
||||
состояний в ответе о записи меняется целиком — публичный контракт HTTP API
|
||||
объявлен проектом необратимым.
|
||||
- **Остановка становится признаком, а не состоянием.** Прежние `failed` и `dead`
|
||||
схлопываются в признак остановки с причиной; достигнутый рубеж при этом
|
||||
сохраняется, и снятие признака продолжает работу с места остановки, а не с
|
||||
начала. Снимает признак владелец панели — поверхности для владельца записи у
|
||||
сервиса пока нет, её заводит задача с экранами.
|
||||
- **Сторожей становится двое.** Число отказов ограничивает повторы внутри шага,
|
||||
время в рубеже — застревание. Сегодня обе роли навешаны на счётчик попыток, и
|
||||
он не справляется ни с одной: операция, зависшая у провайдера, опрашивается
|
||||
вечно.
|
||||
- **Предел времени в рубеже** — два числа: час на свою работу, сутки на чужую.
|
||||
Достигнут предел — запись останавливается с причиной «застряла».
|
||||
- **Переход и откладывание разводятся.** Шаг опроса перестаёт изображать переход
|
||||
в то же самое состояние: откладывание ставит паузу и снимает захват, а рубежа и
|
||||
времени входа в него не трогает.
|
||||
- **Шаг с внешней оплатой проверяет сделанное** прежде, чем платить второй раз.
|
||||
- **Воркеры теряют специализацию**, а их число задаётся настройкой; ноль —
|
||||
законное значение: записи принимаются и не двигаются.
|
||||
- **Переноса данных нет.** Сервис на сервере остановлен, прежние записи удалены
|
||||
решением владельца 2026-08-14, и новая модель заводится с чистого листа.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `recognition`: попытка распознавания у внешнего провайдера — что о ней
|
||||
хранится, почему сырой ответ сохраняется целиком, как из сохранённого строится
|
||||
структура реплик и почему разбор формата провайдера не доходит до конвейера.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `pipeline`: цепочка рубежей и смысл состояния; остановка признаком вместо
|
||||
состояний отказа и смерти; два сторожа вместо одного; предел времени в рубеже;
|
||||
разведение перехода и откладывания; необязательный шаг, чей отказ не роняет
|
||||
запись; захват, возвращающий один идентификатор; воркер без специализации и его
|
||||
число настройкой.
|
||||
- `storage`: аудиозапись центральной сущностью и её приложения отдельными
|
||||
коллекциями; две отдельные ссылки на файлы вместо одной переставляемой;
|
||||
словарь тем на каждого владельца; журнал событий записи; перенос живых записей
|
||||
шагом схемы.
|
||||
- `intake`: перечень состояний в ответе о приёме и об опросе готовности.
|
||||
|
||||
## Impact
|
||||
|
||||
- **Схема хранилища**: новый шаг — коллекции `audio_records`, `texts`,
|
||||
`structures`, `recognitions`, `record_events`, `topics`; прежняя
|
||||
`transcribe_jobs` уходит; правка `files`. Применённые шаги не переписываются.
|
||||
- **Публичный контракт HTTP API**: значения поля состояния. Необратимо.
|
||||
- `internal/entity` — сущность записи, перечень рубежей, переходы, откладывание,
|
||||
остановка признаком.
|
||||
- `internal/contract` — распознаватель отдаёт доменный результат вместо строки,
|
||||
заливка и отправка разделены; контракты репозиториев записи, файлов, текстов,
|
||||
структуры, попыток распознавания и журнала.
|
||||
- `internal/adapter/recognizer/yandex` — разбор потока результата в реплики,
|
||||
раздельные заливка и отправка, отдача сырых байтов на хранение.
|
||||
- `internal/adapter/repo/pocketbase` — запрос захвата, отображение записи,
|
||||
правила панели.
|
||||
- `internal/service` — шаги, таблица выбора следующего шага по рубежу, остановка
|
||||
признаком.
|
||||
- `internal/controller/worker` и `main.go` — пул вместо трёх именованных
|
||||
воркеров.
|
||||
- `internal/config` и `config.example.toml` — число воркеров, срок захвата по
|
||||
шагу, два предела времени в рубеже.
|
||||
- `docs/architecture.md`, `docs/database.md`, инварианты `CLAUDE.md` о колонках
|
||||
очереди и о держателе захвата.
|
||||
- **Предшествующая задача**: `external-call-timeouts` в плане стройки стоит
|
||||
ниже, а по «Рамкам» постановки предшествует — без предела по времени у шага
|
||||
срок захвата не может его превысить.
|
||||
Reference in New Issue
Block a user