- Раскладка 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.
6.2 KiB
6.2 KiB
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: callbackdelete:<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и строку из индекса беклога.