(medium) asking→sold ratio: в asking-медиану попадают новостройки (нет listing_segment guard в seed-миграциях 080/098) #1364

Closed
opened 2026-06-14 18:09:12 +00:00 by bot-backend · 1 comment
Collaborator

Описание

Asking-сторона (ask_median/ask_global) считается по ВСЕМ активным listings лишь с WHERE is_active AND rooms IS NOT NULL AND price_per_m2 BETWEEN ..., без фильтра по listing_segment. listings.listing_segment может быть 'novostroyki' (avito/cian-скраперы пишут оба сегмента), а sold-сторона — rosreestr ДКП (вторичка). Цель ratio — корректировать ВТОРИЧНЫЙ asking к sold. Подмешивание новостроечного asking искажает ask_median, смещает ratio = sold/asking и каждую expected_sold-цену estimator'а. Миграция 102 устанавливает канонический secondary-предикат listing_segment IS NULL OR listing_segment = 'vtorichka' — деривация ratio его нарушает.

Корень

Отсутствует listing_segment-guard в ask-CTE seed-миграций 080 и 098. Примечание: живой daily-refresh task app/tasks/asking_to_sold_ratio.py уже несёт правильный guard (#1186) и перетирает таблицу ежедневно — поэтому дефект активен только в окне bootstrap → первый успешный refresh, а не постоянно.

Evidence

080_asking_to_sold_ratios.sqlask_side CTE (строки 105-114) и ask_global (150-158) без listing_segment-фильтра:

FROM listings WHERE is_active AND rooms IS NOT NULL AND price_per_m2 BETWEEN 30000 AND 600000

098_asking_to_sold_ratios_tiered.sql — bounds (104-108), ask_tiered (141-158), ask_side_all (209-218), ask_global (251-258) аналогично без guard.

Suggested fix

Добавить AND (listing_segment IS NULL OR listing_segment = 'vtorichka') в каждый ask_side/ask_global/ask_tiered/ask_side_all CTE в 080 и 098, чтобы asking-медиана считалась по той же вторичной вселенной, что и rosreestr-сделки, против которых делится.

Обоснование severity

medium: дефект самокорректируется ежедневным refresh; активен только в окне bootstrap fresh-DB, не постоянный production-дефект. Фикс нужен для консистентности seed/bootstrap. confidence 0.92.


week ревью 1 · multi-agent аудит 2026-06-14 · severity=medium · category=data-integrity

Затронутые файлы:

  • /Users/anton/Птица/gendesign/tradein-mvp/backend/data/sql/080_asking_to_sold_ratios.sql
  • /Users/anton/Птица/gendesign/tradein-mvp/backend/data/sql/098_asking_to_sold_ratios_tiered.sql
  • /Users/anton/Птица/gendesign/tradein-mvp/backend/app/tasks/asking_to_sold_ratio.py
## Описание Asking-сторона (`ask_median`/`ask_global`) считается по ВСЕМ активным listings лишь с `WHERE is_active AND rooms IS NOT NULL AND price_per_m2 BETWEEN ...`, без фильтра по `listing_segment`. `listings.listing_segment` может быть 'novostroyki' (avito/cian-скраперы пишут оба сегмента), а sold-сторона — rosreestr ДКП (вторичка). Цель ratio — корректировать ВТОРИЧНЫЙ asking к sold. Подмешивание новостроечного asking искажает `ask_median`, смещает `ratio = sold/asking` и каждую `expected_sold`-цену estimator'а. Миграция 102 устанавливает канонический secondary-предикат `listing_segment IS NULL OR listing_segment = 'vtorichka'` — деривация ratio его нарушает. ## Корень Отсутствует `listing_segment`-guard в ask-CTE seed-миграций 080 и 098. Примечание: живой daily-refresh task `app/tasks/asking_to_sold_ratio.py` уже несёт правильный guard (#1186) и перетирает таблицу ежедневно — поэтому дефект активен только в окне bootstrap → первый успешный refresh, а не постоянно. ## Evidence `080_asking_to_sold_ratios.sql` — `ask_side` CTE (строки 105-114) и `ask_global` (150-158) без listing_segment-фильтра: ```sql FROM listings WHERE is_active AND rooms IS NOT NULL AND price_per_m2 BETWEEN 30000 AND 600000 ``` `098_asking_to_sold_ratios_tiered.sql` — bounds (104-108), ask_tiered (141-158), ask_side_all (209-218), ask_global (251-258) аналогично без guard. ## Suggested fix Добавить `AND (listing_segment IS NULL OR listing_segment = 'vtorichka')` в каждый ask_side/ask_global/ask_tiered/ask_side_all CTE в 080 и 098, чтобы asking-медиана считалась по той же вторичной вселенной, что и rosreestr-сделки, против которых делится. ## Обоснование severity medium: дефект самокорректируется ежедневным refresh; активен только в окне bootstrap fresh-DB, не постоянный production-дефект. Фикс нужен для консистентности seed/bootstrap. confidence 0.92. --- _week ревью 1 · multi-agent аудит 2026-06-14 · severity=**medium** · category=`data-integrity`_ Затронутые файлы: - `/Users/anton/Птица/gendesign/tradein-mvp/backend/data/sql/080_asking_to_sold_ratios.sql` - `/Users/anton/Птица/gendesign/tradein-mvp/backend/data/sql/098_asking_to_sold_ratios_tiered.sql` - `/Users/anton/Птица/gendesign/tradein-mvp/backend/app/tasks/asking_to_sold_ratio.py`
bot-backend added the
week ревью 1
label 2026-06-14 18:09:12 +00:00
bot-backend added the
scope/backend
label 2026-06-15 11:01:53 +00:00
Owner

🔍 week ревью 1 — верификация (ПОДТВЕРЖДЕНО)

Текущее состояние кода: tradein-mvp/backend/data/sql/080_asking_to_sold_ratios.sqlask_side CTE (:105-114, FROM listings WHERE is_active AND rooms IS NOT NULL AND price_per_m2 BETWEEN 30000 AND 600000) и ask_global (:150-158) — БЕЗ listing_segment-фильтра. 098_asking_to_sold_ratios_tiered.sql — аналогично в :103-105 (bounds), :213-215 (ask_side_all), :254-256 (ask_global) — все FROM listings WHERE is_active без guard. Живой daily-task app/tasks/asking_to_sold_ratio.py НЕСЁТ guard AND (listing_segment IS NULL OR listing_segment = 'vtorichka') (#1186) в :98, :144, :223, :269, :332, :375.

Корень: отсутствует listing_segment-guard в ask-CTE seed-миграций 080/098. listings.listing_segment может быть 'novostroyki' (avito/cian пишут оба сегмента), а sold-сторона — rosreestr ДКП (вторичка). Подмешивание новостроечного asking искажает ask_median, смещает ratio = sold/asking и каждую expected_sold-цену estimator'а. Самокорректируется: daily-refresh task перетирает таблицу ежедневно — дефект активен только в окне bootstrap fresh-DB → первый успешный refresh.

Что править:

  • Добавить AND (listing_segment IS NULL OR listing_segment = 'vtorichka') (зеркалить guard из task'а, та же формулировка #1186) в каждый ask-CTE: 080ask_side (:111-113 после WHERE is_active) и ask_global (:155-157); 098 — bounds-CTE (:105), ask_tiered/ask_side_all (:215), ask_global (:256).
  • NB: 098:151 ask_tiered JOIN'ит listings l через bnd — guard добавить с алиасом l.listing_segment (как в task :269).
  • Файлы 080/098 уже applied через _schema_migrations → изменения в самих файлах НЕ переприменятся на существующих DB. Для консистентности bootstrap править эти файлы; на live-DB поведение уже корректно за счёт daily-task. Если нужно переприменить — отдельная idempotent-миграция с новым NN.

Что посмотреть:

  • Код: 080/098 ask-CTE; asking_to_sold_ratio.py (эталон guard #1186); миграция 102 (канонический secondary-предикат). Тест: seed на fresh-DB с novostroyki-listing → ask_median не должна включать новостройки.
  • Vault: релевантных заметок не найдено.
**🔍 week ревью 1 — верификация (ПОДТВЕРЖДЕНО)** **Текущее состояние кода:** `tradein-mvp/backend/data/sql/080_asking_to_sold_ratios.sql` — `ask_side` CTE (`:105-114`, `FROM listings WHERE is_active AND rooms IS NOT NULL AND price_per_m2 BETWEEN 30000 AND 600000`) и `ask_global` (`:150-158`) — БЕЗ `listing_segment`-фильтра. `098_asking_to_sold_ratios_tiered.sql` — аналогично в `:103-105` (bounds), `:213-215` (ask_side_all), `:254-256` (ask_global) — все `FROM listings WHERE is_active` без guard. Живой daily-task `app/tasks/asking_to_sold_ratio.py` НЕСЁТ guard `AND (listing_segment IS NULL OR listing_segment = 'vtorichka')` (#1186) в `:98, :144, :223, :269, :332, :375`. **Корень:** отсутствует `listing_segment`-guard в ask-CTE seed-миграций 080/098. `listings.listing_segment` может быть 'novostroyki' (avito/cian пишут оба сегмента), а sold-сторона — rosreestr ДКП (вторичка). Подмешивание новостроечного asking искажает `ask_median`, смещает `ratio = sold/asking` и каждую `expected_sold`-цену estimator'а. Самокорректируется: daily-refresh task перетирает таблицу ежедневно — дефект активен только в окне bootstrap fresh-DB → первый успешный refresh. **Что править:** - Добавить `AND (listing_segment IS NULL OR listing_segment = 'vtorichka')` (зеркалить guard из task'а, та же формулировка #1186) в каждый ask-CTE: `080` — `ask_side` (`:111-113` после `WHERE is_active`) и `ask_global` (`:155-157`); `098` — bounds-CTE (`:105`), `ask_tiered`/`ask_side_all` (`:215`), `ask_global` (`:256`). - NB: `098:151` `ask_tiered` JOIN'ит `listings l` через `bnd` — guard добавить с алиасом `l.listing_segment` (как в task `:269`). - Файлы 080/098 уже applied через `_schema_migrations` → изменения в самих файлах НЕ переприменятся на существующих DB. Для консистентности bootstrap править эти файлы; на live-DB поведение уже корректно за счёт daily-task. Если нужно переприменить — отдельная idempotent-миграция с новым NN. **Что посмотреть:** - Код: 080/098 ask-CTE; `asking_to_sold_ratio.py` (эталон guard #1186); миграция 102 (канонический secondary-предикат). Тест: seed на fresh-DB с novostroyki-listing → `ask_median` не должна включать новостройки. - Vault: релевантных заметок не найдено.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1364
No description provided.