ADR-0079: Расхождение бренда при совпавшем артикуле решает арбитр

Status: accepted Date: 2026-08-11 Deciders: belkanov, agent-claude

Контекст

ADR-0070 сделал ключом идентичности пару (manufacturer_id, нормализованный артикул). Это закрыло класс ошибок, когда неуникальный артикул склеивал товары разных фирм, но создало слепую зону: Tier1 требует точного совпадения manufacturer_id, Tier2 отбрасывает кандидата с другим непустым брендом — то есть пару «тот же артикул, другой бренд» не видит ни один тир, и оффер уходил в «кандидата нет» (ADR-0078).

Зона оказалась большой. Замер прода 2026-08-11: 124 093 группы mpn_norm (длина ≥5) расщеплены разными manufacturer_id на 270 216 канонов. Разбор показал, что поставщики пишут в поле производителя разные вещи для одного и того же товара: владельца бренда (DKC), линейку или торговую марку (YON (группа DKC)), транслитерацию (VARTON / Вартон), локального поставщика. Контрольный кейс: артикул MD63-1C1-6, оба наименования — YON MD63 1P 1А C 6kA, изделие одно, а каноников два.

Обратное тоже верно: под одним артикулом у разных производителей регулярно лежат разные изделия (IEK | Legrand, Kranz | REXANT, CUTOP | FIT), а внутри одной фирмы один артикул может покрывать целую размерную сетку (ADR-0073, docs/superpowers/specs/2026-08-11-matcher-terminal-strong-fallback-design.md).

Отсюда решение владельца продукта: склейку по производителю/бренду не делать. Сведение записей справочника manufacturers (наполнение alias_slugs) как способ склейки отвергнуто — оно необратимо схлопнуло бы такие пары сразу на 124 093 группах. Но и оставлять пару в «кандидата нет» неверно: тогда оффер-огрызок без характеристик никогда не получит осей и останется вне подбора аналогов, потому что назначение характеристик собирает факты только по офферам своего каноника.

Решение

  1. Совпадение артикула при расходящемся непустом manufacturer_id не даёт ни автоматической склейки, ни терминального решения. Такая пара направляется арбитру (Tier3, LLM) с триггером cross_brand_mpn.
  2. До ответа арбитра решение имеет status = 'conflict', tier = 'none', match_confidence = 'unmatched' и не назначает каноник. Конфликтная ветвь очереди рематча подхватывает такие решения по штатному MATCHER_RECHECK_COOLDOWN, поэтому пара не теряется, если арбитр недоступен.
  3. Арбитраж идёт своим системным промптом. Промпт tier1-конфликта для этого непригоден: он прямо предписывает не выбирать кандидата при расходящемся бренде, то есть отказал бы на каждой паре. Новый промпт снимает этот запрет и одновременно запрещает принимать пару по одному совпадению артикула: решение принимается по наименованию и характеристикам, а при недостаточных данных модель обязана вернуть null.
  4. Низкая уверенность арбитра уводит пару в ручную модерацию — в очередь добавлено значение триггера cross_brand_mpn (миграция 0357).
  5. Кандидаты для арбитража отбираются только при артикуле длиной ≥5 символов и только среди офферов других поставщиков с непустым и отличающимся manufacturer_id; число кандидатов на пару ограничено пятью. Пары с пустым брендом остаются за Tier2 fingerprint.

Последствия

Плюсы

  • Слепая зона закрыта без пакетного мержа справочника: решение принимается по каждой паре отдельно и обратимо.
  • Механизм работает и для оффера-огрызка без характеристик — арбитр видит наименования, тогда как сравнение по осям здесь невозможно.
  • Признак same_mpn_other_brand в причине кандидата и отдельный триггер делают поток различимым в модерации и аналитике.

Минусы

  • Поток LLM-вызовов: 124 093 группы при MATCHER_TIER3_MAX_PER_TICK=300 и тике в минуту — порядка семи часов прогона плюс расход квоты. Кап не снимается, поэтому поток растянут и делится с остальными Tier3-путями.
  • Ошибка арбитра даёт неверную склейку. Смягчение: null при сомнении, модерация при низкой уверенности, решение не терминально.

Нейтральные последствия

  • Прежнее поведение для пар с совпадающим брендом не меняется: Tier1 и Tier2 работают как раньше.
  • Пары, где арбитр вернул null, остаются в конфликте и будут переспрошены после следующего изменения данных.

Рассмотренные альтернативы

Сведение бренд-псевдонимов в справочнике

Отвергнуто владельцем продукта: под одним артикулом у разных брендов лежат разные товары, а мерж записей необратим и применялся бы пакетно.

Склейка по совпадению identity-осей без LLM

Дешевле и детерминированнее, но неприменима именно там, где проблема острее: у оффера-огрызка осей нет (у 130 425 из 440 564 офферов ETM ровно одна характеристика). Оставлено как возможное дополнение — сначала дешёвая проверка осей, арбитр только когда осей недостаточно.

Перенос характеристик по артикулу без склейки

Это та же склейка по артикулу, только на уровне фактов: при разных изделиях каноник получил бы чужие оси, то есть порчу данных вместо пустоты.

Ссылки

  • ADR-0070 — ключ идентичности
  • ADR-0073 — ручная модерация
  • ADR-0078 — провал тиров не матч
  • docs/superpowers/specs/2026-08-11-matcher-terminal-strong-fallback-design.md