gendesign/tradein-mvp/backend/app/services
Light1YT 41e9c820c9
All checks were successful
CI / changes (pull_request) Successful in 7s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
fix(tradein/estimator): ghost-anchor invariant guard — n_analogs=0 forces low (audit #1871 P1.2)
Audit #1871 P1.2: estimate 5fcc1e99 показывал confidence='high' при
n_analogs=0 (median 38.45M ₽, sources=['yandex_valuation']) при реальной
истории листингов дома 4.2-6.5M. По prod-DB это ЕДИНСТВЕННАЯ legacy-строка
от 2026-05-30, давно expired (TTL оценок = 1 день, все 66 high/medium-c-n<5
строк expired). Текущий mainline уже честен. Фикс — defensive invariant,
чтобы ghost не мог родиться снова из будущих external-valuation-blend /
rehydrate / migration путей.

Backend (estimator.py):
- helper _enforce_zero_analog_low(confidence, n_analogs, explanation, ...)
  форсит 'low' + caveat когда n_analogs==0 and confidence!='low'.
  Идемпотентен (low+0 → low без дубля caveat), не трогает high+N>0, реальный
  median внешнего сервиса сохраняется (fake-коэффициенты не вводятся),
  logger.warning для observability.
- Вызов перед сборкой AggregatedEstimate в estimate_quality (единственный
  уязвимый return-путь; _empty_estimate хардкодит low — структурно безопасен).
- +5 unit-тестов (high+0→low+caveat, medium+0→low, low+0 идемпотент,
  high+N unchanged, None-explanation).

Frontend (HeroSummary, HeroTransparency, history):
- effectiveConfidence = n_analogs===0 ? 'low' : confidence — defensive depth
  на случай legacy-rehydrate / контракт-drift. Caveat-текст из backend
  confidence_explanation (не хардкод). Type-safe (ConfidenceLevel).

Refs #1871
2026-06-23 11:05:34 +05:00
..
exporters fix(tradein): trade-in PDF cover uses estimate.expires_at, not hardcoded +30d (#1530) 2026-06-17 20:27:57 +03:00
matching fix(matching): conflict_resolution picks freshest (last_seen_at) value, deterministic (#1539) 2026-06-17 22:43:59 +03:00
scrapers fix(tradein): Mera-audit fix-1 — Cian valuation price sanity bounds 2026-06-21 09:02:32 +03:00
__init__.py feat: add tradein-mvp subproject (Trade-In Estimator под /trade-in) 2026-05-21 00:25:39 +03:00
account_quota.py fix(tradein): atomic conditional quota increment — close TOCTOU (#747) (#749) 2026-05-30 15:52:34 +00:00
brand.py feat: add tradein-mvp subproject (Trade-In Estimator под /trade-in) 2026-05-21 00:25:39 +03:00
cache.py feat(tradein): Phase 3.2 — /api/v1/search endpoint + Redis cache (#479) 2026-05-23 14:44:22 +00:00
cian_price_history.py fix(week-review): автофиксы код-ревью — 169 issue (label «week ревью 1») 2026-06-15 20:21:11 +05:00
cian_session.py feat(tradein): dedicated Cian mobile proxy — separate egress from Avito (#923) 2026-05-31 19:30:14 +00:00
dadata.py fix(week-review): автофиксы код-ревью — 169 issue (label «week ревью 1») 2026-06-15 20:21:11 +05:00
estimator.py fix(tradein/estimator): ghost-anchor invariant guard — n_analogs=0 forces low (audit #1871 P1.2) 2026-06-23 11:05:34 +05:00
geocoder.py feat(tradein): EKB geoportal building ingest + geocoder tier (complete city buildings) 2026-06-19 17:51:07 +03:00
house_imv_backfill.py fix(week-review): cross-file добивка код-ревью (#1338, #1363, #1419, #1421, #1424, #1480) 2026-06-15 20:29:00 +05:00
house_metadata.py feat(tradein): postgres_fdw live read of gendesign.cad_buildings (replaces snapshot) (#493) 2026-05-24 08:57:30 +00:00
image_sanitizer.py fix(week-review): автофиксы код-ревью — 169 issue (label «week ревью 1») 2026-06-15 20:21:11 +05:00
rosreestr_poll.py feat(tradein): scheduled monthly Rosreestr new-quarter poll → ingest alert (#888) (#894) 2026-05-31 13:13:55 +00:00
sber_index.py fix(tradein): correct SberIndex hedonic/asking dataset-paths + 404 warning (#902) 2026-06-17 22:50:52 +03:00
scheduler.py feat(scheduler): per-source interval_days cadence (weekly exhaustive avito) 2026-06-18 23:10:18 +03:00
scrape_pipeline.py fix(scrapers): avito ban-triggered IP rotation (#1790) — changeip on block, bounded 2026-06-20 21:24:12 +03:00
scrape_runs.py feat(admin): unified scrape/runs + scraper/health + rotate-ip API для единой scrapers-страницы 2026-06-20 09:25:57 +03:00
scraper_settings.py chore(tradein): деактивировать N1.ru scraper — источник переехал в Cian 2026-06-16 14:50:51 +03:00
search_query.py feat(tradein): segment=vtorichka|novostroyki|all query-param on listings (#1188) 2026-06-17 21:43:01 +03:00
yandex_address_backfill.py fix(tradein): yandex address-enrich — city-agnostic title regex (спутники ЕКБ) (#875) 2026-05-31 09:08:29 +00:00
yandex_price_history.py feat(tradein): populate offer_price_history for yandex from gate price.previous/trend 2026-06-18 14:23:23 +03:00