Files
jellybit/openspec/changes/archive/2026-08-10-metadata-title-sanitize/specs/recognition/spec.md
T
av 9aecf757e0 recognize: название из метабазы санитизируется перед попаданием в план
- чистка стоит на каждой точке входа значения метабазы в план — сборка матча,
  копия кандидата для ревью, набор закреплённых значений источника и его
  чтение: гарантия, поставленная только на запись, обходится данными,
  сохранёнными прежними версиями
- название, непригодное как имя каталога (пустое или без единой буквы и
  цифры), не подставляется — раздача уходит в review с названной причиной
- гейт подтверждения матча не сдвинут: сравнение с планом идёт по значениям
  провайдера, чистится только копия, уходящая дальше
2026-08-10 10:41:16 +03:00

112 lines
8.9 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.
## MODIFIED Requirements
### Requirement: Санитайзинг человекочитаемых полей плана
Перед структурной валидацией плана и сверкой с базами система SHALL санитизировать
человекочитаемые поля плана — `title`, `original_title`, `provider_hint` — как
недоверенный вывод LLM. Санитайзинг SHALL: (1) удалять управляющие и zero-width
символы (C0/C1, `U+200B` и родственные, BOM `U+FEFF`); (2) сводить внутренние
последовательности пробельных к одиночному пробелу и обрезать края; (3) сворачивать
кирилло-латинские homoglyph-двойники (см. ниже).
Санитайзинг SHALL быть **общим для обоих источников названия** — вывода LLM и
записи метабазы. Значение, пришедшее из метабазы и заменяющее поле плана при
подтверждённом матче, SHALL проходить тот же санитайзинг перед подстановкой (см.
`metadata-match`, требование «Подтверждение матча и каноническое имя»). Второго
способа чистить **названия плана** система SHALL NOT заводить: одна реализация
обслуживает оба источника. Речь только о полях плана — очистка компонента пути
под требования файловой системы (`layout`) и очистка отображаемого ярлыка
(`naming`) остаются своими, отдельными и законными.
Санитайзинг SHALL быть **идемпотентным**: повторное применение к уже
санитизированному значению SHALL NOT изменять его. На этом свойстве стоит
утверждение, что правка не меняет путей на диске для раздач с нормальным
названием, и оно проверяется тестом, а не подразумевается.
Следствие, на которое опирается раскладка: к моменту, когда план уходит в решение
auto/review, каждое из полей `title`, `original_title`, `provider_hint` совпадает
с тем, что дал бы санитайзинг этого значения — независимо от того, пришло оно от
LLM или из метабазы.
Свёртка двойников SHALL работать потокенно (по словам, разделённым не-буквенными
символами): токен, все буквы которого принадлежат одному скрипту, система SHALL
оставлять без изменений (билингвальность реальна — кириллические названия
неприкосновенны); в токене смешанного скрипта система SHALL определять доминирующий
скрипт по числу буквенных рун и заменять буквы-меньшинство их визуальными
двойниками из доминирующего скрипта по курируемой таблице. При отсутствии
доминирующего скрипта (равенство) токен SHALL оставаться без изменений.
Санитайзинг SHALL применяться ТОЛЬКО к перечисленным человекочитаемым полям
**плана**. Применение того же санитайзинга к значениям, пришедшим из метабазы —
каноническому названию и названиям кандидатов, — заказано отдельно, см.
`metadata-match`.
`files[].src` система SHALL NOT санитизировать — эти значения обязаны совпадать с
реальными файлами торрента, и расхождение (в т.ч. homoglyph) SHALL оставаться
основанием отклонить план, а не поводом «чинить» путь. `director` система SHALL
NOT санитизировать в плане — он не участвует в пути на диске, и его очистка
применяется при рендере отображаемого имени (см. `metadata-match`).
Когда санитайзинг реально изменил значение поля, система SHALL логировать это на
уровне `Debug` (названия не относятся к секретам).
#### Scenario: Кириллический двойник в англоязычном названии сворачивается
- **GIVEN** план, где `title` = `Hаrold and the Purple Crayon` (буква `а` в первом
слове — кириллическая `U+0430`)
- **WHEN** план санитизируется
- **THEN** первое слово становится `Harold` (все буквы латинские)
- **AND** в запрос к базе и в сравнение уходит латинское название
#### Scenario: Честное кириллическое название не трогается
- **GIVEN** план российского фильма с `title` = `Тёмный рыцарь`, где все буквы
каждого слова кириллические
- **WHEN** план санитизируется
- **THEN** название остаётся кириллическим без замены букв
#### Scenario: Токен без доминирующего скрипта не трогается
- **GIVEN** план, где короткий токен содержит поровну латинских и кириллических
букв (доминирующего скрипта нет)
- **WHEN** план санитизируется
- **THEN** этот токен остаётся без замены букв (осознанный trade-off: двухбуквенный
homoglyph-typo не сворачивается)
#### Scenario: Zero-width и лишние пробелы вычищаются
- **GIVEN** план, где `title` содержит zero-width символ и сдвоенные пробелы
- **WHEN** план санитизируется
- **THEN** zero-width удалён, внутренние пробелы сведены к одиночным, края обрезаны
#### Scenario: files[].src не санитизируется
- **GIVEN** ответ LLM, где `files[].src` содержит символ-двойник и не совпадает ни
с одним реальным файлом торрента
- **WHEN** план обрабатывается
- **THEN** `files[].src` НЕ изменяется санитайзингом
- **AND** несовпадение src приводит к отклонению плана (эскалация в review), а не к
«починке» пути
#### Scenario: Название из метабазы чистится тем же санитайзингом
- **GIVEN** подтверждённый единичный матч, каноническое название которого содержит
zero-width символ и кириллический двойник внутри латинского слова
- **WHEN** каноническое название подставляется в план
- **THEN** в плане оказывается значение, прошедшее тот же санитайзинг, что и вывод
LLM
#### Scenario: После сверки с базой несанитизированных полей в плане не остаётся
- **GIVEN** план после подтверждённого матча с базой
- **WHEN** оценивается решение auto/review
- **THEN** каждое из полей `title`, `original_title`, `provider_hint` совпадает с
тем, что дал бы санитайзинг этого значения
- **AND** `director` под это требование не подпадает: он чистится при рендере
отображаемого имени
#### Scenario: Повторный санитайзинг ничего не меняет
- **GIVEN** значение, уже прошедшее санитайзинг
- **WHEN** санитайзинг применяется к нему второй раз
- **THEN** значение не изменяется