Files
av 42d5b73a04 docs: перевод документации на канон av-dev
- Раскладка docs/ приведена к канону 2: заведены passport/architecture/
  database/security/review и research; docs/specs, drafts, backlog, review/
  и BRIEF.md разобраны и удалены, беклог переехал в docs/tasks (34 задачи,
  6 целей, слаги на английский).
- Нарративы specs удалены как дубли openspec-спек после поимённой сверки;
  остаток заведён задачами (редактор маппинга ревью, крайние случаи
  именования), отказ от сущности title промоутнут в ADR.
- Проектные копии агентов и скиллов ревью/пайплайна удалены в пользу
  плагинов av-dev-pm и av-dev-pipeline; в task gate добавлен шаг canon
  вместо er-schema.
2026-08-04 09:27:26 +03:00

6.2 KiB
Raw Permalink Blame History

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

  • 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/пустой ответ — успех, отсутствие хеша ошибкой не считать.
  • 1.2 Тест internal/qbt/qbt_test.go: форма запроса delete (hashes через |, deleteFiles=true), обработка ответа.

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

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

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

  • 3.1 Расширить интерфейс QBittorrent воркера методом Delete(...) (internal/worker/worker.go); проброс реализации qbt.Client.
  • 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) НЕ вызывает — толерантен к отсутствию источника.
  • 3.3 Логировать удаление как осознанное пользовательское снятие источника (slog, без секретов): id, состояние-источник, снятые ссылки, code=user_delete.

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

  • 4.1 Роут POST /ui/downloads/{id}/delete → хендлер (по образцу handleUndo), финализация через surfaceAction; трансляция ErrConflict → 409/фрагмент как у прочих действий, а ошибка qBittorrent (не ErrConflict) — внятный отказ пользователю (не тихий успех), не 200-без-эффекта.
  • 4.2 Флаг Deletable (состояние ∈ {done, orphaned, target_missing}) в расчётах кнопок (httpapi.go:648, download.go:108).
  • 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 + подтверждение

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

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

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

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

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