recognize: название из метабазы санитизируется перед попаданием в план
- чистка стоит на каждой точке входа значения метабазы в план — сборка матча, копия кандидата для ревью, набор закреплённых значений источника и его чтение: гарантия, поставленная только на запись, обходится данными, сохранёнными прежними версиями - название, непригодное как имя каталога (пустое или без единой буквы и цифры), не подставляется — раздача уходит в review с названной причиной - гейт подтверждения матча не сдвинут: сравнение с планом идёт по значениям провайдера, чистится только копия, уходящая дальше
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# Значение метабазы чистится на каждой точке входа в план, а три санитайзера не сводятся в один
|
||||
|
||||
- **Дата:** 2026-08-10
|
||||
- **Источник:**
|
||||
[openspec/changes/archive/2026-08-10-metadata-title-sanitize/design.md](../../openspec/changes/archive/2026-08-10-metadata-title-sanitize/design.md),
|
||||
разделы `Decisions` (Решения 1, 1a, 3) и `Non-Goals`
|
||||
|
||||
## Контекст
|
||||
|
||||
Название, приходящее из TMDB/TVDB/TVMaze, попадает в имя каталога библиотеки
|
||||
Jellyfin. Выход LLM мы чистим и считаем недоверенным; название из метабазы того
|
||||
же обращения не получало, хотя приходит так же — из-за периметра. Наблюдаемый
|
||||
исход: каталог из невидимых символов выглядит пустым, кириллическая буква внутри
|
||||
латинского слова даёт вторую папку, неотличимую от первой, и авто-раскладка это
|
||||
пропускала.
|
||||
|
||||
Разбор показал, что точка входа не одна. Их четыре, и каждая ведёт в имя
|
||||
каталога: сборка подтверждённого матча, копия кандидата, уходящая на экран
|
||||
ревью и в хранилище, набор закреплённых значений выбранного человеком
|
||||
источника и **чтение** уже закреплённого значения.
|
||||
|
||||
## Решение
|
||||
|
||||
**Чистка стоит на каждой из четырёх точек, а не в одной «правильной».**
|
||||
|
||||
Цитата из `design.md`, Решение 1a:
|
||||
|
||||
> Закрываются обе одной и той же чисткой, но в трёх местах — по одному на
|
||||
> каждую точку, где значение метабазы входит в домен.
|
||||
|
||||
Плюс четвёртая, добавленная по находке эксплуатационного прохода: чистка **на
|
||||
чтении** закреплённого значения. Гарантия чистоты не может держаться на времени записи строки —
|
||||
кандидаты и закреплённые значения, сохранённые прежними версиями, обходят её,
|
||||
а обычное
|
||||
«Применить» ничего не перезаписывает. Санитайзинг идемпотентен, поэтому лишние
|
||||
точки на уже чистом значении не делают ничего; это же свойство сделано
|
||||
нормативным и покрыто тестом.
|
||||
|
||||
Отдельно: **гейт подтверждения матча чистка не двигает.** Сравнение кандидата с
|
||||
планом идёт по значениям провайдера, чистится только копия, уходящая дальше.
|
||||
Причина в том, что `normalize` и санитайзинг не эквивалентны: невидимый символ
|
||||
внутри слова `normalize` превращает в пробел, а санитайзинг удаляет — чистка до
|
||||
сравнения превратила бы часть нынешних «в review» в «авто».
|
||||
|
||||
## Рассмотренные варианты
|
||||
|
||||
- **Свести три санитайзера проекта в один.** Отвергнуто: у них разный предмет —
|
||||
`recognize.SanitizeTitle` чистит значение, `layout.sanitizeComponent` —
|
||||
компонент пути под требования файловой системы, `naming.sanitize` —
|
||||
отображаемый ярлык. Свёртка гомоглифов — визуально неотличимых букв из разных алфавитов — внутри
|
||||
`sanitizeComponent` сломала бы
|
||||
правило сходимости базы папки: она гоняется и по имени, прочитанному с диска.
|
||||
- **Закрыть только авто-путь, ручной отдать отдельной задаче.** Отвергнуто на
|
||||
чекпоинте: спека `metadata-match` сама называет ручной выбор **основным**
|
||||
путём подтверждения матча — починка коснулась бы менее употребимой половины,
|
||||
а спека утверждала бы свойство, которого нет.
|
||||
- **Чистить в клиентах метабаз.** Отвергнуто: пришлось бы повторять в трёх
|
||||
клиентах и в каждом следующем, а проверка «в плане нет грязных полей»
|
||||
перестала бы читаться в одном месте.
|
||||
- **Разовая правка данных вместо чистки на чтении.** Отвергнута как более
|
||||
дорогая и не закрывающая следующего читателя.
|
||||
- **Полная нормализация Unicode** (NFC/NFKC плюс полная таблица визуально
|
||||
совпадающих символов Unicode) — Non-Goal. Цель — предсказуемое и сверяемое значение, а не исчерпывающая защита
|
||||
от визуального совпадения; курируемая кирилло-латинская таблица закрывает
|
||||
реальный случай.
|
||||
|
||||
## Что осталось нерешённым намеренно
|
||||
|
||||
**Каталог с невидимым символом, уже созданный в библиотеке, кодом не лечится.**
|
||||
Правило сходимости базы папки наследует имя от живой папки-якоря, и очистка
|
||||
извлечённой базы напечатала бы рядом вторую, чистую папку — то есть ровно тот
|
||||
исход с двумя каталогами, против которого затевалось изменение. Лечение —
|
||||
переименовать папку руками, после чего сходимость подхватит новое имя.
|
||||
Изменение закрывает появление новых таких каталогов, а не существующие.
|
||||
|
||||
## Цена
|
||||
|
||||
Точек чистки четыре вместо одной, и правило «значение метабазы чистится на
|
||||
входе в домен» держится на ревью, а не на линтере. Взамен свойство «показанное
|
||||
на экране совпадает с тем, что ляжет на диск» держится устройством кода: чистка
|
||||
стоит в `sourcePins` — общем доме набора закреплённых значений, через который
|
||||
идут и предпросмотр, и закрепление.
|
||||
Reference in New Issue
Block a user