Привёл набор capabilities в OpenSpec к цепочке обработки, чтобы имя capability отвечало одному поведению. Чисто по спекам, код и поведение системы не меняются. Change refactor-capability-boundaries (архивирован): - recognition разделён на recognition (разбор LLM) + metadata-match (сверка с базами) - review выделен из web-ui + мигрирован из docs/specs/review-ux.md - новые capability из docs/specs: file-layout, download-tracking, notifications - identity очищен до инфра-id; приём (инфохэши, дедуп, ядро приёма) — в ingest - уведомление о рассинхроне перенесено из state-reconciliation в notifications - дубль владения путём и безопасного undo оставлен в state-reconciliation Итог: 11 capabilities, openspec validate --strict проходит (+37/−11 требований). Источник истины по мигрированным темам переехал в openspec/specs (шапки в docs). Снят пункт беклога «Пересмотр набора capabilities». Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
6.2 KiB
download-tracking Specification
Purpose
Отслеживание скачивания и прямой путь машины состояний загрузки: поллинг
qBittorrent и сопоставление его состояний (downloading → completed; готовность
только когда файлы на месте), таймауты-предохранители (magnet_timeout/
stuck_after), ошибка qBit → failed, усыновление раздач по категории/тегу и
переходы под per-download блокировкой. Сверка уже разложенного с реальностью —
в state-reconciliation.
Requirements
Requirement: Поллинг qBittorrent и сопоставление состояний
Worker SHALL периодически (worker.poll_interval, дефолт 5 с) опрашивать
qBittorrent и сопоставлять его состояния раздачи с состоянием загрузки в БД.
Готовые к раскладке состояния (uploading/stalledUP/pausedUP/stoppedUP/
queuedUP/forcedUP, с учётом различий имён между qBit v4 и v5) SHALL переводить
загрузку в completed. Ещё качающиеся состояния (downloading/stalledDL/
metaDL/…) SHALL оставлять её в downloading.
Scenario: Раздача завершилась
- GIVEN загрузка в
downloading - WHEN qBittorrent сообщает состояние
stalledUPи файлы на месте - THEN загрузка переходит в
completed
Requirement: Готовность только когда файлы на месте
Переходные состояния qBittorrent система SHALL трактовать как «ждём»
(moving/checkingUP/checkingResumeData/allocating): оставаться в
downloading и НЕ объявлять готовность, даже если выставлены флаги UP, пока
qBit не завершит перенос/проверку. Финальные пути файлов система SHALL брать из
API после завершения переноса.
Scenario: Ждём завершения переноса
- GIVEN загрузка, у которой qBittorrent в состоянии
moving - WHEN идёт тик поллинга
- THEN загрузка остаётся в
downloading, готовность не объявляется
Requirement: Таймауты-предохранители downloading
Система SHALL переводить metaDL/forcedMetaDL дольше magnet_timeout (дефолт
24h, редкий предохранитель) в failed (error_code magnet_timeout), а
stalledDL дольше stuck_after — в stuck (error_code
stalled). Возраст система SHALL считать от времени добавления в qBittorrent
(added_on), а не от создания задачи, чтобы базис переживал retry и усыновление.
Долгий metaDL система НЕ SHALL убивать агрессивно (медленные трекеры — норма).
Scenario: Завис на метаданных дольше таймаута
- GIVEN раздача в
metaDLдольшеmagnet_timeoutотadded_on - WHEN идёт тик поллинга
- THEN загрузка переходит в
failedсerror_codemagnet_timeout
Requirement: Ошибка qBittorrent переводит в failed
Состояния error/missingFiles система SHALL трактовать как настоящий провал и
переводить загрузку в failed (error_code qbit_error) — в отличие от
таймаутов-предохранителей, такой провал сверкой не воскрешается.
Scenario: qBit сообщает об ошибке
- GIVEN раздача в состоянии
missingFiles - WHEN идёт тик поллинга
- THEN загрузка переходит в
failedсerror_codeqbit_error
Requirement: Усыновление раздач по категории или тегу
Worker SHALL периодически сверять раздачи qBittorrent с БД и усыновлять те, у
которых наша категория (qbittorrent.category) ИЛИ тег (qbittorrent.tag), а
записи в БД ещё нет, заводя для них загрузку в состоянии downloading. Категория
ставится на добавляемые нами раздачи (push); тег позволяет подхватить уже
существующую раздачу (pull), не трогая её категорию и файлы.
Scenario: Подхват существующей раздачи по тегу
- GIVEN в qBittorrent есть раздача с тегом
qbittorrent.tag, которой нет в БД - WHEN worker сверяет qBittorrent с БД
- THEN для раздачи заводится загрузка в состоянии
downloading
Requirement: Переходы состояний под per-download блокировкой
Все переходы состояний загрузки SHALL проходить через worker под per-download
блокировкой, чтобы два транспорта не гонялись за одно состояние. Состояние SHALL
быть персистентным в SQLite; активность загрузки SHALL выводиться только из
state, без отдельного флага.
Scenario: Команды сериализуются
- GIVEN две одновременные команды к одной загрузке из разных транспортов
- WHEN они обрабатываются
- THEN переходы применяются последовательно под блокировкой, без гонки