# ✨ Удалять выбранные загрузки с файлами на отдельной странице - **Тип:** feature - **Категория:** Ядро продукта - **Зачем:** удаление с файлами живёт только в danger-секции страницы одной загрузки: чтобы снести десять раздач, надо десять раз пройти путь список → карточка → подтверждение - **Теги:** goal:bulk-download-management Отдельная страница веб-UI, доступная из шапки: список загрузок, у которых удаление разрешено (`done`, `orphaned`, `target_missing` — `internal/worker/review.go:631`), чекбокс у каждой строки и одна кнопка «Удалить выбранные». Кнопка ведёт на подтверждение, где выбранные раздачи названы поимённо, и только оттуда уходит удаление. Страница отдельная, чтобы не вешать режим выбора на основной список: там карточки живые, самополлятся и перерисовываются, а выбор пользователя такое перерисовывание переживать не обязан. Речь только о полном удалении (снять хардлинки + снести раздачу с файлами из qBittorrent). Групповой `Dismiss` (смена статуса без файлов) в эту задачу не входит. ## Затрагивает - новая страница веб-UI и ссылка на неё в `web/templates/partials/header.html`; новый шаблон страницы и её партиалы; - новый POST-эндпоинт группового удаления в `internal/httpapi` (рядом с `POST /ui/downloads/{id}/delete`, `internal/httpapi/httpapi.go:143`); - `Reviewer.Delete` воркера (`internal/worker/review.go:619`) — зовётся по каждой выбранной загрузке; - qBittorrent `torrents/delete` с `deleteFiles=true` — внешний сервис, необратимая операция; - спека `web-ui`, требования «Страницы веб-UI» и «Действия соответствуют состоянию»; - спека `state-reconciliation` в части `Delete` — групповой вызов не меняет условий поштучного, но подтверждение человека теперь одно на пачку. ## Критерии приёмки - Страница открывается из шапки и показывает только те загрузки, для которых удаление разрешено поштучно (оракул: тест `internal/httpapi` — подставной читатель отдаёт задачи во всех состояниях, в разметке строки есть у `done`/`orphaned`/`target_missing` и нет у остальных). - Удаление уходит только после явного подтверждения, и подтверждение называет каждую выбранную раздачу поимённо (оракул: тест — POST без признака подтверждения отвечает отказом и не делает ни одного вызова `Delete` у подставного воркера; ответ подтверждения содержит заголовки всех выбранных). - Отказ на одной загрузке не отменяет остальных, а результат называет удалённые и отказавшие поимённо с причиной (оракул: тест, где второй `Delete` возвращает ошибку — первая и третья удалены, страница результата называет вторую и её причину). - Групповой путь не расширяет прав поштучного: попытка удалить задачу в состоянии, где кнопка недоступна, отклоняется с тем же отказом (оракул: тест — `Delete` для `downloading` возвращает `ErrConflict`, страница показывает отказ, остальные выбранные не затронуты). - Поведение страницы записано дельта-спекой и проходит валидацию (оракул: `openspec validate --strict` и `task gate`). ## Рамки Необратимое действие: `deleteFiles=true` сносит файлы раздачи, гард последней копии в `Delete` выключен сознательно. Ослаблять подтверждение ради удобства пачки нельзя — оно остаётся обязательным и поимённым. Групповой `Dismiss` и автоматическая чистка по сроку хранения (`db-retention-cleanup`) — не эта задача.