diff --git a/tasks/BACKLOG.md b/tasks/BACKLOG.md index 3e8a02f..d9a2c88 100644 --- a/tasks/BACKLOG.md +++ b/tasks/BACKLOG.md @@ -44,15 +44,21 @@ ## Очередь - [🧹 Ронять гейт на изменённой функции, которую не выполняет ни один тест](items/gate-changed-lines-coverage.md) — Свойство «изменённое место покрыто хоть одним тестом» записано в docs/review.md, но не механизировано: за две задачи подряд непокрытые шаги ловили руками. +- [🧹 Проверить шаг гейта migrations так же, как шаг сверки версий Go](items/migrations-step-norm-and-tests.md) — Шаг охраняет critical-инвариант «применённый шаг схемы не переписывается», но своих проверок не имеет: дрейф шаблона имени, переезд каталога или потеря grep в конвейере оставят его вечно зелёным, и это не заметит ничто. +- [🔬 Шаги гейта, у которых правило может потерять предмет](items/gate-steps-subject-guard.md) — У шага migrations страж предмета есть, у шагов docs, tasks и openspec неизвестно: они зовут чужие скрипты из плагинов, и правило, потерявшее файлы, зеленело бы молча. +- [🧹 Настроить конвейер ревью по итогам прогона go-1-26-upgrade](items/review-config-from-go-upgrade.md) — Прогон вскрыл две прорехи настройки: «Типовые узлы» знают только рантайм и не знают рода «проверочный шаг набора проверок», а «Триггеры метки» не видят оси «изменение трогает канон» — и именно она дала обе блокирующие находки. - [🧹 Поднимать сервис локально без действующего токена бота](items/local-run-without-telegram-token.md) — Адаптер Telegram проверяет токен обращением к Telegram и роняет старт, а боевым токеном запускаться запрещено: проверить поведение живым прогоном не может ни одна задача. - [🐞 Убрать код провайдера из журнала запросов хранилища](items/provider-code-out-of-storage-log.md) — Строка запроса с кодом входа целиком уезжает в таблицу _logs и лежит там пять суток, хотя спека access требует, чтобы код в журнал не попадал. - [🐞 Вести учёт употреблённых состояний входа на сервере](items/server-side-login-state.md) — Одноразовость возврата держится на уборке куки, то есть на браузере: сервер не помнит, какие состояния уже потрачены. - [🧹 Строить адрес входа из настроек коллекции, а не из конфига](items/login-url-from-collection-settings.md) — Первая половина входа собрана руками из конфига и на настройки провайдера не смотрит, вторая берётся из коллекции: обновление библиотеки изменит только вторую половину. - [🔬 Четыре недоказанные гипотезы о поверхности входа](items/login-surface-hypotheses.md) — Ревью назвало четыре пути, которых не смогло ни подтвердить, ни опровергнуть: браузера и живого провайдера в прогоне не было. - [🧹 Назвать в необратимом, что откат кода не откатывает шаг схемы](items/rollback-does-not-undo-schema-step.md) — Откат бинаря оставляет применённый шаг схемы в силе, и на этом строятся решения о выкладке: сегодня об этом не сказано нигде. +- [🐞 Починить срок сессии, который ставит откат шага входа](items/rollback-restores-wrong-session-duration.md) — Константа defaultAuthTokenDuration в шаге 202608120001 названа умолчанием библиотеки, но 1209600 — это 14 суток, а умолчание PocketBase 432000, пять суток: откат объявляет возврат к умолчанию и ставит срок вдвое больше выбранных владельцем семи. - [🔬 Адрес объекта в тексте отказа SpeechKit](items/speechkit-error-text-leak.md) — Текст отказа операции приходит от Yandex и уезжает в журнал и в колонку error_text: если он несёт URI объекта, из журнала снова собирается ссылка на чужую запись. - [🧹 Разобрать мелочи http-транспорта](items/http-transport-nits.md) — Маршруты зарегистрированы дважды, и переименование пути в main.go проходит проверки зелёным; обработчик пишет в журнал через стандартный log и дублирует запись, уже сделанную сервисом. +- [🧹 Запретить обращаться к Bot API мимо клиента бота](items/bot-api-only-through-bot-client.md) — Чистка отказа от адреса с токеном живёт в клиенте; свой http.Client в транспорте вернёт утечку молча — правило noctx такую подмену не ловит, а класс уже стоил одного дефекта. - [🧹 Переименовать образец конфига в config.example.toml](items/config-example-toml.md) — Конвенция называет config.dist.toml объявленным расхождением, но тут же пишет это имя как правило — документ противоречит сам себе, а образец расходится с конвенцией. +- [🧹 Свести пять расхождений между документами канона](items/docs-consistency-2026-08-13.md) — Сверка 2026-08-13 нашла шесть мест, где два документа отвечают на один вопрос по-разному; одно сведено при повышении раскладки, а три из пяти оставшихся стоят в architecture.md, и по ним читатель строит решения о выкладке и о периметре. - [✨ Привязать запись к владельцу и отдавать только свои](items/record-ownership.md) — У задачи и файла нет владельца, поэтому знание UUID задачи и есть право её читать. - [✨ Свести приём и чтение записей к одному контракту для приложения](items/json-api-for-spa.md) — Сегодняшний API отвечает 404 на любую ошибку чтения и 500 на любую ошибку приёма: строить на нём экраны нечем. - [✨ Сопоставить пользователя Telegram с учётной записью](items/telegram-account-link.md) — Белый список сверяется с именем пользователя Telegram, которое владелец меняет в любой момент, а записи из бота ни с кем не связаны. @@ -63,6 +69,7 @@ - [🧹 Прервать шаг конвейера отменой контекста](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/pin-runtime-image-base.md) — Финальный слой Dockerfile собирается на alpine:latest, а task image идёт с --pull, поэтому два образа из одного коммита с разницей в неделю несут разный ffmpeg — регрессия конвертации после такой пересборки выглядит как задачи в failed при пустом диффе репозитория, и откат на прежний коммит её не чинит. - [✨ Собрать каркас приложения и раздать его из бинарника](items/spa-skeleton.md) — Экранов нет и собирать их нечем: ни сборки фронтенда, ни раздачи статики в проекте не существует. - [✨ Сделать экран загрузки записи и её состояния](items/upload-and-status-screen.md) — Первое, ради чего приложение открывают: отдать файл и увидеть, что с ним происходит. - [✨ Сделать экран списка своих записей и чтения текста](items/records-list-screen.md) — Расшифровка сегодня доходит одним сообщением и теряется в переписке; вернуться к ней через неделю нечем. @@ -95,12 +102,4 @@ - [🔬 Уведомление SpeechKit о готовности вместо опроса](items/speechkit-callback-fit.md) — Шаг проверки дёргает операцию раз в 5 секунд всё время распознавания: часовая запись даёт порядка 720 обращений к платному сервису вместо одного ответа. - [✨ Считать объём, минуты и расход по каждому пользователю](items/usage-accounting.md) — Ни объём, ни длительность, ни обращения к платным сервисам никуда не записываются: восстановить расход задним числом не из чего. - [✨ Сделать страницу статистики для владельца](items/admin-stats-screen.md) — Собранный учёт читается только запросом к базе руками: ни страницы, ни признака владельца в приложении нет. -- [🧹 Закрепить версию рантайм-базы образа](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, и по ним читатель строит решения о выкладке и о периметре. -- [🧹 Свести Purpose спеки pipeline с её же требованиями](items/pipeline-spec-purpose-drift.md) — Преамбула спеки объявляет сознательно неописанными пять требований, которые в ней же и стоят с 2026-08-12: читатель узнаёт границу нормы из раздела, который ей противоречит. - [🔬 Квота по общему размеру загруженного на пользователя](items/per-user-size-quota.md) — Паспорт и security.md запрещают отказы по квоте пользователю, а заметка владельца просит квоту по умолчанию 5 ГБ — открытое противоречие с границей домена, которое владелец решил не разбирать сейчас. diff --git a/tasks/items/bot-api-only-through-bot-client.md b/tasks/items/bot-api-only-through-bot-client.md index 9495c4d..8bb7123 100644 --- a/tasks/items/bot-api-only-through-bot-client.md +++ b/tasks/items/bot-api-only-through-bot-client.md @@ -1,7 +1,7 @@ # 🧹 Запретить обращаться к Bot API мимо клиента бота - **Тип:** chore -- **Категория:** Очередь — Класс уже дал утечку токена; сегодня его держат две проверки на сегодняшних местах, а не правило. +- **Категория:** Очередь — Правило границы клиента ставится на тот же транспорт, мелочи которого разбирает строка выше: своя обёртка, заведённая раньше правила, вернёт утечку токена молча. - **Зачем:** Чистка отказа от адреса с токеном живёт в клиенте; свой http.Client в транспорте вернёт утечку молча — правило noctx такую подмену не ловит, а класс уже стоил одного дефекта. Токен бота стоит в пути каждого обращения к Bot API, а `http.Client` кладёт diff --git a/tasks/items/docs-consistency-2026-08-13.md b/tasks/items/docs-consistency-2026-08-13.md index 7cc1af6..a9c32d2 100644 --- a/tasks/items/docs-consistency-2026-08-13.md +++ b/tasks/items/docs-consistency-2026-08-13.md @@ -1,14 +1,19 @@ -# 🧹 Свести шесть расхождений между документами канона +# 🧹 Свести пять расхождений между документами канона - **Тип:** chore -- **Категория:** Очередь — Находки одной сверки: чинится одним заходом, пока помнится, чем каждое место было найдено. -- **Зачем:** Сверка 2026-08-13 нашла шесть мест, где два документа отвечают на один вопрос по-разному; четыре из них в architecture.md, и по ним читатель строит решения о выкладке и о периметре. +- **Категория:** Очередь — Документы правятся до того, как на них обопрутся экраны и контракт: расхождение в таблице зависимостей и в периметре читают, принимая решения ниже по списку. +- **Зачем:** Сверка 2026-08-13 нашла шесть мест, где два документа отвечают на один вопрос по-разному; одно сведено при повышении раскладки, а три из пяти оставшихся стоят в architecture.md, и по ним читатель строит решения о выкладке и о периметре. Находки сверки документов агентами `doc-consistency` и `doc-code-drift`, прогнанной 2026-08-13 вместе с работой о контексте и токене. К той работе расхождения отношения не имеют — они старше, и потому не чинились тем же коммитом. +Мест было шесть. Шестое — вид временной метки, где `conventions/database.md` +требовал RFC 3339 с `T`, а хранилище пишет `2006-01-02 15:04:05.000Z`, — сведено +2026-08-13 строкой «*Расхождение:*» в конвенции при повышении раскладки до +версии 3. Остальные пять живы. + Каждое место названо с домом факта, то есть с тем документом, который прав: 1. **Панель администратора против Authelia.** `architecture.md`, «Открытые @@ -26,11 +31,7 @@ 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` и настройка +5. **Дубли текста в `CLAUDE.md`** — подавления `hadolint` и настройка `errcheck` пересказаны там дословно, хотя обе преамбулы договорились, что дом перечня подавлений — `go-linters.md`. @@ -38,7 +39,6 @@ - `docs/architecture.md` — «Открытые вопросы», таблица внешних зависимостей, раздел «Эксплуатация»; -- `docs/conventions/database.md` — вид временной метки; - `docs/security.md` и `docs/database.md` — как дома фактов, если правка потребует уточнить формулировку; - `README.md` — перечень технологий; @@ -46,8 +46,8 @@ ## Критерии приёмки -- Ни одно из шести мест не отвечает на свой вопрос двумя способами. Оракул — - повторный прогон `av-dev:doc-healthcheck`: перечисленные шесть находок не +- Ни одно из пяти мест не отвечает на свой вопрос двумя способами. Оракул — + повторный прогон `av-dev:doc-healthcheck`: перечисленные пять находок не возвращаются. - Провайдер OIDC стоит в таблице внешних зависимостей со своими четырьмя столбцами отказа, и счёт зависимостей в «Открытых вопросах» сходится с diff --git a/tasks/items/gate-steps-subject-guard.md b/tasks/items/gate-steps-subject-guard.md index 10d6999..631ea43 100644 --- a/tasks/items/gate-steps-subject-guard.md +++ b/tasks/items/gate-steps-subject-guard.md @@ -1,7 +1,7 @@ # 🔬 Шаги гейта, у которых правило может потерять предмет - **Тип:** research -- **Категория:** Очередь — Разведка о чужих скриптах: пока ответа нет, неизвестно даже, есть ли работа. +- **Категория:** Очередь — Разведка того же класса: правило, потерявшее предмет. Её исход правит соседние шаги гейта, поэтому идёт до работы над ними. - **Зачем:** У шага migrations страж предмета есть, у шагов docs, tasks и openspec неизвестно: они зовут чужие скрипты из плагинов, и правило, потерявшее файлы, зеленело бы молча. Класс известен и записан: правило, чей предмет исчез, обходит пустой перечень diff --git a/tasks/items/migrations-step-norm-and-tests.md b/tasks/items/migrations-step-norm-and-tests.md index 7974162..7643dfb 100644 --- a/tasks/items/migrations-step-norm-and-tests.md +++ b/tasks/items/migrations-step-norm-and-tests.md @@ -1,7 +1,7 @@ # 🧹 Проверить шаг гейта migrations так же, как шаг сверки версий Go - **Тип:** chore -- **Категория:** Очередь — Шаг уже стоит в гейте и уже назван стражем critical-инварианта в двух документах — необеспеченное обещание дороже отсутствующего. +- **Категория:** Очередь — Страж шага гейта — тот же слой оснований, что и покрытие изменённых строк: проверкам, которым верят ниже по списку, верить можно только после этого. - **Зачем:** Шаг охраняет critical-инвариант «применённый шаг схемы не переписывается», но своих проверок не имеет: дрейф шаблона имени, переезд каталога или потеря grep в конвейере оставят его вечно зелёным, и это не заметит ничто. Шаг заведён 2026-08-13 и проверен мутацией на восьми исходах вручную — правка diff --git a/tasks/items/pin-runtime-image-base.md b/tasks/items/pin-runtime-image-base.md index 5ac4d05..95ea04d 100644 --- a/tasks/items/pin-runtime-image-base.md +++ b/tasks/items/pin-runtime-image-base.md @@ -1,7 +1,7 @@ # 🧹 Закрепить версию рантайм-базы образа - **Тип:** chore -- **Категория:** Очередь — Закрепление базы образа окупается при частых пересборках, а они начнутся с приложением. +- **Категория:** Очередь — Встаёт прямо перед spa-skeleton: частые пересборки образа начинаются с приложением, и до него незакреплённая база ничего не ломает. - **Зачем:** Финальный слой Dockerfile собирается на alpine:latest, а task image идёт с --pull, поэтому два образа из одного коммита с разницей в неделю несут разный ffmpeg — регрессия конвертации после такой пересборки выглядит как задачи в failed при пустом диффе репозитория, и откат на прежний коммит её не чинит. Класс тот же, ради которого задача `go-1-26-upgrade` завела шаг сверки версий: diff --git a/tasks/items/pipeline-spec-purpose-drift.md b/tasks/items/pipeline-spec-purpose-drift.md deleted file mode 100644 index edddb3b..0000000 --- a/tasks/items/pipeline-spec-purpose-drift.md +++ /dev/null @@ -1,54 +0,0 @@ -# 🧹 Свести Purpose спеки pipeline с её же требованиями - -- **Тип:** chore -- **Категория:** Очередь — Правка одного раздела спеки, но откладывать её значит держать нормативный документ противоречащим себе. -- **Зачем:** Преамбула спеки объявляет сознательно неописанными пять требований, которые в ней же и стоят с 2026-08-12: читатель узнаёт границу нормы из раздела, который ей противоречит. - -Нашла сверка документов 2026-08-13. `Purpose` спеки `pipeline` перечисляет как -сознательно неописанные захват задачи и срок его протухания, число попыток, -состояние «мертва» и паузу перед повтором. Ниже в той же спеке эти требования -стоят: их дописало изменение `pocketbase-storage` 2026-08-12, а преамбулу не -поправило. - -Не переехала в спеку одна вещь — цепочка переходов -`created` → `converted` → `transcribe` → `done` либо `failed`. Маркер долга в -[architecture.md](../../docs/architecture.md) уже уточнён под это и называет -неперехавшей именно цепочку, так что после правки `Purpose` два документа -сойдутся. - -Материал для замены — формулировка из отчёта сверки: - -> Описаны: пустой прогон воркера, неделимость захвата и срок его протухания, -> число попыток и состояние «мертва», условность записи результата по признаку -> захвата, нарастающая пауза перед повтором. Сознательно не описаны: цепочка -> переходов `created` → `converted` → `transcribe` → `done` либо `failed`, -> отмена контекста посреди шага, освобождение ресурсов внешних клиентов. Это не -> значит, что такого поведения нет: оно живёт в коде, а требования на него не -> написаны, потому что требование без проверки — предположение, а не норма. -> Первая задача, которая трогает любое из перечисленного, дописывает его сюда. - -## Затрагивает - -- раздел `Purpose` в `openspec/specs/pipeline/spec.md` — требований спеки правка - не касается, они уже написаны; -- маркер долга о поведении в `docs/architecture.md` — как парная сторона - утверждения о том, что ещё не переехало. - -## Критерии приёмки - -- `Purpose` не называет неописанным ни одно требование, которое в спеке стоит. - Оракул — построчная сверка перечня из `Purpose` с заголовками `Requirement` - той же спеки: пересечения нет. -- Спека остаётся годной для инструмента. Оракул — `openspec validate --strict` - отрабатывает без отказа. -- Маркер долга в `docs/architecture.md` и `Purpose` называют неперехавшим одно и - то же. Оракул — чтение обоих мест подряд: перечни совпадают. - -## Рамки - -Правится преамбула, а не требования: поведение сервиса задача не меняет и кода -не трогает. Спека правится изменением openspec своим порядком, а не прямой -правкой файла. Соседняя задача -[context-cancel-in-pipeline](context-cancel-in-pipeline.md) уберёт из перечня -неописанного отмену контекста, когда доедет, — здесь эта строка остаётся на -месте. diff --git a/tasks/items/review-config-from-go-upgrade.md b/tasks/items/review-config-from-go-upgrade.md index 2246b8c..5aab726 100644 --- a/tasks/items/review-config-from-go-upgrade.md +++ b/tasks/items/review-config-from-go-upgrade.md @@ -1,7 +1,7 @@ # 🧹 Настроить конвейер ревью по итогам прогона go-1-26-upgrade - **Тип:** chore -- **Категория:** Очередь — Настройка конвейера ревью по итогам прогона — работа над процессом, не над продуктом. +- **Категория:** Очередь — Настраивает конвейер ревью, которым проверяется всё, что ниже: настройка после половины списка проверила бы вторую половину иначе, чем первую. - **Зачем:** Прогон вскрыл две прорехи настройки: «Типовые узлы» знают только рантайм и не знают рода «проверочный шаг набора проверок», а «Триггеры метки» не видят оси «изменение трогает канон» — и именно она дала обе блокирующие находки. Обе прорехи одного рода — настройка конвейера, живущая в `docs/review.md`, — и diff --git a/tasks/items/rollback-restores-wrong-session-duration.md b/tasks/items/rollback-restores-wrong-session-duration.md index 5a04ed3..5cd5cb0 100644 --- a/tasks/items/rollback-restores-wrong-session-duration.md +++ b/tasks/items/rollback-restores-wrong-session-duration.md @@ -1,7 +1,7 @@ # 🐞 Починить срок сессии, который ставит откат шага входа - **Тип:** fix -- **Категория:** Очередь — Путь сегодня недостижим: откаты не подключены, и пока они не подключены, цена дефекту нулевая. +- **Категория:** Очередь — Оба про откат шага схемы: соседняя строка называет, что откат не отменяет применённое, эта чинит константу, которую он ставит. Порознь их правят дважды в одном файле. - **Зачем:** Константа defaultAuthTokenDuration в шаге 202608120001 названа умолчанием библиотеки, но 1209600 — это 14 суток, а умолчание PocketBase 432000, пять суток: откат объявляет возврат к умолчанию и ставит срок вдвое больше выбранных владельцем семи. Найдено ревью пачки задач о гейте 2026-08-12, проход разбора кода. Дефект