## 1. Телеметрия в клиенте qBittorrent - [x] 1.1 Добавить в `qbt.Torrent` поля `Dlspeed`, `Eta`, `Ratio`, `NumSeeds`, `NumLeechs`, `Uploaded`, `Upspeed` с json-тегами из `/torrents/info` (`dlspeed`, `eta`, `ratio`, `num_seeds`, `num_leechs`, `uploaded`, `upspeed`) - [x] 1.2 Обновить комментарий-доку `Torrent` (подмножество полей расширено) ## 2. Снимок телеметрии в воркере - [x] 2.1 Добавить тип `worker.Live{Progress, DlSpeed, ETA, State, Seeding, Ratio, Seeds, Peers, Uploaded, UpSpeed}` (курированный, без зависимости на qbt у читателей); `Seeding` вычислять через `classify(state) == classReady` - [x] 2.2 Добавить в `Worker` поле снимка `live map[string]Live` + отдельный `sync.RWMutex` (не `w.mu`); инициализация в `New` - [x] 2.3 В `Poll` собрать новую карту из полученного `torrents` — **три ключа на торрент** (lowercase `Hash`/`InfohashV1`/`InfohashV2`, как `byHash`) — и атомарно подменить снимок **сразу после построения `byHash`, до store- операций** (телеметрия обновляется даже при последующем сбое reconcile) - [x] 2.4 Реализовать `Live(infohash string) (Live, bool)` — чтение под RLock, `ok=false` при пустом/неизвестном infohash - [x] 2.5 Тесты: снимок обновляется после `Poll`; `Live` отдаёт данные качающейся и сидирующей задачи (с верным `Seeding`); поиск по любому из трёх хэшей; неизвестный/пустой infohash → `ok=false` ## 3. Транспорт: контракт и фрагмент-роуты - [x] 3.1 Объявить интерфейс `LiveStatus` в `internal/httpapi` и добавить поле `Live LiveStatus` в `Deps` - [x] 3.2 Прокинуть `Live: wrk` в `httpapi.Deps` в `cmd/jellybit/serve.go` - [x] 3.3 Добавить роуты `GET /fragments/downloads/{id}/progress` и `GET /fragments/downloads/{id}/seeding` в `NewRouter` - [x] 3.4 Реализовать обработчики фрагментов: `GetDownload` → infohash/состояние + `Live(infohash)`; рендер партиала; `ok=false` → деградация без живых значений - [x] 3.5 **Начальный рендер со значениями:** расширить `downloadView` и `downloadDetailView` полями телеметрии; в `handleIndex`/`handleDownload` вызвать `Live(infohash)` для нужных задач и положить во view — одна модель для страницы и для фрагмент-роута (без мигания при первой загрузке) - [x] 3.6 Добавить хелперы форматирования скорости/размера/ETA: человекочитаемые единицы (байты→КиБ/МиБ, скорость/с), sentinel'ы qBittorrent (`eta=8640000`→ «—»/«∞», `ratio<0`→«—»); зафиксировать выбор `num_seeds`/`num_leechs` ## 4. Шаблоны и живое обновление - [x] 4.1 Партиал `partials/progress.html` (прогресс-бар + скорость + ETA); включить его в карточку `index.html` для активных загрузок - [x] 4.2 Навесить на прогресс-блок активной карточки `hx-get` / `hx-trigger="every 3s"` / `hx-swap="outerHTML"`; при выходе из downloading фрагмент не содержит атрибутов поллинга (поллинг прекращается) - [x] 4.3 Партиал `partials/seeding.html` (рейтинг, сиды/пиры, отдано, скорость отдачи); секция «Раздача» в `download.html` для сидирующих задач, с htmx-поллингом и деградацией при отсутствии данных - [x] 4.4 Проверить, что фоновое обновление не сбрасывает фильтр/поиск/прокрутку на главной (поллинг точечный, на уровне карточки) - [x] 4.5 При необходимости — стили прогресс-бара/раздачи в `jellybit.css` (использовать существующие токены, без инлайн-хардкода цветов) ## 5. Проверки и ревью - [x] 5.1 `task test` (включая новые тесты воркера и httpapi-рендера фрагментов; в т.ч. тест «фрагмент при выходе из downloading отдаётся без htmx-атрибутов поллинга») - [x] 5.2 `task lint`, `gofmt`, `openspec validate --strict` - [x] 5.3 Ревью кода (второй чекпоинт) перед archive: соответствие спекам и конвенциям (логирование без секретов, ошибки `%w`, отдельный RWMutex)