#774-2 Parts 2/3/4 (Part 1 UNIQUE/dedup deferred — cadnum data not flowing).
Part 2 — fingerprint<->alias sync: two scrapers with same address but coords
beyond the 4-decimal fingerprint rounding produced distinct fp -> duplicate
houses. Add Tier 2b lookup by normalized_address after Tier 2a (fingerprint)
misses, register the new fp via _insert_alias; alias upsert DO NOTHING ->
DO UPDATE SET fingerprint to keep alias synced.
Part 3 — collapse double-nested SAVEPOINT: removed redundant inner
db.begin_nested() around match_or_create_house inside _link_listing_to_house.
Outer per-row SAVEPOINT in save_listings already isolates a failing lot from
siblings and from the listings INSERT (per backend.md). BEHAVIOR NOTE: a
house-match failure now aborts the whole hook for that lot (incl. listing_source
link) atomically instead of partially committing it.
Part 4 — confidence/method wiring deferred to #774-3 (matcher not on ingestion
path; listing_sources columns exist, written as source_link=1.0).
Refs #849
Estimator теперь резолвит target-адрес в КАНОНИЧЕСКИЙ house_id (read-only,
без создания записи) и матчит аналоги по listings.house_id_fk — детерминированный
Tier S «тот же дом» вместо хрупкого address-string prefix match. 99% листингов
слинкованы с houses, поэтому это срабатывает уже сегодня и точнее работает между
разнородными источниками (Avito/Cian/Yandex форматируют адрес по-разному).
- matching/houses.py: match_house_readonly() — read-only target→house резолвер
(cadastr_exact → fingerprint → geo_proximity 50m), без INSERT / house_sources /
advisory lock. Возвращает None если уверенного match нет.
- estimator.estimate_quality: после geocode+DaData резолвит target_house_id
(cadastr от DaData → cadastr_exact заработает по мере backfill houses), graceful.
- estimator._fetch_analogs: новый param target_house_id + канонический Tier S
(WHERE house_id_fk = target) ПЕРЕД address-prefix Tier S (теперь fallback).
tier label 'S' сохранён (frontend уже рендерит).
Эффект: вместо «похожие в радиусе 800-1000м» — «вот что в ЭТОМ доме», прямой
выигрыш в точности оценки. Foundation под cadastr/FIAS exact match (DaData Q1/Q3).
Tests: 9 новых (match_house_readonly tiers + never-inserts + canonical Tier S
fires/fallback/too-few). Ruff clean. Pre-existing 4 fail в test_estimator_source_quota
(unpack 2≠3, красные на main до этого PR) — не затронуты.