recognize: название из метабазы санитизируется перед попаданием в план

- чистка стоит на каждой точке входа значения метабазы в план — сборка матча,
  копия кандидата для ревью, набор закреплённых значений источника и его
  чтение: гарантия, поставленная только на запись, обходится данными,
  сохранёнными прежними версиями
- название, непригодное как имя каталога (пустое или без единой буквы и
  цифры), не подставляется — раздача уходит в review с названной причиной
- гейт подтверждения матча не сдвинут: сравнение с планом идёт по значениям
  провайдера, чистится только копия, уходящая дальше
This commit is contained in:
av
2026-08-10 10:41:16 +03:00
parent bb278e8744
commit 9aecf757e0
26 changed files with 1653 additions and 53 deletions
+14 -4
View File
@@ -84,10 +84,10 @@ func validateSchema(p *Plan, in Input) error {
}
// decide считает решение модели уверенности (см. recognition.md). Авто —
// только если выполнено всё: подтверждённый единичный матч в базе; чистая
// структурная валидация (для сериала — число серий бьётся с базой);
// согласованность с пред-парсом; самооценка LLM не ниже порога. Любая
// невыполненная — причина ухода в review.
// только если выполнено всё: подтверждённый единичный матч в базе; каноническое
// название матча пригодно как имя каталога; чистая структурная валидация (для
// сериала — число серий бьётся с базой); согласованность с пред-парсом;
// самооценка LLM не ниже порога. Любая невыполненная — причина ухода в review.
func decide(p Plan, pre PreParse, match *Match, metadataEnabled bool, threshold float64) Decision {
var reasons []string
@@ -98,6 +98,16 @@ func decide(p Plan, pre PreParse, match *Match, metadataEnabled bool, threshold
reasons = append(reasons, "не найдено в базе или несколько кандидатов")
}
// Каноническое название базы, непригодное как имя каталога (пустое или без
// единой буквы и цифры), в план не подставлено — там осталось название
// распознавания. Матч при этом верен: id, год и режиссёр на месте, поэтому
// отклоняем не матч, а авто-раскладку. Match.Title уже санитизирован
// (buildMatch), второй раз не чистим: два независимых пересчёта одного
// условия разъедутся на первой же правке одного из них.
if match != nil && !UsableTitle(match.Title) {
reasons = append(reasons, "название из базы непригодно как имя каталога")
}
reasons = append(reasons, structuralWarnings(p)...)
if match != nil && p.Type == MediaSeries {