tasks: заведён остаток работы о гейте, контексте и токене

- четыре новые записи: проверить шаг migrations так же, как шаг сверки версий Go;
  свести шесть расхождений между документами канона; запретить обращаться к Bot
  API мимо клиента бота; разведка о шагах гейта, теряющих предмет
- context-cancel-in-pipeline приведена к правде: дописан перечень сделанного
  попутно, критерий с оракулом «тест на трёх прерываниях подряд» разбит надвое —
  проверена была только его узкая половина
This commit is contained in:
av
2026-08-13 10:55:38 +03:00
parent f4d8c7ed50
commit eacaf76d5f
6 changed files with 230 additions and 7 deletions
+31 -6
View File
@@ -2,7 +2,7 @@
- **Тип:** chore
- **Категория:** Очередь — Отмена контекста трогает те же шаги, что и таймауты: одна окрестность кода, два захода дороже одного.
- **Зачем:** Воркер читает ctx только между итерациями: остановка контейнера ждёт конца шага, а на занятом писателе один запрос к хранилищу держится до 9,5 секунды при мягком таймауте в 5.
- **Зачем:** Половина сделана 2026-08-13 — контекст доходит до внешних вызовов, а прерванный шаг оставляет задачу на повтор и не тратит попытку, — но осталось то, ради чего задача заводилась: хранилище контекста не принимает ни одним методом, и бюджет мягкой остановки не замерен.
Долг назван в `docs/architecture.md` — раздел про capability `pipeline`, где
отмена контекста посреди шага перечислена среди неописанного. Ревью задачи
@@ -16,6 +16,28 @@
задачи держится 9,53 секунды (таймаут занятости хранилища — 10 секунд) и
возвращает отличимый отказ, а не «работы нет».
**Сделано попутно 2026-08-13** (коммит `f494dcb`, работа о линтерах и контексте
— задачей не бралась, поэтому карточка и осталась открытой):
- контекст проложен от воркера и обоих входов до внешних вызовов: `ffmpeg` и
`ffprobe` заводятся через `exec.CommandContext`, SpeechKit и Object Storage
принимают его доводом, скачивание записи идёт запросом с контекстом;
- прерванный шаг приговора не выносит: задача остаётся пригодной к повтору,
попытку не тратит и отправителю о несуществующем сбое не сообщает. Отмена
различается по `ctx.Err()`, а не по ошибке: убитый процесс отдаёт
«signal: killed», и `errors.Is` его с отменой не свяжет;
- воркер не считает остановку отказом и не пишет о ней владельцу; задача не
забирается вовсе, если нас уже остановили;
- принятие операции SpeechKit от отмены защищено своим пределом: потерянный
идентификатор заставил бы повтор оплатить ту же запись второй раз;
- проверки: `internal/service/shutdown_test.go`,
`TestShutdownIsNotAFailure` в `internal/controller/worker/worker_test.go`.
Из пяти критериев приёмки закрыты два — «оборванный шаг оставляет задачу
пригодной к повтору» и «прерванный шаг не тратит попытку». Открыты три: бюджет
остановки не замерен (хранилище контекста не принимает, и обрывать в нём нечего),
три отмены подряд ни один тест не гоняет, спека `pipeline` отмену не описывает.
## Затрагивает
- цикл воркера `internal/controller/worker`;
@@ -32,11 +54,14 @@
- Шаг, идущий дольше мягкого таймаута, обрывается по сигналу остановки. Оракул —
тест с шагом, спящим дольше таймаута: остановка укладывается в бюджет, а не
доживает до жёсткой.
- Оборванный шаг оставляет задачу пригодной к повтору. Оракул — тот же тест:
после остановки задача не захвачена и выдаётся следующему.
- Отмена не путается с отказом: прерванный шаг не тратит попытку и не уходит в
«мертва». Оракул — тест на трёх прерываниях подряд, счётчик попыток не растёт
до предела.
- Оборванный шаг оставляет задачу пригодной к повтору. Оракул — тест
`TestShutdownDuringConversionKeepsJobRetryable`: после остановки задача не
захвачена, стоит в прежнем состоянии и выдаётся следующему прогону.
- Прерванный шаг не тратит попытку. Оракул — тот же тест: счётчик попыток задачи
после отмены равен тому, что был до захвата.
- Три отмены подряд не уводят задачу в «мертва». Оракул — тест на трёх
прерываниях подряд: счётчик попыток не растёт до предела, состояние задачи не
меняется.
- Спека `pipeline` описывает отмену, и раздел `Purpose` её больше не числит
неописанной. Оракул — `openspec validate --strict`.