Files
jellybit/docs/adr/ADR-2026-07-02-no-title-entity.md
T
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

82 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Отдельную сущность «тайтл» не вводим
- **Дата:** 2026-07-02
- **Источник:** черновик `docs/drafts/logical-title-model.md` (разбор от
2026-07-01, переработан 2026-07-02; удалён при переводе проекта на канон,
полный текст — в истории git). Первый производный change —
[openspec/changes/archive/2026-07-02-ulid-identity/design.md](../../openspec/changes/archive/2026-07-02-ulid-identity/design.md).
## Решение
Логический тайтл (фильм или сериал, складывающийся из нескольких загрузок во
времени) остаётся **вычисляемой группой**, а не хранимой сущностью: доменная
идентичность — `download` (ULID + множество инфохэшей), связь с диском —
`file_link` с владением целевым путём, а «второй сезон в ту же папку» решается
**правилом сходимости папки** при построении плана раскладки.
## Почему
Разбор шёл от операций, и у тайтла их не нашлось:
> У `title` при разборе **не нашлось ни одной собственной операции**: сходимость
> папки — правило при построении плана; merge докачивания — per-path логика;
> удаление целиком — цикл по вычисляемой группе. Сущность без собственных
> операций — это линза, а линзу достаточно вычислять, не хранить.
Второй аргумент — у папки уже есть дом, и вычисляемый якорь **корректнее**
хранимого:
> «Папка — это title-уровневое состояние, ей нужен дом» разбивается о то, что
> дом у папки уже есть — файловая система и `dst_path` живых `file_link`'ов.
> Реестр дублировал бы то, что и так записано в БД в N экземплярах. Причём
> вычисляемый якорь корректнее хранимого: если все файлы сериала снесли, живых
> ссылок нет — и новая загрузка честно создаёт свежую папку; хранимый
> `title.folder` указывал бы в пустоту.
Третий — отказ **устраняет**, а не решает хвост развилок: жизненный цикл тайтла
(рождение, смерть, пустой тайтл), слияние тайтлов, ad-hoc тайтл без провайдера,
обратная миграция существующих строк, отдельный title-лог.
## Рассмотренные варианты
- **L2 — `title` с ключом `(provider, provider_id)`.** Отвергнут: привязывает
долгоживущую сущность к провайдеру, который может смениться.
- **L2-min — `title` со своим ULID + `title_external_id`** (провайдерные id
множеством-атрибутом, симметрично `download_infohash`). Схема красивая и
решает смену провайдера, ad-hoc тайтлы и слияние. Отвергнут именно по
аргументу выше: собственных операций нет, а сущность тянет жизненный цикл,
миграцию и четыре развилки.
- **L3 — title-центричная медиатека (модель sonarr).** Отвергнут осознанно: мы
не ходим в индексеры, не мониторим тайтлы и не ведём профили качества —
контент приносит пользователь. Это граница домена,
[passport.md](../passport.md) → «Что целью не является».
## Последствия
- `+` Идентичность осталась одноуровневой: `download` — мост между раздачей в
qBittorrent и файлами на диске, и каждая сущность цепочки отвечает на свои
операции.
- `+` Главная боль («второй сезон должен лечь в ту же папку») закрыта дешёвым
правилом при построении плана — реализовано change'ем
`2026-07-10-series-folder-convergence`, требования влиты в
[openspec/specs/file-layout](../../openspec/specs/file-layout/spec.md).
- `+` Устранён, а не отложен, хвост развилок вокруг жизненного цикла тайтла.
- `` Группировка тайтла в UI и «удалить тайтл целиком» придётся каждый раз
**вычислять** по `(provider, provider_id)` и общей папке; дешёвого хранимого
ключа для этого нет.
- `` Рассинхрон «несколько живых папок с одним `(provider, provider_id)`»
разрешается только уходом в review — in-app лечения нет, чинится руками на
диске.
- `` Слияние загрузок при перезаливе «той же вещи» осталось открытым: когда
несколько инфохэшей считать одной загрузкой, а когда разными, — вопрос
переехал в задачу про merge-раскладку.
## Триггер пересмотра
Записан отдельно, чтобы не гонять этот круг заново:
> Сущность `title` возвращается в обсуждение, только когда появится **операция
> или состояние, которому реально негде жить** в `download` + `file_link` —
> например, «переименовать сериал целиком с переносом ссылок» как регулярное
> действие или заметки уровня группы. До того — вычисляем.