A. ДОМ.РФ year backfill: авторитетный commission_year больше не экранируется
невозможным существующим year_built. Валидное существующее значение
выигрывает (COALESCE-семантика #2013 сохранена), но NULL/impossible
(< 1850, > текущий+2, 0) заменяется валидным commission_year → zhkh_year.
parse_int_field получил sanity-гейт [min,max] — мусорный commission_year
не попадает в staging (7 таких строк в текущем staging).
B. propagate_listings_year: добавлен link-consistency guard. Раньше копировал
houses.year_built на listings по house_id_fk без проверки — перепривязанный
FK впрыскивал чужой когортный год. Теперь пропагация только если координаты
объявления в пределах 500м от геометрии дома (ST_DWithin), либо (без коорд.)
консервативный address-фоллбек по short_address. Live: блокирует 1 из 10
текущих кандидатов (>500м mislink).
C. rosreestr_dkp_import: per-row INSERT-ошибки отделены от dedup-skip. Раньше
except инкрементил тот же batch_skipped, что и легитимный ON CONFLICT — сбой
маскировался под дедуп и run рапортовал success. Отдельный rows_errored +
гейт по доле ошибок (> 5% → run FAILED, не silent-green).
D. rosreestr_dkp_import: ON CONFLICT DO NOTHING → DO UPDATE изменяемых сырых
фактов Росреестра (price/area/rooms/floor/year/deal_date/...), чтобы
исправленный/переопубликованный квартал обновлялся. Обогащение
(lat/lon/geom/geocode_tried_at и пр.) не в SET-списке — не затирается.
IS DISTINCT FROM guard сохраняет идемпотентность resume; RETURNING (xmax=0)
отличает insert от update (rows_inserted vs rows_updated).