fix(tradein/v2): канонический sources_used — стабильный счётчик «ИСТОЧНИКОВ X/7» (#2087 M1) #2144
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#2144
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/tradein-v2-source-counter-canonical"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
estimator._canonical_sources(analogs, valuation_flags)— источник правды дляsources_used, зовётся идентично на POST (estimate_quality) и GET-rehydrate (get_estimate) из ТЕХ ЖЕ persistedanalogs+ оценочных флагов. Одинestimate_id→ идентичныйsources_usedна POST/GET/reload.sources_usedиз top-Nanalogs_lots, GET возвращал persisted-колонку (радиусный набор +quarter_index), аsource_countsна GET считался из persistedanalogs→ источник (напр.cian) мог быть вsource_counts, но не вsources_used; счётчик «ИСТОЧНИКОВ X/7» прыгал 4→5 между POST-ответом и reload(GET).source_countsна POST теперь тоже изanalogs_lots(не полной metadata-выборки) → инвариантsource_counts.keys()⊆sources_usedна POST и GET.sources_usedв колонку (history/PDF консистентны для новых строк); GET рехайдрейтит его тем же helper'ом — чинит и СТАРЫЕ строки (листинговая часть пересобирается изanalogs,quarter_index/радиусный шум отбрасывается фильтром оценочных флагов).avito_imv/yandex_valuation/cian_valuation) персистятся в колонкеsources_usedи читаются оттуда на GET — реконструкция не требуется.mappers.tsне тронуты (математика верна — так решено в #2087).Repro (estimate
451de30b, прод,docker exec tradein-backend)sources_used=[avito, avito_imv, domklik, yandex](4/7),source_counts={avito:1, cian:1, domklik:6, yandex:2}→cianв counts, отсутствует вsources_used(рассинхрон).sources_used=[avito, avito_imv, cian, domklik, yandex](5/7),source_counts.keys()⊆sources_used.Test plan
ruff check app/— cleanpytest tests/ -q— 2746 passed, 4 skipped, 1 pre-existing fail (test_search_api::test_search_cache_hit, auth 401 — не связан, падает на base main)test_estimator_source_metrics_2043.py): детерминизмsources_usedпри одинаковых analogs+флагах; инвариантcounts.keys()⊆sources_used; листинговый источник в analogs ⇒ вsources_used; фильтр не-канонических флагов (quarter_index/листинговые метки в колонке не протекают)/estimate/451de30b→sources_usedсодержитcian, совпадает по ключам сsource_countsPart of #2087 (M1). Issue НЕ закрывается — остаются M2/M3.
🤖 Generated with Claude Code
Единый helper estimator._canonical_sources(analogs, valuation_flags) — источник правды для sources_used, зовётся идентично на POST (estimate_quality) и GET-rehydrate (get_estimate) из ТЕХ ЖЕ persisted analogs + оценочных флагов. Root cause: три расходящиеся деривации — POST брал sources_used из top-N analogs_lots, GET возвращал persisted-колонку (радиусный набор + quarter_index), source_counts на GET считался из persisted analogs. Источник (напр. cian) мог попасть в source_counts, но не в sources_used → счётчик «X/7» прыгал 4→5 между POST-ответом и reload(GET). - sources_used = {листинговые из persisted analogs} ∪ {avito_imv/yandex_valuation/ cian_valuation}. Детерминированно отсортирован. - source_counts на POST теперь тоже из analogs_lots (не полной metadata-выборки) → инвариант source_counts.keys() ⊆ sources_used на POST и GET. - POST персистит канонический sources_used в колонку (history/PDF консистентны для новых строк); GET рехайдрейтит его же helper'ом — чинит и СТАРЫЕ строки (листинговая часть пересобирается из analogs, quarter_index/радиусный шум отбрасывается фильтром оценочных флагов). Оценочные флаги персистятся в колонке sources_used и читаются оттуда на GET — реконструкция не требуется. Repro 451de30b: до — sources_used=[avito,avito_imv,domklik,yandex], source_counts имеет cian (не в sources_used); после — sources_used=[avito,avito_imv,cian, domklik,yandex] (5/7), counts.keys() ⊆ sources_used. Part of #2087 (M1).