tasks: заведён остаток работы о гейте, контексте и токене
- четыре новые записи: проверить шаг migrations так же, как шаг сверки версий Go; свести шесть расхождений между документами канона; запретить обращаться к Bot API мимо клиента бота; разведка о шагах гейта, теряющих предмет - context-cancel-in-pipeline приведена к правде: дописан перечень сделанного попутно, критерий с оракулом «тест на трёх прерываниях подряд» разбит надвое — проверена была только его узкая половина
This commit is contained in:
+5
-1
@@ -56,7 +56,7 @@
|
||||
- [🧹 Покрыть тестами шаги конвейера и захват задачи](items/pipeline-step-tests.md) — Тестовых файлов в проекте два, и оба мимо конвейера: потеря ссылки на файл, двойной ответ пользователю и гонка при захвате не поймаются ничем.
|
||||
- [🧹 Покрыть тестами разбор вывода ffprobe](items/metaviewer-adapter-tests.md) — Проверки приёма перестали звать настоящий ffprobe 2026-08-11, а своего теста у адаптера метаданных нет: разбор JSON и отличие «программы нет в PATH» от «обработка отказала» не проверяет ничто.
|
||||
- [🧹 Задать таймауты обращениям к внешним сервисам](items/external-call-timeouts.md) — Ни у Telegram, ни у Object Storage, ни у SpeechKit нет таймаута: молчащий собеседник держит шаг конвейера до истечения часового захвата.
|
||||
- [🧹 Прервать шаг конвейера отменой контекста](items/context-cancel-in-pipeline.md) — Воркер читает ctx только между итерациями: остановка контейнера ждёт конца шага, а на занятом писателе один запрос к хранилищу держится до 9,5 секунды при мягком таймауте в 5.
|
||||
- [🧹 Прервать шаг конвейера отменой контекста](items/context-cancel-in-pipeline.md) — Половина сделана 2026-08-13 — контекст доходит до внешних вызовов, а прерванный шаг оставляет задачу на повтор и не тратит попытку, — но осталось то, ради чего задача заводилась: хранилище контекста не принимает ни одним методом, и бюджет мягкой остановки не замерен.
|
||||
- [🐞 Убирать записанный файл, когда приём отказал на середине](items/orphan-file-on-failed-intake.md) — Отказ чтения метаданных и отказ записи на диск оставляют файл в каталоге хранения без задачи и без учёта: сопоставить его не с чем, удалять приходится руками.
|
||||
- [🧹 Разобрать мелочи слоя хранилища](items/storage-layer-nits.md) — Три мелочи ниже потолка триажа: цикл воркера пишет потерю захвата уровнем ERROR и считает её отказом, тип ошибки заведён там, где конвенция просит sentinel, а FileName несёт два разных смысла.
|
||||
- [✨ Собрать каркас приложения и раздать его из бинарника](items/spa-skeleton.md) — Экранов нет и собирать их нечем: ни сборки фронтенда, ни раздачи статики в проекте не существует.
|
||||
@@ -94,4 +94,8 @@
|
||||
- [🧹 Закрепить версию рантайм-базы образа](items/pin-runtime-image-base.md) — Финальный слой Dockerfile собирается на alpine:latest, а task image идёт с --pull, поэтому два образа из одного коммита с разницей в неделю несут разный ffmpeg — регрессия конвертации после такой пересборки выглядит как задачи в failed при пустом диффе репозитория, и откат на прежний коммит её не чинит.
|
||||
- [🧹 Настроить конвейер ревью по итогам прогона go-1-26-upgrade](items/review-config-from-go-upgrade.md) — Прогон вскрыл две прорехи настройки: «Типовые узлы» знают только рантайм и не знают рода «проверочный шаг набора проверок», а «Триггеры метки» не видят оси «изменение трогает канон» — и именно она дала обе блокирующие находки.
|
||||
- [🐞 Починить срок сессии, который ставит откат шага входа](items/rollback-restores-wrong-session-duration.md) — Константа defaultAuthTokenDuration в шаге 202608120001 названа умолчанием библиотеки, но 1209600 — это 14 суток, а умолчание PocketBase 432000, пять суток: откат объявляет возврат к умолчанию и ставит срок вдвое больше выбранных владельцем семи.
|
||||
- [🧹 Проверить шаг гейта migrations так же, как шаг сверки версий Go](items/migrations-step-norm-and-tests.md) — Шаг охраняет critical-инвариант «применённый шаг схемы не переписывается», но своих проверок не имеет: дрейф шаблона имени, переезд каталога или потеря grep в конвейере оставят его вечно зелёным, и это не заметит ничто.
|
||||
- [🧹 Запретить обращаться к Bot API мимо клиента бота](items/bot-api-only-through-bot-client.md) — Чистка отказа от адреса с токеном живёт в клиенте; свой http.Client в транспорте вернёт утечку молча — правило noctx такую подмену не ловит, а класс уже стоил одного дефекта.
|
||||
- [🔬 Шаги гейта, у которых правило может потерять предмет](items/gate-steps-subject-guard.md) — У шага migrations страж предмета есть, у шагов docs, tasks и openspec неизвестно: они зовут чужие скрипты из плагинов, и правило, потерявшее файлы, зеленело бы молча.
|
||||
- [🧹 Свести шесть расхождений между документами канона](items/docs-consistency-2026-08-13.md) — Сверка 2026-08-13 нашла шесть мест, где два документа отвечают на один вопрос по-разному; четыре из них в architecture.md, и по ним читатель строит решения о выкладке и о периметре.
|
||||
- [🔬 Квота по общему размеру загруженного на пользователя](items/per-user-size-quota.md) — Паспорт и security.md запрещают отказы по квоте пользователю, а заметка владельца просит квоту по умолчанию 5 ГБ — открытое противоречие с границей домена, которое владелец решил не разбирать сейчас.
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# 🧹 Запретить обращаться к Bot API мимо клиента бота
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь — Класс уже дал утечку токена; сегодня его держат две проверки на сегодняшних местах, а не правило.
|
||||
- **Зачем:** Чистка отказа от адреса с токеном живёт в клиенте; свой http.Client в транспорте вернёт утечку молча — правило noctx такую подмену не ловит, а класс уже стоил одного дефекта.
|
||||
|
||||
Токен бота стоит в пути каждого обращения к Bot API, а `http.Client` кладёт
|
||||
адрес запроса в `*url.Error` целиком. Поэтому 2026-08-13 чистка перенесена на
|
||||
границу клиента: `internal/adapter/telegram`, `NewBot` — свой `Do` разворачивает
|
||||
отказ в первопричину, а подменённый логгер вычищает токен из строк самой
|
||||
библиотеки. Случай записан в журнал дефектов
|
||||
[review.md](../../docs/review.md), 2026-08-13.
|
||||
|
||||
Держится это на двух стыках, и оба обходятся одной строкой: скачивание записи
|
||||
берёт клиента у бота (`c.bot.Client.Do`), а не общий, и бот заводится единой
|
||||
точкой, а не `tgbotapi.NewBotAPI`. Проверки на оба стыка есть
|
||||
(`internal/controller/tg/download_test.go`,
|
||||
`internal/adapter/telegram/bot_test.go`), но они судят **сегодняшние** места:
|
||||
новый файл в транспорте, заведший свой клиент, они не увидят.
|
||||
|
||||
Ступень по лестнице механизации — четвёртая: свойство о структуре, а не о
|
||||
вызове, и выражается тест-сканером в `internal/archrules`.
|
||||
|
||||
## Затрагивает
|
||||
|
||||
- `internal/archrules` — новое правило-сканер и его перечень предметов;
|
||||
- `internal/controller/tg` — как поднадзорный пакет;
|
||||
- `internal/adapter/telegram` — как единственное законное место, где клиент
|
||||
заводится;
|
||||
- [go-linters.md](../../docs/conventions/go-linters.md) — строка в перечне
|
||||
механизированного и снятие соответствующей прозы.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- Свой `http.Client`, `http.DefaultClient` или `tgbotapi.NewBotAPI` в
|
||||
`internal/controller/tg` роняет проверку и называет файл. Оракул — мутация:
|
||||
внести каждую из трёх форм по очереди, все три краснеют.
|
||||
- Законное употребление в `internal/adapter/telegram` проверку не роняет.
|
||||
Оракул — тот же прогон на нетронутом дереве, зелёный.
|
||||
- Правило не молчит, потеряв предмет: переименованный или исчезнувший пакет
|
||||
роняет проверку, а не проходит обходом по пустому перечню. Оракул — мутация с
|
||||
переименованием каталога.
|
||||
- Правило записано строкой в `go-linters.md`, и проза, которую оно заменило,
|
||||
оттуда снята. Оракул — чтение записи.
|
||||
|
||||
## Рамки
|
||||
|
||||
Боевым токеном не запускаться: сканер читает исходники, живого бота ему не
|
||||
нужно. Внутренний логгер библиотеки правилом не выражается — его сторожит
|
||||
проверка `TestLibraryLoggerIsActuallyInstalled`, и это остаётся так.
|
||||
@@ -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`.
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# 🧹 Свести шесть расхождений между документами канона
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь — Находки одной сверки: чинится одним заходом, пока помнится, чем каждое место было найдено.
|
||||
- **Зачем:** Сверка 2026-08-13 нашла шесть мест, где два документа отвечают на один вопрос по-разному; четыре из них в architecture.md, и по ним читатель строит решения о выкладке и о периметре.
|
||||
|
||||
Находки сверки документов агентами `doc-consistency` и `doc-code-drift`,
|
||||
прогнанной 2026-08-13 вместе с работой о контексте и токене. К той работе
|
||||
расхождения отношения не имеют — они старше, и потому не чинились тем же
|
||||
коммитом.
|
||||
|
||||
Каждое место названо с домом факта, то есть с тем документом, который прав:
|
||||
|
||||
1. **Панель администратора против Authelia.** `architecture.md`, «Открытые
|
||||
вопросы»: «Панель администратора Authelia не закрывает». `security.md`,
|
||||
«Периметр»: адрес `/_/` закрывает Authelia на обратном прокси решением от
|
||||
2026-08-11. Дом — `security.md`.
|
||||
2. **Провайдер OIDC не назван внешней зависимостью.** Работает с 2026-08-12, у
|
||||
него два таймаута в `database.md` и нормативная спека `access`, но в таблице
|
||||
зависимостей `architecture.md` его нет, а языковая модель числится там
|
||||
«пятой зависимостью». Дом — `architecture.md`, править его.
|
||||
3. **«Таймаута нет ни у одного обращения наружу»** (`architecture.md`,
|
||||
«Эксплуатация») против таймаутов обмена кода у провайдера в `database.md` и
|
||||
требования спеки `access`. Верно узкое утверждение: таймаутов нет у Telegram,
|
||||
Object Storage и SpeechKit. Дом — `database.md`.
|
||||
4. **gin в `README.md`.** Веб-фреймворка нет: HTTP-поверхность — роутер
|
||||
встроенной PocketBase, и `logging.md` прямо говорит, что вместе с gin ушёл и
|
||||
`sloggin`. Дом стека — `CLAUDE.md`.
|
||||
5. **Вид временной метки.** `conventions/database.md`: RFC 3339 с `T`, секундная
|
||||
точность. `docs/database.md`: `2006-01-02 15:04:05.000Z`, и вид обязателен
|
||||
побайтово — сравнение в SQLite строковое. Дом — `docs/database.md`;
|
||||
конвенции нужна строка «*Расхождение:*».
|
||||
6. **Дубли текста в `CLAUDE.md`** — подавления `hadolint` и настройка
|
||||
`errcheck` пересказаны там дословно, хотя обе преамбулы договорились, что
|
||||
дом перечня подавлений — `go-linters.md`.
|
||||
|
||||
## Затрагивает
|
||||
|
||||
- `docs/architecture.md` — «Открытые вопросы», таблица внешних зависимостей,
|
||||
раздел «Эксплуатация»;
|
||||
- `docs/conventions/database.md` — вид временной метки;
|
||||
- `docs/security.md` и `docs/database.md` — как дома фактов, если правка
|
||||
потребует уточнить формулировку;
|
||||
- `README.md` — перечень технологий;
|
||||
- `CLAUDE.md` — раздел «Гейт», два места с пересказом.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- Ни одно из шести мест не отвечает на свой вопрос двумя способами. Оракул —
|
||||
повторный прогон `av-dev-docs:healthcheck`: перечисленные шесть находок не
|
||||
возвращаются.
|
||||
- Провайдер OIDC стоит в таблице внешних зависимостей со своими четырьмя
|
||||
столбцами отказа, и счёт зависимостей в «Открытых вопросах» сходится с
|
||||
таблицей. Оракул — чтение таблицы: число строк против числа в тексте.
|
||||
- Дрейф раскладки и битые ссылки не заведены. Оракул — `task gate` зелёный
|
||||
(шаг `docs`).
|
||||
|
||||
## Рамки
|
||||
|
||||
Правится текст документов, а не поведение сервиса: если расхождение окажется
|
||||
дефектом кода, оно уезжает своей задачей, а не чинится здесь. Спеки
|
||||
`openspec/specs/` не трогаем — у них свой порядок изменения.
|
||||
@@ -0,0 +1,34 @@
|
||||
# 🔬 Шаги гейта, у которых правило может потерять предмет
|
||||
|
||||
- **Тип:** research
|
||||
- **Категория:** Очередь — Разведка о чужих скриптах: пока ответа нет, неизвестно даже, есть ли работа.
|
||||
- **Зачем:** У шага migrations страж предмета есть, у шагов docs, tasks и openspec неизвестно: они зовут чужие скрипты из плагинов, и правило, потерявшее файлы, зеленело бы молча.
|
||||
|
||||
Класс известен и записан: правило, чей предмет исчез, обходит пустой перечень
|
||||
ноль раз и проходит зелёным. В `internal/archrules` от этого стоит
|
||||
`TestПакетыПравилСуществуют` — он падает, когда пакет из правила переименован. У
|
||||
шага `migrations` страж завёлся 2026-08-13: пустой каталог шагов роняет шаг с
|
||||
кодом 3.
|
||||
|
||||
Чего не знаем: ведут ли себя так же `docs.py check`, `tasks.py check` и
|
||||
`openspec.py check`. Скрипты чужие — они живут в плагинах `av-dev-docs`,
|
||||
`av-dev-tasks` и `av-dev-code`, и править их в этом репозитории нельзя. Отсюда и
|
||||
тип записи: способ починки зависит от ответа. Найдётся страж внутри — делать
|
||||
нечего; не найдётся — либо обёртка в `Taskfile.yml` со своей проверкой предмета,
|
||||
либо разговор с владельцем плагина.
|
||||
|
||||
## Вопрос
|
||||
|
||||
Какие шаги гейта проходят зелёными, когда предмет их правила исчез, — и чем это
|
||||
чинится, если сам скрипт править нельзя?
|
||||
|
||||
## Куда ляжет ответ
|
||||
|
||||
`docs/research/gate-steps-subject-guard.md` — записка с перечнем шагов, снятыми
|
||||
исходами (по каждому: что сделали с предметом, каким кодом ответил шаг) и
|
||||
рекомендацией. Исход разведки — задачи на те шаги, где страж нужен и возможен.
|
||||
|
||||
## Рамки
|
||||
|
||||
Скрипты плагинов не правим: они не в этом репозитории. Прогоны идут на временном
|
||||
клоне репозитория, каталоги `docs/` и `tasks/` рабочего дерева не трогаем.
|
||||
@@ -0,0 +1,48 @@
|
||||
# 🧹 Проверить шаг гейта migrations так же, как шаг сверки версий Go
|
||||
|
||||
- **Тип:** chore
|
||||
- **Категория:** Очередь — Шаг уже стоит в гейте и уже назван стражем critical-инварианта в двух документах — необеспеченное обещание дороже отсутствующего.
|
||||
- **Зачем:** Шаг охраняет critical-инвариант «применённый шаг схемы не переписывается», но своих проверок не имеет: дрейф шаблона имени, переезд каталога или потеря grep в конвейере оставят его вечно зелёным, и это не заметит ничто.
|
||||
|
||||
Шаг заведён 2026-08-13 и проверен мутацией на восьми исходах вручную — правка
|
||||
уехавшего шага в дереве и в коммите, удаление, переименование, новый шаг, правка
|
||||
`migrations.go`, отсутствующий ключ в `docs/.docs.json`, каталог без шагов,
|
||||
неразрешимая база диффа. Прогон был разовым: в дереве от него не осталось ничего.
|
||||
|
||||
Прецедент рядом. У шага сверки версий Go есть спека
|
||||
[toolchain](../../openspec/specs/toolchain/spec.md) и 20 мутационно проверенных
|
||||
сценариев в `scripts/check_go_version_test.go`; заведены они после дефекта
|
||||
2026-08-12, когда зелёный шаг не проверял ничего и образ перестал собираться.
|
||||
Долг назван строкой в
|
||||
[go-linters.md](../../docs/conventions/go-linters.md), «Границы: где что живёт».
|
||||
|
||||
**Развилка, решаемая внутри задачи:** нормировать шаг спекой (второй capability
|
||||
о проверке, как `toolchain`) либо ограничиться проверками без нормы. Первое
|
||||
дороже и даёт построчную сверку сценариев; второе закрывает регрессию, но
|
||||
оставляет норму в комментарии `Taskfile.yml`.
|
||||
|
||||
## Затрагивает
|
||||
|
||||
- шаг `migrations` в `Taskfile.yml` — его логика разбора `git diff`;
|
||||
- ключ `migrations` в `docs/.docs.json` — из него шаг берёт каталог;
|
||||
- каталог шагов схемы `internal/adapter/repo/pocketbase/migrations/` как предмет
|
||||
правила;
|
||||
- возможно — новая capability в `openspec/specs/` и файл проверок рядом с
|
||||
`scripts/check_go_version_test.go`.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- Переписанный уехавший шаг схемы роняет проверку. Оракул — прогон сценария на
|
||||
временном клоне репозитория: правка файла шага даёт код 1 и называет файл.
|
||||
- Новый файл шага проверку не роняет, и правка `migrations.go` тоже: строка
|
||||
`Register` нового шага прибавляется именно там. Оракул — те же два сценария.
|
||||
- Каталог без единого файла шага и отсутствующий ключ в `docs/.docs.json` дают
|
||||
код 3, а не тихий ноль. Оракул — два сценария на временном каталоге.
|
||||
- Проверка сценариев идёт в гейте, а не руками. Оракул — `task gate` красный при
|
||||
внесённом нарушении шаблона имени файла шага.
|
||||
|
||||
## Рамки
|
||||
|
||||
Боевой каталог данных и файлы шагов схемы не трогаем: сценарии гоняются на
|
||||
временном клоне репозитория. Чужие скрипты проверок (`docs.py`, `tasks.py`,
|
||||
`openspec.py`) — не наши, они в задаче `gate-steps-subject-guard`.
|
||||
Reference in New Issue
Block a user