приём и чтение записей сведены к одному контракту приложения

- адреса приложения переехали в своё пространство `/app/`, опрос готовности
  убран целиком: рубеж и причину остановки владелец узнаёт карточкой записи,
  текст — отдельным адресом названного вида
- заведена единая точка отображения доменной ошибки и слой, приводящий к той же
  форме отказы библиотеки: тело несёт машиночитаемый код рядом с сообщением
- у записи появились имя файла отправителя, длительность и размер своими
  колонками, а у ленты владельца — свой индекс: без него страница сканировала
  весь архив сервиса
This commit is contained in:
av
2026-08-15 13:51:23 +03:00
parent 79ff12548f
commit 3a2da3004b
55 changed files with 5506 additions and 466 deletions
+18 -13
View File
@@ -568,8 +568,8 @@ MUST не быть привязаны к отдельному шагу: кажд
Запись, захваченная с числом отказов сверх заданного предела, MUST
останавливаться признаком тем, кто её захватил, и MUST не отдаваться шагу в
работу. Остановка эта видна отправителю опросом готовности наравне с прочими —
норму держит capability `intake`.
работу. Остановка эта видна владельцу записи **карточкой записи** наравне с
прочими — норму держит capability `archive`.
Этот сторож MUST отвечать только за повторы внутри шага. Время, проведённое
записью в рубеже, MUST мериться отдельным сторожем: одно число не справляется ни
@@ -583,7 +583,7 @@ MUST не быть привязаны к отдельному шагу: кажд
- **WHEN** запись проходит заданное число отказов
- **THEN** у неё появляется признак остановки
- **AND** следующий захват её не выдаёт
- **AND** опрос готовности отдаёт владельцу записи признак остановки
- **AND** карточка записи отдаёт владельцу признак остановки
#### Scenario: Шаг уносит процесс, не объявив отказа
@@ -608,22 +608,27 @@ MUST не быть привязаны к отдельному шагу: кажд
Шаг конвейера SHALL доводить запись до достигнутого рубежа и MUST не обращаться
к отправителю вовсе — ни с готовым текстом, ни с сообщением о неудаче. Исход
своей записи отправитель узнаёт опросом готовности и в панели владельца; адрес
опроса и содержимое ответа нормирует capability `intake`.
своей записи владелец узнаёт **карточкой записи** и в панели владельца сервиса;
адрес карточки и содержимое ответа нормирует capability `archive`.
Держатель нормы сменился вместе с убранным опросом готовности: прежде исход
отдавал адрес опроса, нормированный capability `intake`, и адреса этого больше
нет. Обязанность при этом не изменилась — изменилось только то, каким адресом
она исполняется.
Требование заведено взамен доставки в чат, убранной вместе с входом Telegram.
Без него молчание конвейера читалось бы как недоделка: прежде ответ уходил, и
всякий, кто помнит это, ищет в шаге отправку, а её отсутствие принимает за
потерянную ветку.
Инвариант проекта «Принятая запись не теряется молча» держится теперь опросом
готовности — там остановка видна признаком — и журналом владельца, где у неё
стоит причина. Обязанность при этом сменила направление: прежде об отказе
Инвариант проекта «Принятая запись не теряется молча» держится теперь карточкой
записи — там остановка видна признаком и причиной — и журналом владельца, где у
неё стоит причина. Обязанность при этом сменила направление: прежде об отказе
сообщали, теперь отказ доступен спросившему. Отправитель, который не
спрашивает, об остановке не узнаёт.
Записи, которой этот канал недоступен, не бывает: у каждой записи есть владелец,
и опрос отдаёт ему её исход. Держится это обязательностью владельца в схеме
и карточка отдаёт ему её исход. Держится это обязательностью владельца в схеме
хранилища — норму держит capability `storage`.
#### Scenario: Готовый текст отправителю не уходит
@@ -631,12 +636,12 @@ MUST не быть привязаны к отдельному шагу: кажд
- **GIVEN** запись дошла до конечного рубежа
- **WHEN** шаг конвейера её завершает
- **THEN** ни одного обращения наружу с текстом расшифровки не уходит
- **AND** текст достаётся опросом готовности
- **AND** текст достаётся отдельным адресом текста записи
#### Scenario: Остановка видна опросом, а не сообщением
#### Scenario: Остановка видна карточкой, а не сообщением
- **GIVEN** запись остановлена по исчерпании отказов
- **WHEN** владелец записи спрашивает её рубеж
- **THEN** ответ несёт достигнутый рубеж и признак остановки
- **WHEN** владелец записи спрашивает её карточку
- **THEN** ответ несёт достигнутый рубеж, признак остановки и её причину
- **AND** в журнале владельца сервиса есть запись об остановке с причиной