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

6.5 KiB
Raw Blame History

Отдельную сущность «тайтл» не вводим

  • Дата: 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.

Решение

Логический тайтл (фильм или сериал, складывающийся из нескольких загрузок во времени) остаётся вычисляемой группой, а не хранимой сущностью: доменная идентичность — 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 → «Что целью не является».

Последствия

  • + Идентичность осталась одноуровневой: download — мост между раздачей в qBittorrent и файлами на диске, и каждая сущность цепочки отвечает на свои операции.
  • + Главная боль («второй сезон должен лечь в ту же папку») закрыта дешёвым правилом при построении плана — реализовано change'ем 2026-07-10-series-folder-convergence, требования влиты в openspec/specs/file-layout.
  • + Устранён, а не отложен, хвост развилок вокруг жизненного цикла тайтла.
  • Группировка тайтла в UI и «удалить тайтл целиком» придётся каждый раз вычислять по (provider, provider_id) и общей папке; дешёвого хранимого ключа для этого нет.
  • Рассинхрон «несколько живых папок с одним (provider, provider_id)» разрешается только уходом в review — in-app лечения нет, чинится руками на диске.
  • Слияние загрузок при перезаливе «той же вещи» осталось открытым: когда несколько инфохэшей считать одной загрузкой, а когда разными, — вопрос переехал в задачу про merge-раскладку.

Триггер пересмотра

Записан отдельно, чтобы не гонять этот круг заново:

Сущность title возвращается в обсуждение, только когда появится операция или состояние, которому реально негде жить в download + file_link — например, «переименовать сериал целиком с переносом ссылок» как регулярное действие или заметки уровня группы. До того — вычисляем.