tasks: заведён урожай ревью remove-telegram-intake
- шесть записей по кластерам причин: журнал под внешним значением, нулевой код ответа в журнале, затирание вложения бедным ответом, открытый анониму адрес подтверждения почты, рубеж расшифровки без работы, пределы длительности - находка про код 500 у отказа приёма дописана в json-api-for-spa: там живёт единая точка отображения доменной ошибки - telegram-account-link и bot-api-only-through-bot-client оставлены с оговоркой, что предмета у них нет до возвращения входа
This commit is contained in:
+1
-1
@@ -306,7 +306,7 @@ API и имя не откатываются обратной правкой по
|
|||||||
- **Что меняем:** правило «пустое не кладётся поверх сохранённого» записано
|
- **Что меняем:** правило «пустое не кладётся поверх сохранённого» записано
|
||||||
нормой в спеку `storage` и держится **хранилищем**, а не шагом: шагов, кладущих
|
нормой в спеку `storage` и держится **хранилищем**, а не шагом: шагов, кладущих
|
||||||
текст, больше одного, и правило у одного из них у остальных читалось бы как
|
текст, больше одного, и правило у одного из них у остальных читалось бы как
|
||||||
снятое. Дефект пред-существующий, чинился решением владельца от 2026-08-14 в
|
снятое. Дефект существовал до той правки, чинился решением владельца от 2026-08-14 в
|
||||||
задаче, которая его нашла
|
задаче, которая его нашла
|
||||||
|
|
||||||
## 2026-08-15 — пустая расшифровка перестала быть заметной вместе с убранным входом [пойман ревью]
|
## 2026-08-15 — пустая расшифровка перестала быть заметной вместе с убранным входом [пойман ревью]
|
||||||
|
|||||||
@@ -95,4 +95,10 @@
|
|||||||
- [🔬 Уведомление SpeechKit о готовности вместо опроса](items/speechkit-callback-fit.md) — Шаг проверки дёргает операцию раз в 5 секунд всё время распознавания: часовая запись даёт порядка 720 обращений к платному сервису вместо одного ответа.
|
- [🔬 Уведомление SpeechKit о готовности вместо опроса](items/speechkit-callback-fit.md) — Шаг проверки дёргает операцию раз в 5 секунд всё время распознавания: часовая запись даёт порядка 720 обращений к платному сервису вместо одного ответа.
|
||||||
- [✨ Считать объём, минуты и расход по каждому пользователю](items/usage-accounting.md) — Ни объём, ни длительность, ни обращения к платным сервисам никуда не записываются: восстановить расход задним числом не из чего.
|
- [✨ Считать объём, минуты и расход по каждому пользователю](items/usage-accounting.md) — Ни объём, ни длительность, ни обращения к платным сервисам никуда не записываются: восстановить расход задним числом не из чего.
|
||||||
- [✨ Сделать страницу статистики для владельца](items/admin-stats-screen.md) — Собранный учёт читается только запросом к базе руками: ни страницы, ни признака владельца в приложении нет.
|
- [✨ Сделать страницу статистики для владельца](items/admin-stats-screen.md) — Собранный учёт читается только запросом к базе руками: ни страницы, ни признака владельца в приложении нет.
|
||||||
|
- [🐞 Не давать отправителю задавать, что уедет в журнал](items/journal-fields-bounded-by-service.md) — Путь запроса уезжает в журнал целиком: аноним пишет туда до мегабайта своего текста одной строкой, а хвост имени файла приезжает внутри чужого текста ошибки вместо своего поля.
|
||||||
|
- [🐞 Писать в журнал код ответа отвергнутого запроса](items/response-code-in-journal.md) — У всякого запроса, отвергнутого роутером, в журнале стоит нулевой код: всплеск отказов доступа неотличим от обычного трафика.
|
||||||
|
- [🐞 Не затирать сохранённое вложение более бедным ответом](items/payload-not-overwritten-by-poorer-answer.md) — Пустой ответ распознавателя сохранённое больше не стирает, а более короткий — стирает: вложение заменяется, и пересчитать архив становится нечем.
|
||||||
|
- [🐞 Закрыть анониму адрес подтверждения почты](items/close-verification-endpoint.md) — Адрес подтверждения почты хранилища открыт без сессии: зная адрес вошедшего, аноним вызывает у сервиса отправку письма — сегодня безвредно, потому что почтового отправителя нет.
|
||||||
|
- [🔬 Судьба рубежа расшифровки, у которого не осталось работы](items/transcribed-stage-without-work.md) — Шаг завершения после убранной доставки только двигает колонку, а рубеж при этом подпадает под сторож застревания: готовая расшифровка платит отдельный захват и час сторожа.
|
||||||
|
- [🔬 Пределы длительности, приходящей из метаданных](items/duration-bounds-from-metadata.md) — Длительность приходит от ffprobe числом и не проверяется ничем: приведение к целому переполняется, а испорченная гистограмма чинится только перезапуском.
|
||||||
- [🔬 Квота по общему размеру загруженного на пользователя](items/per-user-size-quota.md) — Паспорт и security.md запрещают отказы по квоте пользователю, а заметка владельца просит квоту по умолчанию 5 ГБ — открытое противоречие с границей домена, которое владелец решил не разбирать сейчас.
|
- [🔬 Квота по общему размеру загруженного на пользователя](items/per-user-size-quota.md) — Паспорт и security.md запрещают отказы по квоте пользователю, а заметка владельца просит квоту по умолчанию 5 ГБ — открытое противоречие с границей домена, которое владелец решил не разбирать сейчас.
|
||||||
|
|||||||
@@ -21,6 +21,13 @@
|
|||||||
Ступень по лестнице механизации — четвёртая: свойство о структуре, а не о
|
Ступень по лестнице механизации — четвёртая: свойство о структуре, а не о
|
||||||
вызове, и выражается тест-сканером в `internal/archrules`.
|
вызове, и выражается тест-сканером в `internal/archrules`.
|
||||||
|
|
||||||
|
|
||||||
|
**Предмета у задачи сейчас нет.** Клиент бота удалён вместе со входом задачей
|
||||||
|
`remove-telegram-intake` 2026-08-15 — временно, решение записано
|
||||||
|
[ADR-2026-08-15-telegram-intake-removed-temporarily](../../docs/adr/ADR-2026-08-15-telegram-intake-removed-temporarily.md).
|
||||||
|
Запись остаётся гигиеной **возврата**: класс стоил проекту одного дефекта, и
|
||||||
|
запрет надо ставить вместе с новым клиентом, а не после него.
|
||||||
|
|
||||||
## Затрагивает
|
## Затрагивает
|
||||||
|
|
||||||
- `internal/archrules` — новое правило-сканер и его перечень предметов;
|
- `internal/archrules` — новое правило-сканер и его перечень предметов;
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# 🐞 Закрыть анониму адрес подтверждения почты
|
||||||
|
|
||||||
|
- **Тип:** fix
|
||||||
|
- **Категория:** Очередь — Заведена урожаем ревью и встала в конец машинально: место в плане стройки назначает человек.
|
||||||
|
- **Зачем:** Адрес подтверждения почты хранилища открыт без сессии: зная адрес вошедшего, аноним вызывает у сервиса отправку письма — сегодня безвредно, потому что почтового отправителя нет.
|
||||||
|
- **Теги:** review-2026-08-15
|
||||||
|
|
||||||
|
Поверхность коллекции пользователей закрывается тем же способом, что и остальные
|
||||||
|
её адреса.
|
||||||
|
|
||||||
|
Шаг схемы входа через провайдера закрыл заведение записи, вход паролем и
|
||||||
|
одноразовый код — но не запрос подтверждения почты. Проверено прогоном по
|
||||||
|
собранному роутеру: запрос без сессии отвечает `204`.
|
||||||
|
|
||||||
|
Сервис своей проверки почты не делает вовсе: учётные записи заводит и проверяет
|
||||||
|
внешний провайдер, а адрес приезжает вместе с входом. То есть адрес не просто
|
||||||
|
открыт — он не нужен.
|
||||||
|
|
||||||
|
Сегодня безвреден: почтовый сервер не настроен, письмо уходить некуда. Каналом
|
||||||
|
досаждения он становится вместе с задачей `email-notification`, которая почту как
|
||||||
|
раз заводит, — и закрыть его дешевле до неё, чем после.
|
||||||
|
|
||||||
|
## Воспроизведение
|
||||||
|
|
||||||
|
Собрать роутер сервиса и послать `POST /api/collections/users/request-verification`
|
||||||
|
без сессии, назвав адрес почты вошедшего. Ответ — `204`: сервис принял запрос от
|
||||||
|
неузнанного и завёл отправку письма. Ожидается отказ, как у прочих адресов
|
||||||
|
коллекции пользователей.
|
||||||
|
|
||||||
|
Письма при этом не уходит, и потому вреда сегодня нет: почтовый сервер не
|
||||||
|
настроен. Проверка судит **код ответа**, а не доставку.
|
||||||
|
|
||||||
|
## Затрагивает
|
||||||
|
|
||||||
|
- шаг схемы, закрывающий адреса коллекции пользователей: заводится новый, прежний
|
||||||
|
не переписывается;
|
||||||
|
- `docs/security.md`, раздел о поверхности хранилища — перечень закрытых адресов.
|
||||||
|
|
||||||
|
## Критерии приёмки
|
||||||
|
|
||||||
|
- Запрос подтверждения почты без сессии отвергается. Оракул: проверка по
|
||||||
|
собранному роутеру — ответ не `204`, письма не заводится.
|
||||||
|
- Прочие способы открыть сессию остались закрытыми. Оракул: существующие
|
||||||
|
проверки поверхности хранилища зелёные, состав их не сокращён.
|
||||||
|
- Перечень закрытых адресов в модели угроз назван заново. Оракул: строка в
|
||||||
|
`docs/security.md`.
|
||||||
|
|
||||||
|
## Рамки
|
||||||
|
|
||||||
|
Новый шаг схемы — применённые не переписываются. Найдено прогоном ревью задачи
|
||||||
|
`remove-telegram-intake` 2026-08-15, враждебный проход, секция свойств без
|
||||||
|
построенного пути.
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# 🔬 Пределы длительности, приходящей из метаданных
|
||||||
|
|
||||||
|
- **Тип:** research
|
||||||
|
- **Категория:** Очередь — Заведена урожаем ревью и встала в конец машинально: место в плане стройки назначает человек.
|
||||||
|
- **Зачем:** Длительность приходит от ffprobe числом и не проверяется ничем: приведение к целому переполняется, а испорченная гистограмма чинится только перезапуском.
|
||||||
|
- **Теги:** review-2026-08-15
|
||||||
|
|
||||||
|
Длительность записи сервис берёт у источника метаданных и не проверяет ни на
|
||||||
|
знак, ни на потолок. Дальше сервис приводит её к целому, умножает на тысячу и
|
||||||
|
кладёт колонкой записи, а заодно отправляет в гистограмму длительностей.
|
||||||
|
|
||||||
|
Число вне диапазона целого при приведении даёт наименьшее возможное значение —
|
||||||
|
отрицательное. Оно портит сумму семейства метрик необратимо: ряд не чинится
|
||||||
|
ничем, кроме перезапуска процесса, а страница метрик открыта без проверки
|
||||||
|
отправителя.
|
||||||
|
|
||||||
|
Потолок записи у проекта объявлен — шесть часов, — и из него выведен предел
|
||||||
|
размера файла. Длительность такого предела не имеет вовсе.
|
||||||
|
|
||||||
|
## Вопрос
|
||||||
|
|
||||||
|
Может ли `ffprobe` вернуть длительность вне диапазона целого на входе, который
|
||||||
|
собрал отправитель, — и какой предел ставить на границе адаптера?
|
||||||
|
|
||||||
|
Что для ответа нужно узнать:
|
||||||
|
|
||||||
|
- какое поле и в какой форме `ffprobe` берёт за длительность у контейнеров,
|
||||||
|
которые сервис принимает: доверяет ли объявленному в контейнере числу или
|
||||||
|
считает по потоку;
|
||||||
|
- есть ли контейнер, где число объявлено явно и потому подделывается: Matroska
|
||||||
|
либо Ogg с подделанной меткой позиции — первые кандидаты;
|
||||||
|
- что делает приём, если длительность отрицательна либо больше объявленного
|
||||||
|
потолка: отвергать запись, обрезать значение или принимать, назвав в журнале.
|
||||||
|
|
||||||
|
## Куда ляжет ответ
|
||||||
|
|
||||||
|
Наблюдение о поведении `ffprobe` — в `docs/research/`, своей записью с командой и
|
||||||
|
условиями замера: разведки о `ffmpeg` у проекта пока нет ни одной, а открытые
|
||||||
|
вопросы про долгие записи и видео стоят в
|
||||||
|
[architecture.md](../../docs/architecture.md). Выбранный предел — задачей
|
||||||
|
следующим прогоном.
|
||||||
|
|
||||||
|
## Рамки
|
||||||
|
|
||||||
|
Прогон на реальных ключах Yandex запрещён и здесь не нужен: разведка меряет
|
||||||
|
`ffprobe`, а не распознавание. Найдено прогоном ревью задачи
|
||||||
|
`remove-telegram-intake` 2026-08-15, враждебный проход, секция свойств без
|
||||||
|
построенного пути — путь не достроен именно потому, что нужен специально
|
||||||
|
собранный контейнер.
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
# 🐞 Не давать отправителю задавать, что уедет в журнал
|
||||||
|
|
||||||
|
- **Тип:** fix
|
||||||
|
- **Категория:** Очередь — Заведена урожаем ревью и встала в конец машинально: место в плане стройки назначает человек.
|
||||||
|
- **Зачем:** Путь запроса уезжает в журнал целиком: аноним пишет туда до мегабайта своего текста одной строкой, а хвост имени файла приезжает внутри чужого текста ошибки вместо своего поля.
|
||||||
|
- **Теги:** review-2026-08-15
|
||||||
|
|
||||||
|
Длину и содержимое строки журнала задаёт тот, кто прислал запрос, а не сервис.
|
||||||
|
|
||||||
|
Мест два, причина одна. Слой журнала пишет путь запроса полем `http.route`
|
||||||
|
целиком: приведение режет только последний сегмент и только у ссылок на файлы.
|
||||||
|
Отказ подготовки рабочей копии пересказывает отказ библиотеки целиком, а тот
|
||||||
|
несёт полное имя временного файла — вместе с расширением, взятым из имени
|
||||||
|
отправителя.
|
||||||
|
|
||||||
|
Журнал в этом проекте — место, где наблюдаются инварианты: по нему видно
|
||||||
|
недоставку, остановку конвейера и молчаливый выход из шага. Вытесненный чужим
|
||||||
|
текстом, он перестаёт отвечать на вопрос, ради которого заведён.
|
||||||
|
|
||||||
|
Класс проект уже назвал недопустимым в приведении отказа провайдера входа
|
||||||
|
([conventions/logging.md](../../docs/conventions/logging.md)): «без приведения
|
||||||
|
аноним пишет в журнал что угодно и сколько угодно». Здесь тот же канал шире и
|
||||||
|
дешевле — сессия для него не нужна.
|
||||||
|
|
||||||
|
## Воспроизведение
|
||||||
|
|
||||||
|
**Путь запроса.** Поднять сервис и послать запрос, чей путь длиной 900 000 байт:
|
||||||
|
|
||||||
|
```
|
||||||
|
python3 -c 'socket … GET /<900000×"A"> HTTP/1.1'
|
||||||
|
```
|
||||||
|
|
||||||
|
Ответ — `404`, журнал вырастает с 1048 до 901 194 байт одной строкой. Потолок
|
||||||
|
замерен: 1 048 000 байт ещё проходят, 1 100 000 отвергаются кодом `431`.
|
||||||
|
Внедрения строк при этом нет — библиотека журнала квотирует значение.
|
||||||
|
|
||||||
|
**Хвост имени.** Принять запись, чьё имя несёт 280 байт после последней точки.
|
||||||
|
Отказ подготовки рабочей копии приезжает в журнал уровня `ERROR` строкой вида
|
||||||
|
`error="failed to create work file: open /tmp/transcriber-<число>.<280 байт>:
|
||||||
|
file name too long"`, и поля `file_ext` в ней нет вовсе.
|
||||||
|
|
||||||
|
## Затрагивает
|
||||||
|
|
||||||
|
- слой журнала запроса в `main.go` и приведение пути `journalRoute`;
|
||||||
|
- отказ подготовки рабочей копии в `internal/service`, шаг приёма записи;
|
||||||
|
- `docs/conventions/logging.md` — правило о длине поля журнала;
|
||||||
|
- умолчание `MaxHeaderBytes` у сервера: сегодня оно единственное неназванное
|
||||||
|
рядом с названным сроком чтения.
|
||||||
|
|
||||||
|
## Критерии приёмки
|
||||||
|
|
||||||
|
- Путь запроса в журнале не длиннее объявленного предела, и усечение видно в
|
||||||
|
самой строке. Оракул: тот же запрос с путём в 900 000 байт — прирост журнала
|
||||||
|
не превышает предела, а маршрут по первым байтам по-прежнему различим.
|
||||||
|
- Отказ подготовки рабочей копии не пересказывает отказ библиотеки: расширение
|
||||||
|
идёт своим полем. Оракул: проверка приёма с длинным хвостом имени — строки с
|
||||||
|
этим хвостом в журнале нет, поле `file_ext` есть.
|
||||||
|
- Правило записано конвенцией: длину поля журнала не задаёт вызывающий. Оракул:
|
||||||
|
строка в `docs/conventions/logging.md` и ссылка на неё из обеих правок.
|
||||||
|
|
||||||
|
## Рамки
|
||||||
|
|
||||||
|
Приведение пути не должно делать маршрут неразличимым: по журналу отличают
|
||||||
|
`/api/audio` от `/api/status/:id`. Найдено прогоном ревью задачи
|
||||||
|
`remove-telegram-intake` 2026-08-15, враждебный проход; дефект существовал до той
|
||||||
|
правки, и владелец решил вынести его задачей.
|
||||||
@@ -12,6 +12,14 @@
|
|||||||
включая негодный файл. Экран, построенный на таком контракте, показывает «не
|
включая негодный файл. Экран, построенный на таком контракте, показывает «не
|
||||||
найдено» при упавшей базе.
|
найдено» при упавшей базе.
|
||||||
|
|
||||||
|
Отдельная ветка того же класса найдена ревью задачи `remove-telegram-intake`
|
||||||
|
2026-08-15, проход сверки требований: приём отвергает пустого владельца своей
|
||||||
|
ошибкой, а транспорт переводит **любую** ошибку службы в `500`. Спека приёма при
|
||||||
|
этом держит эту проверку ради понятного отказа — «отвечает отправителю понятным
|
||||||
|
отказом до того, как запись попадёт в память». Сегодня путь недостижим: слой
|
||||||
|
предъявления отвергает такого вызывающего раньше. Он становится достижимым вместе
|
||||||
|
с личными токенами и экранами приложения, то есть ровно здесь.
|
||||||
|
|
||||||
## Затрагивает
|
## Затрагивает
|
||||||
|
|
||||||
- `POST /api/audio` и `GET /api/status/:id` — коды ответа и форма ошибки;
|
- `POST /api/audio` и `GET /api/status/:id` — коды ответа и форма ошибки;
|
||||||
@@ -26,8 +34,10 @@
|
|||||||
|
|
||||||
- Код ответа отвечает причине отказа, а не месту, где он случился: сбой базы при
|
- Код ответа отвечает причине отказа, а не месту, где он случился: сбой базы при
|
||||||
чтении даёт `500`, а не `404`, негодный файл — `400` с человекочитаемым
|
чтении даёт `500`, а не `404`, негодный файл — `400` с человекочитаемым
|
||||||
текстом, а не `500`. Оракул — два теста: репозиторий, возвращающий ошибку
|
текстом, а не `500`, а отказ приёма по пустому владельцу — `403`, как и отказ
|
||||||
драйвера, и файл, который отвергает разбор метаданных.
|
слоя предъявления. Оракул — три теста: репозиторий, возвращающий ошибку
|
||||||
|
драйвера; файл, который отвергает разбор метаданных; вызов приёма без учётной
|
||||||
|
записи.
|
||||||
- Тело ошибки одной формы на всех эндпоинтах, не содержит сырого `err.Error()` и
|
- Тело ошибки одной формы на всех эндпоинтах, не содержит сырого `err.Error()` и
|
||||||
опечатки `transcibe`. Оракул — тест на четырёх ветвях отказа (форма совпадает,
|
опечатки `transcibe`. Оракул — тест на четырёх ветвях отказа (форма совпадает,
|
||||||
текста внутренней ошибки в теле нет) плюс пустой `grep -rn 'transcibe'
|
текста внутренней ошибки в теле нет) плюс пустой `grep -rn 'transcibe'
|
||||||
|
|||||||
@@ -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, враждебный
|
||||||
|
проход, секция свойств без построенного пути: пустой случай доказан падающим
|
||||||
|
тестом и закрыт, этот — выведен из того же кода и оракула не имеет. Тексту и
|
||||||
|
структуре реплик защита уже стоит, трогать их не надо.
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# 🐞 Писать в журнал код ответа отвергнутого запроса
|
||||||
|
|
||||||
|
- **Тип:** fix
|
||||||
|
- **Категория:** Очередь — Заведена урожаем ревью и встала в конец машинально: место в плане стройки назначает человек.
|
||||||
|
- **Зачем:** У всякого запроса, отвергнутого роутером, в журнале стоит нулевой код: всплеск отказов доступа неотличим от обычного трафика.
|
||||||
|
- **Теги:** review-2026-08-15
|
||||||
|
|
||||||
|
Код ответа в журнале называет исход запроса, а не ноль.
|
||||||
|
|
||||||
|
Обработчик, завершившийся отказом роутера — `401` без сессии, `403` без учётной
|
||||||
|
записи, `404` на чужой записи, `500` на сбое, — пишет статус в ответ **после**
|
||||||
|
возврата из слоя журнала. Слой читает его раньше и получает ноль. Верное значение
|
||||||
|
попадает в журнал только у обработчиков, отвечающих самостоятельно.
|
||||||
|
|
||||||
|
Разграничение записей по владельцу устроено так, что чужая запись отвечает тем
|
||||||
|
же, чем несуществующая. Единственное место, где виден перебор идентификаторов, —
|
||||||
|
журнал; а именно эти строки и приезжают с нулём.
|
||||||
|
|
||||||
|
## Воспроизведение
|
||||||
|
|
||||||
|
Поднять сервис и послать четыре запроса подряд:
|
||||||
|
|
||||||
|
```
|
||||||
|
/auth/login → 302 ... http.route=/auth/login http.status_code=302
|
||||||
|
/api/audio → 404 ... http.route=/api/audio http.status_code=0
|
||||||
|
/nope → 404 ... http.route=/nope http.status_code=0
|
||||||
|
POST /api/audio без сессии → 401 ... http.status_code=0
|
||||||
|
```
|
||||||
|
|
||||||
|
Первый обработчик отвечает сам и код пишет верно; три остальных отвергнуты
|
||||||
|
роутером и дают ноль.
|
||||||
|
|
||||||
|
## Затрагивает
|
||||||
|
|
||||||
|
- слой журнала запроса в `main.go`: место, где снимается код ответа;
|
||||||
|
- `docs/conventions/logging.md` — словарь полей запроса.
|
||||||
|
|
||||||
|
## Критерии приёмки
|
||||||
|
|
||||||
|
- Отвергнутый запрос пишет в журнал свой код. Оракул: те же четыре запроса —
|
||||||
|
`401`, `403`, `404` и `500` приезжают в поле кода, ноля в нём нет ни у одного.
|
||||||
|
- Обработчик, отвечающий самостоятельно, продолжает писать свой код. Оракул:
|
||||||
|
запрос входа по-прежнему даёт `302`.
|
||||||
|
|
||||||
|
## Рамки
|
||||||
|
|
||||||
|
Найдено прогоном ревью задачи `remove-telegram-intake` 2026-08-15, враждебный
|
||||||
|
проход, оракул — живой прогон. Дефект существовал до той правки: изменение этот
|
||||||
|
слой не трогало. Метрики кодов ответа у сервиса нет вовсе, и заводить её этой задачей не
|
||||||
|
надо — она про журнал.
|
||||||
@@ -10,6 +10,14 @@
|
|||||||
Берётся после `record-ownership`: связывать не с чем, пока у записи нет
|
Берётся после `record-ownership`: связывать не с чем, пока у записи нет
|
||||||
владельца.
|
владельца.
|
||||||
|
|
||||||
|
|
||||||
|
**Предмета у задачи сейчас нет.** Вход Telegram убран задачей
|
||||||
|
`remove-telegram-intake` 2026-08-15 — временно, решение записано
|
||||||
|
[ADR-2026-08-15-telegram-intake-removed-temporarily](../../docs/adr/ADR-2026-08-15-telegram-intake-removed-temporarily.md).
|
||||||
|
Эта задача остаётся **условием возврата**: без связи чата с учётной записью вход
|
||||||
|
вернуть нельзя — записи снова пошли бы без владельца, а колонка владельца пустого
|
||||||
|
значения больше не принимает.
|
||||||
|
|
||||||
## Затрагивает
|
## Затрагивает
|
||||||
|
|
||||||
- связь учётной записи с чатом Telegram: таблица и её миграция;
|
- связь учётной записи с чатом Telegram: таблица и её миграция;
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# 🔬 Судьба рубежа расшифровки, у которого не осталось работы
|
||||||
|
|
||||||
|
- **Тип:** research
|
||||||
|
- **Категория:** Очередь — Заведена урожаем ревью и встала в конец машинально: место в плане стройки назначает человек.
|
||||||
|
- **Зачем:** Шаг завершения после убранной доставки только двигает колонку, а рубеж при этом подпадает под сторож застревания: готовая расшифровка платит отдельный захват и час сторожа.
|
||||||
|
- **Теги:** review-2026-08-15
|
||||||
|
|
||||||
|
Шаг завершения заводился ради ответа отправителю: он читал текст расшифровки и
|
||||||
|
отправлял его в чат, а перевод записи на конечный рубеж был хвостом этой работы.
|
||||||
|
Доставка убрана задачей `remove-telegram-intake`, и от шага осталось присваивание
|
||||||
|
колонки с сохранением.
|
||||||
|
|
||||||
|
Цена этого остатка не нулевая. Между «расшифровка сохранена» и конечным рубежом
|
||||||
|
лежит полный круг воркера: захват с ростом числа отказов, чтение, сохранение,
|
||||||
|
строка журнала событий, счётчик. Рубеж при этом числится своей работой и потому
|
||||||
|
подпадает под предел простоя — час. Запись с готовым текстом, простоявшая этот час
|
||||||
|
без свободного воркера (очередь, ноль воркеров настройкой, окно выкладки),
|
||||||
|
останавливается признаком с причиной «застряла», и отправитель видит остановку у
|
||||||
|
записи, чья расшифровка лежит в хранилище.
|
||||||
|
|
||||||
|
Вопрос не в том, дефект это или нет: сегодня и то и другое поведение законно.
|
||||||
|
Вопрос в том, чем рубеж остаётся дальше.
|
||||||
|
|
||||||
|
## Вопрос
|
||||||
|
|
||||||
|
Остаётся ли рубеж расшифровки отдельным шагом конвейера — и если да, какую работу
|
||||||
|
он держит?
|
||||||
|
|
||||||
|
Две формы ответа видны сразу, и у каждой своя цена:
|
||||||
|
|
||||||
|
- **шаг опроса двигает запись сразу на конечный рубеж**, а рубеж расшифровки
|
||||||
|
уходит к отжившим состояниям. Круг воркера и час сторожа исчезают; цена —
|
||||||
|
правка спеки конвейера (цепочка рубежей, требование «Рубеж записи называет
|
||||||
|
достигнутое») и значение, остающееся в перечне схемы;
|
||||||
|
- **рубеж остаётся объявленным местом** под задачу выводов из текста — заголовок,
|
||||||
|
пересказ и темы считаются шагом, который встанет ровно сюда. Тогда это надо
|
||||||
|
сказать словами в конвейере и в спеке, иначе следующий читатель прочтёт шаг как
|
||||||
|
забытый код. Цена — час сторожа над готовой записью, и его тоже надо назвать.
|
||||||
|
|
||||||
|
Ответ зависит от того, чем станет `llm-insights-adapter`: отдельным шагом
|
||||||
|
конвейера или продолжением распознавания. Открытым вопросом это стоит в
|
||||||
|
[architecture.md](../../docs/architecture.md).
|
||||||
|
|
||||||
|
## Куда ляжет ответ
|
||||||
|
|
||||||
|
Решение — в `docs/adr/`, продвижением записки; сама записка не нужна, разведка
|
||||||
|
здесь выбирает форму, а не измеряет внешний мир. Изменение спеки конвейера, если
|
||||||
|
выбран первый ответ, заводит следующий прогон.
|
||||||
|
|
||||||
|
## Рамки
|
||||||
|
|
||||||
|
Перечень рубежей закрыт схемой, и удаление значения из него потребовало бы шага
|
||||||
|
схемы: разведка это только называет ценой, а решение принимает человек. Найдено
|
||||||
|
прогоном ревью задачи `remove-telegram-intake` 2026-08-15, архитектурный проход;
|
||||||
|
оракула у находки нет — случай выведен из кода и предела простоя, но не
|
||||||
|
воспроизведён.
|
||||||
Reference in New Issue
Block a user