Runbook: склейка дублей канонических характеристик

Контекст: ADR-0074. Аудит 2026-07-12 нашёл ≥1,237 кодов-дублей (rated_ac_current/rated_current_ac, conductor_count/core_count/ number_of_cores). Справочник склейки — characteristic_merge_map (миграция 0244), data-driven по ADR-0072.

Модель

КолонкаСмысл
alias_codesurvivor_codelower-код дубля → lower-код канона
reasontoken_signature (перестановка слов) / same_display_name / llm / произвольный
sourceseed / llm / moderator
statuspendingapprovedapplied; rejected
needs_reviewtrue = требует модератора перед approve

Survivor выбирается по максимуму supplier-маппингов (usage-прокси), tie-break — старейший created_at.

Процедура

  1. Seed (идемпотентен): psql < scripts/characteristic-merge/seed_auto.sql. Классы: token_signature (unit+data_type совпадают → сразу approved), same_display_name (совпадают → approved c confidence 0.9; иначе pending+needs_review — height/height_u отфильтрованы юнитом).
  2. Модерация: pending-строки апрувить вручную (UPDATE ... SET status='approved', needs_review=false WHERE id=...), мусорные — rejected.
  3. Apply (идемпотентен, резюм-safe, off-peak — шаг facts IO-тяжёлый): psql < scripts/characteristic-merge/apply.sql. Каскад: mappings → supplier_characteristic_map → facts.canonical_key (батчи 20k; row-триггеры сами ведут char_fact_stats и ставят dirty-каноники) → manual-assignments переезжают в свободные слоты → авто-assignments alias’а удаляются → valuenorm-словарь/очередь. Воркер canonical-assignment пересчитает survivor из фактов штатно; manual-строки защищены (ADR-0073).
  4. Контроль: note applied-строк содержит facts=N manual_moved=N manual_conflicts=N. Конфликтные manual-assignments остаются на alias-характеристике — разобрать руками через admin API и снести пустые alias-строки characteristics финальным проходом.

Профилактика

Provisioner (шаг 1.5 EnsureByCanonicalName) не даёт рождаться новым словопорядковым дублям (expression index characteristic_token_signature); charnorm-промпт несёт naming-conventions. Семантические синонимы всё ещё возможны — периодически перезапускать seed + LLM-предложения в справочник.