## 1. qbt — удаление раздачи

- [x] 1.1 В `internal/qbt/qbt.go` добавить `Delete(ctx, hashes []string, deleteFiles bool) error`:
  `POST /api/v2/torrents/delete`, форма `hashes=<h1>|<h2>|…` + `deleteFiles`.
  Логирование `ExtCall{Operation:"torrents/delete"}` как у `Add`; 2xx/пустой
  ответ — успех, отсутствие хеша ошибкой не считать.
- [x] 1.2 Тест `internal/qbt/qbt_test.go`: форма запроса delete (hashes через `|`,
  `deleteFiles=true`), обработка ответа.

## 2. layout — снятие ссылок без гарда последней копии

- [x] 2.1 Добавить в `Layouter` и реализацию `internal/layout/layout.go` путь
  снятия целевых ссылок **без** `ErrLastCopy` (напр. `Remove(ctx, links)`):
  безусловный `unlink` только своих ссылок строго под `paths.movies`/`series`;
  санитизация/проверка «под библиотекой» сохраняется, `paths.downloads` не
  трогаем.

## 3. worker — команда Delete

- [x] 3.1 Расширить интерфейс `QBittorrent` воркера методом `Delete(...)`
  (`internal/worker/worker.go`); проброс реализации `qbt.Client`.
- [x] 3.2 В `internal/worker/review.go` добавить `Delete(ctx, id string) error`:
  под `w.mu`; `GetDownload`; предусловие `d.State ∈ {done, orphaned,
  target_missing}`, иначе `ErrConflict`. Порядок: (а) снять цель как в `Undo`
  (`LatestBatchID`/`ListFileLinksByBatch`/фильтр `isLaidOut`, `superseded`
  пропускаем) через новый `layouter.Remove`, затем `DeleteFileLinksByBatch`; (б)
  `qbt.Delete(d.Infohashes, deleteFiles=true)` (хеши уже загружены с задачей,
  отдельный store-геттер не нужен) — ошибку присутствующей раздачи пробросить
  (не метить `deleted`), отсутствие раздачи ошибкой не считать; (в)
  `transition(ctx, d, StateDeleted, "user_delete", <причина>)`. Delete
  source-preflight (`ensureSourceReady`/`reconcileToReality`) НЕ вызывает —
  толерантен к отсутствию источника.
- [x] 3.3 Логировать удаление как осознанное пользовательское снятие источника
  (`slog`, без секретов): id, состояние-источник, снятые ссылки, `code=user_delete`.

## 4. Транспорт HTTP/веб + подтверждение

- [x] 4.1 Роут `POST /ui/downloads/{id}/delete` → хендлер (по образцу
  `handleUndo`), финализация через `surfaceAction`; трансляция `ErrConflict` →
  409/фрагмент как у прочих действий, а ошибка qBittorrent (не `ErrConflict`) —
  внятный отказ пользователю (не тихий успех), не 200-без-эффекта.
- [x] 4.2 Флаг `Deletable` (состояние ∈ {done, orphaned, target_missing}) в
  расчётах кнопок (`httpapi.go:648`, `download.go:108`).
- [x] 4.3 Danger-секция в самом низу `web/templates/partials/download_main.html`:
  кнопка «Удалить» → confirm (htmx-своп фрагмента «Точно удалить? …
  [Да, удалить] [Отмена]») → `POST …/delete`. Деградация без htmx
  (`<details>`/отдельный шаг), ошибка на htmx-пути = 200 + фрагмент.
- [ ] 4.4 (Опц., НЕ в этом change) JSON-роут `POST /api/downloads/{id}/delete`.
  Осознанно отложено: REST в спеке помечен опциональным, а деструктивный endpoint
  без естественного шага подтверждения заводим только по реальной нужде.

## 5. Транспорт Telegram + подтверждение

- [x] 5.1 В `internal/tgbot` добавить действие `delete`: callback `delete:<id>`
  показывает confirm-keyboard (`delete_confirm:<id>` / «Отмена»); удаление — на
  подтверждающем callback (`Reviewer.Delete`). Кнопка «Удалить» — в карточке
  задачи для состояний done/orphaned/target_missing.
- [x] 5.2 Ветка ошибок как у прочих действий (`ErrConflict` → понятный отказ;
  ошибка qBittorrent → отдельное сообщение об отказе, не тихий успех), после
  успеха — `answer` + `refreshCard`.

## 6. Тесты воркера

- [x] 6.1 Delete из `done`: сняты ссылки + вызван `qbt.Delete(deleteFiles=true)`
  + переход `deleted`/`user_delete`.
- [x] 6.2 Delete из `orphaned`: последняя копия снята (гард выключен), отсутствие
  раздачи не ошибка, переход `deleted`.
- [x] 6.3 Delete из `target_missing`: снятие цели идемпотентно (нет живых ссылок),
  раздача снесена, переход `deleted`.
- [x] 6.4 Delete из `review`/иного — отказ `ErrConflict`, состояние не меняется.
- [x] 6.5 Ошибка `qbt.Delete` при присутствующей раздаче — задача остаётся, в
  `deleted` не уходит; повторный delete идемпотентен.

## 7. Ревью и сверка

- [x] 7.1 `task test` и `task lint` зелёные.
- [x] 7.2 Ревью кода (второй чекпоинт) перед archive.
- [x] 7.3 `openspec validate download-delete --strict` зелёный.
- [x] 7.4 Синк нарратива `docs/specs/workflow.md`: явные пользовательские рёбра
  `done/orphaned/target_missing → deleted` (delete) и различие Undo vs delete;
  удалить `docs/tasks/items/udalenie-edinoe-okno.md` и строку из индекса беклога.
