Привёл набор 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>
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