Files
transcriber/openspec/changes/archive/2026-08-15-app-json-contract/specs/pipeline/spec.md
T
av 3a2da3004b приём и чтение записей сведены к одному контракту приложения
- адреса приложения переехали в своё пространство `/app/`, опрос готовности
  убран целиком: рубеж и причину остановки владелец узнаёт карточкой записи,
  текст — отдельным адресом названного вида
- заведена единая точка отображения доменной ошибки и слой, приводящий к той же
  форме отказы библиотеки: тело несёт машиночитаемый код рядом с сообщением
- у записи появились имя файла отправителя, длительность и размер своими
  колонками, а у ленты владельца — свой индекс: без него страница сканировала
  весь архив сервиса
2026-08-15 13:51:23 +03:00

96 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## MODIFIED Requirements
### Requirement: Число отказов ограничивает повторы шага
У аудиозаписи SHALL быть число отказов. Оно MUST расти при каждом захвате и MUST
возвращаться к нулю, когда шаг завершился без отказа либо отложил работу. Рост
при захвате, а не при отказе, засчитывает попытку и записи, брошенной на
середине: шаг, уносящий с собой процесс, до объявления отказа не доходит
никогда.
**Остановка сервиса отказом не считается.** Шаг, прерванный отменой по
собственной остановке сервиса, MUST возвращать число отказов назад и MUST не
выносить записи приговора: запись не виновата в том, что нас перезапустили, и
несколько выкладок подряд иначе останавливают здоровую многочасовую запись с
приговором «отказы исчерпаны». Всякая другая причина, по которой шаг не дошёл до
объявления исхода, отказ тратит.
Запись, захваченная с числом отказов сверх заданного предела, MUST
останавливаться признаком тем, кто её захватил, и MUST не отдаваться шагу в
работу. Остановка эта видна владельцу записи **карточкой записи** наравне с
прочими — норму держит capability `archive`.
Этот сторож MUST отвечать только за повторы внутри шага. Время, проведённое
записью в рубеже, MUST мериться отдельным сторожем: одно число не справляется ни
с одной из двух обязанностей — опрос, вернувший «ещё в работе», обнуляет его, и
зависшая чужая операция опрашивается вечно, а не обнулял бы — убивал бы здоровую
запись.
#### Scenario: Запись отказывает на каждой попытке
- **GIVEN** шаг конвейера отказывает на каждой попытке
- **WHEN** запись проходит заданное число отказов
- **THEN** у неё появляется признак остановки
- **AND** следующий захват её не выдаёт
- **AND** карточка записи отдаёт владельцу признак остановки
#### Scenario: Шаг уносит процесс, не объявив отказа
- **GIVEN** шаг конвейера обрывается вместе с процессом на каждой попытке
- **WHEN** запись захватывается снова заданное число раз
- **THEN** у неё появляется признак остановки
#### Scenario: Остановка сервиса отказа не тратит
- **GIVEN** шаг работает над записью
- **WHEN** сервис останавливают, и шаг прерывается отменой
- **THEN** число отказов записи прежнее
- **AND** признака остановки у записи не появляется
#### Scenario: Прошедшая запись отказов не копит
- **GIVEN** запись прошла подряд несколько рубежей без единого отказа
- **WHEN** смотрят её число отказов
- **THEN** оно не приблизилось к пределу
### Requirement: Конвейер ответа отправителю не шлёт
Шаг конвейера SHALL доводить запись до достигнутого рубежа и MUST не обращаться
к отправителю вовсе — ни с готовым текстом, ни с сообщением о неудаче. Исход
своей записи владелец узнаёт **карточкой записи** и в панели владельца сервиса;
адрес карточки и содержимое ответа нормирует capability `archive`.
Держатель нормы сменился вместе с убранным опросом готовности: прежде исход
отдавал адрес опроса, нормированный capability `intake`, и адреса этого больше
нет. Обязанность при этом не изменилась — изменилось только то, каким адресом
она исполняется.
Требование заведено взамен доставки в чат, убранной вместе с входом Telegram.
Без него молчание конвейера читалось бы как недоделка: прежде ответ уходил, и
всякий, кто помнит это, ищет в шаге отправку, а её отсутствие принимает за
потерянную ветку.
Инвариант проекта «Принятая запись не теряется молча» держится теперь карточкой
записи — там остановка видна признаком и причиной — и журналом владельца, где у
неё стоит причина. Обязанность при этом сменила направление: прежде об отказе
сообщали, теперь отказ доступен спросившему. Отправитель, который не
спрашивает, об остановке не узнаёт.
Записи, которой этот канал недоступен, не бывает: у каждой записи есть владелец,
и карточка отдаёт ему её исход. Держится это обязательностью владельца в схеме
хранилища — норму держит capability `storage`.
#### Scenario: Готовый текст отправителю не уходит
- **GIVEN** запись дошла до конечного рубежа
- **WHEN** шаг конвейера её завершает
- **THEN** ни одного обращения наружу с текстом расшифровки не уходит
- **AND** текст достаётся отдельным адресом текста записи
#### Scenario: Остановка видна карточкой, а не сообщением
- **GIVEN** запись остановлена по исчерпании отказов
- **WHEN** владелец записи спрашивает её карточку
- **THEN** ответ несёт достигнутый рубеж, признак остановки и её причину
- **AND** в журнале владельца сервиса есть запись об остановке с причиной