Единое окно: полное пользовательское удаление загрузки (delete)

Вторая половина «единого окна»: команда «Удалить» снимает наши библиотечные
хардлинки (гард последней копии осознанно выключен, в отличие от Undo) и сносит
раздачу с файлами из qBittorrent (deleteFiles=true) → терминальный deleted.
Доступна из done/orphaned/target_missing, идемпотентна к отсутствующей стороне;
инициатор различается через error_code=user_delete. Подтверждение обязательно:
веб — danger-секция внизу страницы (hx-confirm + details), Telegram — двухшаговый
inline-confirm. qbt.Delete + layout.Remove (unlink без ErrLastCopy, только свои
ссылки под movies/series). Граф переходов не менялся — рёбра уже были.

OpenSpec: state-reconciliation +1 требование; синк workflow.md; беклог закрыт.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
av
2026-07-10 12:17:51 +03:00
co-authored by Claude Opus 4.8
parent e68f96df9e
commit 3f1a928000
25 changed files with 893 additions and 76 deletions
+17 -3
View File
@@ -51,10 +51,11 @@ stateDiagram-v2
done --> target_missing: сверка — цель удалена
done --> orphaned: сверка — источник пропал
done --> deleted: Удалить (delete)
target_missing --> recognizing: Привязать заново
target_missing --> orphaned: источник тоже пропал
target_missing --> deleted: источник тоже пропал
orphaned --> deleted: цель тоже удалена
target_missing --> deleted: Удалить / источник тоже пропал (сверка)
orphaned --> deleted: Удалить / цель тоже удалена (сверка)
target_missing --> done: healing (цель вернулась)
orphaned --> done: healing (источник вернулся)
@@ -96,7 +97,8 @@ stateDiagram-v2
[architecture.md](architecture.md) → «Раскладка файлов».
- **done** — при входе неблокирующе дёргаем пересканирование Jellyfin
(опц., см. [architecture.md](architecture.md) → «Пересканирование
Jellyfin»); доступен **Undo**`reverted` (убрать созданные ссылки).
Jellyfin»); доступен **Undo**`reverted` (убрать созданные ссылки) и
**Удалить**`deleted` (полное удаление, см. ниже).
- **stuck / failed / cancelled** — не качается дольше таймаута; ошибка
(ретраибельна); «Отклонить».
- **reverted / cancelled → recognizing** — «Привязать заново»: после
@@ -120,6 +122,18 @@ stateDiagram-v2
- **deleted** — нет ни источника, ни цели; **терминально**: сверка его
больше не переоценивает (см. ниже).
**Undo vs Удалить (delete).** Это разные пользовательские операции. **Undo**
(из `done`) — «перераспознать»: снимает только наши библиотечные ссылки, раздачу
в qBittorrent бережёт, гард последней копии включён (не сотрёт единственный
файл) → `reverted`. **Удалить** (из `done`, `orphaned`, `target_missing`) —
«убрать окончательно, освободить место»: снимает наши ссылки **и** сносит раздачу
с файлами из qBittorrent, гард последней копии осознанно выключен (обход
инварианта «источник неприкосновенен» — только по подтверждению) →
терминальный `deleted`. Идемпотентно к отсутствующей стороне, так что подчищает
остатки из любого из трёх состояний. Инициатор в `deleted` различается по
`error_code`: пользовательское удаление — `user_delete`, вывод сверкой —
`reconcile`. Полные требования — `openspec/specs/state-reconciliation/`.
Сверка трогает только `done`/`target_missing`/`orphaned` — терминальный
`deleted`, активные и пользовательски-терминальные (`reverted`/`cancelled`/
`failed`/`stuck`) состояния не задевает. Реальность «лечится» сама: при