ORDER BY has_active_listings DESC, id во всех priority-бакетах + новый
--priority listings (focused sweep: NULL cadnum + есть active listing).
Дневной 100/day budget сначала закрывает дома, которые реально появляются
в оценках → estimator #6 cadastr_exact same-building матчинг загорается на
real estimate targets за дни, а не недели.
Correlated EXISTS бьёт по partial-индексу listings_house_id_fk_idx.
Write-path / EKB-guard / SAVEPOINT / qc_geo rules не тронуты.
code-reviewer: ✅ APPROVE no critical. Tests: 40 passed. Ruff clean.
Добавляет scripts/geocode_deals_nominatim.py для ~14% сделок всё ещё
lat IS NULL после house-centroid прохода (Step 2) — улицы без геокодированных
домов, которые centroid-join не покрывает. Переиспользует geocoder.geocode
(cache → Cadastral FDW → Yandex → Nominatim), дедупит по адресу (distinct
улицы определяют число вызовов, не 7k строк), и стампит geocode_tried_at на
КАЖДОЙ попытке (успех И чистый промах) чтобы un-geocodable строки не крутились
по 1 req/s. Resume: lat IS NULL + 30-day staleness, per-address SAVEPOINT,
--limit/--dry-run/--batch/--stale-days.
Pre-push review (code-reviewer): ✅ APPROVE, no critical — проверены SQL-safety,
no-infinite-reloop (mark-tried на miss, no-stamp на raised exc), window-cap,
geom-not-manual, dedup, async. Tests: 17 passed. Ruff clean.
Dry-run на проде показал, что DaData без явного города мапит короткие
street-адреса в Москву по умолчанию («улица Просторная» → «г Москва, ул
Просторная»). Это бы привело к catastrophic false-positive enrichment'у
если бы скрипт запустили без --dry-run.
Two-layer fix:
1. `_with_city_context(addr)` — pre-pends «Екатеринбург, » если в адресе
ещё нет упоминания города. Идемпотентен, case-insensitive.
2. `_result_in_ekb(result)` — post-filter. Проверяет `raw.city`
(структурное поле DaData) с fallback на substring search в
`canonical_address`. Защищает даже если DaData проигнорирует city-префикс
и всё равно вернёт другой город (например для homonym streets).
`_is_enriched_result()` теперь требует обе проверки: qc_geo IN (0,1)
AND coords AND city == Екатеринбург. Только так делаем UPDATE coords/cadnum.
Без этого fix'а нельзя запускать backfill — все non-EKB false-positives
перезаписали бы NULL coords мусором.
9 новых тестов: city context (prepend / idempotent / case / empty),
post-filter (raw.city / canonical / Moscow reject / None).
Combined 29 passed.