Files
avandClaude Opus 4.8 e2ea1840c9 Распознавание: санитайзинг названий от LLM + безгодовой фолбэк сверки
Кейс «Harold and the Purple Crayon»: LLM отдал title с кириллической
буквой-двойником, сырое название ушло в запрос TVDB дословно (не нашлось),
а гейт нормализации кир/лат двойники не сворачивал — двойной промах, пустой
список кандидатов, ручной ввод id.

- recognition: санитайзинг человекочитаемых полей плана (title/original_title/
  provider_hint) на границе разбора, до валидации: strip control/zero-width,
  collapse пробелов, потокенная свёртка homoglyph-двойников по курируемой
  кир↔лат таблице. files[].src не трогаем (обязаны биться с торрентом).
- metadata-match: тот же fold в normalize (гейт) как defense-in-depth;
  безгодовой второй проход сверки как fallback при известном годе и промахе
  первого — восстанавливает off-by-one авто-матчи и пополняет кандидатов
  review. В fallback требуем известный год кандидата (год-unknown → review,
  не авто); гейт год ±1 и инвариант авто-матча не двигаются.

Спеки recognition/metadata-match обновлены, change заархивирован.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 15:45:19 +03:00

3.1 KiB
Raw Permalink Blame History

1. Homoglyph-таблица и fold

  • 1.1 В internal/recognize завести курируемую таблицу кирилло-латинских двойников (~15–20 пар, строчные и заглавные) как единственный источник правды
  • 1.2 Реализовать потокенную foldHomoglyphs(s string) string: одно-скриптовый токен не трогаем, в смешанном — доминирующий скрипт по числу буквенных рун, меньшинство мапим по таблице; при равенстве оставляем как есть
  • 1.3 Юнит-тесты foldHomoglyphs: «Hаrold»→«Harold», честная кириллица нетронута, смешанное двуязычное название, равенство скриптов, пустой ввод

2. Санитайзинг полей плана (recognition)

  • 2.1 Реализовать sanitizeTitle: strip control/zero-width (C0/C1, U+200B и родственные, BOM), collapse пробелов + trim, затем foldHomoglyphs
  • 2.2 Применить санитайзинг к title, original_title, provider_hint на границе разбора плана — после unmarshal, ДО структурной валидации и сверки; files[].src не трогать
  • 2.3 slog.Debug when поле реально переписано (before/after), логгер из ctx
  • 2.4 Тесты: поля чистятся, files[].src неприкосновенны, лог при изменении

3. Fold в гейте нормализации (metadata-match)

  • 3.1 Добавить foldHomoglyphs в normalize (internal/recognize/metadata.go) после ё→е, переиспользуя ту же таблицу
  • 3.2 Тест: план и кандидат, различающиеся лишь скриптом буквы, нормализованно совпадают

4. Безгодовой второй проход сверки (metadata-match)

  • 4.1 Обернуть перебор в matchMetadata в два прохода: pass 1 — как сейчас (Search с годом), pass 2 — только при plan.Year > 0 и отсутствии матча в pass 1, тот же перебор searchKeys × providers с Year = 0
  • 4.2 Кандидаты копятся через оба прохода с существующей дедупликацией по provider:id и потолком maxCandidates; гейт strongMatches не менять
  • 4.3 Тесты: кривой год находится без года; при матче в pass 1 второго прохода нет; при year = 0 второго прохода нет; несколько кандидатов из pass 2 → матч не подтверждён

5. Проверка и вычитка

  • 5.1 task test и task lint — зелёные
  • 5.2 openspec validate --strict sanitize-llm-titles-yearless-retry