Files
avandClaude Opus 4.8 512567c8ba Рефакторинг границ capabilities: цепочка загрузка→матч→ревью→раскладка (openspec)
Привёл набор capabilities в OpenSpec к цепочке обработки, чтобы имя capability
отвечало одному поведению. Чисто по спекам, код и поведение системы не меняются.

Change refactor-capability-boundaries (архивирован):
- recognition разделён на recognition (разбор LLM) + metadata-match (сверка с базами)
- review выделен из web-ui + мигрирован из docs/specs/review-ux.md
- новые capability из docs/specs: file-layout, download-tracking, notifications
- identity очищен до инфра-id; приём (инфохэши, дедуп, ядро приёма) — в ingest
- уведомление о рассинхроне перенесено из state-reconciliation в notifications
- дубль владения путём и безопасного undo оставлен в state-reconciliation

Итог: 11 capabilities, openspec validate --strict проходит (+37/−11 требований).
Источник истины по мигрированным темам переехал в openspec/specs (шапки в docs).
Снят пункт беклога «Пересмотр набора capabilities».

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 21:17:51 +03:00

5.5 KiB

identity Specification

Purpose

Как система идентифицирует сущности домена: ULID-ключи (канонический lowercase-вид, нормализация и валидация на входных границах) и корреляция сущностей в логах по id. Инфохэши загрузки (download_infohash), дедупликация приёма и инвариант «не более одной активной загрузки на infohash» (атомарный возврат в активное состояние) — в capability ingest.

Requirements

Requirement: ULID как первичный ключ сущностей

Каждая сущность домена SHALL иметь первичный ключ ULID — TEXT, 26 символов Crockford base32, генерируемый приложением в момент создания записи через единственную точку генерации (internal/ident). Сущности: download, recognition, hint, override, metadata_candidate, file_link. Канонический вид SHALL быть lowercase. Числовые AUTOINCREMENT-ключи в новых таблицах использоваться SHALL NOT. Идентификатор партии раскладки (apply_batch_id) SHALL генерироваться тем же способом.

Scenario: Создание загрузки

  • WHEN принимается новая загрузка
  • THEN её id — валидный ULID в lowercase
  • AND id уникален глобально (не совпадает с id других сущностей)

Scenario: Хронологическая сортировка

  • GIVEN две загрузки, созданные последовательно
  • WHEN записи сортируются по id лексикографически
  • THEN порядок совпадает с порядком создания

Requirement: Нормализация и валидация id на входных границах

Внешние идентификаторы SHALL валидироваться как ULID и нормализоваться к lowercase до обращения к хранилищу — это касается всех входных границ: URL /download/{id}, параметры форм и команд. Синтаксически невалидный id SHALL обрабатываться как несуществующая сущность (404 для страниц), без обращения к БД.

Scenario: Uppercase-вариант id в URL

  • GIVEN существующая загрузка с id 01jz… (lowercase)
  • WHEN клиент открывает /download/01JZ… (uppercase)
  • THEN открывается страница той же загрузки

Scenario: Мусор вместо id

  • WHEN клиент открывает /download/abc!!!
  • THEN ответ — 404, запрос к БД не выполняется

Requirement: Корреляция сущностей в логах

Записи журнала, относящиеся к сущности, SHALL содержать её id в атрибуте <entity>_id (download_id, recognition_id, batch_id, …); работа в контексте загрузки ведётся через scoped-логгер с download_id. Благодаря глобальной уникальности ULID поиск по значению id (grep/jq) SHALL находить все записи журнала, относящиеся к сущности, независимо от имени поля.

Scenario: Путь загрузки по логам

  • GIVEN загрузка прошла приём, распознавание и раскладку
  • WHEN журнал фильтруется по значению её id
  • THEN находятся записи всех этапов (ingest, recognition, file-layout)

Requirement: Миграция существующих записей

Существующие записи SHALL получить ULID-идентификаторы одной миграцией с сохранением всех связей (FK) и хронологии: timestamp-часть ULID SHALL браться из created_at записи, чтобы лексикографический порядок новых id соответствовал историческому порядку создания. Существующий download.infohash SHALL быть перенесён в download_infohash (нормализация к lowercase, kind по длине hex: 40 — v1, 64 — v2); столбцы download.infohash и download.idempotency_key SHALL быть удалены.

Scenario: Связи и порядок после миграции

  • GIVEN БД с загрузками, распознаваниями и файловыми ссылками на числовых id
  • WHEN миграция выполнена
  • THEN все FK-связи сохранены (распознавания/ссылки указывают на те же загрузки)
  • AND порядок загрузок по id совпадает с порядком по created_at
  • AND каждый прежний infohash представлен записью в download_infohash