## ADDED Requirements

### Requirement: Пустой результат не кладётся поверх сохранённого

Хранилище SHALL не заменять сохранённое содержимое приложения записи — текст и
структуру реплик — пустым. Замена пустым MUST оставлять прежнее значение и
считаться сделанной работой, а не отказом.

Требование стоит на повторном опросе одной и той же операции распознавания.
Повтор — обычное дело: держатель захвата умер, сохранение рубежа отказало,
человек снял признак остановки в панели. Провайдер при этом вправе ответить
пустым потоком, отказом это не считается, и безусловная замена стирала бы
расшифровку живого человека — без следа и без возврата, потому что сервис
объявлен архивом и удаления по требованию не знает.

Та же защита MUST стоять у сырого ответа провайдера: разное правило у двух
хранителей одного результата читается как недосмотр, и один из них молча теряет
то, ради чего второй заведён.

Норма записана со стороны **хранилища**, а не шага: шагов, кладущих текст,
больше одного, и правило, записанное у одного из них, у остальных читалось бы
как снятое.

#### Scenario: Пустой второй ответ не стирает расшифровку

- **GIVEN** расшифровка записи сохранена
- **WHEN** ту же операцию опрашивают снова, и провайдер отвечает пустым
- **THEN** сохранённая расшифровка остаётся прежней
- **AND** шаг завершается без отказа

## MODIFIED Requirements

### Requirement: Сервис поднимается на чистом каталоге данных

Сервис SHALL приводить хранилище в рабочий вид сам: на пустом каталоге данных он
MUST завести свою схему и принимать записи своим входом — приёмом по HTTP — без
единого ручного шага до первого запуска.

Прежние данные не переносятся. Каталог, оставшийся от прежней раскладки, MUST не
читаться и не считаться источником: сервис начинает с чистого листа, и это
решение задачи, а не следствие отказа.

Схема MUST заводиться версионированными шагами, а применённый шаг MUST не
переписываться — только новым шагом. Иначе повторный запуск на уже заведённом
каталоге разошёлся бы с первым молча.

Каталог данных у сервиса MUST быть один: база и файлы записей лежат под ним
вместе, и второго пути к ним не заводится.

#### Scenario: Первый запуск на пустом каталоге

- **GIVEN** каталог данных пуст
- **WHEN** сервис запускается
- **THEN** он заводит своё хранилище и продолжает работу
- **AND** принятая следом запись доходит до состояния `done`

#### Scenario: Повторный запуск на заведённом каталоге

- **GIVEN** сервис уже запускался на этом каталоге и завёл хранилище
- **WHEN** он запускается снова
- **THEN** он не заводит схему второй раз и не теряет прежние записи

### Requirement: Пароль владельца от панели не лежит в конфигурации

Сервис SHALL не заводить в конфигурации ключа под пароль владельца от панели.
Пароль MUST задаваться самим владельцем, а хранилище MUST держать только его
отпечаток.

Требование стоит на инварианте проекта «Секрет не покидает конфиг» с другой
стороны: секрет, которого в конфигурации нет, не утекает вместе с ней и не
уезжает в выкладку третьим путём. Пароль от панели открывает все записи и все
файлы разом — это самое чувствительное, что есть у сервиса.

Приглашение завести владельца сервис MUST печатать только пока владельца нет, и
оно MUST истекать по времени. Приглашение равносильно паролю от панели, а
печатается оно в журнал контейнера, откуда строку не убрать: бессрочное отдало бы
панель всякому читателю логов навсегда.

Пока владелец пароля не задал, сервис MUST принимать записи: панель без владельца
приёму не мешает.

#### Scenario: Владелец пароля ещё не задал

- **GIVEN** каталог данных пуст и владелец панели не заведён
- **WHEN** сервис запускается
- **THEN** он принимает записи
- **AND** ни один ключ конфигурации не несёт пароля от панели

#### Scenario: Владелец заведён, приглашение больше не печатается

