## 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)
