Files
jellybit/openspec/specs/live-status/spec.md
T
av a5d873b62d web-ui: карточка и страница обновляются, пока задачу может двигать фон
- условие самообновления — доменный предикат store.State.IsObservable() вместо
  фазы catched; один поллер на поверхность, интервалы 5 с и 15 с
- отказ тика отвечает 200 и самозавершающимся фрагментом с корневым id цели
  вместо 404/500, который htmx не свопит
- заведён ADR-2026-08-10-observability-is-not-terminality, переписан раздел
  «Живой поллинг» в конвенции веб-UI
2026-08-10 14:02:38 +03:00

11 KiB

live-status Specification

Purpose

Живая телеметрия раздач: единственный сэмплер — воркер, снимок держится в памяти на тике поллинга и в БД не персистится. Состав телеметрии (прогресс, скорость, ETA, размер; для сидирующих — рейтинг, сиды и пиры, отдано), узкий контракт чтения транспортом и живое обновление интерфейса поллингом фрагментов. Что именно рисуется на странице и в карточке — web-ui; откуда берутся сами состояния загрузки — download-tracking.

Requirements

Requirement: Снимок живой телеметрии

Воркер — единственный сэмплер qBittorrent — SHALL на каждом тике поллинга обновлять in-memory снимок телеметрии всех известных раздач. Снимок MUST NOT персиститься в БД: он волатилен и переживает только до рестарта процесса.

Scenario: Обновление снимка на тике

  • WHEN воркер завершает успешный тик поллинга qBittorrent
  • THEN снимок телеметрии содержит актуальные данные по каждой раздаче, присутствующей в ответе qBittorrent

Scenario: Снимок волатилен

  • WHEN процесс только что перезапущен и первый тик поллинга ещё не прошёл
  • THEN снимок пуст, а UI отображает задачи без живых значений, не падая

Requirement: Состав телеметрии

Телеметрия одной раздачи SHALL включать прогресс (доля 0..1), скорость загрузки, ETA и общий размер раздачи (total size); а для сидирующих раздач дополнительно — рейтинг, число сидов и пиров, объём отданного и скорость отдачи. Общий размер SHALL быть доступен для любой раздачи, присутствующей в снимке (не только сидирующей). Значения SHALL извлекаться из ответа qBittorrent /torrents/info без дополнительного сетевого вызова.

Scenario: Телеметрия качающейся задачи

  • WHEN торрент задачи находится в состоянии загрузки
  • THEN в снимке для неё доступны прогресс, скорость загрузки и ETA

Scenario: Общий размер доступен для любой раздачи в снимке

  • WHEN торрент задачи присутствует в снимке в любом состоянии
  • THEN в телеметрии для неё доступен общий размер раздачи

Scenario: Телеметрия сидирующей задачи

  • WHEN торрент задачи завершён и раздаётся
  • THEN в снимке для неё доступны рейтинг, число сидов/пиров, объём отданного и скорость отдачи

Requirement: Чтение телеметрии транспортом

Сервис SHALL предоставлять чтение снимка телеметрии по задаче через изолированный контракт, не зависящий от способа доставки в браузер (поллинг сейчас, SSE в будущем). Если для задачи нет записи в снимке (соответствующий торрент отсутствовал в qBittorrent на последнем тике), чтение SHALL сообщать об отсутствии данных, а UI MUST деградировать без живых значений, не падая.

Scenario: Данные есть

  • WHEN транспорт читает телеметрию задачи, чей торрент был в последнем тике
  • THEN он получает живые значения этой задачи

Scenario: Данных нет

  • WHEN транспорт читает телеметрию задачи, торрента которой нет в qBittorrent
  • THEN он получает признак отсутствия данных и рендерит страницу без живых значений

Requirement: Свежесть не выше тика поллинга

Живые значения, видимые в браузере, SHALL быть не свежее последнего тика поллинга воркера; браузер MUST NOT опрашивать qBittorrent напрямую. Любой запрос UI за телеметрией SHALL обслуживаться из in-memory снимка, не порождая обращения к qBittorrent — поэтому частота обновления UI может быть выбрана свободно (в т.ч. чаще тика для плавности), не нагружая qBittorrent.

Scenario: Браузер не обгоняет воркер

  • WHEN браузер запрашивает фрагмент телеметрии чаще, чем длится тик поллинга
  • THEN он получает значения последнего тика, и обращения к qBittorrent при этом не происходит

Requirement: Живой прогресс активных загрузок

Веб-UI SHALL показывать прогресс, скорость и ETA активных (downloading) загрузок на главной без перезагрузки страницы, обновляя их тем же самообновлением, которым обновляется сама карточка (см. web-ui, «Самообновление живой задачи»). Обновление MUST NOT сбрасывать клиентские фильтр, поиск и прокрутку.

Когда задача покидает состояние downloading, показ скорости и ETA SHALL прекращаться: вне качания эти величины смысла не имеют. Снимок при этом продолжает питать прочие живые значения карточки и страницы — размер и рейтинг раздачи, — и прекращение показа цифр качания MUST NOT означать прекращения обновления поверхности: она продолжает отражать смену состояния, пока задача наблюдаема.

Живые цифры MUST браться из снимка воркера; при отсутствии данных по задаче поверхность деградирует без них, не ломая остального отображения.

Scenario: Прогресс растёт без перезагрузки

  • WHEN загрузка качается и пользователь смотрит на главную
  • THEN её прогресс-бар, скорость и ETA обновляются на месте без перезагрузки страницы

Scenario: Клиентское состояние сохраняется

  • WHEN применён фильтр или поиск и происходит фоновое обновление
  • THEN выбранный фильтр, текст поиска и позиция прокрутки не сбрасываются

Scenario: Завершение убирает цифры качания, но не обновление

  • WHEN загрузка переходит из downloading в другое наблюдаемое состояние
  • THEN блок прогресса, скорости и ETA с карточки исчезает
  • AND карточка продолжает обновляться и приносит новое состояние
  • AND размер и рейтинг раздачи по-прежнему берутся из снимка

Requirement: Секция раздачи на странице загрузки

Страница /download/{id} SHALL показывать секцию «Раздача» с живой статистикой (рейтинг, число сидов и пиров, объём отданного, скорость отдачи) для задач, чей торрент сидирует. Если живых данных по задаче нет, секция SHALL отсутствовать либо явно показывать «нет данных», не ломая остальную страницу.

Секция MUST NOT опрашивать сервер самостоятельно: она лежит внутри области, которую страница обновляет целиком, и собственный опрос секции подменял бы разметку страницы. Её цифры SHALL приходить с тиком самообновления страницы (см. web-ui, «Самообновление живой задачи»), а частота их обновления SHALL совпадать с частотой обновления страницы.

Scenario: Сидирующая задача показывает раздачу

  • WHEN открыта страница задачи, торрент которой раздаётся
  • THEN в секции «Раздача» видны рейтинг, сиды/пиры, отдано и скорость отдачи

Scenario: Нет живых данных — секция деградирует

  • WHEN открыта страница задачи, торрента которой нет в qBittorrent
  • THEN секция «Раздача» отсутствует или показывает «нет данных», а распознавание, файлы и история отображаются нормально

Scenario: Секция обновляется тиком страницы

  • GIVEN открыта страница наблюдаемой задачи, чья раздача сидирует
  • WHEN страница отрисована
  • THEN секция «Раздача» не несёт собственного опроса
  • AND её цифры обновляются вместе с остальной страницей