docs: точного числа накопленного в документах больше нет
- CLAUDE.md, «Язык»: ссылаться можно на конкретную запись или на весь корпус разом, но не на их количество — число протухает молча, машина его не считает. Изъятие названо: неизменное число и историческое в записи о прошлом остаются. - Сняты счёты capability, прогонов ревью, типизированных ошибок, воркеров, сверок документов и правил линтера в docs/, спеке pipeline и CLAUDE.md. - Заодно исправлено то, что этот же счёт и скрывал: типизированных ошибок три, а не две — LostAcquisitionError был потерян из перечня.
This commit is contained in:
@@ -141,7 +141,7 @@ task gate # весь набор проверок разом
|
||||
**затронутых файлах** дешёвую часть: `gofmt` (правит на месте и добавляет в
|
||||
коммит), `golangci-lint` по пакетам тронутых файлов, `shellcheck`, `hadolint`,
|
||||
`gitleaks` по индексу. Только гейту остаются сборка, `go vet`, тесты целиком,
|
||||
сверка версий Go, три сверки документов и `govulncheck`: они смотрят всё
|
||||
сверка версий Go, сверки документов и `govulncheck`: они смотрят всё
|
||||
дерево либо требуют сети, а pre-commit обязан быть быстрым.
|
||||
- **Шагу `vulns` нужна сеть**, и он один такой: база уязвимостей живёт на
|
||||
vuln.go.dev. Без сети шаг краснеет, а не пропускается молча; сам инструмент
|
||||
@@ -155,8 +155,8 @@ task gate # весь набор проверок разом
|
||||
которого образ перестаёт собираться, но собираемости не проверяет. Собрать
|
||||
образ по-прежнему может только человек — `task image`, и на подъёме версии
|
||||
это обязательно;
|
||||
- собираемость `Dockerfile`: `hadolint` судит форму, а не сборку, и два его
|
||||
правила подавлены поимённо — `DL3007` до задачи `pin-runtime-image-base` и
|
||||
- собираемость `Dockerfile`: `hadolint` судит форму, а не сборку, и часть его
|
||||
правил подавлена поимённо — `DL3007` до задачи `pin-runtime-image-base` и
|
||||
`DL3018` по существу (alpine не держит старые версии пакетов, закрепление
|
||||
ломает сборку через недели). Причины стоят строками в `Taskfile.yml`;
|
||||
- `gitleaks` — висит на pre-commit в `lefthook.yml` и смотрит только индекс
|
||||
@@ -231,3 +231,18 @@ task gate # весь набор проверок разом
|
||||
- Документация, комментарии, сообщения коммитов — русский.
|
||||
- Код и идентификаторы — английский.
|
||||
- Текст, который видит пользователь Telegram, — русский.
|
||||
- **Точного числа накопленного в документах нет.** «Три capability», «пять
|
||||
прогонов ревью», «две типизированные ошибки» расходятся с действительностью на
|
||||
первой же задаче, которая прибавит четвёртую, — и расходятся молча: машина
|
||||
такое не считает, а читатель верит написанному. Ссылаться можно только на
|
||||
**конкретную запись** (по имени, со ссылкой) либо на **весь корпус разом**
|
||||
(«заведённые capability», «записи журнала ниже»). Само перечисление при этом
|
||||
законно: перечень обновляют вместе с предметом, а число живёт отдельно от него
|
||||
и потому протухает в одиночку.
|
||||
|
||||
*Изъятие:* число, которое не растёт с работой, остаётся числом — количество
|
||||
уровней журнала в библиотеке, ступеней сборки образа, состояний списка на
|
||||
экране. Так же законно **историческое** число в записи о прошлом: «решением от
|
||||
2026-08-13 закрыты четыре задачи» описывает событие, а не сегодняшний счёт.
|
||||
Настройки с числовым значением — свой случай, их дом
|
||||
[docs/database.md](docs/database.md).
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
[passport.md](passport.md) и в [tasks/BACKLOG.md](../tasks/BACKLOG.md); что из
|
||||
этого ещё не решено — в разделе «Открытые вопросы».
|
||||
|
||||
Заведены четыре capability, и все нормируют **поведение сервиса** для его
|
||||
потребителей. Инструмент, которым сервис собирают, спеками не нормируется вовсе:
|
||||
Заведённые capability нормируют **поведение сервиса** для его потребителей —
|
||||
все до одной. Инструмент, которым сервис собирают, спеками не нормируется вовсе:
|
||||
у набора проверок и сборки другой потребитель — тот, кто собирает, — и решением
|
||||
от 2026-08-13 его нормы живут в самих шагах, их проверках и
|
||||
[conventions/go-linters.md](conventions/go-linters.md).
|
||||
@@ -87,8 +87,8 @@
|
||||
|
||||
<!-- канон: поведение → openspec/specs/pipeline; ещё НЕ переехало: цепочка переходов состояний -->
|
||||
|
||||
Конвейер: `created` → `converted` → `transcribe` → `done` либо `failed`. Три
|
||||
воркера двигают по одному переходу, каждый опрашивает базу раз в секунду. Что
|
||||
Конвейер: `created` → `converted` → `transcribe` → `done` либо `failed`. Каждый
|
||||
переход двигает свой воркер, и каждый опрашивает базу раз в секунду. Что
|
||||
делает задача, исчерпавшая попытки, нормирует
|
||||
[pipeline](../openspec/specs/pipeline/spec.md), «Число попыток и состояние
|
||||
«мертва»».
|
||||
@@ -141,7 +141,7 @@
|
||||
`transcriber_worker_job_count` с меткой `error="true"` и по логам контейнера.
|
||||
Отдельного оповещения нет.
|
||||
- **Характер потока:** непрерывный, но разреженный. Бот держит длинный опрос,
|
||||
три воркера опрашивают базу вхолостую с паузой из
|
||||
воркеры опрашивают базу вхолостую с паузой из
|
||||
[database.md](database.md), «Настройки с числовым значением».
|
||||
|
||||
## Единые точки проекта
|
||||
@@ -228,8 +228,9 @@
|
||||
конвертер этот случай не проверялся.
|
||||
- **Очередь.** Модель очереди сделана задачей `pocketbase-storage` 2026-08-12
|
||||
([ADR](adr/ADR-2026-08-11-queue-as-pocketbase-collection.md)) и нормирована
|
||||
спекой `pipeline`. Не решено, отказываться ли от холостого опроса: три воркера
|
||||
дают 259 200 запросов к базе в сутки — расчёт из паузы воркера, а не замер
|
||||
спекой `pipeline`. Не решено, отказываться ли от холостого опроса: он
|
||||
даёт сотни тысяч запросов к базе в сутки — расчёт из числа воркеров и их
|
||||
паузы, а не замер
|
||||
([research/job-queue.md](research/job-queue.md), «Как снималось»), — при
|
||||
нагрузке в единицы записей в день, и во что это обходится, никто не мерил.
|
||||
- **Наблюдаемость.** `/metrics` остаётся и развивается. Чем — дописывать
|
||||
|
||||
@@ -21,7 +21,7 @@ severity — в [CLAUDE.md](../../CLAUDE.md).
|
||||
UUID вместо ULID, лог пишется на каждом шаге и дублируется воркером, `msg` —
|
||||
предложение с заглавной буквы вместо константной категории.
|
||||
|
||||
Из этого перечня закрыты два. Доменные ошибки проверялись приведением типа до
|
||||
Часть перечня закрыта. Доменные ошибки проверялись приведением типа до
|
||||
2026-08-11, задача `errors-as-instead-of-typecast`. Время брали `time.Now()` по
|
||||
месту до 2026-08-13 — теперь его читает единая точка `internal/clock`, и правило
|
||||
держит линтер. Оба места больше не долг, а регрессия.
|
||||
|
||||
@@ -65,14 +65,15 @@ transcriber — **приложение, а не библиотека**: внеш
|
||||
вызывающему нужны **данные** ошибки. Достаём `errors.As`. Не плодим типы там,
|
||||
где хватает sentinel.
|
||||
|
||||
Сегодня в проекте две типизированные ошибки, и обе несут данные:
|
||||
`contract.JobNotFoundError` (состояние и сообщение) и `contract.NoopJobError`
|
||||
(состояние). Третья, `tg.EmptyBotTokenError`, была ровно тем случаем, против
|
||||
которого написано правило — тип без полей, — и снята задачей
|
||||
`local-run-without-telegram-token` 2026-08-13; её место занял sentinel
|
||||
`telegram.ErrEmptyToken`. Рядом с ним живёт `contract.ErrDeliveryChannelDown` —
|
||||
тоже sentinel и по той же причине: заглушка отправителя не знает ни задачи, ни
|
||||
чата, и нести ей нечего.
|
||||
Типизированные ошибки проекта несут данные все до одной:
|
||||
`contract.JobNotFoundError` (состояние и сообщение), `contract.NoopJobError`
|
||||
(состояние), `contract.LostAcquisitionError` (идентификатор задачи).
|
||||
|
||||
`tg.EmptyBotTokenError` был ровно тем случаем, против которого написано правило —
|
||||
тип без полей, — и снят задачей `local-run-without-telegram-token` 2026-08-13;
|
||||
его место занял sentinel `telegram.ErrEmptyToken`. Рядом живёт
|
||||
`contract.ErrDeliveryChannelDown` — тоже sentinel и по той же причине: заглушка
|
||||
отправителя не знает ни задачи, ни чата, и нести ей нечего.
|
||||
|
||||
## Граница и трансляция: приватный и публичный канал
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
| Ядро (`internal/service`) не знает ни адаптеров, ни транспортов | `internal/archrules` → `TestЯдроНеЗнаетОбАдаптерах`, `TestЯдроНеЗнаетОТранспортах` |
|
||||
| Транспорты (`controller/http`, `controller/tg`, `controller/worker`) не знают друг о друге | `internal/archrules` → `TestТранспортыНеЗнаютДругОДруге` |
|
||||
| Адаптер не знает ни ядра, ни транспортов | `internal/archrules` → `TestАдаптерыНеЗнаютНиЯдра_НиТранспортов` |
|
||||
| Колонки очереди согласованы: перечень захвата ↔ структура захвата ↔ шаг схемы ↔ запись коллекции ↔ перенос поля в задачу | `internal/archrules` → четыре правила о захвате. Закрывает инвариант «колонки правятся в четырёх местах» (CLAUDE.md, major), которого компилятор не держит. Литерал колонки ищется в телах нужных функций: по файлу целиком условие выполнялось бы тегами `db:"…"` самой структуры, и правило было бы зелёным всегда |
|
||||
| Колонки очереди согласованы: перечень захвата ↔ структура захвата ↔ шаг схемы ↔ запись коллекции ↔ перенос поля в задачу | `internal/archrules` → правила о захвате. Закрывает инвариант «колонки правятся в четырёх местах» (CLAUDE.md, major), которого компилятор не держит. Литерал колонки ищется в телах нужных функций: по файлу целиком условие выполнялось бы тегами `db:"…"` самой структуры, и правило было бы зелёным всегда |
|
||||
|
||||
### Отмена и внешний собеседник
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@ Object Storage, скачивание файла из Telegram и опрос оп
|
||||
|
||||
Обращения к Telegram этому правилу следуют, и точка чистки одна на все вызовы —
|
||||
`internal/adapter/telegram`, `NewBot`. Токен стоит в пути **каждого** обращения к
|
||||
Bot API, поэтому чистка на месте употребления закрывала бы один вызов из пяти:
|
||||
Bot API, поэтому чистка на месте употребления закрывала бы один вызов из всех:
|
||||
|
||||
- отказ транспорта разворачивает в первопричину клиент бота (`safeClient`), а
|
||||
библиотека отдаёт наш отказ вызывающему нетронутым — этим закрыты `getFile`,
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ CGO сборке не нужен.
|
||||
### `files`
|
||||
|
||||
Один файл на одну физическую копию: исходник, результат конвертации и копия в
|
||||
Object Storage — три разные записи.
|
||||
Object Storage — каждая своей записью.
|
||||
|
||||
| Поле | Тип | Что |
|
||||
| --- | --- | --- |
|
||||
|
||||
+9
-7
@@ -2,13 +2,15 @@
|
||||
|
||||
## Как настроен конвейер
|
||||
|
||||
Артефакты семи прогонов лежат в `openspec/changes/archive/<id>/review/`: у трёх
|
||||
ранних, начиная с `fix-http-handler-tests` 2026-08-11, это `triage.md`, у трёх
|
||||
поздних — `report.md`, у седьмого (`start-without-telegram-token` 2026-08-13) —
|
||||
снова `triage.md`. Сверх них конвейер прогонялся 2026-08-13 на работе, шедшей
|
||||
без своего изменения openspec; артефакта в архиве у тех прогонов нет, и урожай их
|
||||
виден только записями журнала ниже. Разделы ниже заведены наперёд по коду
|
||||
2026-08-11 и с тех пор правятся урожаем прогонов.
|
||||
Артефакты прогонов лежат в `openspec/changes/archive/<id>/review/` — под именем
|
||||
`triage.md` либо `report.md`: имя менялось по ходу, и оба встречаются. Самый
|
||||
ранний — `fix-http-handler-tests` 2026-08-11, самый поздний —
|
||||
`start-without-telegram-token` 2026-08-13.
|
||||
|
||||
Конвейер прогонялся и на работе, шедшей без своего изменения openspec; артефакта
|
||||
в архиве у таких прогонов нет, и урожай их виден только записями журнала ниже.
|
||||
Разделы ниже заведены наперёд по коду 2026-08-11 и с тех пор правятся урожаем
|
||||
прогонов.
|
||||
|
||||
**Проход, поднявший сервис, обязан его остановить.** Живой прогон стал доступен
|
||||
2026-08-13 (см. «Недоступно проверке»), и первый же им воспользовался: враждебный
|
||||
|
||||
@@ -25,9 +25,9 @@ done | failed`, отмена контекста посреди шага и ос
|
||||
узнаваться по смыслу значения, а не по его точной форме, и MUST переживать
|
||||
пояснения, добавленные к этому значению на любом промежуточном шаге пути.
|
||||
|
||||
Требование стоит на инварианте проекта «`NoopJobError` — не ошибка»: три воркера
|
||||
опрашивают базу раз в секунду, и пустой прогон, принятый за отказ, даёт три
|
||||
записи отказа в секунду и столько же засчитанных сбоев, которых не было.
|
||||
Требование стоит на инварианте проекта «`NoopJobError` — не ошибка»: воркеры
|
||||
опрашивают базу раз в секунду, и пустой прогон, принятый за отказ, даёт от
|
||||
каждого запись отказа в секунду и столько же засчитанных сбоев, которых не было.
|
||||
|
||||
Признак пустого прогона MUST рождаться только ответом хранилища на опрос этим же
|
||||
шагом. Слой, придающий отказу собственный смысл, MUST не сохранять чужой признак
|
||||
|
||||
Reference in New Issue
Block a user