docs: разобран урожай судей после подъёма канона

- перечень команд пользователя убран из architecture.md в спеки review и
  state-reconciliation, где ему дом: обзор успел разойтись с ними в обе стороны
- README перестал дублировать деплой и статус — теперь ссылается на дом
- статус «заведена ли задача под пробел» сведён в один регистр открытых вопросов
- шаги tasks.py и openspec.py названы в перечне «что красит безусловно»
- из спеки download-tracking сняты числа умолчаний: их дом — database.md
This commit is contained in:
av
2026-08-09 19:24:59 +03:00
parent c5d62d76ee
commit 69853a96c9
8 changed files with 37 additions and 36 deletions
+6 -2
View File
@@ -106,8 +106,12 @@ Go 1.26, один статический бинарь (`CGO_ENABLED=0`). Module
- **Что красит безусловно:** сборка, `go vet`, `golangci-lint`, `gofmt`, тесты, - **Что красит безусловно:** сборка, `go vet`, `golangci-lint`, `gofmt`, тесты,
флаки-прогон (второй прогон разошёлся с первым), `-race`, накат миграций с флаки-прогон (второй прогон разошёлся с первым), `-race`, накат миграций с
нуля, `gitleaks`, канон документации (`docs.py check` — раскладка `docs/`, нуля, `gitleaks`, канон документации (`docs.py check` — раскладка `docs/`,
битые ссылки, «миграция изменена, а `database.md` нет»). Причина одна: у битые ссылки, «миграция изменена, а `database.md` нет»), каталог задач
каждого из них есть объективный оракул, спорить не о чем. (`tasks.py check --dir tasks` — согласованность `tasks/BACKLOG.md` и
`tasks/items/`), форма конфига OpenSpec (`openspec.py check` — незаменённый
пример в `openspec/config.yaml`). Причина одна: у каждого из них есть
объективный оракул, спорить не о чем. Каждый из трёх последних краснеет и
когда своего скрипта нет: молча пропущенная проверка неотличима от пройденной.
- **Чего в гейте намеренно нет и кто обязан это гонять:** - **Чего в гейте намеренно нет и кто обязан это гонять:**
- `govulncheck` даёт `WARN`, а не `FAIL`: находка тут — состояние - `govulncheck` даёт `WARN`, а не `FAIL`: находка тут — состояние
зависимостей, а не диффа. Разбирает агент ревью по трассам вызовов. зависимостей, а не диффа. Разбирает агент ревью по трассам вызовов.
+13 -20
View File
@@ -35,14 +35,10 @@ Arr-стек (prowlarr/radarr/sonarr) плохо ложится на русск
## Статус ## Статус
Рабочий прототип с полным сквозным путём: приём magnet → загрузка в Рабочий прототип: сквозной путь приём → загрузка → распознавание → раскладка
qBittorrent → распознавание (LLM + опционально базы метаданных работает целиком, автоматически при уверенном результате либо через
TMDB/TVDB/TVMaze) → раскладка в библиотеку хардлинками, автоматически при подтверждение человеком. Что уже умеет и что дальше —
уверенном результате либо через подтверждение человеком. Транспорты приёма: [tasks/ROADMAP.md](tasks/ROADMAP.md).
REST API, веб-UI, Telegram-бот и CLI (`jellybit add`).
Из источников поддержаны magnet и `.torrent`-файл; фетч `.torrent` по обычной
ссылке — в планах. Что дальше — [tasks/ROADMAP.md](tasks/ROADMAP.md).
## Документация ## Документация
@@ -73,7 +69,8 @@ REST API, веб-UI, Telegram-бот и CLI (`jellybit add`).
Go (один статический бинарь), SQLite (`modernc.org/sqlite` + `sqlx`, Go (один статический бинарь), SQLite (`modernc.org/sqlite` + `sqlx`,
миграции `goose`), HTTP — `chi` + `html/template` + htmx, конфигурация — миграции `goose`), HTTP — `chi` + `html/template` + htmx, конфигурация —
TOML, логи — структурированный JSON (`slog`). Подробнее — в TOML, логи — структурированный JSON (`slog`). Полный перечень с версиями —
[CLAUDE.md](CLAUDE.md) → «Стек»; как эти компоненты сложены —
[docs/architecture.md](docs/architecture.md). [docs/architecture.md](docs/architecture.md).
## Конфигурация ## Конфигурация
@@ -118,16 +115,12 @@ jellybit recognize <infohash> --dry-run [--context "..."] --config ./config.toml
## Доставка ## Доставка
Рассчитан на домашний медиа-сервер. Артефакты репозитория — статический Рассчитан на домашний медиа-сервер. Артефакты репозитория — статический бинарь
бинарь (`task build`) и `Dockerfile` (упаковка в `distroless/static`). Образ (`task build`) и `Dockerfile`; образ собирается целиком локально на
собирается целиком **локально** на control-хосте (`task image`) и едет на control-хосте (`task image`) и едет на сервер через `docker save`/`load`.
сервер через `docker save`/`load` (роль `app_image` в umbar), поэтому
Go-тулчейн и `docker build` на сервере не нужны. В distroless нет shell/curl,
поэтому HEALTHCHECK зовёт сам бинарь: `jellybit healthcheck` (GET `/healthz`
по порту из конфига, exit 0/1).
Контейнер: `user 1000:1000`, порт `8080` на хост, mount `/srv/media` (единая
песочница для хардлинков) + том `/config` (ro, `config.toml`, восстановим при
деплое) + data-том `/data` (SQLite, бекапить); к qBittorrent — по сети Docker.
Конкретная деплой-обвязка (плейбук, секреты) держится в отдельном приватном Конкретная деплой-обвязка (плейбук, секреты) держится в отдельном приватном
репозитории и в комплект не входит. репозитории и в комплект не входит.
Параметры запуска — сеть, пользователь, монтирования, healthcheck, — разделение
ответственности с umbar и единая песочница `/srv/media`:
[docs/architecture.md](docs/architecture.md) → «Деплой».
+1 -1
View File
@@ -11,7 +11,7 @@
Верно одно из трёх: Верно одно из трёх:
<!-- копия: adr-когда-заводить из av-dev-pm/skills/canon/references/canon.md --> <!-- копия: adr-когда-заводить из av-dev-docs/skills/canon/references/canon.md -->
- **дорогой откат** — переделка стоит дороже переписывания одного файла; - **дорогой откат** — переделка стоит дороже переписывания одного файла;
- **намеренный отказ** от очевидного подхода; - **намеренный отказ** от очевидного подхода;
- **пересмотр прежнего решения** — тогда у старой записи обязателен статус - **пересмотр прежнего решения** — тогда у старой записи обязателен статус
+4 -2
View File
@@ -48,8 +48,10 @@
| `archrules` | собственный анализатор архитектурных правил (часть гейта) | — | | `archrules` | собственный анализатор архитектурных правил (часть гейта) | — |
Транспорты приёма — HTTP API, веб-UI, Telegram и CLI (`jellybit add`) — ведут в Транспорты приёма — HTTP API, веб-UI, Telegram и CLI (`jellybit add`) — ведут в
один `ingest`; действия пользователя (apply / refine / reject / defer / undo / один `ingest`; действия пользователя идут командами к `worker`. Перечень команд
retry / delete / dismiss) идут командами к `worker`. и их эффекты — нормативно в [review](../openspec/specs/review/spec.md), пути
закрытия и удаления — в
[state-reconciliation](../openspec/specs/state-reconciliation/spec.md).
## Внешние границы и форматы ## Внешние границы и форматы
+2 -1
View File
@@ -214,4 +214,5 @@ erDiagram
| `ingest.MaxTorrentSize` | `8 MiB` | предел размера принимаемого `.torrent`; проверяется **до** разбора, поэтому bencode-аллокации на эту величину не масштабируются (см. [research/torrent-bencode-limits.md](research/torrent-bencode-limits.md)) | | `ingest.MaxTorrentSize` | `8 MiB` | предел размера принимаемого `.torrent`; проверяется **до** разбора, поэтому bencode-аллокации на эту величину не масштабируются (см. [research/torrent-bencode-limits.md](research/torrent-bencode-limits.md)) |
**Ретеншена нет ни у одной таблицы**, лимита на размер тела ответа LLM нет, **Ретеншена нет ни у одной таблицы**, лимита на размер тела ответа LLM нет,
кэша метабаз нет — всё три пункта в беклоге. кэша метабаз нет; состояние по каждому пробелу и заведённые под них задачи —
[architecture.md](architecture.md) → «Открытые вопросы».
+1 -2
View File
@@ -101,8 +101,7 @@ REST API работают **без авторизации** осознанно;
бесконечный ответ LLM — это вопросы устойчивости и ресурсов бесконечный ответ LLM — это вопросы устойчивости и ресурсов
([architecture.md](architecture.md) → «Эксплуатация»), а не безопасности. ([architecture.md](architecture.md) → «Эксплуатация»), а не безопасности.
Отсутствие лимита на размер ответа LLM — известный пробел Отсутствие лимита на размер ответа LLM — известный пробел
([architecture.md](architecture.md) → «Открытые вопросы»); задачей он пока не ([architecture.md](architecture.md) → «Открытые вопросы»).
заведён.
- **Целостность содержимого медиафайлов.** Что в контейнере mkv — не наша забота. - **Целостность содержимого медиафайлов.** Что в контейнере mkv — не наша забота.
- **Цепочка поставки** — модули Go, базовый образ distroless, плагины тулинга. - **Цепочка поставки** — модули Go, базовый образ distroless, плагины тулинга.
- **Приватность запросов к внешним сервисам.** Названия раздач уезжают в LLM и - **Приватность запросов к внешним сервисам.** Названия раздач уезжают в LLM и
+3 -3
View File
@@ -10,7 +10,7 @@ qBittorrent и сопоставление его состояний (downloading
## Requirements ## Requirements
### Requirement: Поллинг qBittorrent и сопоставление состояний ### Requirement: Поллинг qBittorrent и сопоставление состояний
Worker SHALL периодически (`worker.poll_interval`, дефолт 5 с) опрашивать Worker SHALL периодически (`worker.poll_interval`) опрашивать
qBittorrent и сопоставлять его состояния раздачи с состоянием загрузки в БД. qBittorrent и сопоставлять его состояния раздачи с состоянием загрузки в БД.
Готовые к раскладке состояния (`uploading`/`stalledUP`/`pausedUP`/`stoppedUP`/ Готовые к раскладке состояния (`uploading`/`stalledUP`/`pausedUP`/`stoppedUP`/
`queuedUP`/`forcedUP`, с учётом различий имён между qBit v4 и v5) SHALL переводить `queuedUP`/`forcedUP`, с учётом различий имён между qBit v4 и v5) SHALL переводить
@@ -39,8 +39,8 @@ API после завершения переноса.
### Requirement: Таймауты-предохранители downloading ### Requirement: Таймауты-предохранители downloading
Система SHALL переводить `metaDL`/`forcedMetaDL` дольше `magnet_timeout` (дефолт Система SHALL переводить `metaDL`/`forcedMetaDL` дольше `magnet_timeout`
`24h`, редкий предохранитель) в `failed` (`error_code` `magnet_timeout`), а (редкий предохранитель) в `failed` (`error_code` `magnet_timeout`), а
`stalledDL` дольше `stuck_after` — в `stuck` (`error_code` `stalledDL` дольше `stuck_after` — в `stuck` (`error_code`
`stalled`). Возраст система SHALL считать от времени добавления в qBittorrent `stalled`). Возраст система SHALL считать от времени добавления в qBittorrent
(`added_on`), а не от создания задачи, чтобы базис переживал retry и усыновление. (`added_on`), а не от создания задачи, чтобы базис переживал retry и усыновление.
+7 -5
View File
@@ -2,12 +2,14 @@
Что **можно взять**. Одна задача = один файл `items/<slug>.md` Что **можно взять**. Одна задача = один файл `items/<slug>.md`
+ строка здесь. Целей тут нет — они в [ROADMAP.md](ROADMAP.md): беклог — то, что + строка здесь. Целей тут нет — они в [ROADMAP.md](ROADMAP.md): беклог — то, что
берут, роадмап — то, подо что берут. Порядка внутри секции нет: «что делать берут, роадмап — то, подо что берут. Ведётся скиллом `av-dev-tasks:tasks`.
дальше» отвечает набор спринта. Ведётся скиллом `tasks`.
Секции «блокеры» здесь нет и не заводится: блокерэто состояние **Порядок строк внутри секции — это приоритет.** Первая строка секции — то, что
(спринт не может продолжаться ни одной задачей), оно живёт до ответа делают следующим. Порядок назначает человек на груминге
человека, а его следы — вопросами в файлах задач. (`av-dev-tasks:groom`), машина его не выводит.
Секции «блокеры» здесь нет и не заводится: блокер — это состояние, оно живёт до
ответа человека, а его следы — вопросами в файлах задач.
## Ядро продукта ## Ядро продукта