tasks: заведён урожай ревью remove-telegram-intake

- шесть записей по кластерам причин: журнал под внешним значением, нулевой
  код ответа в журнале, затирание вложения бедным ответом, открытый анониму
  адрес подтверждения почты, рубеж расшифровки без работы, пределы длительности
- находка про код 500 у отказа приёма дописана в json-api-for-spa: там живёт
  единая точка отображения доменной ошибки
- telegram-account-link и bot-api-only-through-bot-client оставлены с оговоркой,
  что предмета у них нет до возвращения входа
This commit is contained in:
av
2026-08-15 07:46:16 +03:00
parent 8f7c3a057a
commit b4b19db6e4
11 changed files with 364 additions and 3 deletions
@@ -0,0 +1,57 @@
# 🐞 Не затирать сохранённое вложение более бедным ответом
- **Тип:** fix
- **Категория:** Очередь — Заведена урожаем ревью и встала в конец машинально: место в плане стройки назначает человек.
- **Зачем:** Пустой ответ распознавателя сохранённое больше не стирает, а более короткий — стирает: вложение заменяется, и пересчитать архив становится нечем.
- **Теги:** review-2026-08-15
Сохранённый ответ провайдера заменяется только тем, что не беднее прежнего.
Задача `remove-telegram-intake` закрыла половину этого класса: текст и структура
реплик пустым больше не затираются, и норма записана требованием спеки хранилища
«Пустой результат не кладётся поверх сохранённого». Сырой ответ провайдера
защищён только от **пустого** — условием на непустую длину; ответ не пустой, но
короче прежнего, проходит это условие и заменяет сохранённое.
Цена именно у вложения самая высокая. Оно единственное, из чего архив
пересчитывается без повторной оплаты: решение хранить его дословно записано
[ADR-2026-08-14-provider-payload-stored-verbatim](../../docs/adr/ADR-2026-08-14-provider-payload-stored-verbatim.md),
и обещание там прямое — «когда мы научимся размечать говорящих, архив
пересчитается из сохранённого без единого рубля».
## Воспроизведение
Повторный опрос одной операции наступает штатно: держатель захвата умер,
сохранение рубежа отказало, человек снял признак остановки в панели.
Довести запись до сохранённого ответа, вернуть её на рубеж отправленной и
опросить снова, отдав со стороны провайдера ответ короче прежнего. Вложение
заменяется на короткое; прежнего не остаётся нигде. Оракул того же класса для
пустого ответа уже написан — `TestEmptySecondAnswerKeepsArchivedText`, — и этот
пишется по его образцу.
## Затрагивает
- коллекция `recognitions`, колонка `payload` — сохранённый ответ провайдера вложением;
- спека `storage`, требование «Пустой результат не кладётся поверх сохранённого»
— норма расширяется с пустого на «беднее прежнего»;
- определение «беднее»: длина в байтах либо число реплик разбора — выбирается
задачей.
## Критерии приёмки
- Более короткий ответ сохранённое вложение не заменяет. Оракул: проверка по
образцу `TestEmptySecondAnswerKeepsArchivedText` — второй опрос отдаёт ответ
короче, сохранённое вложение остаётся прежним, и проверка убеждается, что до
второго обращения дело дошло.
- Более полный ответ сохранённое заменяет. Оракул: та же проверка наоборот —
второй ответ длиннее, вложение обновилось.
- Норма записана требованием спеки, а не только кодом. Оракул: правленое
требование `storage` и ссылка на него из кода.
## Рамки
Найдено прогоном ревью задачи `remove-telegram-intake` 2026-08-15, враждебный
проход, секция свойств без построенного пути: пустой случай доказан падающим
тестом и закрыт, этот — выведен из того же кода и оракула не имеет. Тексту и
структуре реплик защита уже стоит, трогать их не надо.