docs: устранены расхождения документов между собой и с кодом
- README разгружен: контракт HTTP API, таблицы БД, состояния задач и белый список отданы нормативным источникам ссылками - в `architecture.md` и `review.md` числа и механика захвата задачи заменены ссылками на `database.md`, а описание конвейера ревью — на прогон от 2026-08-11 - в `CLAUDE.md` и `security.md` поправлены границы домена, оценка объёма записи и адрес очереди задач
This commit is contained in:
+10
-7
@@ -21,6 +21,7 @@
|
||||
- **Очередь таблицей.** Состояние задачи лежит в SQLite, воркер забирает работу
|
||||
запросом с захватом. Внешний брокер не заводим: нагрузка — единицы записей в
|
||||
день (оценка владельца, не замер: `research/` пуст).
|
||||
<!-- канон: поведение → openspec/specs/pipeline -->
|
||||
- **Шаг конвейера идемпотентен по повтору.** Задача, брошенная на середине,
|
||||
достаётся снова по истечении срока захвата и проходит шаг заново.
|
||||
- **Ядро зависит от интерфейсов.** `internal/service` знает только
|
||||
@@ -87,7 +88,8 @@
|
||||
`transcriber_worker_job_count` с меткой `error="true"` и по логам контейнера.
|
||||
Отдельного оповещения нет.
|
||||
- **Характер потока:** непрерывный, но разреженный. Бот держит длинный опрос,
|
||||
три воркера опрашивают базу раз в секунду вхолостую.
|
||||
три воркера опрашивают базу вхолостую с паузой из
|
||||
[database.md](database.md), «Настройки с числовым значением».
|
||||
|
||||
## Единые точки проекта
|
||||
|
||||
@@ -136,8 +138,9 @@
|
||||
- **Долгие записи.** Потолок сегодня неизвестен и не замерялся: 20 МиБ на приём
|
||||
из Telegram — точно, ограничения `deferred-general` по длине — нет. Расчётный
|
||||
потолок проекта — шесть часов, и он взят с запасом, а не замером.
|
||||
- **Приём большого файла.** Форма читается целиком, предел
|
||||
`router.MaxMultipartMemory` — 32 МиБ, обрыв начинает загрузку заново.
|
||||
- **Приём большого файла.** Форма читается целиком, предел памяти под multipart
|
||||
задан числом в [database.md](database.md), «Настройки с числовым значением»;
|
||||
обрыв начинает загрузку заново.
|
||||
Загрузку частями разбирает разведка `chunked-upload-choice`; её выбор меняет
|
||||
публичный контракт приёма и потому идёт через решение в `adr/`.
|
||||
- **Учёт расхода.** Распознавание и языковая модель оплачиваются по факту, а
|
||||
@@ -152,10 +155,10 @@
|
||||
- **Видео.** Дорожку из видеофайла бот принимает по MIME-типу `video/`, но
|
||||
конвертер этот случай не проверялся.
|
||||
- **Очередь.** Принцип «очередь таблицей» и захват `FindAndAcquire` написаны
|
||||
вручную: захват не транзакционен, повторов с нарастающей паузой нет, число
|
||||
попыток не считается, очереди мёртвых задач нет. Пересматривается разведкой
|
||||
`job-queue-choice` — раньше смены хранилища, чтобы не переписывать захват
|
||||
дважды.
|
||||
вручную; чем именно захват несовершенен — [database.md](database.md),
|
||||
«Представление данных». Не решено здесь другое: пересматривать ли модель
|
||||
очереди целиком — разведка `job-queue-choice`, раньше смены хранилища, чтобы
|
||||
не переписывать захват дважды.
|
||||
- **Наблюдаемость.** `/metrics` остаётся и развивается. Чем — дописывать
|
||||
счётчики через `client_golang` или перейти на OpenTelemetry с трассировкой —
|
||||
решает разведка `opentelemetry-fit`. Коллектор был бы процессом, которого в
|
||||
|
||||
@@ -95,7 +95,7 @@ OpenSpec.
|
||||
| Когда добавляем | Поля |
|
||||
| --- | --- |
|
||||
| на входящий HTTP-запрос | `transport` (`http`, `telegram`), `http.method`, `http.route`, `http.status_code`, `duration_ms` |
|
||||
| на задачу | `capability` (значения — по именам заведённых capability в `openspec/specs/`; пока их нет, поле не заполняется), `job_id`, `file_id`, `source` |
|
||||
| на задачу | `capability` (значения — по именам заведённых capability в `openspec/specs/`), `job_id`, `file_id`, `source` |
|
||||
| на запись об ошибке | `error` |
|
||||
| на вызов внешнего сервиса | `ext.service`, `ext.operation`, `ext.status_code`, `duration_ms`, `retry` |
|
||||
|
||||
|
||||
+10
-5
@@ -2,8 +2,11 @@
|
||||
|
||||
## Как настроен конвейер
|
||||
|
||||
Конвейера ревью в проекте пока нет: плагин не подключён, ни одного прогона не
|
||||
было. Раздел заполнен наперёд по коду — он и служит настройкой первому прогону.
|
||||
Конвейер ревью прогонялся один раз — 2026-08-11, на изменении
|
||||
`fix-http-handler-tests`; его триаж лежит в
|
||||
`openspec/changes/archive/2026-08-11-fix-http-handler-tests/review/triage.md`.
|
||||
Разделы ниже заполнены наперёд по коду и правятся по итогам прогонов: «Типовые
|
||||
ложноположительные» первым прогоном уже пользовались.
|
||||
|
||||
### Типовые узлы
|
||||
|
||||
@@ -71,8 +74,9 @@
|
||||
[conventions/errors.md](conventions/errors.md).
|
||||
- **«Захват задачи не в транзакции — гонка двух воркеров».** По построению её
|
||||
нет: три воркера читают три разных состояния, и одну строку они не делят.
|
||||
Находка становится настоящей ровно тогда, когда появится второй экземпляр
|
||||
процесса или второй воркер на то же состояние.
|
||||
Механика захвата и её слабые места — [database.md](database.md),
|
||||
«Представление данных». Находка становится настоящей ровно тогда, когда
|
||||
появится второй экземпляр процесса или второй воркер на то же состояние.
|
||||
- **«Файлы и объекты не удаляются, диск растёт».** Факт верный и записан в
|
||||
[database.md](database.md); срок хранения не задан сознательно, задачи на него нет.
|
||||
Новой находкой это не считается, пока не измерен рост.
|
||||
@@ -103,7 +107,8 @@
|
||||
`createTranscribeJob` — сегодня через него идут оба входа
|
||||
([architecture.md](architecture.md), «Единые точки проекта»).
|
||||
- `architecture`: не поехало ли поведение в `architecture.md` вместо спеки —
|
||||
спек ещё нет, и соблазн описать поведение в обзоре максимальный.
|
||||
заведена одна capability (`openspec/specs/intake`), поведение прочих узлов
|
||||
живёт в обзоре под маркерами долга, и соблазн дописать туда ещё максимальный.
|
||||
- `conventions`: новая колонка правится во всех четырёх местах репозитория
|
||||
(CLAUDE.md, «Инварианты»).
|
||||
- `autotests`: покрыт ли изменённый шаг конвейера хоть одним тестом — сегодня
|
||||
|
||||
+5
-3
@@ -159,7 +159,8 @@ Telegram отправителю.
|
||||
`internal/service/transcribe.go:107` кладёт `file_name` на общем шаге заведения
|
||||
задачи, то есть для обоих входов. Это нарушение инварианта приватности из
|
||||
`CLAUDE.md`, оно объявлено критическим, и чинит его задача
|
||||
`no-user-filename-in-log` — первая строка очереди.
|
||||
`no-user-filename-in-log`; её место в очереди —
|
||||
[tasks/BACKLOG.md](../tasks/BACKLOG.md).
|
||||
|
||||
Токен бота попадает в URL скачивания файла (`file.Link(token)`), и этот URL
|
||||
нигде не логируется.
|
||||
@@ -177,8 +178,9 @@ Telegram отправителю.
|
||||
- **Стойкость к целенаправленной нагрузке.** Ограничения по числу запросов и по
|
||||
размеру файла нет, и защищаться от исчерпания диска мы сейчас не пытаемся.
|
||||
- **Исчерпание диска приглашёнными.** Записи и тексты хранятся бессрочно
|
||||
(паспорт, 2026-08-11), шестичасовая запись весит гигабайты, а квот нет и не
|
||||
будет: решено считать расход и показывать его владельцу, а не отказывать
|
||||
(паспорт, 2026-08-11), шестичасовая запись весит единицы гигабайт — оценка, а не
|
||||
замер: `research/` пуст, потолок длины стоит открытым вопросом
|
||||
`architecture.md`, «Долгие записи», — а квот нет и не будет: решено считать расход и показывать его владельцу, а не отказывать
|
||||
(цель `usage-stats`). Перебравшего останавливает разговор или отзыв доступа в
|
||||
Authelia. Рост каталога `data/files` при этом ничем не наблюдается —
|
||||
открытый вопрос `architecture.md`.
|
||||
|
||||
Reference in New Issue
Block a user