tasks: закрыты четыре задачи о гейте, заведён урожай ревью

- сняты fix-migrations-path-in-docs-config, gate-step-exit-codes,
  response-assertions-judge-result, gate-dependency-vulnerabilities
- заведена rollback-restores-wrong-session-duration: откат шага входа ставит
  14 суток, называя их умолчанием библиотеки в пять
This commit is contained in:
av
2026-08-12 22:06:11 +03:00
parent 3925c637f3
commit 8bffd30955
6 changed files with 54 additions and 158 deletions
+1 -4
View File
@@ -39,11 +39,7 @@
## Очередь ## Очередь
- [🐞 Починить путь миграций в настройке сверки документов](items/fix-migrations-path-in-docs-config.md) — Ключ migrations указывает на каталог migrations/, которого в репозитории нет: шаг гейта зелен при изменённой миграции и нетронутом database.md, а конвенции числят этот род механизированным.
- [🧹 Привести коды выхода шагов гейта к общему словарю](items/gate-step-exit-codes.md) — CLAUDE.md объявляет словарь кодов общим для четырёх проверочных шагов и описывает код 3 как «каталог или файл не найден», но все четыре обёртки в Taskfile.yml при отсутствующем скрипте отдают 1 («дрейф»), из-за чего читатель кода идёт искать разъехавшееся там, где просто неполно дерево.
- [🧹 Судить ответ в тестах по готовому ответу](items/response-assertions-judge-result.md) — Проверка, читающая изменяемую карту заголовков обработчика, зелена при неработающем коде: класс всплыл трижды, последний раз на уборке куки входа.
- [🧹 Ронять гейт на изменённой функции, которую не выполняет ни один тест](items/gate-changed-lines-coverage.md) — Свойство «изменённое место покрыто хоть одним тестом» записано в docs/review.md, но не механизировано: за две задачи подряд непокрытые шаги ловили руками. - [🧹 Ронять гейт на изменённой функции, которую не выполняет ни один тест](items/gate-changed-lines-coverage.md) — Свойство «изменённое место покрыто хоть одним тестом» записано в docs/review.md, но не механизировано: за две задачи подряд непокрытые шаги ловили руками.
- [🧹 Ловить уязвимости в зависимостях шагом гейта](items/gate-dependency-vulnerabilities.md) — govulncheck находит две достижимые уязвимости в клиентах Yandex, а ни гейт, ни список «чего в гейте нет» о нём не знают: узнать о третьей будет неоткуда.
- [🧹 Поднимать сервис локально без действующего токена бота](items/local-run-without-telegram-token.md) — Адаптер Telegram проверяет токен обращением к Telegram и роняет старт, а боевым токеном запускаться запрещено: проверить поведение живым прогоном не может ни одна задача. - [🧹 Поднимать сервис локально без действующего токена бота](items/local-run-without-telegram-token.md) — Адаптер Telegram проверяет токен обращением к Telegram и роняет старт, а боевым токеном запускаться запрещено: проверить поведение живым прогоном не может ни одна задача.
- [🐞 Убрать код провайдера из журнала запросов хранилища](items/provider-code-out-of-storage-log.md) — Строка запроса с кодом входа целиком уезжает в таблицу _logs и лежит там пять суток, хотя спека access требует, чтобы код в журнал не попадал. - [🐞 Убрать код провайдера из журнала запросов хранилища](items/provider-code-out-of-storage-log.md) — Строка запроса с кодом входа целиком уезжает в таблицу _logs и лежит там пять суток, хотя спека access требует, чтобы код в журнал не попадал.
- [🐞 Вести учёт употреблённых состояний входа на сервере](items/server-side-login-state.md) — Одноразовость возврата держится на уборке куки, то есть на браузере: сервер не помнит, какие состояния уже потрачены. - [🐞 Вести учёт употреблённых состояний входа на сервере](items/server-side-login-state.md) — Одноразовость возврата держится на уборке куки, то есть на браузере: сервер не помнит, какие состояния уже потрачены.
@@ -98,4 +94,5 @@
- [🧹 Завести недостающие проверки: shellcheck, hadolint и тест скрипта сверки версий](items/gate-extra-linters.md) — Скрипты оболочки и Dockerfile машина не проверяет: shellcheck и hadolint не заведены, lefthook смотрит только gitleaks, а единственный исполняемый скрипт проекта на shell — scripts/check-go-version.sh — разбирали на ревью глазами построчно, потому что машине его показать нечем. - [🧹 Завести недостающие проверки: shellcheck, hadolint и тест скрипта сверки версий](items/gate-extra-linters.md) — Скрипты оболочки и Dockerfile машина не проверяет: shellcheck и hadolint не заведены, lefthook смотрит только gitleaks, а единственный исполняемый скрипт проекта на shell — scripts/check-go-version.sh — разбирали на ревью глазами построчно, потому что машине его показать нечем.
- [🧹 Закрепить версию рантайм-базы образа](items/pin-runtime-image-base.md) — Финальный слой Dockerfile собирается на alpine:latest, а task image идёт с --pull, поэтому два образа из одного коммита с разницей в неделю несут разный ffmpeg — регрессия конвертации после такой пересборки выглядит как задачи в failed при пустом диффе репозитория, и откат на прежний коммит её не чинит. - [🧹 Закрепить версию рантайм-базы образа](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) — Прогон вскрыл две прорехи настройки: «Типовые узлы» знают только рантайм и не знают рода «проверочный шаг набора проверок», а «Триггеры метки» не видят оси «изменение трогает канон» — и именно она дала обе блокирующие находки. - [🧹 Настроить конвейер ревью по итогам прогона go-1-26-upgrade](items/review-config-from-go-upgrade.md) — Прогон вскрыл две прорехи настройки: «Типовые узлы» знают только рантайм и не знают рода «проверочный шаг набора проверок», а «Триггеры метки» не видят оси «изменение трогает канон» — и именно она дала обе блокирующие находки.
- [🐞 Починить срок сессии, который ставит откат шага входа](items/rollback-restores-wrong-session-duration.md) — Константа defaultAuthTokenDuration в шаге 202608120001 названа умолчанием библиотеки, но 1209600 — это 14 суток, а умолчание PocketBase 432000, пять суток: откат объявляет возврат к умолчанию и ставит срок вдвое больше выбранных владельцем семи.
- [🔬 Квота по общему размеру загруженного на пользователя](items/per-user-size-quota.md) — Паспорт и security.md запрещают отказы по квоте пользователю, а заметка владельца просит квоту по умолчанию 5 ГБ — открытое противоречие с границей домена, которое владелец решил не разбирать сейчас. - [🔬 Квота по общему размеру загруженного на пользователя](items/per-user-size-quota.md) — Паспорт и security.md запрещают отказы по квоте пользователю, а заметка владельца просит квоту по умолчанию 5 ГБ — открытое противоречие с границей домена, которое владелец решил не разбирать сейчас.
@@ -1,48 +0,0 @@
# 🐞 Починить путь миграций в настройке сверки документов
- **Тип:** fix
- **Категория:** Очередь — Шаг гейта зелен при изменённой миграции и нетронутом database.md: на этом шаге стоит приёмка всякой следующей задачи, трогающей схему.
- **Зачем:** Ключ migrations указывает на каталог migrations/, которого в репозитории нет: шаг гейта зелен при изменённой миграции и нетронутом database.md, а конвенции числят этот род механизированным.
Найдено ревью задачи `oidc-login` 2026-08-12, отчёт триажа —
[review/report.md](../../openspec/changes/archive/2026-08-12-oidc-login/review/report.md),
раздел «Promote candidates».
Проверка сверяет изменённые файлы с префиксом `migrations/`, а шаги схемы лежат
в `internal/adapter/repo/pocketbase/`. Совпадений не бывает никогда, значит шаг
проходит зелёным всегда. Оракул сегодняшнего состояния:
`git ls-files | grep -c "^migrations/"` отдаёт `0`.
Цена уже заплачена дважды: задача `oidc-login` изменила шаг схемы и не тронула
`docs/database.md`, и гейт этого не заметил — расхождение нашёл человек на
ревью. Так же провалится всякая следующая миграция.
Развилка внутри задачи: либо поправить путь, либо снять пометку
«механизировано» в `docs/conventions/README.md` и отдать род человеку. Второе
дешевле, но тогда проверять его будет некому.
## Воспроизведение
1. Изменить любой файл шагов схемы в `internal/adapter/repo/pocketbase/`.
2. `docs/database.md` не трогать.
3. Прогнать `task docs BASE=origin/master`.
4. Шаг проходит зелёным, хотя должен назвать расхождение. Сегодняшнее состояние
настройки видно командой `git ls-files | grep -c "^migrations/"` — она отдаёт
`0`, то есть каталога с таким именем в репозитории нет.
## Затрагивает
- `docs/.docs.json`, ключ `migrations`;
- `docs/conventions/README.md`, таблица «Механизировано», строка про миграцию;
- шаг `docs` в `Taskfile.yml` — его исход меняется.
## Критерии приёмки
- Изменённый шаг схемы при нетронутом `docs/database.md` роняет шаг гейта.
Оракул — правка любого файла шагов схемы без правки схемы в документах, затем
`task docs BASE=origin/master`: ненулевой код возврата.
- Изменённый шаг схемы вместе с правкой `docs/database.md` шаг гейта проходит.
Оракул — то же с обеими правками: код возврата 0.
- Строка «Механизировано» в `docs/conventions/README.md` соответствует тому, что
проверка делает на самом деле. Оракул — чтение таблицы против исхода первых
двух проверок.
@@ -1,35 +0,0 @@
# 🧹 Ловить уязвимости в зависимостях шагом гейта
- **Тип:** chore
- **Категория:** Очередь — govulncheck находит две достижимые уязвимости, а гейт о нём не знает: о третьей узнать будет неоткуда.
- **Зачем:** govulncheck находит две достижимые уязвимости в клиентах Yandex, а ни гейт, ни список «чего в гейте нет» о нём не знают: узнать о третьей будет неоткуда.
Обе найдены ревью задачи `pocketbase-storage` 2026-08-12 и достижимы из кода
распознавателя: `GO-2026-6061` в `google.golang.org/grpc` (исправлено в v1.82.1)
и `GO-2026-5764` в `aws-sdk-go-v2``aws/protocol/eventstream` (в v1.7.8) и
`service/s3` (в v1.97.3). Ни одну из них эта задача не вносила: `git diff
origin/master -- go.mod` их версий не касался.
## Затрагивает
- версии `google.golang.org/grpc`, `aws-sdk-go-v2/service/s3` и
`aws-sdk-go-v2/aws/protocol/eventstream` в `go.mod`;
- набор шагов `task gate` в `Taskfile.yml`;
- семантика гейта в `CLAUDE.md` — раздел «Гейт», список «что красит безусловно»
либо «чего в гейте намеренно нет».
## Критерии приёмки
- Обе объявленные уязвимости из кода недостижимы. Оракул — `govulncheck ./...`
печатает «No vulnerabilities found» либо оставляет только недостижимые.
- Новая достижимая уязвимость роняет гейт. Оракул — прогон `task gate` на
дереве с намеренно откаченной версией `grpc`: шаг краснеет с именем уязвимости.
- Место шага названо в `CLAUDE.md`: либо в перечне красящих безусловно, либо в
«чего в гейте нет» с указанием, кто тогда обязан его гонять. Оракул —
`task gate`, шаг `docs.py check`.
## Рамки
Обновление версий трогает клиентов Yandex — прогон на реальных ключах запрещён,
подставляй `internal/adapter/recognizer/memory.go`. Решение, ронять ли гейт на
уязвимости без исправления, принимает человек.
-35
View File
@@ -1,35 +0,0 @@
# 🧹 Привести коды выхода шагов гейта к общему словарю
- **Тип:** chore
- **Категория:** Очередь — Словарь кодов уже разъехался с обёртками: три новых шага гейта ниже напишутся по неверному образцу и попадут под переписывание.
- **Зачем:** CLAUDE.md объявляет словарь кодов общим для четырёх проверочных шагов и описывает код 3 как «каталог или файл не найден», но все четыре обёртки в Taskfile.yml при отсутствующем скрипте отдают 1 («дрейф»), из-за чего читатель кода идёт искать разъехавшееся там, где просто неполно дерево.
Расхождение старое и повторено всеми четырьмя обёртками одинаково — дефектом
одного изменения оно не является. Сами скрипты словарю следуют: при пропавшем
источнике `scripts/check-go-version.sh` выходит с кодом 3, проверено прогоном
2026-08-12.
Развилка тут есть, и решать её придётся: либо обёртки приводятся к 3, либо из
описания кода 3 в памятке убирается «или файл не найден» и `exit 1` объявляется
нормой для «скрипта шага нет». Второй вариант меняет только текст памятки, а не
код всех четырёх обёрток, и тоже не расходится с фактами.
## Затрагивает
- шаги `docs`, `tasks`, `openspec` и `go-version` в `Taskfile.yml`;
- словарь кодов в `CLAUDE.md`, раздел «Гейт».
## Критерии приёмки
- Один и тот же исход даёт один и тот же код у всех четырёх шагов. Оракул —
убрать по очереди каждый из четырёх скриптов и прогнать его шаг: коды
совпадают между собой и с тем, что обещает памятка.
- Памятка и поведение сошлись. Оракул — прочитать словарь в `CLAUDE.md` и
сверить с четырьмя прогонами выше поимённо.
- Гейт зелёный целиком. Оракул — `task gate`.
## Рамки
Скрипты плагинов (`docs.py`, `tasks.py`, `openspec.py`) не правятся: они живут
вне репозитория, и их коды уже следуют словарю. Правятся только обёртки в
`Taskfile.yml` и текст памятки.
@@ -1,36 +0,0 @@
# 🧹 Судить ответ в тестах по готовому ответу
- **Тип:** chore
- **Категория:** Очередь — Тест, читающий живую карту заголовков, зелен при неработающем коде: класс всплывал трижды и трижды стоил ложной приёмки.
- **Зачем:** Проверка, читающая изменяемую карту заголовков обработчика, зелена при неработающем коде: класс всплыл трижды, последний раз на уборке куки входа.
Найдено ревью задачи `oidc-login` 2026-08-12, отчёт триажа —
[review/report.md](../../openspec/changes/archive/2026-08-12-oidc-login/review/report.md),
раздел «Promote candidates», первый пункт.
Инструмент проверки устроен зеркально настоящему серверу: у сервера заголовки
фиксируются в момент, когда ответ начинают писать, а у него живая карта остаётся
доступной и после. Проверка, читающая живую карту, видит то, чего клиент не
получит, и остаётся зелёной при любой регрессии в этом месте.
Класс повторяется третий раз — записи журнала дефектов от 2026-08-10, 2026-08-11
и 2026-08-12, — и все три раза стоил зелёного гейта при неработающем поведении.
Отсюда продвижение: правило в конвенции плюс механизация, а после механизации проза
из конвенции убирается.
## Затрагивает
- `docs/conventions/` — новая запись либо раздел существующей: чем судят ответ;
- `docs/conventions/README.md`, перечень механизированного;
- набор шагов `task gate` — место, где живёт проверка правила;
- существующие тесты обработчиков: те, что читают живую карту заголовков.
## Критерии приёмки
- Правило записано в конвенциях одной формулировкой, и названо место
механизации. Оракул — чтение `docs/conventions/README.md`: строка есть, ссылка
ведёт в существующее место.
- Проверка, читающая живую карту заголовков, роняет гейт. Оракул — завести такую
строку в любом тесте и прогнать гейт: ненулевой код возврата.
- Проза из конвенции убрана после механизации, а не осталась дублем. Оракул —
чтение записи: правило названо один раз, дальше ссылка на механизацию.
@@ -0,0 +1,53 @@
# 🐞 Починить срок сессии, который ставит откат шага входа
- **Тип:** fix
- **Категория:** Очередь — Путь сегодня недостижим: откаты не подключены, и пока они не подключены, цена дефекту нулевая.
- **Зачем:** Константа defaultAuthTokenDuration в шаге 202608120001 названа умолчанием библиотеки, но 1209600 — это 14 суток, а умолчание PocketBase 432000, пять суток: откат объявляет возврат к умолчанию и ставит срок вдвое больше выбранных владельцем семи.
Найдено ревью пачки задач о гейте 2026-08-12, проход разбора кода. Дефект
пришёл из задачи `oidc-login` и её правкой не был замечен; переезд шагов схемы в
свой каталог развёл противоречащие комментарии по разным файлам, где раньше они
стояли в трёх строках друг от друга: `provider.go` пишет «умолчание библиотеки в
пять суток», шаг — «умолчание библиотеки» о числе в 14 суток.
Оракул сегодняшнего состояния:
`grep -n "Duration: 432000" ~/go/pkg/mod/github.com/pocketbase/pocketbase@v0.39.10/core/collection_model_auth_options.go`
отдаёт строку `Duration: 432000, // 5days`. Числа `1209600` в модуле нет ни разу
как длительности токена.
**Развилка, которую решает человек:** правка меняет поведение **применённого**
шага схемы, а такое спрашивается всегда. Либо константа приводится к 432000
внутри существующего шага (откат ещё не исполнялся ни разу, и переписывание
трогает только будущие откаты), либо 14 суток объявляются выбранным числом — и
тогда меняется имя константы и её комментарий, а не значение.
## Воспроизведение
1. Подключить откаты (`migratecmd`) — сегодня их не подключает ничто.
2. Откатить шаг `202608120001_oidc_login.go`.
3. Прочитать `AuthToken.Duration` коллекции `users`: там 1209600 — вдвое больше
семи суток, выбранных владельцем, и в 2.8 раза больше умолчания библиотеки,
к которому откат обещал вернуть.
## Затрагивает
- `internal/adapter/repo/pocketbase/migrations/202608120001_oidc_login.go`,
константа `defaultAuthTokenDuration` и комментарий `down202608120001`;
- `internal/adapter/repo/pocketbase/provider.go`, комментарий к
`SessionDuration` — он называет умолчание пятью сутками;
- поведение откатов, если их подключат.
## Критерии приёмки
- Число в откате и число, названное умолчанием библиотеки, совпадают. Оракул —
чтение константы против `core/collection_model_auth_options.go` версии
PocketBase из `go.mod`.
- Комментарии шага и `provider.go` говорят об умолчании одно и то же. Оракул —
чтение обоих мест подряд.
- Гейт зелёный целиком. Оракул — `task gate`.
## Рамки
Правка применённого шага схемы — необратимое: решение принимает человек. Новым
шагом это не лечится: новый шаг накатывается вперёд, а исправить нужно ветку
отката прежнего.