## ADDED Requirements

### Requirement: Действия обновляют интерфейс на месте

Мутирующие действия над загрузкой в списке (`/`) и на странице `/download/{id}` SHALL выполняться htmx-запросом и обновлять затронутую область HTML на месте (partial swap), без навигации на другую страницу и без сброса контекста списка (фильтр, поиск, страница пагинации, позиция прокрутки).
Сервер SHALL отвечать на такой запрос HTML-фрагментом обновлённой области, а не
редиректом.

Область свопа SHALL соответствовать поверхности действия: в списке — карточка
загрузки (`<article class="card">`) целиком, отражающая новое состояние, бейдж
и допустимый набор действий; на странице `/download/{id}` — содержимое
страницы, отражающее новое состояние загрузки. После свопа набор показанных
действий MUST соответствовать новому состоянию (см. «Действия соответствуют
состоянию»).

Поведение MUST деградировать без htmx: если запрос действия пришёл без признака
htmx (нет заголовка `HX-Request`), обработчик SHALL отвечать прежним
PRG-редиректом, и действие исполняется тем же доменным вызовом. Формы действий
остаются обычными POST-формами.

Ошибка действия (доменная или валидации) SHALL показываться на месте — в
подменённом фрагменте той же области, — а не только через параметр `?err=`
после редиректа; при ошибке активное состояние загрузки не меняется молча.
Ответ на htmx-запрос действия SHALL иметь статус `200` даже при ошибке действия
(иначе htmx не подменит фрагмент): сообщение об ошибке несёт сам фрагмент.

После свопа карточка SHALL оставаться на своём месте в списке, даже если её
новое состояние уже не подходит под активный фильтр; согласованность списка с
фильтром восстанавливается при следующей полной загрузке. Клиентского
переупорядочивания или пересчёта доменного состояния не выполняется.

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

- **GIVEN** в списке есть загрузка в состоянии `done` с действием отката
- **WHEN** пользователь нажимает «Откатить» (htmx активен)
- **THEN** карточка этой загрузки подменяется на месте на её новое состояние
  (`reverted`) с соответствующим бейджем и набором действий
- **AND** список не перезагружается: фильтр, поиск, страница и позиция прокрутки
  сохраняются

#### Scenario: Действие со страницы загрузки оставляет на странице

- **GIVEN** открыта страница `GET /download/{id}` загрузки в состоянии `done`
- **WHEN** пользователь нажимает «Откатить» или «Привязать заново» (htmx активен)
- **THEN** содержимое страницы обновляется на месте под новое состояние
  загрузки, без перехода на список и без прыжка прокрутки наверх

#### Scenario: Деградация без htmx — прежний редирект

- **WHEN** действие над загрузкой приходит POST-запросом без заголовка
  `HX-Request` (htmx недоступен)
- **THEN** обработчик исполняет то же доменное действие и отвечает
  PRG-редиректом, как раньше; поведение без JavaScript не ломается

#### Scenario: Ошибка действия показана на месте

- **GIVEN** пользователь запускает действие через htmx
- **WHEN** доменный вызов возвращает ошибку (например, состояние уже изменилось)
- **THEN** ответ имеет статус `200`, а сообщение об ошибке показывается в
  подменённом фрагменте той же области, а не только на отдельной странице после
  редиректа
- **AND** активное состояние загрузки не меняется

#### Scenario: Свопнутая карточка остаётся вне фильтра

- **GIVEN** список отфильтрован по группе состояний (например, `review`) и в нём
  есть карточка загрузки
- **WHEN** действие через htmx переводит загрузку в состояние вне этого фильтра
  (например, `cancelled`)
- **THEN** карточка подменяется на месте новым состоянием и остаётся видимой до
  следующей полной загрузки списка, без клиентского переупорядочивания
