Замер прода 2026-08-06 (SELECT source, sale_type, count(*) FROM listings):
avito : free 2976 · assignment 46 · NULL 45020
cian : free 14800 · fz214 4673 · alternative 1327 · dupt 409 ·
investment 64 · dzhsk 3 · pdkp 1 · NULL 463
domklik : «Свободная продажа» 5546 · «Альтернатива» 553 ·
«Первичная продажа» 2 · NULL 493
n1/yandex: колонку не пишут вовсе (382 и 16721 NULL)
Итого 6101 русская строка против 24 299 английских: `WHERE sale_type='free'`
не видит ни одного Домклика, `GROUP BY sale_type` разносит одну и ту же
свободную продажу на две корзины.
ВАЖНОЕ ОГРАНИЧЕНИЕ, найденное по ходу: читателей у колонки НОЛЬ. Grep по
всему репозиторию даёт только писателей (providers/{avito,cian,domclick},
base.py, scripts/ingest_domclick_jsonl.py) и DDL — ни фильтра, ни агрегата,
ни поля схемы API, ни элемента фронтенда. Поэтому канон целиком тут НЕ
вводится: выбирать между cian-токеном dupt и avito-токеном assignment
(409 против 46 строк, оба = переуступка) без единого читателя — монетка.
Чинится ровно объективно сломанное: один писатель клал сырую русскую фразу
там, где два других уже кладут английский токен.
Проверка смысла перед маппингом (cross-tab sale_type × listing_segment):
это одна ось — структура сделки. Русские значения Домклика на 100%
vtorichka и совпадают с полюсами cian free/alternative; хвост cian
fz214/dupt/investment/dzhsk/pdkp — 5089 из 5107 строк novostroyki, т.е.
ПОДмножество той же оси (какой договор на первичке), а не другой признак.
Конфликта смыслов нет, склейка free/alternative безопасна.
canon_sale_type стоит на ЕДИНОЙ границе записи (save_detail_enrichment), а
не в парсере: в listings пишут три производителя одного dataclass'а — парсер
карточки, ingest_domclick_jsonl.py и domclick_local_runner.py (именно он
налил 6101 русскую строку 2026-07-18).
«Первичная продажа» (2 строки) осознанно НЕ маппится: у cian первичка
закодирована типом ДОГОВОРА, а маркетинговая метка Домклика договор не
называет — угадывать нельзя. Строки остаются как есть, факт зафиксирован в
COMMENT ON COLUMN вместе со всем фактическим словарём по источникам.
Миграция 220 (backfill 5546 + 553 = 6099 строк, счётчики сняты на проде ДО
применения; повторный прогон — 0 строк).
Refs #2674