- **GIVEN** владелец панели заведён
- **WHEN** сервис запускается снова
- **THEN** приглашения завести владельца в журнале нет

### Requirement: Владелец задачи лежит связью с учётной записью

Хранилище SHALL держать владельца аудиозаписи отдельной колонкой — связью с
учётной записью, — и эта колонка MUST не иметь умолчания: запись, чей владелец
не назван, не достаётся никому по недосмотру схемы.

Колонка MUST не допускать пустого значения. Прежде допускала, и цену платили за
записи, принятые ботом: связи чата с учётной записью сервис не вёл. С убранным
входом заводить ничью запись стало некому, и обязательность переезжает из одного
лишь приёма в схему — туда, где её держит хранилище, а не договорённость. Разница
не косметическая: пока обязательность жила в приёме, ничью запись заводили руками
в панели, и она уходила в конвейер, стоила денег на распознавание и не доставалась
потом никому.

Владелец MUST не назначаться и не меняться конвейером.

#### Scenario: Колонка появляется на пустой базе

- **WHEN** сервис поднимается на чистом каталоге данных
- **THEN** у аудиозаписи есть колонка владельца
- **AND** умолчания у неё нет
- **AND** пустого значения она не принимает

#### Scenario: Запись без владельца не сохраняется

- **GIVEN** сервис поднят
- **WHEN** аудиозапись пытаются сохранить с пустым владельцем — приёмом,
  конвейером или руками в панели
- **THEN** хранилище её не сохраняет

#### Scenario: Конвейер владельца не назначает

- **GIVEN** запись с владельцем прошла шаг конвейера
- **WHEN** смотрят её владельца
- **THEN** он прежний

### Requirement: Файл записи сужается владельцем наравне с задачей

Хранилище SHALL держать владельца и у файла записи — той же связью с учётной
записью, — и правило просмотра файлов MUST пускать к файлу только его владельца.

Владелец файла MUST назначаться при приёме, из предъявленной сессии, а колонка
файла MUST не допускать пустого значения наравне с колонкой записи. Прежде пустое
значение оставалось у файлов, заведённых конвейером для записи без владельца;
таких записей больше не заводится, и разное правило у записи и у её файла
читалось бы как недосмотр.

Файл, заведённый шагом конвейера, — приведённую копию заводит именно он —
MUST получать владельца своей записи. Иного источника владельца у файла нет, и
шаг, оставивший его пустым, упрётся в отказ сохранения: запись накопит отказы и
остановится признаком на первом же приведении.

Ссылки на файлы у записи две — на принятую копию и на приведённую, — и обе живут
до конца, но владелец файла MUST по-прежнему лежать своей колонкой, а не
выводиться через запись: файл переживает свою запись, и заведённый шагом до
сохранения записи он остаётся с владельцем и без ссылки.

Отказ наступает **на переходе по ссылке**, а не на выдаче токена файла: токен
хранилище выдаёт на предъявителя, а не на файл, и о файле при выдаче не
спрашивает вовсе. Требовать отказа при выдаче значит требовать механизма,
которого нет, — а проверка, написанная под такое требование, зеленела бы, не
касаясь пути, по которому аудио и уходит.

#### Scenario: Чужой файл не отдаётся

- **GIVEN** запись принята одним вошедшим
- **WHEN** другой вошедший идёт по ссылке на файл этой записи со своим токеном
- **THEN** содержимого он не получает

#### Scenario: Свой файл отдаётся

- **GIVEN** человек принял запись
- **WHEN** он идёт по ссылке на файл своей записи со своим токеном
- **THEN** содержимое отдаётся

#### Scenario: Файл без владельца не сохраняется

- **GIVEN** сервис поднят
- **WHEN** файл записи пытаются сохранить с пустым владельцем
- **THEN** хранилище его не сохраняет

#### Scenario: Приведённая копия получает владельца записи

- **GIVEN** запись с владельцем дошла до приведения
- **WHEN** шаг заводит приведённую копию файла
- **THEN** владельцем копии стоит владелец записи
- **AND** шаг завершается без отказа
