Живой прод-замер показал, что исходная постановка issue (avito 47% NULL
lat) уже почти закрыта побочно: dedup-миграции 159-167 сократили active
avito-set с 30k до 9.2k, nightly geocode_missing_listings крутится с PR
#1549 — текущий NULL lat = 4.3% (393/9181), не 47%.
Реальная находка: точный street+house геокодер против локального
ekb_geoportal_buildings (#1841, backfill_listings_coords_geoportal.py)
существовал, но был manual-only script, ни разу не запускавшийся
recurring. Подключил его в in-app scheduler по паттерну cadastral_geo_match
— run-lifecycle wrapper, dispatch, kit-registry entry (обязательно для
scraper_kit strangler-migration completeness), seed-миграция на окно
05:00-06:00 UTC (до geocode_missing_listings 06:00-09:00 — точный
house-level матч забирает адрес раньше грубого Nominatim-геокода).
Dedup-на-ингесте (вторая половина исходного DoD) — отдельный follow-up,
out of scope здесь.
Финальный PR issue #2045 (BE-3): GET /api/v1/trade-in/location-coef для
LocationDrawer. FDW foreign table -> локальное зеркало osm_poi_ekb_local
(TRUNCATE+INSERT, тот же паттерн что cad_buildings_local/cadastral_geo_match,
избегает ~1.16s/row FDW round-trip) -> straight-line POI-скоринг, портированный
из Site Finder poi_score.py::compute_poi_weighted_top7 (CATEGORY_WEIGHTS as-is,
радиус 1200м для квартир вместо Ptica 2000м для участков). score->coef -
новая MVP-эвристика (0.95..1.05, не откалибрована на реальных дельтах).
Graceful fallback (не 500, не фабрикуем факторы): пустая/не отрефрешенная
osm_poi_ekb_local или отсутствие lat/lon у оценки -> coef=1.0, factors=[],
geo_source="unavailable".
Scheduler: source=osm_poi_ekb_refresh, daily, зарегистрирован и в боевом
dispatch (scheduler.py), и в kit-registry (product_handlers.py) - иначе
test_kit_registry_completeness падает на ship-dark инварианте (#2192).
Frontend wiring (mappers.ts/LocationDrawer.tsx) - вне scope, отдельная задача
после проверки endpoint'а curl'ом на деплое.