## 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` в плане стройки стоит ниже, а по «Рамкам» постановки предшествует — без предела по времени у шага срок захвата не может его превысить.