diff --git a/tasks/BACKLOG.md b/tasks/BACKLOG.md index 54a0e85..f1fea1d 100644 --- a/tasks/BACKLOG.md +++ b/tasks/BACKLOG.md @@ -53,7 +53,6 @@ - [🧹 Разобрать мелочи http-транспорта](items/http-transport-nits.md) — Маршруты зарегистрированы дважды, и переименование пути в main.go проходит проверки зелёным; обработчик пишет в журнал через стандартный log и дублирует запись, уже сделанную сервисом. - [🧹 Переименовать образец конфига в config.example.toml](items/config-example-toml.md) — Конвенция называет config.dist.toml объявленным расхождением, но тут же пишет это имя как правило — документ противоречит сам себе, а образец расходится с конвенцией. - [🔬 Стоит ли брать OpenTelemetry вместо голого Prometheus](items/opentelemetry-fit.md) — Метрик одиннадцать штук на пять счётчиков, трассировки нет вовсе: путь одной записи по конвейеру собирается только чтением логов глазами. -- [🧹 Обновить Go до 1.26 и сверять версию шагом гейта](items/go-1-26-upgrade.md) — Модуль объявляет go 1.25.0, образ собирается на golang:1.25-alpine, на машине разработки стоит 1.26.5, и расхождение этих чисел не ловит ни один шаг гейта: разъехавшийся Dockerfile прошёл весь конвейер зелёным. - [🧹 Ловить уязвимости в зависимостях шагом гейта](items/gate-dependency-vulnerabilities.md) — govulncheck находит две достижимые уязвимости в клиентах Yandex, а ни гейт, ни список «чего в гейте нет» о нём не знают: узнать о третьей будет неоткуда. - [🧹 Считать покрытие изменённых строк шагом гейта](items/gate-changed-lines-coverage.md) — Свойство «изменённое место покрыто хоть одним тестом» записано в docs/review.md, но не механизировано: за две задачи подряд непокрытые шаги ловили руками. - [🧹 Прервать шаг конвейера отменой контекста](items/context-cancel-in-pipeline.md) — Воркер читает ctx только между итерациями: остановка контейнера ждёт конца шага, а на занятом писателе один запрос к хранилищу держится до 9,5 секунды при мягком таймауте в 5. @@ -61,4 +60,8 @@ - [🔬 Уведомление SpeechKit о готовности вместо опроса](items/speechkit-callback-fit.md) — Шаг проверки дёргает операцию раз в 5 секунд всё время распознавания: часовая запись даёт порядка 720 обращений к платному сервису вместо одного ответа. - [🔬 Адрес объекта в тексте отказа SpeechKit](items/speechkit-error-text-leak.md) — Текст отказа операции приходит от Yandex и уезжает в журнал и в колонку error_text: если он несёт URI объекта, из журнала снова собирается ссылка на чужую запись. - [✨ Проигрывать загруженную запись на экране записи](items/play-recording-in-app.md) — Послушать загруженное приложение не даёт, а самой копии для этого у задачи нет: указатель на файл перезаписывается на каждом шаге конвейера и у готовой задачи ведёт на объект в Object Storage. +- [🧹 Завести недостающие проверки: 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/gate-step-exit-codes.md) — CLAUDE.md объявляет словарь кодов общим для четырёх проверочных шагов и описывает код 3 как «каталог или файл не найден», но все четыре обёртки в Taskfile.yml при отсутствующем скрипте отдают 1 («дрейф»), из-за чего читатель кода идёт искать разъехавшееся там, где просто неполно дерево. +- [🧹 Настроить конвейер ревью по итогам прогона go-1-26-upgrade](items/review-config-from-go-upgrade.md) — Прогон вскрыл две прорехи настройки: «Типовые узлы» знают только рантайм и не знают рода «проверочный шаг набора проверок», а «Триггеры метки» не видят оси «изменение трогает канон» — и именно она дала обе блокирующие находки. - [🔬 Квота по общему размеру загруженного на пользователя](items/per-user-size-quota.md) — Паспорт и security.md запрещают отказы по квоте пользователю, а заметка владельца просит квоту по умолчанию 5 ГБ — открытое противоречие с границей домена, которое владелец решил не разбирать сейчас. diff --git a/tasks/INBOX.md b/tasks/INBOX.md new file mode 100644 index 0000000..17feb1a --- /dev/null +++ b/tasks/INBOX.md @@ -0,0 +1,4 @@ +# Входящие + +Сырые заметки до разбора. Разбирает владелец; разобранное уезжает задачами и +здесь не остаётся. diff --git a/tasks/items/gate-extra-linters.md b/tasks/items/gate-extra-linters.md new file mode 100644 index 0000000..937eb41 --- /dev/null +++ b/tasks/items/gate-extra-linters.md @@ -0,0 +1,57 @@ +# 🧹 Завести недостающие проверки: shellcheck, hadolint и тест скрипта сверки версий + +- **Тип:** chore +- **Категория:** Очередь +- **Зачем:** Скрипты оболочки и Dockerfile машина не проверяет: shellcheck и hadolint не заведены, lefthook смотрит только gitleaks, а единственный исполняемый скрипт проекта на shell — scripts/check-go-version.sh — разбирали на ревью глазами построчно, потому что машине его показать нечем. + +Собрана из двух источников: заметки владельца от 2026-08-12 про lefthook и +находки N-1 ревью `go-1-26-upgrade` про непроверенный скрипт сверки версий. +Собраны вместе, потому что правятся одним заходом: и то и другое — недостающие +проверки на файлы, которых сегодняшний гейт не видит. + +Цена shellcheck замерена на прогоне 2026-08-12: shell-файлов в репозитории два, +в гейте из них один, единственное замечание — ложный `SC1007` на идиому +`CDPATH= cd`, и оно уже подавлено в скрипте директивой. То есть шаг стоит одной +строки. + +Отдельная половина — **скрипт сверки версий не проверен ничем.** Его норма +(`openspec/specs/toolchain/spec.md`) описывает девятнадцать сценариев, а гейт +гоняет скрипт ровно на согласованном дереве, то есть проверяет один из них — тот, +где всё сошлось. Остальные восемнадцать подтверждены разовыми ручными прогонами +2026-08-12 и после первой же правки образца перестанут выполняться молча. + +Проверки go (`go vet`, `gofmt`) в гейте уже есть; в заметке речь о том, чтобы +часть из них ловил и pre-commit, не дожидаясь полного прогона. + +## Затрагивает + +- `lefthook.yml` — набор предкоммитных проверок; +- набор шагов `task gate` в `Taskfile.yml`; +- новый тест-скрипт для `scripts/check-go-version.sh`; +- `Dockerfile` — как объект проверки hadolint, не правкой содержимого; +- семантика гейта в `CLAUDE.md`, раздел «Гейт», подраздел «чего в гейте + намеренно нет»; +- таблица «Механизировано» в `docs/conventions/README.md`. + +## Критерии приёмки + +- `shellcheck` краснеет на негодном скрипте оболочки. Оракул — внести в + `scripts/check-go-version.sh` неэкранированную переменную и прогнать шаг: он + краснеет; вернуть — зеленеет. +- `hadolint` краснеет на негодном `Dockerfile`. Оракул — тот же приём: снять + закрепление версии базового образа и прогнать шаг. +- Каждый сценарий нормы `toolchain` проверяется машиной, а не памятью. Оракул — + тест-скрипт мутирует дерево по одному сценарию за прогон; снять любую проверку + из `check-go-version.sh` — тест краснеет и называет сценарий. +- Предкоммитные проверки не дублируют гейт молча. Оракул — `CLAUDE.md`, раздел + «Гейт», называет, что именно ловит pre-commit и что остаётся только гейту. +- Гейт зелёный целиком. Оракул — `task gate`. + +## Рамки + +Мутационный тест работает на временной копии дерева: `t.TempDir()`-подход, в +`data/` не писать, рабочее дерево после прогона обязано остаться нетронутым. + +Заметка владельца называет проверки go в lefthook, но полный набор `task gate` в +pre-commit не переносится: он идёт минуты, а pre-commit обязан быть быстрым. +Что именно из него дублировать — решение этой задачи, и его надо назвать. diff --git a/tasks/items/gate-step-exit-codes.md b/tasks/items/gate-step-exit-codes.md new file mode 100644 index 0000000..62db7b5 --- /dev/null +++ b/tasks/items/gate-step-exit-codes.md @@ -0,0 +1,35 @@ +# 🧹 Привести коды выхода шагов гейта к общему словарю + +- **Тип:** 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` и текст памятки. diff --git a/tasks/items/go-1-26-upgrade.md b/tasks/items/go-1-26-upgrade.md deleted file mode 100644 index bcb7f7f..0000000 --- a/tasks/items/go-1-26-upgrade.md +++ /dev/null @@ -1,53 +0,0 @@ -# 🧹 Обновить Go до 1.26 и сверять версию шагом гейта - -- **Тип:** chore -- **Категория:** Очередь -- **Зачем:** Модуль объявляет go 1.25.0, образ собирается на golang:1.25-alpine, на машине разработки стоит 1.26.5, и расхождение этих чисел не ловит ни один шаг гейта: разъехавшийся Dockerfile прошёл весь конвейер зелёным. - -Задача собрана из двух: заметки владельца от 2026-08-12 про обновление и находки -ревью `pocketbase-storage` про несверяемые версии. Собраны вместе, потому что -правятся одним заходом: поднимать число в трёх местах и не заводить сверку -значит завести то же расхождение заново на следующей зависимости. - -Версия 1.25 в модуле появилась не выбором, а требованием PocketBase: `go mod -tidy` поднял директиву сам, а образ подтянули следом. То есть сегодняшнее число -никем не назначено. - -Случай расхождения записан журналом дефектов `docs/review.md` за 2026-08-12: -`go mod tidy` поднял директиву до 1.25, `Dockerfile` остался на -`golang:1.24-alpine` с `GOTOOLCHAIN=local`, и шесть проходов ревью вместе с -восемью шагами гейта показали зелёное. Нашлось случайным свойством прогона, а не -устройством проверок. - -Сборку образа в гейт не заводим — она дорога. Дешёвая замена: сравнение двух -строк. - -## Затрагивает - -- директива `go` в `go.mod`; -- сборочный слой `Dockerfile`; -- строка стека в `CLAUDE.md` и в `README.md`; -- набор шагов `task gate` в `Taskfile.yml`; -- семантика гейта в `CLAUDE.md`, раздел «Гейт». - -## Критерии приёмки - -- Модуль, образ и документы называют одну версию Go. Оракул — `grep` по четырём - местам: `go.mod`, `Dockerfile`, `CLAUDE.md`, `README.md`; все дают одно число. -- Сборка на объявленной версии проходит. Оракул — `task image` и - `CGO_ENABLED=0 go build ./...` на чистом дереве. -- Расхождение версий роняет гейт. Оракул — прогон `task gate` на дереве, где - версия в `Dockerfile` понижена на минор: шаг краснеет и называет оба числа. -- Совпадение гейт не роняет, а сам шаг не требует docker и работает без сети. - Оракул — `task gate` на неизменённом дереве и прогон с - `DOCKER_HOST=/dev/null`. -- Гейт зелёный целиком. Оракул — `task gate`. - -## Рамки - -Версию выбирает человек: обновление до неподдерживаемой ещё зависимостями версии -ломает сборку молча. Выкладку не запускаем. - -Сравнение строковое: `Dockerfile` называет `golang:.-alpine`, а -`go.mod` — `..`. Сверяются мажор и минор; патч сборочного -образа своей жизнью живёт законно. diff --git a/tasks/items/pin-runtime-image-base.md b/tasks/items/pin-runtime-image-base.md new file mode 100644 index 0000000..3e070c8 --- /dev/null +++ b/tasks/items/pin-runtime-image-base.md @@ -0,0 +1,42 @@ +# 🧹 Закрепить версию рантайм-базы образа + +- **Тип:** chore +- **Категория:** Очередь +- **Зачем:** Финальный слой Dockerfile собирается на alpine:latest, а task image идёт с --pull, поэтому два образа из одного коммита с разницей в неделю несут разный ffmpeg — регрессия конвертации после такой пересборки выглядит как задачи в failed при пустом диффе репозитория, и откат на прежний коммит её не чинит. + +Класс тот же, ради которого задача `go-1-26-upgrade` завела шаг сверки версий: +объявленное и собранное расходятся, а сверять некому. Разница в том, что там +расходились числа внутри репозитория, а здесь репозиторий вообще не называет +числа — версия рантайм-базы задана словом «последняя». + +Цена названа условием, а не замером: сколько именно и как часто меняется +`ffmpeg` в `alpine:latest`, никто не мерил. Известно, что он в таблице отказов +`docs/architecture.md` стоит со столбцом «Отдаёт мусор»: «Выходной файл пуст, +отказ вылезет на распознавании». + +Перечень мест, где объявлена версия Go, эта задача не расширяет: он закрыт +решением `go-1-26-upgrade`, и рантайм-база — другой предмет. + +## Затрагивает + +- финальный слой `Dockerfile` — тег базового образа; +- раздел «Деплой» в `docs/architecture.md`. + +## Критерии приёмки + +- Репозиторий называет версию рантайм-базы числом, а не словом «последняя». + Оракул — `grep` по `Dockerfile`: тег финального слоя содержит мажор и минор. +- Два образа, собранные из одного коммита, несут один `ffmpeg`. Оракул — собрать + `task image` дважды с интервалом и сравнить `ffmpeg -version` внутри обоих. +- Решение записано там, где его найдёт разбор отказа конвертации. Оракул — + раздел «Деплой» `docs/architecture.md` называет закреплённую базу и причину. +- Гейт зелёный целиком. Оракул — `task gate`. + +## Рамки + +Версию базы выбирает человек: перескок на минор новее может принести другой +`ffmpeg` ровно с тем же исходом, ради предотвращения которого задача и заведена. +Выкладку не запускаем. + +Если решено оставить `latest`, задача кончается записью принятого остатка в +`docs/architecture.md`, а не правкой `Dockerfile`. Это законный исход. diff --git a/tasks/items/review-config-from-go-upgrade.md b/tasks/items/review-config-from-go-upgrade.md new file mode 100644 index 0000000..683f40d --- /dev/null +++ b/tasks/items/review-config-from-go-upgrade.md @@ -0,0 +1,48 @@ +# 🧹 Настроить конвейер ревью по итогам прогона go-1-26-upgrade + +- **Тип:** chore +- **Категория:** Очередь +- **Зачем:** Прогон вскрыл две прорехи настройки: «Типовые узлы» знают только рантайм и не знают рода «проверочный шаг набора проверок», а «Триггеры метки» не видят оси «изменение трогает канон» — и именно она дала обе блокирующие находки. + +Обе прорехи одного рода — настройка конвейера, живущая в `docs/review.md`, — и +правятся одним заходом. + +**Род узла.** Сегодня перечень покрывает шаг конвейера, транспорт, клиент +внешнего сервиса, репозиторий и обёртку над внешним процессом. Скриптов гейта в +проекте четыре, и свойства у них свои: отличает ли шаг расхождение от сломанного +окружения, есть ли исход функция коммита, а не машины, покрыт ли шаг мутационным +прогоном. Без этого рода находка «шаг набора проверок не проверен ничем» +добывается заново каждый раз. + +**Ось метки.** Два прохода независимо сказали, что метка `medium` занижена: +изменение заводило новую capability и новый каталог верхнего уровня. Триаж +проверил и подтвердил — по записанному правилу разметка была верна, потому что +такой оси в правиле нет вовсе, а обе блокирующие находки прогона пришли именно +по ней. + +Провенанс обоих — отчёт +`openspec/changes/archive/2026-08-12-go-1-26-upgrade/review/report.md`, +кандидаты в правило P-2 и P-5. + +## Затрагивает + +- раздел «Типовые узлы» в `docs/review.md`; +- раздел «Триггеры метки» в `docs/review.md`. + +## Критерии приёмки + +- Проверочный шаг набора проверок описан родом со своими свойствами. Оракул — + открыть «Типовые узлы» и найти род; свойств не меньше трёх, и каждое + сформулировано проверяемо. +- Правило выбора метки видит заведение новой capability и нового каталога + верхнего уровня. Оракул — приложить правило к прогону `go-1-26-upgrade` задним + числом: метка выходит выше `medium`. +- Гейт зелёный целиком. Оракул — `task gate`. + +## Рамки + +Правится только настройка конвейера в `docs/review.md`. Устав самого конвейера +живёт в плагине `av-dev-code` и этой задачей не трогается: проект вправе +настраивать свои темы и триггеры, но не переписывать чужой скилл. + +Журнал дефектов в том же файле не трогается — записи неизменяемы.