From a43adea723ce5571f1c2acf2cfafe7cab611c9f3 Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Wed, 8 Jul 2026 15:43:26 +0300 Subject: [PATCH] =?UTF-8?q?=D0=91=D0=B5=D0=BA=D0=BB=D0=BE=D0=B3:=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=BE=D1=84=D0=BE=D1=80=D0=BC=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20path-2=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=B2=20=D0=BE=D0=B4=D0=BD=D1=83=20=D0=B7=D0=B0=D0=B4?= =?UTF-8?q?=D0=B0=D1=87=D1=83=20=C2=AB=D0=BF=D0=BE=D0=BB=D0=BD=D0=BE=D0=B5?= =?UTF-8?q?=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Обсудили: для сервиса одного окна основное действие — удалить всё разом (раздачу + файлы в библиотеке), чтобы освободить место. Из-за хардлинков иначе место и не вернуть — снять надо обе ссылки на инод. Задача udalenie-edinoe-okno переписана в одну конкретную: - действие «Удалить» на загрузке: снять живые хардлинки (undo-механика, superseded пропускаем) + удалить раздачу с файлами из qBittorrent (новый метод qbt); осознанно обходим предохранитель последней копии; обязательное подтверждение; - undo (перераспознать, гард включён) vs delete (освободить место, гард выключен) зафиксированы как разные действия; - терминальное состояние — переиспользуем deleted, инициатора (user/reconcile) пишем в лог перехода; в граф добавить ребро done→deleted; - тайтл-группа и экран состава выкинуты как оверинжиниринг; мультивыбор в списке — опциональный шаг. Реализовать как OpenSpec-change. Приоритет высокий (недостающая половина одного окна, частый сценарий «досмотрел → освободить место»). Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/backlog/README.md | 2 +- docs/backlog/udalenie-edinoe-okno.md | 55 ++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/docs/backlog/README.md b/docs/backlog/README.md index e19d1ef..6028792 100644 --- a/docs/backlog/README.md +++ b/docs/backlog/README.md @@ -18,7 +18,7 @@ Tududi (проект `jellybit`) больше **не** держит беклог - [Проблема второго сезона (сходимость папки сериала)](vtoroy-sezon-shodimost-papki.md) — Второй/третий сезон должен ложиться в ТУ ЖЕ папку сериала, а не заводить рядом почти… - [Раздачи с докачиванием (merge при повторном добавлении)](merge-dokachivanie.md) — Свежий сериал раздают по мере выхода: торрент с 5 из 10 эпизодов позже перезаливают… -- [Удаление средствами jellybit («единое окно», path 2)](udalenie-edinoe-okno.md) — Распознавание ручного удаления и пометка рассинхрона уже сделаны (state-reconciliation… +- [Полное удаление загрузки из jellybit («единое окно», path 2)](udalenie-edinoe-okno.md) — Действие «Удалить»: снять хардлинки + снести раздачу+файлы из qBittorrent → освободить место (отдельно от undo) - [Ретеншн и очистка БД](retention-ochistka-bd.md) — Терминальные задачи (done/cancelled/failed/reverted), их попытки recognition с сырыми… - [Eval-харнес распознавания (корпус кейсов + метрика точности)](eval-harness-raspoznavaniya.md) — Распознавание — ядро продукта, но смена модели или правка промпта сейчас вслепую… - [Гейт дозаписи хешей в dedup-ветке CreateDownloadIfNoActive (F1)](review-f1-gate-dozapisi-heshey.md) — dedup-ветка дописывает все хеши в найденную задачу без гарда — риск инварианта ≤1 активной _(ревью 2026-07-08)_ diff --git a/docs/backlog/udalenie-edinoe-okno.md b/docs/backlog/udalenie-edinoe-okno.md index 7cc48e0..b78ea3b 100644 --- a/docs/backlog/udalenie-edinoe-okno.md +++ b/docs/backlog/udalenie-edinoe-okno.md @@ -1,13 +1,52 @@ -# Удаление средствами jellybit («единое окно», path 2) +# Полное удаление загрузки из jellybit («единое окно», path 2) **Приоритет:** высокий -Распознавание ручного удаления и пометка рассинхрона уже сделаны (state-reconciliation: target_missing/orphaned/deleted, безопасный undo с nlink-гардом, preflight). Осталось (path 2) — удалять просмотренное из самого jellybit, не идя руками в qBittorrent/Jellyfin. «Тайтл» — вычисляемая группа загрузок по (provider, provider_id)/общей папке, без новой сущности; удаление целиком — обход загрузок группы штатным undo; удаление раздачи из qBittorrent — осознанный выход за инвариант «источник неприкосновенен», только по явному подтверждению. +Безопасная половина «единого окна» уже в проде: распознавание ручного удаления и +пометка рассинхрона (state-reconciliation — `target_missing`/`orphaned`/`deleted`, +undo с nlink-гардом, preflight). Осталась вторая половина — **удалять из самого +jellybit**, не идя руками в qBittorrent/Jellyfin. -Шаги: -- удаление одной загрузки: снять живые хардлинки (штатный undo, superseded пропускаем, nlink-гард) + опц. удалить раздачу из qBittorrent с файлами — с подтверждением -- вычисляемая группа «тайтл» в UI: состав сериала/фильма одним экраном -- удаление тайтла целиком: обход загрузок группы + опц. снос опустевшей папки -- после полного удаления память о тайтле не остаётся +Основной сценарий одного окна — «досмотрел → освободить место»: удалить и раздачу, +и файлы в библиотеке разом. Из-за хардлинков иначе место и не вернуть — файл в +`downloads/` (qBittorrent) и наша ссылка в библиотеке указывают на **один инод**, +диск освобождается только когда исчезает последняя ссылка. Значит delete обязан +снять **обе** стороны. -Связано: drafts/logical-title-model.md §5.3/§6.4, ADR-2026-06-13-hardlinks, specs/architecture.md, specs/workflow.md +## Действие «Удалить» (одна загрузка) + +- Снять живые хардлинки загрузки (переиспользуем undo-механику: `superseded` + пропускаем) **+** удалить раздачу с файлами из qBittorrent (новый метод в `qbt`, + `deleteTorrents` с `deleteFiles=true`). +- **Осознанно обходим** предохранитель последней копии (undo при «источник удалён, + цель — последняя копия» отказывает — `worker/review.go:499`; здесь мы наоборот + хотим снять последнюю копию). +- Обязательное **подтверждение** — это выход за инвариант «источник + неприкосновенен», не по случайному клику. Логировать как осознанное удаление + источника. + +## undo vs delete (зафиксировать различие) + +- **undo** — «перераспознать»: снимает только наши ссылки, раздачу в qBittorrent + сохраняет, данные бережёт (гард включён). Уже готов. +- **delete** — «убрать окончательно, освободить место»: снимает наши ссылки И + сносит раздачу+файлы, гард выключен, состояние терминальное. + +## Терминальное состояние + +Переиспользуем существующий `deleted` (исход тот же: и источник, и цель сняты) — +**без** нового статуса. Инициатора (`user` против reconciliation) пишем в причину/ +лог перехода. В граф переходов добавить явное пользовательское ребро +`done → deleted` (сейчас `deleted` выводит только сверка с реальностью). + +## Опционально (если понадобится) + +Мультивыбор в списке + «удалить выбранное» — тонкая обёртка над тем же действием +для редкого случая «снести сериал целиком». Вычисляемую группу-«тайтл» и экран +состава **не** делаем — оверинжиниринг ради редкого сценария. + +Оформить как OpenSpec-change (дельта `state-reconciliation`/`review` + метод `qbt` ++ ребро графа + action в UI/боте). + +Связано: drafts/logical-title-model.md §5.3/§6.4, ADR-2026-06-13-hardlinks, +openspec/specs/state-reconciliation, specs/workflow.md, пакеты qbt, worker, layout.