- адреса приложения переехали в своё пространство `/app/`, опрос готовности убран целиком: рубеж и причину остановки владелец узнаёт карточкой записи, текст — отдельным адресом названного вида - заведена единая точка отображения доменной ошибки и слой, приводящий к той же форме отказы библиотеки: тело несёт машиночитаемый код рядом с сообщением - у записи появились имя файла отправителя, длительность и размер своими колонками, а у ленты владельца — свой индекс: без него страница сканировала весь архив сервиса
96 lines
7.4 KiB
Markdown
96 lines
7.4 KiB
Markdown
## 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** в журнале владельца сервиса есть запись об остановке с причиной
|