Приём (Ingest) стал быстрым: синхронно только парс magnet, синтез контекста из полей ссылки, атомарный дедуп и запись загрузки в новое состояние `catched` — ответ клиенту сразу. Медленный вывод имени (LLM) и добавление в qBittorrent вынесены в асинхронный шаг машины состояний, который двигает worker. - store: состояние `catched` (нетерминальное, активная группа); атомарный переход PromoteCatched (catched → downloading + display_name) с гардом state='catched' (ре-валидация после сетевых вызовов вне блокировки) - ingest: убраны namer/qbt из пути приёма; пишем `catched`, отвечаем сразу - worker.processCatched: вне w.mu выводит имя и qbt.Add, под w.mu — короткий переход; сбой add оставляет catched (ретрай тиком); предохранитель catch_timeout → failed(qbit_add)+notify; catched исключён из проверок пропажи - config: worker.catch_timeout (дефолт 10m) - веб-UI: бейдж catched, активная группа, самозавершающийся htmx-поллинг карточки/страницы до перехода в downloading; Telegram-текст без сырого catched - OpenSpec: дельты ingest/download-tracking/web-ui влиты в спеки, change заархивирован Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.1 KiB
ADDED Requirements
Requirement: Отображение промежуточного состояния catched
Веб-UI SHALL отображать состояние catched как штатную промежуточную фазу
(«поймано, добавляется в qBittorrent»): бейдж статуса загрузки SHALL иметь
понятную человекочитаемую подпись для catched (а не сырое catched), а
загрузка в catched SHALL относиться к активной группе списка.
Пока отображаемое имя ещё не выведено (в catched download.display_name
пуст), заголовок загрузки SHALL деградировать по существующему фолбеку
(распознанное название или усечённый источник) — см. «Заголовок загрузки из
имени раздачи». Секция раздачи/живого прогресса для catched SHALL корректно
отсутствовать (раздачи в qBittorrent ещё нет), не создавая ошибок отображения.
Карточка/страница загрузки в catched SHALL самообновляться самозавершающимся
htmx-поллингом (см. конвенцию веб-UI): по переходе загрузки в downloading
интерфейс SHALL отражать это без перезагрузки страницы (подхватить бейдж,
выведенное имя и появившийся живой прогресс), а поллинг фазы catched SHALL
завершаться, как только загрузка её покинула.
Scenario: Бейдж и группа для catched
- WHEN загрузка находится в состоянии
catched - THEN её бейдж статуса имеет человекочитаемую подпись для
catched - AND загрузка попадает в активную группу списка
Scenario: Заголовок catched без имени
- GIVEN загрузка в
catchedс пустымdownload.display_name - WHEN рендерится карточка/страница загрузки
- THEN заголовок берётся из фолбека (распознанное название или усечённый источник), без ошибок отображения
- AND секция раздачи/живого прогресса не показывается (раздачи ещё нет)
Scenario: Самообновление при переходе в downloading
- GIVEN открытая карточка загрузки в
catched - WHEN worker перевёл загрузку в
downloading - THEN интерфейс без перезагрузки показывает состояние
downloading(бейдж, имя, живой прогресс) - AND поллинг фазы
catchedзавершается